summaryrefslogblamecommitdiffstats
path: root/src/server/globals.c
blob: c9b9411b8262ae49d0d9711b24207e2fa97f89a0 (plain) (tree)
1
2
3
4
5
6
7
8
9
                    
                
                          

                   

                     

                         
 
                        
                              
          
                              
                       











                                                  
                                                                     
                                      
           










                                                                    
 
                                                                                                                                   
                                                                                                                                                                         
                                                                                                                                   


                                                                                                                                           
 

                                  

                                                                          

                                      



                                                                                     
 
                                                                                                    
 






                                                                            
                                           
                                                    
                                                
                                                         
                                               
                                                       
                                                 



                                                 

                                                         








                                                                                               

                                                                                                                                     
                                                                                                                                       







                                                                                 
                 






                                                                



                                                                                                   

                                              
















                                                                                                                                             

                                           
                                                          











                                                                               
         




                                                                                          


                                                                                 




































                                                                                                                          
 













                                                                                                    
                                                                                  



























                                                                                                 
                            


                    
                                                                                    
 
                              




                                                                                                       
                           


                    
                                                                         
 
                              




                                                                                                                              
                      


                    
                                                                          
 
                              




                                                                                                                        
                      








                                                           



                                                                       










                                                                   




                                                           
                            
                                     
                           













                                    
#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;
}