summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--conf/server.conf10
-rw-r--r--src/server/globals.c160
-rw-r--r--src/server/globals.h29
-rw-r--r--src/server/image.c7
-rw-r--r--src/server/net.c2
-rw-r--r--src/server/uplink.c4
-rw-r--r--src/serverconfig.h1
7 files changed, 193 insertions, 20 deletions
diff --git a/conf/server.conf b/conf/server.conf
index b751dbe..f748a4a 100644
--- a/conf/server.conf
+++ b/conf/server.conf
@@ -19,6 +19,16 @@ removeMissingImages=true
uplinkTimeout=1250
; timeout in ms for send/recv on connections to clients (using an image on this server)
clientTimeout=15000
+; set this to true to close handles of unused images after some timeout
+closeUnusedFd=false
+; set this to true to load files without the .r[0-9]+ extension too, assuming RID=1
+vmdkLegacyMode=false
+
+[limits]
+maxClients=2000
+maxImages=1000
+maxPayload=9M
+maxReplicationSize=150G
; Log related config
[logging]
diff --git a/src/server/globals.c b/src/server/globals.c
index 50d7885..854698a 100644
--- a/src/server/globals.c
+++ b/src/server/globals.c
@@ -3,29 +3,47 @@
#include "../shared/log.h"
#include <string.h>
#include <stdlib.h>
+#include <inttypes.h>
+#include <limits.h>
char *_configDir = NULL;
-char *_basePath = NULL;
-bool _vmdkLegacyMode = false;
volatile bool _shutdown = false;
+// [dnbd3]
+int _listenPort = PORT;
+char *_basePath = NULL;
int _serverPenalty = 0;
int _clientPenalty = 0;
-bool _removeMissingImages = true;
bool _isProxy = false;
-bool _proxyPrivateOnly = false;
bool _backgroundReplication = true;
bool _lookupMissingForProxy = true;
-int _listenPort = PORT;
+bool _removeMissingImages = true;
int _uplinkTimeout = SOCKET_TIMEOUT_UPLINK;
int _clientTimeout = SOCKET_TIMEOUT_CLIENT;
bool _closeUnusedFd = false;
+bool _vmdkLegacyMode = false;
+// Not really needed anymore since we have '+' and '-' in alt-servers
+bool _proxyPrivateOnly = false;
+// [limits]
+int _maxClients = SERVER_MAX_CLIENTS;
+int _maxImages = SERVER_MAX_IMAGES;
+int _maxPayload = 9000000; // 9MB
+uint64_t _maxReplicationSize = (uint64_t)100000000000LL;
#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 = atoi(value) != 0 || strcmp(value, "true") == 0 || strcmp(value, "True") == 0 || strcmp(value, "TRUE") == 0; } while (0)
-#define SAVE_TO_VAR_INT(ss, kk) do { if (strcmp(section, #ss) == 0 && strcmp(key, #kk) == 0) _ ## kk = atoi(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 handleMaskString( const char *value, void(*func)(logmask_t) );
+static const char* units = "KMGTPEZY";
+
+static bool parse64(const char *in, int64_t *out, const char *optname);
+static bool parse64u(const char *in, uint64_t *out, const char *optname);
+static bool parse32(const char *in, int *out, const char *optname) UNUSED;
+static bool parse32u(const char *in, int *out, const char *optname);
+
static int ini_handler(void *custom UNUSED, const char* section, const char* key, const char* value)
{
if ( _basePath == NULL ) SAVE_TO_VAR_STR( dnbd3, basePath );
@@ -36,11 +54,15 @@ static int ini_handler(void *custom UNUSED, const char* section, const char* key
SAVE_TO_VAR_BOOL( dnbd3, lookupMissingForProxy );
SAVE_TO_VAR_BOOL( dnbd3, removeMissingImages );
SAVE_TO_VAR_BOOL( dnbd3, closeUnusedFd );
- SAVE_TO_VAR_INT( dnbd3, serverPenalty );
- SAVE_TO_VAR_INT( dnbd3, clientPenalty );
- SAVE_TO_VAR_INT( dnbd3, uplinkTimeout );
- SAVE_TO_VAR_INT( dnbd3, clientTimeout );
- SAVE_TO_VAR_INT( dnbd3, listenPort );
+ 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( dnbd3, listenPort );
+ SAVE_TO_VAR_UINT( limits, maxClients );
+ SAVE_TO_VAR_UINT( limits, maxImages );
+ SAVE_TO_VAR_UINT( limits, maxPayload );
+ SAVE_TO_VAR_UINT64( limits, maxReplicationSize );
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, "file" ) == 0 ) {
@@ -82,9 +104,13 @@ void globals_loadConfig()
logadd( LOG_ERROR, "listenPort must be 1-65535, but is %d", _listenPort );
exit( EXIT_FAILURE );
}
- // Silently "fix" invalid values
- if ( _serverPenalty < 0 ) _serverPenalty = 0;
- if ( _clientPenalty < 0 ) _clientPenalty = 0;
+ // Cap to hard limit
+ if ( _maxClients > SERVER_MAX_CLIENTS ) _maxClients = SERVER_MAX_CLIENTS;
+ if ( _maxImages > SERVER_MAX_IMAGES ) _maxImages = SERVER_MAX_IMAGES;
+ // Dump config as interpreted
+ char buffer[2000];
+ globals_dumpConfig( buffer, sizeof(buffer) );
+ logadd( LOG_DEBUG1, "Effective configuration:\n%s", buffer );
}
#define SETLOGBIT(name) do { if ( strstr( value, #name ) != NULL ) mask |= LOG_ ## name; } while (0)
@@ -100,3 +126,109 @@ static void handleMaskString( const char *value, void(*func)(logmask_t) )
(*func)( mask );
}
+static bool parse64(const char *in, int64_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, uint64_t *out, const char *optname)
+{
+ int64_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, int *out, const char *optname)
+{
+ int64_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, int *out, const char *optname)
+{
+ int64_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) P_ARG(#var "=%" type "\n", _ ## var)
+#define PINT(var) PVAR(var, "d")
+#define PUINT64(var) PVAR(var, PRIu64)
+#define PSTR(var) PVAR(var, "s")
+#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);
+ PBOOL(backgroundReplication);
+ PBOOL(lookupMissingForProxy);
+ 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;
+}
+
diff --git a/src/server/globals.h b/src/server/globals.h
index 1475981..c1d5d78 100644
--- a/src/server/globals.h
+++ b/src/server/globals.h
@@ -219,8 +219,37 @@ extern bool _lookupMissingForProxy;
extern int _listenPort;
/**
+ * Max number of DNBD3 clients we accept
+ */
+extern int _maxClients;
+
+/**
+ * Max number of Images we support (in baseDir)
+ */
+extern int _maxImages;
+
+/**
+ * Maximum payload length we accept on uplinks and thus indirectly
+ * from clients in case the requested range is not cached locally.
+ * Usually this isn't even a megabyte for "real" clients (blockdev
+ * or fuse).
+ */
+extern int _maxPayload;
+
+/**
+ * If in proxy mode, don't replicate images that are
+ * larger than this according to the uplink server.
+ */
+extern uint64_t _maxReplicationSize;
+
+/**
* Load the server configuration.
*/
void globals_loadConfig();
+/**
+ * Dump the effective configuration in use to given buffer.
+ */
+size_t globals_dumpConfig(char *buffer, size_t size);
+
#endif /* GLOBALS_H_ */
diff --git a/src/server/image.c b/src/server/image.c
index 45463a0..ca00c63 100644
--- a/src/server/image.c
+++ b/src/server/image.c
@@ -762,7 +762,7 @@ static bool image_addToList(dnbd3_image_t *image)
break;
}
if ( i >= _num_images ) {
- if ( _num_images >= SERVER_MAX_IMAGES ) {
+ if ( _num_images >= _maxImages ) {
spin_unlock( &imageListLock );
return false;
}
@@ -1270,7 +1270,10 @@ static dnbd3_image_t *loadImageProxy(char * const name, const uint16_t revision,
if ( revision != 0 && remoteRid != revision ) goto server_fail; // Want specific revision but uplink supplied different rid
if ( revision == 0 && image != NULL && image->rid >= remoteRid ) goto server_fail; // Not actually a failure: Highest remote rid is <= highest local rid - don't clone!
if ( remoteImageSize < DNBD3_BLOCK_SIZE || remoteName == NULL || strcmp( name, remoteName ) != 0 ) goto server_fail;
- if ( remoteImageSize > SERVER_MAX_PROXY_IMAGE_SIZE ) goto server_fail;
+ if ( remoteImageSize > _maxReplicationSize ) {
+ logadd( LOG_MINOR, "Won't proxy '%s:%d': Larger than maxReplicationSize", name, (int)revision );
+ goto server_fail;
+ }
pthread_mutex_lock( &reloadLock );
ok = image_ensureDiskSpace( remoteImageSize )
&& image_clone( sock, name, remoteRid, remoteImageSize ); // This sets up the file+map+crc and loads the img
diff --git a/src/server/net.c b/src/server/net.c
index e77da45..86b0a84 100644
--- a/src/server/net.c
+++ b/src/server/net.c
@@ -726,7 +726,7 @@ static bool addToList(dnbd3_client_t *client)
spin_unlock( &_clients_lock );
return true;
}
- if ( _num_clients >= SERVER_MAX_CLIENTS ) {
+ if ( _num_clients >= _maxClients ) {
spin_unlock( &_clients_lock );
logadd( LOG_ERROR, "Maximum number of clients reached!" );
return false;
diff --git a/src/server/uplink.c b/src/server/uplink.c
index de53657..bed7b54 100644
--- a/src/server/uplink.c
+++ b/src/server/uplink.c
@@ -564,13 +564,13 @@ static void uplink_handleReceive(dnbd3_connection_t *link)
logadd( LOG_INFO, "Uplink: Connection error %d (%s)", ret, link->image->path );
goto error_cleanup;
}
- if ( inReply.size > 9000000 ) { // TODO: Configurable
+ if ( inReply.size > (uint32_t)_maxPayload ) {
logadd( LOG_WARNING, "Pure evil: Uplink server sent too much payload for %s", link->image->path );
goto error_cleanup;
}
if ( link->recvBufferLen < inReply.size ) {
- link->recvBufferLen = MIN(9000000, inReply.size + 65536); // XXX dont miss occurrence
+ link->recvBufferLen = MIN((uint32_t)_maxPayload, inReply.size + 65536);
link->recvBuffer = realloc( link->recvBuffer, link->recvBufferLen );
}
if ( (uint32_t)sock_recv( link->fd, link->recvBuffer, inReply.size ) != inReply.size ) {
diff --git a/src/serverconfig.h b/src/serverconfig.h
index 5e5afd9..677acce 100644
--- a/src/serverconfig.h
+++ b/src/serverconfig.h
@@ -33,7 +33,6 @@
#define SERVER_RTT_DELAY_FAILED 180
#define SERVER_REMOTE_IMAGE_CHECK_CACHETIME 120 // 2 minutes
-#define SERVER_MAX_PROXY_IMAGE_SIZE 100000000000LL // 100GB
// Which is the minimum protocol version the server expects from the client
#define MIN_SUPPORTED_CLIENT 2