From 5947f170f91f1985eb316a9fd59c9ca8f2ea73f9 Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 9 May 2013 16:57:59 +0200 Subject: [PATCH 01/19] Obtain absoute path of configuration file, expose it in INFO. --- src/redis.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/redis.h | 1 + 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/redis.c b/src/redis.c index 1e8e5d779..feb4a14a1 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1199,6 +1199,7 @@ void createSharedObjects(void) { void initServerConfig() { 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; @@ -2077,7 +2078,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 +2099,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 +2775,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 +2894,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..784992248 100644 --- a/src/redis.h +++ b/src/redis.h @@ -672,6 +672,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 */ From 7e049fafd38a602b9689f4311183c3968a3fa182 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 10 May 2013 00:15:18 +0200 Subject: [PATCH 02/19] CONFIG REWRITE: Initial support code and design. --- src/config.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/redis.c | 6 +- src/redis.h | 3 + 3 files changed, 428 insertions(+), 4 deletions(-) diff --git a/src/config.c b/src/config.c index 27c3f48c8..7fd1bf53d 100644 --- a/src/config.c +++ b/src/config.c @@ -513,7 +513,7 @@ void loadServerConfig(char *filename, char *options) { } /*----------------------------------------------------------------------------- - * CONFIG command for remote configuration + * CONFIG SET implementation *----------------------------------------------------------------------------*/ void configSetCommand(redisClient *c) { @@ -813,6 +813,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 +1042,412 @@ 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); +} + +/* Rewrite a simple "option-name " configuration option. */ +void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) { + int force = value != defvalue; + /* TODO: check if we can write it using MB, GB, or other suffixes. */ + sds line = sdscatprintf(sdsempty(),"%s %lld",option,value); + + rewriteConfigRewriteLine(state,option,line,force); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) { +} + +void rewriteConfigSaveOption(struct rewriteConfigState *state) { +} + +void rewriteConfigDirOption(struct rewriteConfigState *state) { +} + +void rewriteConfigSlaveofOption(struct rewriteConfigState *state) { +} + +void rewriteConfigAppendonlyOption(struct rewriteConfigState *state) { +} + +void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) { +} + +void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) { +} + +sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) { + sds content = sdsempty(); + int j; + + for (j = 0; j < state->numlines; j++) { + content = sdscatsds(content,state->lines[j]); + content = sdscatlen(content,"\n",1); + } + return content; +} + +void rewriteConfigReleaseState(struct rewriteConfigState *state) { + sdsfreesplitres(state->lines,state->numlines); + dictRelease(state->option_to_line); + zfree(state); +} + +/* 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; + + /* 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,"bindaddr",server.bindaddr,NULL); + rewriteConfigStringOption(state,"unixsocket",server.unixsocket,NULL); + rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,0); + rewriteConfigNumericalOption(state,"timeout",server.maxidletime,0); + rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,0); + rewriteConfigEnumOption(state,"loglevel",server.verbosity, + "debug", REDIS_DEBUG, + "verbose", REDIS_VERBOSE, + "notice", REDIS_NOTICE, + "warning", REDIS_WARNING, + NULL, REDIS_NOTICE); + rewriteConfigStringOption(state,"logfile",server.logfile,"stdout"); + rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,0); + 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,1); + rewriteConfigYesNoOption(state,"rdbcompression",server.rdb_compression,1); + rewriteConfigYesNoOption(state,"rdbchecksum",server.rdb_checksum,1); + rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,"dump.rdb"); + rewriteConfigDirOption(state); + rewriteConfigSlaveofOption(state); + rewriteConfigStringOption(state,"masterauth",server.masterauth,NULL); + rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,1); + rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,1); + 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); + rewriteConfigNumericalOption(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); + 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,0); + 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_MAXMEMORY_VOLATILE_LRU); + rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,3); + rewriteConfigAppendonlyOption(state); + rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync, + "eveysec", AOF_FSYNC_EVERYSEC, + "always", AOF_FSYNC_ALWAYS, + "no", AOF_FSYNC_NO, + NULL, AOF_FSYNC_EVERYSEC); + rewriteConfigYesNoOption(state,"no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite,0); + 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,"active-rehashing",server.activerehashing,1); + rewriteConfigClientoutputbufferlimitOption(state); + rewriteConfigNumericalOption(state,"hz",server.hz,REDIS_DEFAULT_HZ); + rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,1); + + /* 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); + printf("%s\n", newcontent); + + sdsfree(newcontent); + rewriteConfigReleaseState(state); + return 0; +} + +/*----------------------------------------------------------------------------- + * CONFIG command entry point + *----------------------------------------------------------------------------*/ + void configCommand(redisClient *c) { if (!strcasecmp(c->argv[1]->ptr,"set")) { if (c->argc != 4) goto badarity; @@ -1057,6 +1467,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 feb4a14a1..ecbb1e4a7 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1219,7 +1219,7 @@ void initServerConfig() { server.loading = 0; server.logfile = NULL; /* NULL = log on standard output */ server.syslog_enabled = 0; - server.syslog_ident = zstrdup("redis"); + server.syslog_ident = zstrdup(REDIS_DEFAULT_SYSLOG_IDENT); server.syslog_facility = LOG_LOCAL0; server.daemonize = 0; server.aof_state = REDIS_AOF_OFF; @@ -1238,7 +1238,7 @@ void initServerConfig() { 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.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE); server.rdb_filename = zstrdup("dump.rdb"); server.aof_filename = zstrdup("appendonly.aof"); server.requirepass = NULL; @@ -1264,7 +1264,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; diff --git a/src/redis.h b/src/redis.h index 784992248..2c05b8e69 100644 --- a/src/redis.h +++ b/src/redis.h @@ -98,6 +98,9 @@ #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" /* Protocol and I/O related defines */ #define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */ From 2bc31e55eedf24b7ecac0172c47006e79231dcde Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 13 May 2013 11:11:35 +0200 Subject: [PATCH 03/19] CONFIG REWRITE: support for save and syslog-facility. --- src/config.c | 55 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/src/config.c b/src/config.c index 7fd1bf53d..340e93d46 100644 --- a/src/config.c +++ b/src/config.c @@ -31,6 +31,22 @@ #include "redis.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} +}; + /*----------------------------------------------------------------------------- * Config file parsing *----------------------------------------------------------------------------*/ @@ -164,21 +180,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++) { @@ -1298,9 +1299,33 @@ void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int } 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); } 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); + } } void rewriteConfigDirOption(struct rewriteConfigState *state) { From d95592b11689130382d7bf71aedf8963cc7f7431 Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 13 May 2013 11:26:43 +0200 Subject: [PATCH 04/19] CONFIG REWRITE: support for dir and slaveof. --- src/config.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/config.c b/src/config.c index 340e93d46..52c4dca1f 100644 --- a/src/config.c +++ b/src/config.c @@ -1329,9 +1329,21 @@ void rewriteConfigSaveOption(struct rewriteConfigState *state) { } void rewriteConfigDirOption(struct rewriteConfigState *state) { + char cwd[1024]; + + if (getcwd(cwd,sizeof(cwd)) == NULL) return; /* no rewrite on error. */ + rewriteConfigStringOption(state,"dir",cwd,NULL); } void rewriteConfigSlaveofOption(struct rewriteConfigState *state) { + 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(),"slaveof %s %d", + server.masterhost, server.masterport); + rewriteConfigRewriteLine(state,"slaveof",line,1); } void rewriteConfigAppendonlyOption(struct rewriteConfigState *state) { From c184f36d21cae47a4b9b4484b657d2e58fdc4f58 Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 13 May 2013 18:34:18 +0200 Subject: [PATCH 05/19] CONFIG REWRITE: support for client-output-buffer-limit. --- src/config.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- src/redis.c | 13 ++++--------- src/redis.h | 2 ++ 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/config.c b/src/config.c index 52c4dca1f..5d72081ca 100644 --- a/src/config.c +++ b/src/config.c @@ -47,6 +47,12 @@ static struct { {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 *----------------------------------------------------------------------------*/ @@ -1336,23 +1342,59 @@ void rewriteConfigDirOption(struct rewriteConfigState *state) { } 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(),"slaveof %s %d", + line = sdscatprintf(sdsempty(),"%s %s %d", option, server.masterhost, server.masterport); - rewriteConfigRewriteLine(state,"slaveof",line,1); + rewriteConfigRewriteLine(state,option,line,1); } 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); } 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 = sdscatprintf(sdsempty(),"%s %s", option, flags); + sdsfree(flags); + rewriteConfigRewriteLine(state,option,line,force); } 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; + + line = sdscatprintf(sdsempty(),"%s %s %llu %llu %ld", + option, + getClientLimitClassName(j), + server.client_obuf_limits[j].hard_limit_bytes, + server.client_obuf_limits[j].soft_limit_bytes, + (long) server.client_obuf_limits[j].soft_limit_seconds); + rewriteConfigRewriteLine(state,option,line,force); + } } sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) { diff --git a/src/redis.c b/src/redis.c index ecbb1e4a7..5baa75006 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1198,6 +1198,8 @@ void createSharedObjects(void) { } void initServerConfig() { + int j; + getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE); server.configfile = NULL; server.hz = REDIS_DEFAULT_HZ; @@ -1303,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; diff --git a/src/redis.h b/src/redis.h index 2c05b8e69..4ff187fd3 100644 --- a/src/redis.h +++ b/src/redis.h @@ -493,6 +493,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. From ee721f1e5c703dd5a674239e17384be66a9fd544 Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 14 May 2013 10:22:55 +0200 Subject: [PATCH 06/19] CONFIG REWRITE: strip multiple empty lines. --- src/config.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 5d72081ca..a9bb57728 100644 --- a/src/config.c +++ b/src/config.c @@ -1397,11 +1397,20 @@ void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state } } +/* 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; + 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); } From 78f94d90eb5b750c3aa38a17ee390bfa4238e91c Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 14 May 2013 11:17:18 +0200 Subject: [PATCH 07/19] CONFIG REWRITE: remove orphaned lines. --- src/config.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index a9bb57728..907d2a562 100644 --- a/src/config.c +++ b/src/config.c @@ -1241,6 +1241,7 @@ void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, lo 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, @@ -1249,6 +1250,7 @@ void rewriteConfigYesNoOption(struct rewriteConfigState *state, char *option, in 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; @@ -1267,6 +1269,7 @@ void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, c 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); @@ -1274,6 +1277,7 @@ void rewriteConfigNumericalOption(struct rewriteConfigState *state, char *option 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); @@ -1281,6 +1285,9 @@ void rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, in 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; @@ -1304,6 +1311,7 @@ void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int 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; @@ -1320,6 +1328,7 @@ void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) { rewriteConfigRewriteLine(state,option,line,force); } +/* Rewrite the save option. */ void rewriteConfigSaveOption(struct rewriteConfigState *state) { int j; sds line; @@ -1334,6 +1343,7 @@ void rewriteConfigSaveOption(struct rewriteConfigState *state) { } } +/* Rewrite the dir option, always using absolute paths.*/ void rewriteConfigDirOption(struct rewriteConfigState *state) { char cwd[1024]; @@ -1341,6 +1351,7 @@ void rewriteConfigDirOption(struct rewriteConfigState *state) { rewriteConfigStringOption(state,"dir",cwd,NULL); } +/* Rewrite the slaveof option. */ void rewriteConfigSlaveofOption(struct rewriteConfigState *state) { char *option = "slaveof"; sds line; @@ -1353,6 +1364,7 @@ void rewriteConfigSlaveofOption(struct rewriteConfigState *state) { 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"; @@ -1363,6 +1375,7 @@ void rewriteConfigAppendonlyOption(struct rewriteConfigState *state) { 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"; @@ -1374,6 +1387,7 @@ void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) { 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"; @@ -1417,12 +1431,40 @@ sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) { 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); +} + /* Rewrite the configuration file at "path". * If the configuration file already exists, we try at best to retain comments * and overall structure. @@ -1520,7 +1562,7 @@ int rewriteConfig(char *path) { /* 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); + rewriteConfigRemoveOrphaned(state); /* Step 4: generate a new configuration file from the modified state * and write it into the original file. */ From e252045454bfa90cd5c9d937e249821969c7425f Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 14 May 2013 12:32:25 +0200 Subject: [PATCH 08/19] CONFIG REWRITE: actually rewrite the config file, atomically. --- src/config.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/src/config.c b/src/config.c index 907d2a562..27fee376e 100644 --- a/src/config.c +++ b/src/config.c @@ -28,9 +28,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ - #include "redis.h" +#include +#include + static struct { const char *name; const int value; @@ -1465,6 +1467,63 @@ void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) { 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); + 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. @@ -1476,6 +1535,7 @@ void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) { 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; @@ -1567,11 +1627,11 @@ int rewriteConfig(char *path) { /* Step 4: generate a new configuration file from the modified state * and write it into the original file. */ newcontent = rewriteConfigGetContentFromState(state); - printf("%s\n", newcontent); + retval = rewriteConfigOverwriteFile(server.configfile,newcontent); sdsfree(newcontent); rewriteConfigReleaseState(state); - return 0; + return retval; } /*----------------------------------------------------------------------------- From 9e74d216e1385dd7d45880b14c12100599e38cfb Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 14 May 2013 12:45:04 +0200 Subject: [PATCH 09/19] CONFIG REWRITE: Use sane perms when creating redis.conf from scratch. --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 27fee376e..5c9f5d92e 100644 --- a/src/config.c +++ b/src/config.c @@ -1481,7 +1481,7 @@ void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) { * set accordingly. */ int rewriteConfigOverwriteFile(char *configfile, sds content) { int retval = 0; - int fd = open(configfile,O_RDWR|O_CREAT); + int fd = open(configfile,O_RDWR|O_CREAT,0644); int content_size = sdslen(content), padding = 0; struct stat sb; sds content_padded; From 310dbba01cf694a13d077a5156be756bba5eac8b Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 10:12:29 +0200 Subject: [PATCH 10/19] Added a define for most configuration defaults. Also the logfile option was modified to always have an explicit value and to log to stdout when an empty string is used as log file. Previously there was special handling of the string "stdout" that set the logfile to NULL, this always required some special handling. --- redis.conf | 4 ++-- src/config.c | 46 ++++++++++++++++++++--------------------- src/redis.c | 58 ++++++++++++++++++++++++++-------------------------- src/redis.h | 20 ++++++++++++++++++ 4 files changed, 73 insertions(+), 55 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 5c9f5d92e..917387a61 100644 --- a/src/config.c +++ b/src/config.c @@ -164,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"); @@ -1550,38 +1547,39 @@ int rewriteConfig(char *path) { rewriteConfigNumericalOption(state,"port",server.port,REDIS_SERVERPORT); rewriteConfigStringOption(state,"bindaddr",server.bindaddr,NULL); rewriteConfigStringOption(state,"unixsocket",server.unixsocket,NULL); - rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,0); - rewriteConfigNumericalOption(state,"timeout",server.maxidletime,0); - rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,0); + 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_NOTICE); - rewriteConfigStringOption(state,"logfile",server.logfile,"stdout"); - rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,0); + 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,1); - rewriteConfigYesNoOption(state,"rdbcompression",server.rdb_compression,1); - rewriteConfigYesNoOption(state,"rdbchecksum",server.rdb_checksum,1); - rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,"dump.rdb"); + 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,1); - rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,1); + 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); rewriteConfigNumericalOption(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); + rewriteConfigBytesOption(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,0); + 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, @@ -1589,15 +1587,15 @@ int rewriteConfig(char *path) { "allkeys-random", REDIS_MAXMEMORY_ALLKEYS_RANDOM, "volatile-ttl", REDIS_MAXMEMORY_VOLATILE_TTL, "noeviction", REDIS_MAXMEMORY_NO_EVICTION, - NULL, REDIS_MAXMEMORY_VOLATILE_LRU); - rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,3); + NULL, REDIS_DEFAULT_MAXMEMORY_POLICY); + rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,REDIS_DEFAULT_MAXMEMORY_SAMPLES); rewriteConfigAppendonlyOption(state); rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync, "eveysec", AOF_FSYNC_EVERYSEC, "always", AOF_FSYNC_ALWAYS, "no", AOF_FSYNC_NO, - NULL, AOF_FSYNC_EVERYSEC); - rewriteConfigYesNoOption(state,"no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite,0); + 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); @@ -1614,10 +1612,10 @@ int rewriteConfig(char *path) { 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,"active-rehashing",server.activerehashing,1); + rewriteConfigYesNoOption(state,"active-rehashing",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,1); + 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 diff --git a/src/redis.c b/src/redis.c index 5baa75006..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 */ @@ -1208,25 +1208,25 @@ void initServerConfig() { 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.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; @@ -1239,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.aof_rewrite_incremental_fsync = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC; server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE); - server.rdb_filename = zstrdup("dump.rdb"); + 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; @@ -1288,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; diff --git a/src/redis.h b/src/redis.h index 4ff187fd3..3002dd973 100644 --- a/src/redis.h +++ b/src/redis.h @@ -101,6 +101,23 @@ #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. */ @@ -244,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) @@ -255,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 @@ -277,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 */ From 86a6aad98424434a4d0ed80f3a8db059608a8b14 Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 10:14:36 +0200 Subject: [PATCH 11/19] Test default config modified to set logfile to empty string. Required because of recent changes in the way logfile is set to standard output. --- tests/assets/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 72e980231c9595bde6b14a810b2380fdba805681 Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 11:04:53 +0200 Subject: [PATCH 12/19] CONFIG REWRITE: repl-disable-tcp-nodelay is a boolean option. --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 917387a61..8938e019f 100644 --- a/src/config.c +++ b/src/config.c @@ -1575,7 +1575,7 @@ int rewriteConfig(char *path) { rewriteConfigNumericalOption(state,"repl-timeout",server.repl_timeout,REDIS_REPL_TIMEOUT); rewriteConfigNumericalOption(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); - rewriteConfigBytesOption(state,"repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay,REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY); + 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); From 402a0f1ca0c317cf1ba374c633984c7fefb07be1 Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 11:06:56 +0200 Subject: [PATCH 13/19] CONFIG REWRITE: fixed typo in AOF fsync policy. --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 8938e019f..53fd979bf 100644 --- a/src/config.c +++ b/src/config.c @@ -1591,7 +1591,7 @@ int rewriteConfig(char *path) { rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,REDIS_DEFAULT_MAXMEMORY_SAMPLES); rewriteConfigAppendonlyOption(state); rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync, - "eveysec", AOF_FSYNC_EVERYSEC, + "everysec", AOF_FSYNC_EVERYSEC, "always", AOF_FSYNC_ALWAYS, "no", AOF_FSYNC_NO, NULL, REDIS_DEFAULT_AOF_FSYNC); From 328843849f070c5cbdf154b7a96da6dcd317bdd6 Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 11:09:19 +0200 Subject: [PATCH 14/19] CONFIG REWRITE: "active-rehashing" -> "activerehashing". --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 53fd979bf..0b03e0de1 100644 --- a/src/config.c +++ b/src/config.c @@ -1612,7 +1612,7 @@ int rewriteConfig(char *path) { 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,"active-rehashing",server.activerehashing,REDIS_DEFAULT_ACTIVE_REHASHING); + 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); From c590e18d15bed4dc576c86bc4b10eed2b0997e15 Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 11:15:18 +0200 Subject: [PATCH 15/19] CONFIG REWRITE: correctly escape the notify-keyspace-events option. --- src/config.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 0b03e0de1..57905f649 100644 --- a/src/config.c +++ b/src/config.c @@ -1381,7 +1381,9 @@ void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) { sds line, flags; flags = keyspaceEventsFlagsToString(server.notify_keyspace_events); - line = sdscatprintf(sdsempty(),"%s %s", option, flags); + line = sdsnew(option); + line = sdscatlen(line, " ", 1); + line = sdscatrepr(line, flags, sdslen(flags)); sdsfree(flags); rewriteConfigRewriteLine(state,option,line,force); } From 8a44e6c4905b4cf59ffca92d80ff4b9560850e49 Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 11:33:02 +0200 Subject: [PATCH 16/19] CONFIG REWRITE: when rewriting amount of bytes use GB, MB, KB if possible. --- src/config.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/config.c b/src/config.c index 57905f649..a2276c48e 100644 --- a/src/config.c +++ b/src/config.c @@ -1231,13 +1231,34 @@ void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sd 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 " configuration option. */ void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) { + char buf[64]; int force = value != defvalue; - /* TODO: check if we can write it using MB, GB, or other suffixes. */ - sds line = sdscatprintf(sdsempty(),"%s %lld",option,value); + sds line; + rewriteConfigFormatMemory(buf,sizeof(buf),value); + line = sdscatprintf(sdsempty(),"%s %s",option,buf); rewriteConfigRewriteLine(state,option,line,force); + } /* Rewrite a yes/no option. */ @@ -1401,12 +1422,15 @@ void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state (server.client_obuf_limits[j].soft_limit_seconds != clientBufferLimitsDefaults[j].soft_limit_seconds); sds line; + char hard[64], soft[64]; - line = sdscatprintf(sdsempty(),"%s %s %llu %llu %ld", - option, - getClientLimitClassName(j), - server.client_obuf_limits[j].hard_limit_bytes, - server.client_obuf_limits[j].soft_limit_bytes, + 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); } From cd48e4fc4022b8110111e621654781fee08b88c3 Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 11:38:43 +0200 Subject: [PATCH 17/19] CONFIG REWRITE: bindaddr -> bind. --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index a2276c48e..07d402c21 100644 --- a/src/config.c +++ b/src/config.c @@ -1571,7 +1571,7 @@ int rewriteConfig(char *path) { rewriteConfigYesNoOption(state,"daemonize",server.daemonize,0); rewriteConfigStringOption(state,"pidfile",server.pidfile,REDIS_DEFAULT_PID_FILE); rewriteConfigNumericalOption(state,"port",server.port,REDIS_SERVERPORT); - rewriteConfigStringOption(state,"bindaddr",server.bindaddr,NULL); + 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); From 25f2be6267f0a0a91154f44099653a2d561d8df7 Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 11:39:29 +0200 Subject: [PATCH 18/19] CONFIG REWRITE: backlog size is a bytes option. --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index 07d402c21..d68176d77 100644 --- a/src/config.c +++ b/src/config.c @@ -1599,7 +1599,7 @@ int rewriteConfig(char *path) { 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); - rewriteConfigNumericalOption(state,"repl-backlog-size",server.repl_backlog_size,REDIS_DEFAULT_REPL_BACKLOG_SIZE); + 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); From 78167807beab6f34967a7310a93211a1efbbcff9 Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 15 May 2013 11:55:14 +0200 Subject: [PATCH 19/19] Use memtoll() when parsing the backlog-size option. --- src/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index d68176d77..c41d57184 100644 --- a/src/config.c +++ b/src/config.c @@ -256,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;