summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2013-05-15 11:58:21 +0200
committerantirez <antirez@gmail.com>2013-05-15 11:58:21 +0200
commit92f18d04a04918651825e34caa7372c6790aff7e (patch)
tree2b7d421f4d728a16ee9e90a22949a9ccbf85b212
parent25e049cc6859d154ece478246f7e6703b4e57cdb (diff)
parent78167807beab6f34967a7310a93211a1efbbcff9 (diff)
downloadredis-92f18d04a04918651825e34caa7372c6790aff7e.tar.gz
Merge branch 'config-rewrite' into unstable
-rw-r--r--redis.conf4
-rw-r--r--src/config.c681
-rw-r--r--src/redis.c137
-rw-r--r--src/redis.h26
-rw-r--r--tests/assets/default.conf2
5 files changed, 781 insertions, 69 deletions
diff --git a/redis.conf b/redis.conf
index fc2e927ae..5a88c3116 100644
--- a/redis.conf
+++ b/redis.conf
@@ -63,10 +63,10 @@ tcp-keepalive 0
# warning (only very important / critical messages are logged)
loglevel notice
-# Specify the log file name. Also 'stdout' can be used to force
+# Specify the log file name. Also the emptry string can be used to force
# Redis to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
-logfile stdout
+logfile ""
# To enable logging to the system logger, just set 'syslog-enabled' to yes,
# and optionally update the other syslog parameters to suit your needs.
diff --git a/src/config.c b/src/config.c
index 27c3f48c8..c41d57184 100644
--- a/src/config.c
+++ b/src/config.c
@@ -28,9 +28,33 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-
#include "redis.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+
+static struct {
+ const char *name;
+ const int value;
+} validSyslogFacilities[] = {
+ {"user", LOG_USER},
+ {"local0", LOG_LOCAL0},
+ {"local1", LOG_LOCAL1},
+ {"local2", LOG_LOCAL2},
+ {"local3", LOG_LOCAL3},
+ {"local4", LOG_LOCAL4},
+ {"local5", LOG_LOCAL5},
+ {"local6", LOG_LOCAL6},
+ {"local7", LOG_LOCAL7},
+ {NULL, 0}
+};
+
+clientBufferLimitsConfig clientBufferLimitsDefaults[REDIS_CLIENT_LIMIT_NUM_CLASSES] = {
+ {0, 0, 0}, /* normal */
+ {1024*1024*256, 1024*1024*64, 60}, /* slave */
+ {1024*1024*32, 1024*1024*8, 60} /* pubsub */
+};
+
/*-----------------------------------------------------------------------------
* Config file parsing
*----------------------------------------------------------------------------*/
@@ -140,12 +164,9 @@ void loadServerConfigFromString(char *config) {
} else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
FILE *logfp;
+ zfree(server.logfile);
server.logfile = zstrdup(argv[1]);
- if (!strcasecmp(server.logfile,"stdout")) {
- zfree(server.logfile);
- server.logfile = NULL;
- }
- if (server.logfile) {
+ if (server.logfile[0] != '\0') {
/* Test if we are able to open the file. The server will not
* be able to abort just for this problem later... */
logfp = fopen(server.logfile,"a");
@@ -164,21 +185,6 @@ void loadServerConfigFromString(char *config) {
if (server.syslog_ident) zfree(server.syslog_ident);
server.syslog_ident = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) {
- struct {
- const char *name;
- const int value;
- } validSyslogFacilities[] = {
- {"user", LOG_USER},
- {"local0", LOG_LOCAL0},
- {"local1", LOG_LOCAL1},
- {"local2", LOG_LOCAL2},
- {"local3", LOG_LOCAL3},
- {"local4", LOG_LOCAL4},
- {"local5", LOG_LOCAL5},
- {"local6", LOG_LOCAL6},
- {"local7", LOG_LOCAL7},
- {NULL, 0}
- };
int i;
for (i = 0; validSyslogFacilities[i].name; i++) {
@@ -250,7 +256,7 @@ void loadServerConfigFromString(char *config) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"repl-backlog-size") && argc == 2) {
- long long size = strtoll(argv[1],NULL,10);
+ long long size = memtoll(argv[1],NULL);
if (size <= 0) {
err = "repl-backlog-size must be 1 or greater.";
goto loaderr;
@@ -513,7 +519,7 @@ void loadServerConfig(char *filename, char *options) {
}
/*-----------------------------------------------------------------------------
- * CONFIG command for remote configuration
+ * CONFIG SET implementation
*----------------------------------------------------------------------------*/
void configSetCommand(redisClient *c) {
@@ -813,6 +819,10 @@ badfmt: /* Bad format errors */
(char*)c->argv[2]->ptr);
}
+/*-----------------------------------------------------------------------------
+ * CONFIG GET implementation
+ *----------------------------------------------------------------------------*/
+
#define config_get_string_field(_name,_var) do { \
if (stringmatch(pattern,_name,0)) { \
addReplyBulkCString(c,_name); \
@@ -1038,6 +1048,620 @@ void configGetCommand(redisClient *c) {
setDeferredMultiBulkLength(c,replylen,matches*2);
}
+/*-----------------------------------------------------------------------------
+ * CONFIG REWRITE implementation
+ *----------------------------------------------------------------------------*/
+
+/* IGNORE:
+ *
+ * rename-command
+ * include
+ *
+ * Special handling:
+ *
+ * notify-keyspace-events
+ * client-output-buffer-limit
+ * save
+ * appendonly
+ * appendfsync
+ * dir
+ * maxmemory-policy
+ * loglevel
+ * unixsocketperm
+ * slaveof
+ *
+ * Type of config directives:
+ *
+ * CUSTOM
+ * VERBATIM
+ * YESNO
+ * L
+ * LL
+ *
+ */
+
+/* We use the following dictionary type to store where a configuration
+ * option is mentioned in the old configuration file, so it's
+ * like "maxmemory" -> list of line numbers (first line is zero). */
+unsigned int dictSdsHash(const void *key);
+int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);
+void dictSdsDestructor(void *privdata, void *val);
+void dictListDestructor(void *privdata, void *val);
+
+dictType optionToLineDictType = {
+ dictSdsHash, /* hash function */
+ NULL, /* key dup */
+ NULL, /* val dup */
+ dictSdsKeyCompare, /* key compare */
+ dictSdsDestructor, /* key destructor */
+ dictListDestructor /* val destructor */
+};
+
+/* The config rewrite state. */
+struct rewriteConfigState {
+ dict *option_to_line; /* Option -> list of config file lines map */
+ int numlines; /* Number of lines in current config */
+ sds *lines; /* Current lines as an array of sds strings */
+ int has_tail; /* True if we already added directives that were
+ not present in the original config file. */
+};
+
+/* Append the new line to the current configuration state. */
+void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) {
+ state->lines = zrealloc(state->lines, sizeof(char*) * (state->numlines+1));
+ state->lines[state->numlines++] = line;
+}
+
+/* Populate the option -> list of line numbers map. */
+void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) {
+ list *l = dictFetchValue(state->option_to_line,option);
+
+ if (l == NULL) {
+ l = listCreate();
+ dictAdd(state->option_to_line,sdsdup(option),l);
+ }
+ listAddNodeTail(l,(void*)(long)linenum);
+}
+
+/* Read the old file, split it into lines to populate a newly created
+ * config rewrite state, and return it to the caller.
+ *
+ * If it is impossible to read the old file, NULL is returned.
+ * If the old file does not exist at all, an empty state is returned. */
+struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
+ FILE *fp = fopen(path,"r");
+ struct rewriteConfigState *state = zmalloc(sizeof(*state));
+ char buf[REDIS_CONFIGLINE_MAX+1];
+ int linenum = -1;
+
+ if (fp == NULL && errno != ENOENT) return NULL;
+
+ state->option_to_line = dictCreate(&optionToLineDictType,NULL);
+ state->numlines = 0;
+ state->lines = NULL;
+ state->has_tail = 0;
+ if (fp == NULL) return state;
+
+ /* Read the old file line by line, populate the state. */
+ while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) {
+ int argc;
+ sds *argv;
+ sds line = sdstrim(sdsnew(buf),"\r\n\t ");
+
+ linenum++; /* Zero based, so we init at -1 */
+
+ /* Handle comments and empty lines. */
+ if (line[0] == '#' || line[0] == '\0') {
+ rewriteConfigAppendLine(state,line);
+ continue;
+ }
+
+ /* Not a comment, split into arguments. */
+ argv = sdssplitargs(line,&argc);
+ if (argv == NULL) {
+ /* Apparently the line is unparsable for some reason, for
+ * instance it may have unbalanced quotes. Load it as a
+ * comment. */
+ sds aux = sdsnew("# ??? ");
+ aux = sdscatsds(aux,line);
+ sdsfree(line);
+ rewriteConfigAppendLine(state,aux);
+ continue;
+ }
+
+ sdstolower(argv[0]); /* We only want lowercase config directives. */
+
+ /* Now we populate the state according to the content of this line.
+ * Append the line and populate the option -> line numbers map. */
+ rewriteConfigAppendLine(state,line);
+ rewriteConfigAddLineNumberToOption(state,argv[0],linenum);
+
+ sdsfreesplitres(argv,argc);
+ }
+ fclose(fp);
+ return state;
+}
+
+/* Rewrite the specified configuration option with the new "line".
+ * It progressively uses lines of the file that were already used for the same
+ * configuraiton option in the old version of the file, removing that line from
+ * the map of options -> line numbers.
+ *
+ * If there are lines associated with a given configuration option and
+ * "force" is non-zero, the line is appended to the configuration file.
+ * Usually "force" is true when an option has not its default value, so it
+ * must be rewritten even if not present previously.
+ *
+ * The first time a line is appended into a configuration file, a comment
+ * is added to show that starting from that point the config file was generated
+ * by CONFIG REWRITE.
+ *
+ * "line" is either used, or freed, so the caller does not need to free it
+ * in any way. */
+void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force) {
+ sds o = sdsnew(option);
+ list *l = dictFetchValue(state->option_to_line,o);
+
+ if (!l && !force) {
+ /* Option not used previously, and we are not forced to use it. */
+ sdsfree(line);
+ sdsfree(o);
+ return;
+ }
+
+ if (l) {
+ listNode *ln = listFirst(l);
+ int linenum = (long) ln->value;
+
+ /* There are still lines in the old configuration file we can reuse
+ * for this option. Replace the line with the new one. */
+ listDelNode(l,ln);
+ if (listLength(l) == 0) dictDelete(state->option_to_line,o);
+ sdsfree(state->lines[linenum]);
+ state->lines[linenum] = line;
+ } else {
+ /* Append a new line. */
+ if (!state->has_tail) {
+ rewriteConfigAppendLine(state,
+ sdsnew("# Generated by CONFIG REWRITE"));
+ state->has_tail = 1;
+ }
+ rewriteConfigAppendLine(state,line);
+ }
+ sdsfree(o);
+}
+
+/* Write the long long 'bytes' value as a string in a way that is parsable
+ * inside redis.conf. If possible uses the GB, MB, KB notation. */
+int rewriteConfigFormatMemory(char *buf, size_t len, long long bytes) {
+ int gb = 1024*1024*1024;
+ int mb = 1024*1024;
+ int kb = 1024;
+
+ if (bytes && (bytes % gb) == 0) {
+ return snprintf(buf,len,"%lldgb",bytes/gb);
+ } else if (bytes && (bytes % mb) == 0) {
+ return snprintf(buf,len,"%lldmb",bytes/mb);
+ } else if (bytes && (bytes % kb) == 0) {
+ return snprintf(buf,len,"%lldkb",bytes/kb);
+ } else {
+ return snprintf(buf,len,"%lld",bytes);
+ }
+}
+
+/* Rewrite a simple "option-name <bytes>" configuration option. */
+void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
+ char buf[64];
+ int force = value != defvalue;
+ sds line;
+
+ rewriteConfigFormatMemory(buf,sizeof(buf),value);
+ line = sdscatprintf(sdsempty(),"%s %s",option,buf);
+ rewriteConfigRewriteLine(state,option,line,force);
+
+}
+
+/* Rewrite a yes/no option. */
+void rewriteConfigYesNoOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
+ int force = value != defvalue;
+ sds line = sdscatprintf(sdsempty(),"%s %s",option,
+ value ? "yes" : "no");
+
+ rewriteConfigRewriteLine(state,option,line,force);
+}
+
+/* Rewrite a string option. */
+void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, char *value, char *defvalue) {
+ int force = 1;
+ sds line;
+
+ /* String options set to NULL need to be not present at all in the
+ * configuration file to be set to NULL again at the next reboot. */
+ if (value == NULL) return;
+
+ /* Compare the strings as sds strings to have a binary safe comparison. */
+ if (defvalue && strcmp(value,defvalue) == 0) force = 0;
+
+ line = sdsnew(option);
+ line = sdscatlen(line, " ", 1);
+ line = sdscatrepr(line, value, strlen(value));
+
+ rewriteConfigRewriteLine(state,option,line,force);
+}
+
+/* Rewrite a numerical (long long range) option. */
+void rewriteConfigNumericalOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
+ int force = value != defvalue;
+ sds line = sdscatprintf(sdsempty(),"%s %lld",option,value);
+
+ rewriteConfigRewriteLine(state,option,line,force);
+}
+
+/* Rewrite a octal option. */
+void rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
+ int force = value != defvalue;
+ sds line = sdscatprintf(sdsempty(),"%s %o",option,value);
+
+ rewriteConfigRewriteLine(state,option,line,force);
+}
+
+/* Rewrite an enumeration option, after the "value" every enum/value pair
+ * is specified, terminated by NULL. After NULL the default value is
+ * specified. See how the function is used for more information. */
+void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int value, ...) {
+ va_list ap;
+ char *enum_name, *matching_name;
+ int enum_val, def_val, force;
+ sds line;
+
+ va_start(ap, value);
+ while(1) {
+ enum_name = va_arg(ap,char*);
+ enum_val = va_arg(ap,int);
+ if (enum_name == NULL) {
+ def_val = enum_val;
+ break;
+ }
+ if (value == enum_val) matching_name = enum_name;
+ }
+ va_end(ap);
+
+ force = value != def_val;
+ line = sdscatprintf(sdsempty(),"%s %s",option,matching_name);
+ rewriteConfigRewriteLine(state,option,line,force);
+}
+
+/* Rewrite the syslog-fability option. */
+void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) {
+ int value = server.syslog_facility, j;
+ int force = value != LOG_LOCAL0;
+ char *name, *option = "syslog-facility";
+ sds line;
+
+ for (j = 0; validSyslogFacilities[j].name; j++) {
+ if (validSyslogFacilities[j].value == value) {
+ name = (char*) validSyslogFacilities[j].name;
+ break;
+ }
+ }
+ line = sdscatprintf(sdsempty(),"%s %s",option,name);
+ rewriteConfigRewriteLine(state,option,line,force);
+}
+
+/* Rewrite the save option. */
+void rewriteConfigSaveOption(struct rewriteConfigState *state) {
+ int j;
+ sds line;
+
+ /* Note that if there are no save parameters at all, all the current
+ * config line with "save" will be detected as orphaned and deleted,
+ * resulting into no RDB persistence as expected. */
+ for (j = 0; j < server.saveparamslen; j++) {
+ line = sdscatprintf(sdsempty(),"save %ld %d",
+ server.saveparams[j].seconds, server.saveparams[j].changes);
+ rewriteConfigRewriteLine(state,"save",line,1);
+ }
+}
+
+/* Rewrite the dir option, always using absolute paths.*/
+void rewriteConfigDirOption(struct rewriteConfigState *state) {
+ char cwd[1024];
+
+ if (getcwd(cwd,sizeof(cwd)) == NULL) return; /* no rewrite on error. */
+ rewriteConfigStringOption(state,"dir",cwd,NULL);
+}
+
+/* Rewrite the slaveof option. */
+void rewriteConfigSlaveofOption(struct rewriteConfigState *state) {
+ char *option = "slaveof";
+ sds line;
+
+ /* If this is a master, we want all the slaveof config options
+ * in the file to be removed. */
+ if (server.masterhost == NULL) return;
+ line = sdscatprintf(sdsempty(),"%s %s %d", option,
+ server.masterhost, server.masterport);
+ rewriteConfigRewriteLine(state,option,line,1);
+}
+
+/* Rewrite the appendonly option. */
+void rewriteConfigAppendonlyOption(struct rewriteConfigState *state) {
+ int force = server.aof_state != REDIS_AOF_OFF;
+ char *option = "appendonly";
+ sds line;
+
+ line = sdscatprintf(sdsempty(),"%s %s", option,
+ (server.aof_state == REDIS_AOF_OFF) ? "no" : "yes");
+ rewriteConfigRewriteLine(state,option,line,force);
+}
+
+/* Rewrite the notify-keyspace-events option. */
+void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) {
+ int force = server.notify_keyspace_events != 0;
+ char *option = "notify-keyspace-events";
+ sds line, flags;
+
+ flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);
+ line = sdsnew(option);
+ line = sdscatlen(line, " ", 1);
+ line = sdscatrepr(line, flags, sdslen(flags));
+ sdsfree(flags);
+ rewriteConfigRewriteLine(state,option,line,force);
+}
+
+/* Rewrite the client-output-buffer-limit option. */
+void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) {
+ int j;
+ char *option = "client-output-buffer-limit";
+
+ for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {
+ int force = (server.client_obuf_limits[j].hard_limit_bytes !=
+ clientBufferLimitsDefaults[j].hard_limit_bytes) ||
+ (server.client_obuf_limits[j].soft_limit_bytes !=
+ clientBufferLimitsDefaults[j].soft_limit_bytes) ||
+ (server.client_obuf_limits[j].soft_limit_seconds !=
+ clientBufferLimitsDefaults[j].soft_limit_seconds);
+ sds line;
+ char hard[64], soft[64];
+
+ rewriteConfigFormatMemory(hard,sizeof(hard),
+ server.client_obuf_limits[j].hard_limit_bytes);
+ rewriteConfigFormatMemory(soft,sizeof(soft),
+ server.client_obuf_limits[j].soft_limit_bytes);
+
+ line = sdscatprintf(sdsempty(),"%s %s %s %s %ld",
+ option, getClientLimitClassName(j), hard, soft,
+ (long) server.client_obuf_limits[j].soft_limit_seconds);
+ rewriteConfigRewriteLine(state,option,line,force);
+ }
+}
+
+/* Glue together the configuration lines in the current configuration
+ * rewrite state into a single string, stripping multiple empty lines. */
+sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {
+ sds content = sdsempty();
+ int j, was_empty = 0;
+
+ for (j = 0; j < state->numlines; j++) {
+ /* Every cluster of empty lines is turned into a single empty line. */
+ if (sdslen(state->lines[j]) == 0) {
+ if (was_empty) continue;
+ was_empty = 1;
+ } else {
+ was_empty = 0;
+ }
+ content = sdscatsds(content,state->lines[j]);
+ content = sdscatlen(content,"\n",1);
+ }
+ return content;
+}
+
+/* Free the configuration rewrite state. */
+void rewriteConfigReleaseState(struct rewriteConfigState *state) {
+ sdsfreesplitres(state->lines,state->numlines);
+ dictRelease(state->option_to_line);
+ zfree(state);
+}
+
+/* At the end of the rewrite process the state contains the remaining
+ * map between "option name" => "lines in the original config file".
+ * Lines used by the rewrite process were removed by the function
+ * rewriteConfigRewriteLine(), all the other lines are "orphaned" and
+ * should be replaced by empty lines.
+ *
+ * This function does just this, iterating all the option names and
+ * blanking all the lines still associated. */
+void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
+ dictIterator *di = dictGetIterator(state->option_to_line);
+ dictEntry *de;
+
+ while((de = dictNext(di)) != NULL) {
+ list *l = dictGetVal(de);
+
+ while(listLength(l)) {
+ listNode *ln = listFirst(l);
+ int linenum = (long) ln->value;
+
+ sdsfree(state->lines[linenum]);
+ state->lines[linenum] = sdsempty();
+ listDelNode(l,ln);
+ }
+ }
+ dictReleaseIterator(di);
+}
+
+/* This function overwrites the old configuration file with the new content.
+ *
+ * 1) The old file length is obtained.
+ * 2) If the new content is smaller, padding is added.
+ * 3) A single write(2) call is used to replace the content of the file.
+ * 4) Later the file is truncated to the length of the new content.
+ *
+ * This way we are sure the file is left in a consistent state even if the
+ * process is stopped between any of the four operations.
+ *
+ * The function returns 0 on success, otherwise -1 is returned and errno
+ * set accordingly. */
+int rewriteConfigOverwriteFile(char *configfile, sds content) {
+ int retval = 0;
+ int fd = open(configfile,O_RDWR|O_CREAT,0644);
+ int content_size = sdslen(content), padding = 0;
+ struct stat sb;
+ sds content_padded;
+
+ /* 1) Open the old file (or create a new one if it does not
+ * exist), get the size. */
+ if (fd == -1) return -1; /* errno set by open(). */
+ if (fstat(fd,&sb) == -1) {
+ close(fd);
+ return -1; /* errno set by fstat(). */
+ }
+
+ /* 2) Pad the content at least match the old file size. */
+ content_padded = sdsdup(content);
+ if (content_size < sb.st_size) {
+ /* If the old file was bigger, pad the content with
+ * a newline plus as many "#" chars as required. */
+ padding = sb.st_size - content_size;
+ content_padded = sdsgrowzero(content_padded,sb.st_size);
+ content_padded[content_size] = '\n';
+ memset(content_padded+content_size+1,'#',padding-1);
+ }
+
+ /* 3) Write the new content using a single write(2). */
+ if (write(fd,content_padded,strlen(content_padded)) == -1) {
+ retval = -1;
+ goto cleanup;
+ }
+
+ /* 4) Truncate the file to the right length if we used padding. */
+ if (padding) {
+ if (ftruncate(fd,content_size) == -1) {
+ /* Non critical error... */
+ }
+ }
+
+cleanup:
+ sdsfree(content_padded);
+ close(fd);
+ return retval;
+}
+
+/* Rewrite the configuration file at "path".
+ * If the configuration file already exists, we try at best to retain comments
+ * and overall structure.
+ *
+ * Configuration parameters that are at their default value, unless already
+ * explicitly included in the old configuration file, are not rewritten.
+ *
+ * On error -1 is returned and errno is set accordingly, otherwise 0. */
+int rewriteConfig(char *path) {
+ struct rewriteConfigState *state;
+ sds newcontent;
+ int retval;
+
+ /* Step 1: read the old config into our rewrite state. */
+ if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;
+
+ /* Step 2: rewrite every single option, replacing or appending it inside
+ * the rewrite state. */
+
+ /* TODO: Turn every default into a define, use it also in
+ * initServerConfig(). */
+ rewriteConfigYesNoOption(state,"daemonize",server.daemonize,0);
+ rewriteConfigStringOption(state,"pidfile",server.pidfile,REDIS_DEFAULT_PID_FILE);
+ rewriteConfigNumericalOption(state,"port",server.port,REDIS_SERVERPORT);
+ rewriteConfigStringOption(state,"bind",server.bindaddr,NULL);
+ rewriteConfigStringOption(state,"unixsocket",server.unixsocket,NULL);
+ rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,REDIS_DEFAULT_UNIX_SOCKET_PERM);
+ rewriteConfigNumericalOption(state,"timeout",server.maxidletime,REDIS_MAXIDLETIME);
+ rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,REDIS_DEFAULT_TCP_KEEPALIVE);
+ rewriteConfigEnumOption(state,"loglevel",server.verbosity,
+ "debug", REDIS_DEBUG,
+ "verbose", REDIS_VERBOSE,
+ "notice", REDIS_NOTICE,
+ "warning", REDIS_WARNING,
+ NULL, REDIS_DEFAULT_VERBOSITY);
+ rewriteConfigStringOption(state,"logfile",server.logfile,REDIS_DEFAULT_LOGFILE);
+ rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,REDIS_DEFAULT_SYSLOG_ENABLED);
+ rewriteConfigStringOption(state,"syslog-ident",server.syslog_ident,REDIS_DEFAULT_SYSLOG_IDENT);
+ rewriteConfigSyslogfacilityOption(state);
+ rewriteConfigSaveOption(state);
+ rewriteConfigNumericalOption(state,"databases",server.dbnum,REDIS_DEFAULT_DBNUM);
+ rewriteConfigYesNoOption(state,"stop-writes-on-bgsave-error",server.stop_writes_on_bgsave_err,REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR);
+ rewriteConfigYesNoOption(state,"rdbcompression",server.rdb_compression,REDIS_DEFAULT_RDB_COMPRESSION);
+ rewriteConfigYesNoOption(state,"rdbchecksum",server.rdb_checksum,REDIS_DEFAULT_RDB_CHECKSUM);
+ rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,REDIS_DEFAULT_RDB_FILENAME);
+ rewriteConfigDirOption(state);
+ rewriteConfigSlaveofOption(state);
+ rewriteConfigStringOption(state,"masterauth",server.masterauth,NULL);
+ rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA);
+ rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,REDIS_DEFAULT_SLAVE_READ_ONLY);
+ rewriteConfigNumericalOption(state,"repl-ping-slave-period",server.repl_ping_slave_period,REDIS_REPL_PING_SLAVE_PERIOD);
+ rewriteConfigNumericalOption(state,"repl-timeout",server.repl_timeout,REDIS_REPL_TIMEOUT);
+ rewriteConfigBytesOption(state,"repl-backlog-size",server.repl_backlog_size,REDIS_DEFAULT_REPL_BACKLOG_SIZE);
+ rewriteConfigBytesOption(state,"repl-backlog-ttl",server.repl_backlog_time_limit,REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT);
+ rewriteConfigYesNoOption(state,"repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay,REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY);
+ rewriteConfigNumericalOption(state,"slave-priority",server.slave_priority,REDIS_DEFAULT_SLAVE_PRIORITY);
+ rewriteConfigStringOption(state,"requirepass",server.requirepass,NULL);
+ rewriteConfigNumericalOption(state,"maxclients",server.maxclients,REDIS_MAX_CLIENTS);
+ rewriteConfigBytesOption(state,"maxmemory",server.maxmemory,REDIS_DEFAULT_MAXMEMORY);
+ rewriteConfigEnumOption(state,"maxmemory-policy",server.maxmemory_policy,
+ "volatile-lru", REDIS_MAXMEMORY_VOLATILE_LRU,
+ "allkeys-lru", REDIS_MAXMEMORY_ALLKEYS_LRU,
+ "volatile-random", REDIS_MAXMEMORY_VOLATILE_RANDOM,
+ "allkeys-random", REDIS_MAXMEMORY_ALLKEYS_RANDOM,
+ "volatile-ttl", REDIS_MAXMEMORY_VOLATILE_TTL,
+ "noeviction", REDIS_MAXMEMORY_NO_EVICTION,
+ NULL, REDIS_DEFAULT_MAXMEMORY_POLICY);
+ rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,REDIS_DEFAULT_MAXMEMORY_SAMPLES);
+ rewriteConfigAppendonlyOption(state);
+ rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync,
+ "everysec", AOF_FSYNC_EVERYSEC,
+ "always", AOF_FSYNC_ALWAYS,
+ "no", AOF_FSYNC_NO,
+ NULL, REDIS_DEFAULT_AOF_FSYNC);
+ rewriteConfigYesNoOption(state,"no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite,REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE);
+ rewriteConfigNumericalOption(state,"auto-aof-rewrite-percentage",server.aof_rewrite_perc,REDIS_AOF_REWRITE_PERC);
+ rewriteConfigBytesOption(state,"auto-aof-rewrite-min-size",server.aof_rewrite_min_size,REDIS_AOF_REWRITE_MIN_SIZE);
+ rewriteConfigNumericalOption(state,"lua-time-limit",server.lua_time_limit,REDIS_LUA_TIME_LIMIT);
+ rewriteConfigYesNoOption(state,"cluster-enabled",server.cluster_enabled,0);
+ rewriteConfigStringOption(state,"cluster-config-file",server.cluster_configfile,REDIS_DEFAULT_CLUSTER_CONFIG_FILE);
+ rewriteConfigNumericalOption(state,"cluster-node-timeout",server.cluster_node_timeout,REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT);
+ rewriteConfigNumericalOption(state,"slowlog-log-slower-than",server.slowlog_log_slower_than,REDIS_SLOWLOG_LOG_SLOWER_THAN);
+ rewriteConfigNumericalOption(state,"slowlog-max-len",server.slowlog_max_len,REDIS_SLOWLOG_MAX_LEN);
+ rewriteConfigNotifykeyspaceeventsOption(state);
+ rewriteConfigNumericalOption(state,"hash-max-ziplist-entries",server.hash_max_ziplist_entries,REDIS_HASH_MAX_ZIPLIST_ENTRIES);
+ rewriteConfigNumericalOption(state,"hash-max-ziplist-value",server.hash_max_ziplist_value,REDIS_HASH_MAX_ZIPLIST_VALUE);
+ rewriteConfigNumericalOption(state,"list-max-ziplist-entries",server.list_max_ziplist_entries,REDIS_LIST_MAX_ZIPLIST_ENTRIES);
+ rewriteConfigNumericalOption(state,"list-max-ziplist-value",server.list_max_ziplist_value,REDIS_LIST_MAX_ZIPLIST_VALUE);
+ rewriteConfigNumericalOption(state,"set-max-intset-entries",server.set_max_intset_entries,REDIS_SET_MAX_INTSET_ENTRIES);
+ rewriteConfigNumericalOption(state,"zset-max-ziplist-entries",server.zset_max_ziplist_entries,REDIS_ZSET_MAX_ZIPLIST_ENTRIES);
+ rewriteConfigNumericalOption(state,"zset-max-ziplist-value",server.zset_max_ziplist_value,REDIS_ZSET_MAX_ZIPLIST_VALUE);
+ rewriteConfigYesNoOption(state,"activerehashing",server.activerehashing,REDIS_DEFAULT_ACTIVE_REHASHING);
+ rewriteConfigClientoutputbufferlimitOption(state);
+ rewriteConfigNumericalOption(state,"hz",server.hz,REDIS_DEFAULT_HZ);
+ rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC);
+
+ /* Step 3: remove all the orphaned lines in the old file, that is, lines
+ * that were used by a config option and are no longer used, like in case
+ * of multiple "save" options or duplicated options. */
+ rewriteConfigRemoveOrphaned(state);
+
+ /* Step 4: generate a new configuration file from the modified state
+ * and write it into the original file. */
+ newcontent = rewriteConfigGetContentFromState(state);
+ retval = rewriteConfigOverwriteFile(server.configfile,newcontent);
+
+ sdsfree(newcontent);
+ rewriteConfigReleaseState(state);
+ return retval;
+}
+
+/*-----------------------------------------------------------------------------
+ * CONFIG command entry point
+ *----------------------------------------------------------------------------*/
+
void configCommand(redisClient *c) {
if (!strcasecmp(c->argv[1]->ptr,"set")) {
if (c->argc != 4) goto badarity;
@@ -1057,6 +1681,17 @@ void configCommand(redisClient *c) {
server.aof_delayed_fsync = 0;
resetCommandTableStats();
addReply(c,shared.ok);
+ } else if (!strcasecmp(c->argv[1]->ptr,"rewrite")) {
+ if (c->argc != 2) goto badarity;
+ if (server.configfile == NULL) {
+ addReplyError(c,"The server is running without a config file");
+ return;
+ }
+ if (rewriteConfig(server.configfile) == -1) {
+ addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno));
+ } else {
+ addReply(c,shared.ok);
+ }
} else {
addReplyError(c,
"CONFIG subcommand must be one of GET, SET, RESETSTAT");
diff --git a/src/redis.c b/src/redis.c
index 1e8e5d779..f86dafa53 100644
--- a/src/redis.c
+++ b/src/redis.c
@@ -269,11 +269,12 @@ void redisLogRaw(int level, const char *msg) {
FILE *fp;
char buf[64];
int rawmode = (level & REDIS_LOG_RAW);
+ int log_to_stdout = server.logfile[0] == '\0';
level &= 0xff; /* clear flags */
if (level < server.verbosity) return;
- fp = (server.logfile == NULL) ? stdout : fopen(server.logfile,"a");
+ fp = log_to_stdout ? stdout : fopen(server.logfile,"a");
if (!fp) return;
if (rawmode) {
@@ -289,8 +290,7 @@ void redisLogRaw(int level, const char *msg) {
}
fflush(fp);
- if (server.logfile) fclose(fp);
-
+ if (!log_to_stdout) fclose(fp);
if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);
}
@@ -318,13 +318,13 @@ void redisLog(int level, const char *fmt, ...) {
* where we need printf-alike features are served by redisLog(). */
void redisLogFromHandler(int level, const char *msg) {
int fd;
+ int log_to_stdout = server.logfile[0] == '\0';
char buf[64];
- if ((level&0xff) < server.verbosity ||
- (server.logfile == NULL && server.daemonize)) return;
- fd = server.logfile ?
- open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644) :
- STDOUT_FILENO;
+ if ((level&0xff) < server.verbosity || (log_to_stdout && server.daemonize))
+ return;
+ fd = log_to_stdout ? STDOUT_FILENO :
+ open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);
if (fd == -1) return;
ll2string(buf,sizeof(buf),getpid());
if (write(fd,"[",1) == -1) goto err;
@@ -336,7 +336,7 @@ void redisLogFromHandler(int level, const char *msg) {
if (write(fd,msg,strlen(msg)) == -1) goto err;
if (write(fd,"\n",1) == -1) goto err;
err:
- if (server.logfile) close(fd);
+ if (!log_to_stdout) close(fd);
}
/* Return the UNIX time in microseconds */
@@ -1198,32 +1198,35 @@ void createSharedObjects(void) {
}
void initServerConfig() {
+ int j;
+
getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
+ server.configfile = NULL;
server.hz = REDIS_DEFAULT_HZ;
server.runid[REDIS_RUN_ID_SIZE] = '\0';
server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
server.port = REDIS_SERVERPORT;
server.bindaddr = NULL;
server.unixsocket = NULL;
- server.unixsocketperm = 0;
+ server.unixsocketperm = REDIS_DEFAULT_UNIX_SOCKET_PERM;
server.ipfd = -1;
server.sofd = -1;
server.dbnum = REDIS_DEFAULT_DBNUM;
- server.verbosity = REDIS_NOTICE;
+ server.verbosity = REDIS_DEFAULT_VERBOSITY;
server.maxidletime = REDIS_MAXIDLETIME;
- server.tcpkeepalive = 0;
+ server.tcpkeepalive = REDIS_DEFAULT_TCP_KEEPALIVE;
server.active_expire_enabled = 1;
server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN;
server.saveparams = NULL;
server.loading = 0;
- server.logfile = NULL; /* NULL = log on standard output */
- server.syslog_enabled = 0;
- server.syslog_ident = zstrdup("redis");
+ server.logfile = zstrdup(REDIS_DEFAULT_LOGFILE);
+ server.syslog_enabled = REDIS_DEFAULT_SYSLOG_ENABLED;
+ server.syslog_ident = zstrdup(REDIS_DEFAULT_SYSLOG_IDENT);
server.syslog_facility = LOG_LOCAL0;
- server.daemonize = 0;
+ server.daemonize = REDIS_DEFAULT_DAEMONIZE;
server.aof_state = REDIS_AOF_OFF;
- server.aof_fsync = AOF_FSYNC_EVERYSEC;
- server.aof_no_fsync_on_rewrite = 0;
+ server.aof_fsync = REDIS_DEFAULT_AOF_FSYNC;
+ server.aof_no_fsync_on_rewrite = REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE;
server.aof_rewrite_perc = REDIS_AOF_REWRITE_PERC;
server.aof_rewrite_min_size = REDIS_AOF_REWRITE_MIN_SIZE;
server.aof_rewrite_base_size = 0;
@@ -1236,21 +1239,21 @@ void initServerConfig() {
server.aof_fd = -1;
server.aof_selected_db = -1; /* Make sure the first time will not match */
server.aof_flush_postponed_start = 0;
- server.aof_rewrite_incremental_fsync = 1;
- server.pidfile = zstrdup("/var/run/redis.pid");
- server.rdb_filename = zstrdup("dump.rdb");
+ server.aof_rewrite_incremental_fsync = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC;
+ server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE);
+ server.rdb_filename = zstrdup(REDIS_DEFAULT_RDB_FILENAME);
server.aof_filename = zstrdup("appendonly.aof");
server.requirepass = NULL;
- server.rdb_compression = 1;
- server.rdb_checksum = 1;
- server.stop_writes_on_bgsave_err = 1;
- server.activerehashing = 1;
+ server.rdb_compression = REDIS_DEFAULT_RDB_COMPRESSION;
+ server.rdb_checksum = REDIS_DEFAULT_RDB_CHECKSUM;
+ server.stop_writes_on_bgsave_err = REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR;
+ server.activerehashing = REDIS_DEFAULT_ACTIVE_REHASHING;
server.notify_keyspace_events = 0;
server.maxclients = REDIS_MAX_CLIENTS;
server.bpop_blocked_clients = 0;
- server.maxmemory = 0;
- server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
- server.maxmemory_samples = 3;
+ server.maxmemory = REDIS_DEFAULT_MAXMEMORY;
+ server.maxmemory_policy = REDIS_DEFAULT_MAXMEMORY_POLICY;
+ server.maxmemory_samples = REDIS_DEFAULT_MAXMEMORY_SAMPLES;
server.hash_max_ziplist_entries = REDIS_HASH_MAX_ZIPLIST_ENTRIES;
server.hash_max_ziplist_value = REDIS_HASH_MAX_ZIPLIST_VALUE;
server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;
@@ -1263,7 +1266,7 @@ void initServerConfig() {
server.repl_timeout = REDIS_REPL_TIMEOUT;
server.cluster_enabled = 0;
server.cluster_node_timeout = REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT;
- server.cluster_configfile = zstrdup("nodes.conf");
+ server.cluster_configfile = zstrdup(REDIS_DEFAULT_CLUSTER_CONFIG_FILE);
server.lua_caller = NULL;
server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
server.lua_client = NULL;
@@ -1285,10 +1288,10 @@ void initServerConfig() {
server.repl_master_initial_offset = -1;
server.repl_state = REDIS_REPL_NONE;
server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
- server.repl_serve_stale_data = 1;
- server.repl_slave_ro = 1;
+ server.repl_serve_stale_data = REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA;
+ server.repl_slave_ro = REDIS_DEFAULT_SLAVE_READ_ONLY;
server.repl_down_since = 0; /* Never connected, repl is down since EVER. */
- server.repl_disable_tcp_nodelay = 0;
+ server.repl_disable_tcp_nodelay = REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY;
server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
server.master_repl_offset = 0;
@@ -1302,15 +1305,8 @@ void initServerConfig() {
server.repl_no_slaves_since = time(NULL);
/* Client output buffer limits */
- server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].hard_limit_bytes = 0;
- server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].soft_limit_bytes = 0;
- server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].soft_limit_seconds = 0;
- server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_SLAVE].hard_limit_bytes = 1024*1024*256;
- server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_SLAVE].soft_limit_bytes = 1024*1024*64;
- server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_SLAVE].soft_limit_seconds = 60;
- server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_PUBSUB].hard_limit_bytes = 1024*1024*32;
- server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_PUBSUB].soft_limit_bytes = 1024*1024*8;
- server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_PUBSUB].soft_limit_seconds = 60;
+ for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++)
+ server.client_obuf_limits[j] = clientBufferLimitsDefaults[j];
/* Double constants initialization */
R_Zero = 0.0;
@@ -2077,7 +2073,8 @@ sds genRedisInfoString(char *section) {
"uptime_in_seconds:%ld\r\n"
"uptime_in_days:%ld\r\n"
"hz:%d\r\n"
- "lru_clock:%ld\r\n",
+ "lru_clock:%ld\r\n"
+ "config_file:%s\r\n",
REDIS_VERSION,
redisGitSHA1(),
strtol(redisGitDirty(),NULL,10) > 0,
@@ -2097,7 +2094,8 @@ sds genRedisInfoString(char *section) {
uptime,
uptime/(3600*24),
server.hz,
- (unsigned long) server.lruclock);
+ (unsigned long) server.lruclock,
+ server.configfile ? server.configfile : "");
}
/* Clients */
@@ -2772,6 +2770,58 @@ void redisSetProcTitle(char *title) {
#endif
}
+/* Given the filename, return the absolute path as an SDS string, or NULL
+ * if it fails for some reason. Note that "filename" may be an absolute path
+ * already, this will be detected and handled correctly.
+ *
+ * The function does not try to normalize everything, but only the obvious
+ * case of one or more "../" appearning at the start of "filename"
+ * relative path. */
+sds getAbsolutePath(char *filename) {
+ char cwd[1024];
+ sds abspath;
+ sds relpath = sdsnew(filename);
+
+ relpath = sdstrim(relpath," \r\n\t");
+ if (relpath[0] == '/') return relpath; /* Path is already absolute. */
+
+ /* If path is relative, join cwd and relative path. */
+ if (getcwd(cwd,sizeof(cwd)) == NULL) {
+ sdsfree(relpath);
+ return NULL;
+ }
+ abspath = sdsnew(cwd);
+ if (sdslen(abspath) && abspath[sdslen(abspath)-1] != '/')
+ abspath = sdscat(abspath,"/");
+
+ /* At this point we have the current path always ending with "/", and
+ * the trimmed relative path. Try to normalize the obvious case of
+ * trailing ../ elements at the start of the path.
+ *
+ * For every "../" we find in the filename, we remove it and also remove
+ * the last element of the cwd, unless the current cwd is "/". */
+ while (sdslen(relpath) >= 3 &&
+ relpath[0] == '.' && relpath[1] == '.' && relpath[2] == '/')
+ {
+ relpath = sdsrange(relpath,3,-1);
+ if (sdslen(abspath) > 1) {
+ char *p = abspath + sdslen(abspath)-2;
+ int trimlen = 1;
+
+ while(*p != '/') {
+ p--;
+ trimlen++;
+ }
+ abspath = sdsrange(abspath,0,-(trimlen+1));
+ }
+ }
+
+ /* Finally glue the two parts together. */
+ abspath = sdscatsds(abspath,relpath);
+ sdsfree(relpath);
+ return abspath;
+}
+
int main(int argc, char **argv) {
struct timeval tv;
@@ -2839,6 +2889,7 @@ int main(int argc, char **argv) {
resetServerSaveParams();
loadServerConfig(configfile,options);
sdsfree(options);
+ if (configfile) server.configfile = getAbsolutePath(configfile);
} else {
redisLog(REDIS_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf", argv[0], server.sentinel_mode ? "sentinel" : "redis");
}
diff --git a/src/redis.h b/src/redis.h
index 11fc7d662..3002dd973 100644
--- a/src/redis.h
+++ b/src/redis.h
@@ -98,6 +98,26 @@
#define REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT (60*60) /* 1 hour */
#define REDIS_REPL_BACKLOG_MIN_SIZE (1024*16) /* 16k */
#define REDIS_BGSAVE_RETRY_DELAY 5 /* Wait a few secs before trying again. */
+#define REDIS_DEFAULT_PID_FILE "/var/run/redis.pid"
+#define REDIS_DEFAULT_SYSLOG_IDENT "redis"
+#define REDIS_DEFAULT_CLUSTER_CONFIG_FILE "nodes.conf"
+#define REDIS_DEFAULT_DAEMONIZE 0
+#define REDIS_DEFAULT_UNIX_SOCKET_PERM 0
+#define REDIS_DEFAULT_TCP_KEEPALIVE 0
+#define REDIS_DEFAULT_LOGFILE ""
+#define REDIS_DEFAULT_SYSLOG_ENABLED 0
+#define REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR 1
+#define REDIS_DEFAULT_RDB_COMPRESSION 1
+#define REDIS_DEFAULT_RDB_CHECKSUM 1
+#define REDIS_DEFAULT_RDB_FILENAME "dump.rdb"
+#define REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA 1
+#define REDIS_DEFAULT_SLAVE_READ_ONLY 1
+#define REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY 0
+#define REDIS_DEFAULT_MAXMEMORY 0
+#define REDIS_DEFAULT_MAXMEMORY_SAMPLES 3
+#define REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE 0
+#define REDIS_DEFAULT_ACTIVE_REHASHING 1
+#define REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC 1
/* Protocol and I/O related defines */
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
@@ -241,6 +261,7 @@
#define REDIS_NOTICE 2
#define REDIS_WARNING 3
#define REDIS_LOG_RAW (1<<10) /* Modifier to log without timestamp */
+#define REDIS_DEFAULT_VERBOSITY REDIS_NOTICE
/* Anti-warning macro... */
#define REDIS_NOTUSED(V) ((void) V)
@@ -252,6 +273,7 @@
#define AOF_FSYNC_NO 0
#define AOF_FSYNC_ALWAYS 1
#define AOF_FSYNC_EVERYSEC 2
+#define REDIS_DEFAULT_AOF_FSYNC AOF_FSYNC_EVERYSEC
/* Zip structure related defaults */
#define REDIS_HASH_MAX_ZIPLIST_ENTRIES 512
@@ -274,6 +296,7 @@
#define REDIS_MAXMEMORY_ALLKEYS_LRU 3
#define REDIS_MAXMEMORY_ALLKEYS_RANDOM 4
#define REDIS_MAXMEMORY_NO_EVICTION 5
+#define REDIS_DEFAULT_MAXMEMORY_POLICY REDIS_MAXMEMORY_VOLATILE_LRU
/* Scripting */
#define REDIS_LUA_TIME_LIMIT 5000 /* milliseconds */
@@ -490,6 +513,8 @@ typedef struct clientBufferLimitsConfig {
time_t soft_limit_seconds;
} clientBufferLimitsConfig;
+extern clientBufferLimitsConfig clientBufferLimitsDefaults[REDIS_CLIENT_LIMIT_NUM_CLASSES];
+
/* The redisOp structure defines a Redis Operation, that is an instance of
* a command with an argument vector, database ID, propagation target
* (REDIS_PROPAGATE_*), and command pointer.
@@ -672,6 +697,7 @@ typedef struct {
struct redisServer {
/* General */
+ char *configfile; /* Absolute config file path, or NULL */
int hz; /* serverCron() calls frequency in hertz */
redisDb *db;
dict *commands; /* Command table */
diff --git a/tests/assets/default.conf b/tests/assets/default.conf
index 9f95700d1..17d21b07c 100644
--- a/tests/assets/default.conf
+++ b/tests/assets/default.conf
@@ -6,7 +6,7 @@ pidfile /var/run/redis.pid
port 6379
timeout 0
loglevel verbose
-logfile stdout
+logfile ''
databases 16
save 900 1