summaryrefslogtreecommitdiffstats
path: root/src/server/globals.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/globals.c')
-rw-r--r--src/server/globals.c321
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;
+}
+