diff options
Diffstat (limited to 'src/server/globals.c')
-rw-r--r-- | src/server/globals.c | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/src/server/globals.c b/src/server/globals.c new file mode 100644 index 0000000..c9b9411 --- /dev/null +++ b/src/server/globals.c @@ -0,0 +1,321 @@ +#include "globals.h" +#include "ini.h" +#include "../shared/log.h" +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <limits.h> +#include <sys/resource.h> +#include <errno.h> + +char *_configDir = NULL; +atomic_bool _shutdown = false; +// [dnbd3] +atomic_int _listenPort = PORT; +char *_basePath = NULL; +atomic_int _serverPenalty = 0; +atomic_int _clientPenalty = 0; +atomic_bool _isProxy = false; +atomic_int _backgroundReplication = BGR_FULL; +atomic_int _bgrMinClients = 0; +atomic_bool _lookupMissingForProxy = true; +atomic_bool _sparseFiles = false; +atomic_bool _removeMissingImages = true; +atomic_int _uplinkTimeout = SOCKET_TIMEOUT_UPLINK; +atomic_int _clientTimeout = SOCKET_TIMEOUT_CLIENT; +atomic_bool _closeUnusedFd = false; +atomic_bool _vmdkLegacyMode = false; +// Not really needed anymore since we have '+' and '-' in alt-servers +atomic_bool _proxyPrivateOnly = false; +// [limits] +atomic_int _maxClients = SERVER_MAX_CLIENTS; +atomic_int _maxImages = SERVER_MAX_IMAGES; +atomic_int _maxPayload = 9000000; // 9MB +atomic_uint_fast64_t _maxReplicationSize = (uint64_t)100000000000LL; + +/** + * True when loading config the first time. Consecutive loads will + * ignore certain values which cannot be changed safely at runtime. + */ +static atomic_bool initialLoad = true; +static pthread_mutex_t loadLock = PTHREAD_MUTEX_INITIALIZER; + +#define IS_TRUE(value) (atoi(value) != 0 || strcmp(value, "true") == 0 || strcmp(value, "True") == 0 || strcmp(value, "TRUE") == 0) +#define SAVE_TO_VAR_STR(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) { if (_ ## kk != NULL) free(_ ## kk); _ ## kk = strdup(value); } } while (0) +#define SAVE_TO_VAR_BOOL(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) _ ## kk = IS_TRUE(value); } while (0) +#define SAVE_TO_VAR_INT(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) parse32(value, &_ ## kk, #ss); } while (0) +#define SAVE_TO_VAR_UINT(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) parse32u(value, &_ ## kk, #ss); } while (0) +#define SAVE_TO_VAR_UINT64(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) parse64u(value, &_ ## kk, #ss); } while (0) + +static void sanitizeFixedConfig(); + +static void handleMaskString( const char *value, void(*func)(logmask_t) ); + +static const char* units = "KMGTPEZY"; + +static bool parse64(const char *in, atomic_int_fast64_t *out, const char *optname); +static bool parse64u(const char *in, atomic_uint_fast64_t *out, const char *optname); +static bool parse32(const char *in, atomic_int *out, const char *optname) UNUSED; +static bool parse32u(const char *in, atomic_int *out, const char *optname); + +static int ini_handler(void *custom UNUSED, const char* section, const char* key, const char* value) +{ + if ( initialLoad ) { + if ( _basePath == NULL ) SAVE_TO_VAR_STR( dnbd3, basePath ); + SAVE_TO_VAR_BOOL( dnbd3, vmdkLegacyMode ); + SAVE_TO_VAR_UINT( dnbd3, listenPort ); + SAVE_TO_VAR_UINT( limits, maxClients ); + SAVE_TO_VAR_UINT( limits, maxImages ); + } + SAVE_TO_VAR_BOOL( dnbd3, isProxy ); + SAVE_TO_VAR_BOOL( dnbd3, proxyPrivateOnly ); + SAVE_TO_VAR_INT( dnbd3, bgrMinClients ); + SAVE_TO_VAR_BOOL( dnbd3, lookupMissingForProxy ); + SAVE_TO_VAR_BOOL( dnbd3, sparseFiles ); + SAVE_TO_VAR_BOOL( dnbd3, removeMissingImages ); + SAVE_TO_VAR_BOOL( dnbd3, closeUnusedFd ); + SAVE_TO_VAR_UINT( dnbd3, serverPenalty ); + SAVE_TO_VAR_UINT( dnbd3, clientPenalty ); + SAVE_TO_VAR_UINT( dnbd3, uplinkTimeout ); + SAVE_TO_VAR_UINT( dnbd3, clientTimeout ); + SAVE_TO_VAR_UINT( limits, maxPayload ); + SAVE_TO_VAR_UINT64( limits, maxReplicationSize ); + if ( strcmp( section, "dnbd3" ) == 0 && strcmp( key, "backgroundReplication" ) == 0 ) { + if ( strcmp( value, "hashblock" ) == 0 ) { + _backgroundReplication = BGR_HASHBLOCK; + } else if ( IS_TRUE( value ) ) { + _backgroundReplication = BGR_FULL; + } else { + _backgroundReplication = BGR_DISABLED; + } + } + if ( strcmp( section, "logging" ) == 0 && strcmp( key, "fileMask" ) == 0 ) handleMaskString( value, &log_setFileMask ); + if ( strcmp( section, "logging" ) == 0 && strcmp( key, "consoleMask" ) == 0 ) handleMaskString( value, &log_setConsoleMask ); + if ( strcmp( section, "logging" ) == 0 && strcmp( key, "consoleTimestamps" ) == 0 ) log_setConsoleTimestamps( IS_TRUE(value) ); + if ( strcmp( section, "logging" ) == 0 && strcmp( key, "file" ) == 0 ) { + if ( log_openLogFile( value ) ) { + logadd( LOG_INFO, "Opened log file %s", value ); + } else { + logadd( LOG_ERROR, "Could not open log file %s", value ); + exit( EXIT_FAILURE ); + } + } + return 1; +} + +void globals_loadConfig() +{ + char *name = NULL; + asprintf( &name, "%s/%s", _configDir, CONFIG_FILENAME ); + if ( name == NULL ) return; + if ( pthread_mutex_trylock( &loadLock ) != 0 ) { + logadd( LOG_INFO, "Ignoring config reload request due to already running reload" ); + return; + } + ini_parse( name, &ini_handler, NULL ); + free( name ); + if ( initialLoad ) { + sanitizeFixedConfig(); + } + if ( _backgroundReplication == BGR_FULL && _sparseFiles && _bgrMinClients < 5 ) { + logadd( LOG_WARNING, "Ignoring 'sparseFiles=true' since backgroundReplication is set to true and bgrMinClients is too low" ); + _sparseFiles = false; + } + // Dump config as interpreted + char buffer[2000]; + globals_dumpConfig( buffer, sizeof(buffer) ); + logadd( LOG_DEBUG1, "Effective configuration:\n%s", buffer ); + initialLoad = false; + pthread_mutex_unlock( &loadLock ); +} + +static void sanitizeFixedConfig() +{ + // Validate settings after loading: + // base path for images valid? + if ( _basePath == NULL || _basePath[0] == '\0' ) { + logadd( LOG_WARNING, "No/empty basePath in " CONFIG_FILENAME ); + free( _basePath ); + _basePath = NULL; + } else if ( _basePath[0] != '/' ) { + logadd( LOG_WARNING, "basePath must be absolute!" ); + free( _basePath ); + _basePath = NULL; + } else { + char *end = _basePath + strlen( _basePath ) - 1; + while ( end >= _basePath && *end == '/' ) { + *end-- = '\0'; + } + } + // listen port + if ( _listenPort < 1 || _listenPort > 65535 ) { + logadd( LOG_ERROR, "listenPort must be 1-65535, but is %d", _listenPort ); + exit( EXIT_FAILURE ); + } + // Cap to hard limit + if ( _maxClients > SERVER_MAX_CLIENTS ) _maxClients = SERVER_MAX_CLIENTS; + if ( _maxImages > SERVER_MAX_IMAGES ) _maxImages = SERVER_MAX_IMAGES; + // Consider rlimits + struct rlimit limit; + if ( getrlimit( RLIMIT_NOFILE, &limit ) != 0 ) { + logadd( LOG_DEBUG1, "getrlimit failed, errno %d", errno ); + } else { + const rlim_t required = (rlim_t)( _maxClients + _maxImages * ( _isProxy ? 2 : 1 ) + 50 ); + if ( limit.rlim_cur != RLIM_INFINITY && limit.rlim_cur < required ) { + rlim_t current = limit.rlim_cur; + if ( required <= limit.rlim_max || limit.rlim_max == RLIM_INFINITY ) { + limit.rlim_cur = required; + } else { + limit.rlim_cur = limit.rlim_max; + } + if ( current != limit.rlim_cur && setrlimit( RLIMIT_NOFILE, &limit ) == 0 ) { + current = limit.rlim_cur; + logadd( LOG_INFO, "LIMIT_NOFILE (ulimit -n) soft limit increased to %d", (int)current ); + } + if ( current < required ) { + logadd( LOG_WARNING, "This process can only have %d open file handles," + " which is not enough for the selected maxClients and maxImages counts." + " Consider increasing the limit to at least %d (RLIMIT_NOFILE, ulimit -n)" + " to support the current configuration. maxClients and maxImages have" + " been lowered for this session.", (int)current, (int)required ); + do { + if ( _maxClients > 500 && _maxImages > 150 ) { + _maxImages -= _maxImages / 20 + 1; + _maxClients -= _maxClients / 20 + 1; + } else if ( _maxImages > 100 ) { + _maxImages -= _maxImages / 20 + 1; + if ( _maxClients > 200 ) _maxClients -= _maxClients / 25 + 1; + } else { + break; + } + } while ( (rlim_t)( _maxClients + _maxImages * ( _isProxy ? 2 : 1 ) + 50 ) > current ); + } + } + } +} + +#define SETLOGBIT(name) do { if ( strstr( value, #name ) != NULL ) mask |= LOG_ ## name; } while (0) +static void handleMaskString( const char *value, void(*func)(logmask_t) ) +{ + logmask_t mask = 0; + SETLOGBIT( ERROR ); + SETLOGBIT( WARNING ); + SETLOGBIT( MINOR ); + SETLOGBIT( INFO ); + SETLOGBIT( DEBUG1 ); + SETLOGBIT( DEBUG2 ); + (*func)( mask ); +} + +static bool parse64(const char *in, atomic_int_fast64_t *out, const char *optname) +{ + if ( *in == '\0' ) { + logadd( LOG_WARNING, "Ignoring empty numeric setting '%s'", optname ); + return false; + } + char *end; + long long int num = strtoll( in, &end, 10 ); + if ( end == in ) { + logadd( LOG_WARNING, "Ignoring value '%s' for '%s': Not a number", in, optname ); + return false; + } + int exp, base = 1024; + while ( *end == ' ' ) end++; + if ( *end == '\0' ) { + exp = 0; + } else { + char *pos = strchr( units, *end > 'Z' ? (*end - 32) : *end ); + if ( pos == NULL ) { + logadd( LOG_ERROR, "Invalid unit '%s' for '%s'", end, optname ); + return false; + } + exp = (int)( pos - units ) + 1; + end++; + if ( *end == 'B' || *end == 'b' ) { + base = 1000; + } + } + while ( exp-- > 0 ) num *= base; + *out = (int64_t)num; + return true; +} + +static bool parse64u(const char *in, atomic_uint_fast64_t *out, const char *optname) +{ + atomic_int_fast64_t v; + if ( !parse64( in, &v, optname ) ) return false; + if ( v < 0 ) { + logadd( LOG_WARNING, "Ignoring value '%s' for '%s': Cannot be negative", in, optname ); + return false; + } + *out = (uint64_t)v; + return true; +} + +static bool parse32(const char *in, atomic_int *out, const char *optname) +{ + atomic_int_fast64_t v; + if ( !parse64( in, &v, optname ) ) return false; + if ( v < INT_MIN || v > INT_MAX ) { + logadd( LOG_WARNING, "'%s' must be between %d and %d, but is '%s'", optname, (int)INT_MIN, (int)INT_MAX, in ); + return false; + } + *out = (int)v; + return true; +} + +static bool parse32u(const char *in, atomic_int *out, const char *optname) +{ + atomic_int_fast64_t v; + if ( !parse64( in, &v, optname ) ) return false; + if ( v < 0 || v > INT_MAX ) { + logadd( LOG_WARNING, "'%s' must be between %d and %d, but is '%s'", optname, (int)0, (int)INT_MAX, in ); + return false; + } + *out = (int)v; + return true; +} + +#define P_ARG(...) do { \ + int r = snprintf(buffer, rem, __VA_ARGS__); \ + if ( r < 0 || (size_t)r >= rem ) return size - 1; \ + rem -= r; \ + buffer += r; \ +} while (0) +#define PVAR(var,type,cast) P_ARG(#var "=%" type "\n", (cast) _ ## var) +#define PINT(var) PVAR(var, "d", int) +#define PUINT64(var) PVAR(var, PRIu64, uint64_t) +#define PSTR(var) PVAR(var, "s", const char*) +#define PBOOL(var) P_ARG(#var "=%s\n", _ ## var ? "true" : "false") + +size_t globals_dumpConfig(char *buffer, size_t size) +{ + size_t rem = size; + P_ARG("[dnbd3]\n"); + PINT(listenPort); + PSTR(basePath); + PINT(serverPenalty); + PINT(clientPenalty); + PBOOL(isProxy); + if ( _backgroundReplication == BGR_HASHBLOCK ) { + P_ARG("backgroundReplication=hashblock\n"); + } else { + PBOOL(backgroundReplication); + } + PINT(bgrMinClients); + PBOOL(lookupMissingForProxy); + PBOOL(sparseFiles); + PBOOL(removeMissingImages); + PINT(uplinkTimeout); + PINT(clientTimeout); + PBOOL(closeUnusedFd); + PBOOL(vmdkLegacyMode); + PBOOL(proxyPrivateOnly); + P_ARG("[limits]\n"); + PINT(maxClients); + PINT(maxImages); + PINT(maxPayload); + PUINT64(maxReplicationSize); + return size - rem; +} + |