diff options
Diffstat (limited to 'src/config.c')
-rw-r--r-- | src/config.c | 878 |
1 files changed, 543 insertions, 335 deletions
diff --git a/src/config.c b/src/config.c index a52e00f66..3b5d4d349 100644 --- a/src/config.c +++ b/src/config.c @@ -40,11 +40,6 @@ * Config file name-value maps. *----------------------------------------------------------------------------*/ -typedef struct configEnum { - const char *name; - const int val; -} configEnum; - typedef struct deprecatedConfig { const char *name; const int argc_min; @@ -168,7 +163,7 @@ int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT] = { 0, 200, 800 }; * rewrite. */ typedef struct boolConfigData { int *config; /* The pointer to the server config this value is stored in */ - const int default_value; /* The default value of the config on rewrite */ + int default_value; /* The default value of the config on rewrite */ int (*is_valid_fn)(int val, const char **err); /* Optional function to check validity of new value (generic doc above) */ } boolConfigData; @@ -182,7 +177,7 @@ typedef struct stringConfigData { typedef struct sdsConfigData { sds *config; /* Pointer to the server config this value is stored in. */ - const char *default_value; /* Default value of the config on rewrite. */ + char *default_value; /* Default value of the config on rewrite. */ int (*is_valid_fn)(sds val, const char **err); /* Optional function to check validity of new value (generic doc above) */ int convert_empty_to_null; /* Boolean indicating if empty SDS strings should be stored as a NULL value. */ @@ -191,7 +186,7 @@ typedef struct sdsConfigData { typedef struct enumConfigData { int *config; /* The pointer to the server config this value is stored in */ configEnum *enum_value; /* The underlying enum type this data represents */ - const int default_value; /* The default value of the config on rewrite */ + int default_value; /* The default value of the config on rewrite */ int (*is_valid_fn)(int val, const char **err); /* Optional function to check validity of new value (generic doc above) */ } enumConfigData; @@ -208,11 +203,6 @@ typedef enum numericType { NUMERIC_TYPE_TIME_T, } numericType; -#define INTEGER_CONFIG 0 /* No flags means a simple integer configuration */ -#define MEMORY_CONFIG (1<<0) /* Indicates if this value can be loaded as a memory value */ -#define PERCENT_CONFIG (1<<1) /* Indicates if this value can be loaded as a percent (and stored as a negative int) */ -#define OCTAL_CONFIG (1<<2) /* This value uses octal representation */ - typedef struct numericConfigData { union { int *i; @@ -230,7 +220,7 @@ typedef struct numericConfigData { numericType numeric_type; /* An enum indicating the type of this value */ long long lower_bound; /* The lower bound of this numeric value */ long long upper_bound; /* The upper bound of this numeric value */ - const long long default_value; /* The default value of the config on rewrite */ + long long default_value; /* The default value of the config on rewrite */ int (*is_valid_fn)(long long val, const char **err); /* Optional function to check validity of new value (generic doc above) */ } numericConfigData; @@ -242,43 +232,44 @@ typedef union typeData { numericConfigData numeric; } typeData; +typedef struct standardConfig standardConfig; + typedef int (*apply_fn)(const char **err); typedef struct typeInterface { /* Called on server start, to init the server with default value */ - void (*init)(typeData data); + void (*init)(standardConfig *config); /* Called on server startup and CONFIG SET, returns 1 on success, * 2 meaning no actual change done, 0 on error and can set a verbose err * string */ - int (*set)(typeData data, sds *argv, int argc, const char **err); + int (*set)(standardConfig *config, sds *argv, int argc, const char **err); /* Optional: called after `set()` to apply the config change. Used only in * the context of CONFIG SET. Returns 1 on success, 0 on failure. * Optionally set err to a static error string. */ apply_fn apply; /* Called on CONFIG GET, returns sds to be used in reply */ - sds (*get)(typeData data); + sds (*get)(standardConfig *config); /* Called on CONFIG REWRITE, required to rewrite the config state */ - void (*rewrite)(typeData data, const char *name, struct rewriteConfigState *state); + void (*rewrite)(standardConfig *config, const char *name, struct rewriteConfigState *state); } typeInterface; -typedef struct standardConfig { +struct standardConfig { const char *name; /* The user visible name of this config */ const char *alias; /* An alias that can also be used for this config */ - const unsigned int flags; /* Flags for this specific config */ + unsigned int flags; /* Flags for this specific config */ typeInterface interface; /* The function pointers that define the type interface */ typeData data; /* The type specific data exposed used by the interface */ -} standardConfig; + configType type; /* The type of config this is. */ + void *privdata; /* privdata for this config, for module configs this is a ModuleConfig struct */ +}; -#define MODIFIABLE_CONFIG 0 /* This is the implied default for a standard - * config, which is mutable. */ -#define IMMUTABLE_CONFIG (1ULL<<0) /* Can this value only be set at startup? */ -#define SENSITIVE_CONFIG (1ULL<<1) /* Does this value contain sensitive information */ -#define DEBUG_CONFIG (1ULL<<2) /* Values that are useful for debugging. */ -#define MULTI_ARG_CONFIG (1ULL<<3) /* This config receives multiple arguments. */ -#define HIDDEN_CONFIG (1ULL<<4) /* This config is hidden in `config get <pattern>` (used for tests/debugging) */ -#define PROTECTED_CONFIG (1ULL<<5) /* Becomes immutable if enable-protected-configs is enabled. */ -#define DENY_LOADING_CONFIG (1ULL<<6) /* This config is forbidden during loading. */ +dict *configs = NULL; /* Runtime config values */ -standardConfig configs[]; +/* Lookup a config by the provided sds string name, or return NULL + * if the config does not exist */ +static standardConfig *lookupConfig(sds name) { + dictEntry *de = dictFind(configs, name); + return de ? dictGetVal(de) : NULL; +} /*----------------------------------------------------------------------------- * Enum access functions @@ -407,12 +398,6 @@ static int updateClientOutputBufferLimit(sds *args, int arg_len, const char **er return 1; } -void initConfigValues() { - for (standardConfig *config = configs; config->name != NULL; config++) { - if (config->interface.init) config->interface.init(config->data); - } -} - /* Note this is here to support detecting we're running a config set from * within conf file parsing. This is only needed to support the deprecated * abnormal aggregate `save T C` functionality. Remove in the future. */ @@ -458,29 +443,23 @@ void loadServerConfigFromString(char *config) { sdstolower(argv[0]); /* Iterate the configs that are standard */ - int match = 0; - for (standardConfig *config = configs; config->name != NULL; config++) { - if ((!strcasecmp(argv[0],config->name) || - (config->alias && !strcasecmp(argv[0],config->alias)))) - { - /* For normal single arg configs enforce we have a single argument. - * Note that MULTI_ARG_CONFIGs need to validate arg count on their own */ - if (!(config->flags & MULTI_ARG_CONFIG) && argc != 2) { - err = "wrong number of arguments"; - goto loaderr; - } - /* Set config using all arguments that follows */ - if (!config->interface.set(config->data, &argv[1], argc-1, &err)) { - goto loaderr; - } - - match = 1; - break; + standardConfig *config = lookupConfig(argv[0]); + if (config) { + /* For normal single arg configs enforce we have a single argument. + * Note that MULTI_ARG_CONFIGs need to validate arg count on their own */ + if (!(config->flags & MULTI_ARG_CONFIG) && argc != 2) { + err = "wrong number of arguments"; + goto loaderr; + } + /* Set config using all arguments that follows */ + if (!config->interface.set(config, &argv[1], argc-1, &err)) { + goto loaderr; } - } - /* If there's no matching above, we try matching them with deprecated configs */ - if (!match) { + sdsfreesplitres(argv,argc); + continue; + } else { + int match = 0; for (deprecatedConfig *config = deprecated_configs; config->name != NULL; config++) { if (!strcasecmp(argv[0], config->name) && config->argc_min <= argc && @@ -490,11 +469,10 @@ void loadServerConfigFromString(char *config) { break; } } - } - - if (match) { - sdsfreesplitres(argv,argc); - continue; + if (match) { + sdsfreesplitres(argv,argc); + continue; + } } /* Execute config directives */ @@ -535,6 +513,13 @@ void loadServerConfigFromString(char *config) { } } else if (!strcasecmp(argv[0],"loadmodule") && argc >= 2) { queueLoadModule(argv[1],&argv[2],argc-2); + } else if (strchr(argv[0], '.')) { + if (argc != 2) { + err = "Module config specified without value"; + goto loaderr; + } + sds name = sdsdup(argv[0]); + if (!dictReplace(server.module_configs_queue, name, sdsdup(argv[1]))) sdsfree(name); } else if (!strcasecmp(argv[0],"sentinel")) { /* argc == 1 is handled by main() as we need to enter the sentinel * mode ASAP. */ @@ -597,9 +582,10 @@ loaderr: * Both filename and options can be NULL, in such a case are considered * empty. This way loadServerConfig can be used to just load a file or * just load a string. */ +#define CONFIG_READ_LEN 1024 void loadServerConfig(char *filename, char config_from_stdin, char *options) { sds config = sdsempty(); - char buf[CONFIG_MAX_LINE+1]; + char buf[CONFIG_READ_LEN+1]; FILE *fp; glob_t globbuf; @@ -631,7 +617,7 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) { globbuf.gl_pathv[i], strerror(errno)); exit(1); } - while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) + while(fgets(buf,CONFIG_READ_LEN+1,fp) != NULL) config = sdscat(config,buf); fclose(fp); } @@ -647,7 +633,7 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) { filename, strerror(errno)); exit(1); } - while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) + while(fgets(buf,CONFIG_READ_LEN+1,fp) != NULL) config = sdscat(config,buf); fclose(fp); } @@ -657,7 +643,7 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) { if (config_from_stdin) { serverLog(LL_WARNING,"Reading config from stdin"); fp = stdin; - while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) + while(fgets(buf,CONFIG_READ_LEN+1,fp) != NULL) config = sdscat(config,buf); } @@ -682,12 +668,45 @@ static int performInterfaceSet(standardConfig *config, sds value, const char **e } /* Set the config */ - res = config->interface.set(config->data, argv, argc, errstr); + res = config->interface.set(config, argv, argc, errstr); if (config->flags & MULTI_ARG_CONFIG) sdsfreesplitres(argv, argc); return res; } -static void restoreBackupConfig(standardConfig **set_configs, sds *old_values, int count, apply_fn *apply_fns) { +/* Find the config by name and attempt to set it to value. */ +int performModuleConfigSetFromName(sds name, sds value, const char **err) { + standardConfig *config = lookupConfig(name); + if (!config || !(config->flags & MODULE_CONFIG)) { + *err = "Config name not found"; + return 0; + } + return performInterfaceSet(config, value, err); +} + +/* Find config by name and attempt to set it to its default value. */ +int performModuleConfigSetDefaultFromName(sds name, const char **err) { + standardConfig *config = lookupConfig(name); + serverAssert(config); + if (!(config->flags & MODULE_CONFIG)) { + *err = "Config name not found"; + return 0; + } + switch (config->type) { + case BOOL_CONFIG: + return setModuleBoolConfig(config->privdata, config->data.yesno.default_value, err); + case SDS_CONFIG: + return setModuleStringConfig(config->privdata, config->data.sds.default_value, err); + case NUMERIC_CONFIG: + return setModuleNumericConfig(config->privdata, config->data.numeric.default_value, err); + case ENUM_CONFIG: + return setModuleEnumConfig(config->privdata, config->data.enumd.default_value, err); + default: + serverPanic("Config type of module config is not allowed."); + } + return 0; +} + +static void restoreBackupConfig(standardConfig **set_configs, sds *old_values, int count, apply_fn *apply_fns, list *module_configs) { int i; const char *errstr = "unknown error"; /* Set all backup values */ @@ -703,6 +722,10 @@ static void restoreBackupConfig(standardConfig **set_configs, sds *old_values, i serverLog(LL_WARNING, "Failed applying restored failed CONFIG SET command: %s", errstr); } } + if (module_configs) { + if (!moduleConfigApplyConfig(module_configs, &errstr, NULL)) + serverLog(LL_WARNING, "Failed applying restored failed CONFIG SET command: %s", errstr); + } } /*----------------------------------------------------------------------------- @@ -714,6 +737,8 @@ void configSetCommand(client *c) { const char *invalid_arg_name = NULL; const char *err_arg_name = NULL; standardConfig **set_configs; /* TODO: make this a dict for better performance */ + list *module_configs_apply; + const char **config_names; sds *new_values; sds *old_values = NULL; apply_fn *apply_fns; /* TODO: make this a set for better performance */ @@ -728,7 +753,9 @@ void configSetCommand(client *c) { } config_count = (c->argc - 2) / 2; + module_configs_apply = listCreate(); set_configs = zcalloc(sizeof(standardConfig*)*config_count); + config_names = zcalloc(sizeof(char*)*config_count); new_values = zmalloc(sizeof(sds*)*config_count); old_values = zcalloc(sizeof(sds*)*config_count); apply_fns = zcalloc(sizeof(apply_fn)*config_count); @@ -736,73 +763,76 @@ void configSetCommand(client *c) { /* Find all relevant configs */ for (i = 0; i < config_count; i++) { - for (standardConfig *config = configs; config->name != NULL; config++) { - if ((!strcasecmp(c->argv[2+i*2]->ptr,config->name) || - (config->alias && !strcasecmp(c->argv[2]->ptr,config->alias)))) { - - /* Note: it's important we run over ALL passed configs and check if we need to call `redactClientCommandArgument()`. - * This is in order to avoid anyone using this command for a log/slowlog/monitor/etc. displaying sensitive info. - * So even if we encounter an error we still continue running over the remaining arguments. */ - if (config->flags & SENSITIVE_CONFIG) { - redactClientCommandArgument(c,2+i*2+1); - } + standardConfig *config = lookupConfig(c->argv[2+i*2]->ptr); + /* Fail if we couldn't find this config */ + if (!config) { + if (!invalid_args) { + invalid_arg_name = c->argv[2+i*2]->ptr; + invalid_args = 1; + } + continue; + } - if (!invalid_args) { - if (config->flags & IMMUTABLE_CONFIG || - (config->flags & PROTECTED_CONFIG && !allowProtectedAction(server.enable_protected_configs, c))) - { - /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ - errstr = (config->flags & IMMUTABLE_CONFIG) ? "can't set immutable config" : "can't set protected config"; - err_arg_name = c->argv[2+i*2]->ptr; - invalid_args = 1; - } + /* Note: it's important we run over ALL passed configs and check if we need to call `redactClientCommandArgument()`. + * This is in order to avoid anyone using this command for a log/slowlog/monitor/etc. displaying sensitive info. + * So even if we encounter an error we still continue running over the remaining arguments. */ + if (config->flags & SENSITIVE_CONFIG) { + redactClientCommandArgument(c,2+i*2+1); + } - if (server.loading && config->flags & DENY_LOADING_CONFIG) { - /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ - deny_loading_error = 1; - invalid_args = 1; - } + /* We continue to make sure we redact all the configs */ + if (invalid_args) continue; - /* If this config appears twice then fail */ - for (j = 0; j < i; j++) { - if (set_configs[j] == config) { - /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ - errstr = "duplicate parameter"; - err_arg_name = c->argv[2+i*2]->ptr; - invalid_args = 1; - break; - } - } - set_configs[i] = config; - new_values[i] = c->argv[2+i*2+1]->ptr; - } - break; - } + if (config->flags & IMMUTABLE_CONFIG || + (config->flags & PROTECTED_CONFIG && !allowProtectedAction(server.enable_protected_configs, c))) + { + /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ + errstr = (config->flags & IMMUTABLE_CONFIG) ? "can't set immutable config" : "can't set protected config"; + err_arg_name = c->argv[2+i*2]->ptr; + invalid_args = 1; + continue; } - /* Fail if we couldn't find this config */ - /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ - if (!invalid_args && !set_configs[i]) { - invalid_arg_name = c->argv[2+i*2]->ptr; + + if (server.loading && config->flags & DENY_LOADING_CONFIG) { + /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ + deny_loading_error = 1; invalid_args = 1; + continue; + } + + /* If this config appears twice then fail */ + for (j = 0; j < i; j++) { + if (set_configs[j] == config) { + /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ + errstr = "duplicate parameter"; + err_arg_name = c->argv[2+i*2]->ptr; + invalid_args = 1; + break; + } } + set_configs[i] = config; + config_names[i] = config->name; + new_values[i] = c->argv[2+i*2+1]->ptr; } if (invalid_args) goto err; /* Backup old values before setting new ones */ for (i = 0; i < config_count; i++) - old_values[i] = set_configs[i]->interface.get(set_configs[i]->data); + old_values[i] = set_configs[i]->interface.get(set_configs[i]); /* Set all new values (don't apply yet) */ for (i = 0; i < config_count; i++) { int res = performInterfaceSet(set_configs[i], new_values[i], &errstr); if (!res) { - restoreBackupConfig(set_configs, old_values, i+1, NULL); + restoreBackupConfig(set_configs, old_values, i+1, NULL, NULL); err_arg_name = set_configs[i]->name; goto err; } else if (res == 1) { /* A new value was set, if this config has an apply function then store it for execution later */ - if (set_configs[i]->interface.apply) { + if (set_configs[i]->flags & MODULE_CONFIG) { + addModuleConfigApply(module_configs_apply, set_configs[i]->privdata); + } else if (set_configs[i]->interface.apply) { /* Check if this apply function is already stored */ int exists = 0; for (j = 0; apply_fns[j] != NULL && j <= i; j++) { @@ -824,11 +854,20 @@ void configSetCommand(client *c) { for (i = 0; i < config_count && apply_fns[i] != NULL; i++) { if (!apply_fns[i](&errstr)) { serverLog(LL_WARNING, "Failed applying new configuration. Possibly related to new %s setting. Restoring previous settings.", set_configs[config_map_fns[i]]->name); - restoreBackupConfig(set_configs, old_values, config_count, apply_fns); + restoreBackupConfig(set_configs, old_values, config_count, apply_fns, NULL); err_arg_name = set_configs[config_map_fns[i]]->name; goto err; } } + /* Apply all module configs that were set. */ + if (!moduleConfigApplyConfig(module_configs_apply, &errstr, &err_arg_name)) { + serverLogRaw(LL_WARNING, "Failed applying new module configuration. Restoring previous settings."); + restoreBackupConfig(set_configs, old_values, config_count, apply_fns, module_configs_apply); + goto err; + } + + RedisModuleConfigChangeV1 cc = {.num_changes = config_count, .config_names = config_names}; + moduleFireServerEvent(REDISMODULE_EVENT_CONFIG, REDISMODULE_SUBEVENT_CONFIG_CHANGE, &cc); addReply(c,shared.ok); goto end; @@ -845,12 +884,14 @@ err: } end: zfree(set_configs); + zfree(config_names); zfree(new_values); for (i = 0; i < config_count; i++) sdsfree(old_values[i]); zfree(old_values); zfree(apply_fns); zfree(config_map_fns); + listRelease(module_configs_apply); } /*----------------------------------------------------------------------------- @@ -858,38 +899,51 @@ end: *----------------------------------------------------------------------------*/ void configGetCommand(client *c) { - void *replylen = addReplyDeferredLen(c); - int matches = 0; int i; + dictEntry *de; + dictIterator *di; + /* Create a dictionary to store the matched configs */ + dict *matches = dictCreate(&externalStringType); + for (i = 0; i < c->argc - 2; i++) { + robj *o = c->argv[2+i]; + sds name = o->ptr; + + /* If the string doesn't contain glob patterns, just directly + * look up the key in the dictionary. */ + if (!strpbrk(name, "[*?")) { + if (dictFind(matches, name)) continue; + standardConfig *config = lookupConfig(name); + + if (config) { + dictAdd(matches, name, config); + } + continue; + } - for (standardConfig *config = configs; config->name != NULL; config++) { - int matched_conf = 0; - int matched_alias = 0; - for (i = 0; i < c->argc - 2 && (!matched_conf || !matched_alias); i++) { - robj *o = c->argv[2+i]; - char *pattern = o->ptr; - + /* Otherwise, do a match against all items in the dictionary. */ + di = dictGetIterator(configs); + + while ((de = dictNext(di)) != NULL) { + standardConfig *config = dictGetVal(de); /* Note that hidden configs require an exact match (not a pattern) */ - if (!matched_conf && - (((config->flags & HIDDEN_CONFIG) && !strcasecmp(pattern, config->name)) || - (!(config->flags & HIDDEN_CONFIG) && stringmatch(pattern, config->name, 1)))) { - addReplyBulkCString(c, config->name); - addReplyBulkSds(c, config->interface.get(config->data)); - matches++; - matched_conf = 1; - } - if (!matched_alias && config->alias && - (((config->flags & HIDDEN_CONFIG) && !strcasecmp(pattern, config->alias)) || - (!(config->flags & HIDDEN_CONFIG) && stringmatch(pattern, config->alias, 1)))) { - addReplyBulkCString(c, config->alias); - addReplyBulkSds(c, config->interface.get(config->data)); - matches++; - matched_alias = 1; + if (config->flags & HIDDEN_CONFIG) continue; + if (dictFind(matches, config->name)) continue; + if (stringmatch(name, de->key, 1)) { + dictAdd(matches, de->key, config); } } + dictReleaseIterator(di); } - - setDeferredMapLen(c,replylen,matches); + + di = dictGetIterator(matches); + addReplyMapLen(c, dictSize(matches)); + while ((de = dictNext(di)) != NULL) { + standardConfig *config = (standardConfig *) dictGetVal(de); + addReplyBulkCString(c, de->key); + addReplyBulkSds(c, config->interface.get(config)); + } + dictReleaseIterator(di); + dictRelease(matches); } /*----------------------------------------------------------------------------- @@ -996,17 +1050,32 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) { FILE *fp = fopen(path,"r"); if (fp == NULL && errno != ENOENT) return NULL; - char buf[CONFIG_MAX_LINE+1]; + struct redis_stat sb; + if (fp && redis_fstat(fileno(fp),&sb) == -1) return NULL; + int linenum = -1; struct rewriteConfigState *state = rewriteConfigCreateState(); - if (fp == NULL) return state; + if (fp == NULL || sb.st_size == 0) return state; - /* Read the old file line by line, populate the state. */ - while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) { + /* Load the file content */ + sds config = sdsnewlen(SDS_NOINIT,sb.st_size); + if (fread(config,1,sb.st_size,fp) == 0) { + sdsfree(config); + rewriteConfigReleaseState(state); + fclose(fp); + return NULL; + } + + int i, totlines; + sds *lines = sdssplitlen(config,sdslen(config),"\n",1,&totlines); + + /* Read the old content line by line, populate the state. */ + for (i = 0; i < totlines; i++) { int argc; sds *argv; - sds line = sdstrim(sdsnew(buf),"\r\n\t "); + sds line = sdstrim(lines[i],"\r\n\t "); + lines[i] = NULL; linenum++; /* Zero based, so we init at -1 */ @@ -1020,12 +1089,13 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) { /* Not a comment, split into arguments. */ argv = sdssplitargs(line,&argc); - if (argv == NULL) { + if (argv == NULL || (!server.sentinel_mode && !lookupConfig(argv[0]))) { /* Apparently the line is unparsable for some reason, for - * instance it may have unbalanced quotes. Load it as a - * comment. */ + * instance it may have unbalanced quotes, or may contain a + * config that doesn't exist anymore. Load it as a comment. */ sds aux = sdsnew("# ??? "); aux = sdscatsds(aux,line); + if (argv) sdsfreesplitres(argv, argc); sdsfree(line); rewriteConfigAppendLine(state,aux); continue; @@ -1062,6 +1132,8 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) { sdsfreesplitres(argv,argc); } fclose(fp); + sdsfreesplitres(lines,totlines); + sdsfree(config); return state; } @@ -1235,8 +1307,8 @@ void rewriteConfigEnumOption(struct rewriteConfigState *state, const char *optio } /* Rewrite the save option. */ -void rewriteConfigSaveOption(typeData data, const char *name, struct rewriteConfigState *state) { - UNUSED(data); +void rewriteConfigSaveOption(standardConfig *config, const char *name, struct rewriteConfigState *state) { + UNUSED(config); int j; sds line; @@ -1296,8 +1368,8 @@ void rewriteConfigUserOption(struct rewriteConfigState *state) { } /* Rewrite the dir option, always using absolute paths.*/ -void rewriteConfigDirOption(typeData data, const char *name, struct rewriteConfigState *state) { - UNUSED(data); +void rewriteConfigDirOption(standardConfig *config, const char *name, struct rewriteConfigState *state) { + UNUSED(config); char cwd[1024]; if (getcwd(cwd,sizeof(cwd)) == NULL) { @@ -1308,8 +1380,8 @@ void rewriteConfigDirOption(typeData data, const char *name, struct rewriteConfi } /* Rewrite the slaveof option. */ -void rewriteConfigReplicaOfOption(typeData data, const char *name, struct rewriteConfigState *state) { - UNUSED(data); +void rewriteConfigReplicaOfOption(standardConfig *config, const char *name, struct rewriteConfigState *state) { + UNUSED(config); sds line; /* If this is a master, we want all the slaveof config options @@ -1325,8 +1397,8 @@ void rewriteConfigReplicaOfOption(typeData data, const char *name, struct rewrit } /* Rewrite the notify-keyspace-events option. */ -void rewriteConfigNotifyKeyspaceEventsOption(typeData data, const char *name, struct rewriteConfigState *state) { - UNUSED(data); +void rewriteConfigNotifyKeyspaceEventsOption(standardConfig *config, const char *name, struct rewriteConfigState *state) { + UNUSED(config); int force = server.notify_keyspace_events != 0; sds line, flags; @@ -1339,8 +1411,8 @@ void rewriteConfigNotifyKeyspaceEventsOption(typeData data, const char *name, st } /* Rewrite the client-output-buffer-limit option. */ -void rewriteConfigClientOutputBufferLimitOption(typeData data, const char *name, struct rewriteConfigState *state) { - UNUSED(data); +void rewriteConfigClientOutputBufferLimitOption(standardConfig *config, const char *name, struct rewriteConfigState *state) { + UNUSED(config); int j; for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) { int force = (server.client_obuf_limits[j].hard_limit_bytes != @@ -1367,8 +1439,8 @@ void rewriteConfigClientOutputBufferLimitOption(typeData data, const char *name, } /* Rewrite the oom-score-adj-values option. */ -void rewriteConfigOOMScoreAdjValuesOption(typeData data, const char *name, struct rewriteConfigState *state) { - UNUSED(data); +void rewriteConfigOOMScoreAdjValuesOption(standardConfig *config, const char *name, struct rewriteConfigState *state) { + UNUSED(config); int force = 0; int j; sds line; @@ -1387,8 +1459,8 @@ void rewriteConfigOOMScoreAdjValuesOption(typeData data, const char *name, struc } /* Rewrite the bind option. */ -void rewriteConfigBindOption(typeData data, const char *name, struct rewriteConfigState *state) { - UNUSED(data); +void rewriteConfigBindOption(standardConfig *config, const char *name, struct rewriteConfigState *state) { + UNUSED(config); int force = 1; sds line, addresses; int is_default = 0; @@ -1508,10 +1580,14 @@ sds getConfigDebugInfo() { /* Iterate the configs and "rewrite" the ones that have * the debug flag. */ - for (standardConfig *config = configs; config->name != NULL; config++) { + dictIterator *di = dictGetIterator(configs); + dictEntry *de; + while ((de = dictNext(di)) != NULL) { + standardConfig *config = dictGetVal(de); if (!(config->flags & DEBUG_CONFIG)) continue; - config->interface.rewrite(config->data, config->name, state); + config->interface.rewrite(config, config->name, state); } + dictReleaseIterator(di); sds info = rewriteConfigGetContentFromState(state); rewriteConfigReleaseState(state); return info; @@ -1599,9 +1675,15 @@ int rewriteConfig(char *path, int force_write) { * the rewrite state. */ /* Iterate the configs that are standard */ - for (standardConfig *config = configs; config->name != NULL; config++) { - if (config->interface.rewrite) config->interface.rewrite(config->data, config->name, state); + dictIterator *di = dictGetIterator(configs); + dictEntry *de; + while ((de = dictNext(di)) != NULL) { + standardConfig *config = dictGetVal(de); + /* Only rewrite the primary names */ + if (config->flags & ALIAS_CONFIG) continue; + if (config->interface.rewrite) config->interface.rewrite(config, de->key, state); } + dictReleaseIterator(di); rewriteConfigUserOption(state); rewriteConfigLoadmoduleOption(state); @@ -1656,38 +1738,46 @@ static char loadbuf[LOADBUF_SIZE]; */ /* Bool Configs */ -static void boolConfigInit(typeData data) { - *data.yesno.config = data.yesno.default_value; +static void boolConfigInit(standardConfig *config) { + *config->data.yesno.config = config->data.yesno.default_value; } -static int boolConfigSet(typeData data, sds *argv, int argc, const char **err) { +static int boolConfigSet(standardConfig *config, sds *argv, int argc, const char **err) { UNUSED(argc); int yn = yesnotoi(argv[0]); if (yn == -1) { *err = "argument must be 'yes' or 'no'"; return 0; } - if (data.yesno.is_valid_fn && !data.yesno.is_valid_fn(yn, err)) + if (config->data.yesno.is_valid_fn && !config->data.yesno.is_valid_fn(yn, err)) return 0; - int prev = *(data.yesno.config); + int prev = config->flags & MODULE_CONFIG ? getModuleBoolConfig(config->privdata) : *(config->data.yesno.config); if (prev != yn) { - *(data.yesno.config) = yn; + if (config->flags & MODULE_CONFIG) { + return setModuleBoolConfig(config->privdata, yn, err); + } + *(config->data.yesno.config) = yn; return 1; } return 2; } -static sds boolConfigGet(typeData data) { - return sdsnew(*data.yesno.config ? "yes" : "no"); +static sds boolConfigGet(standardConfig *config) { + if (config->flags & MODULE_CONFIG) { + return sdsnew(getModuleBoolConfig(config->privdata) ? "yes" : "no"); + } + return sdsnew(*config->data.yesno.config ? "yes" : "no"); } -static void boolConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) { - rewriteConfigYesNoOption(state, name,*(data.yesno.config), data.yesno.default_value); +static void boolConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) { + int val = config->flags & MODULE_CONFIG ? getModuleBoolConfig(config->privdata) : *(config->data.yesno.config); + rewriteConfigYesNoOption(state, name, val, config->data.yesno.default_value); } #define createBoolConfig(name, alias, flags, config_addr, default, is_valid, apply) { \ embedCommonConfig(name, alias, flags) \ embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite, apply) \ + .type = BOOL_CONFIG, \ .data.yesno = { \ .config = &(config_addr), \ .default_value = (default), \ @@ -1696,61 +1786,69 @@ static void boolConfigRewrite(typeData data, const char *name, struct rewriteCon } /* String Configs */ -static void stringConfigInit(typeData data) { - *data.string.config = (data.string.convert_empty_to_null && !data.string.default_value) ? NULL : zstrdup(data.string.default_value); +static void stringConfigInit(standardConfig *config) { + *config->data.string.config = (config->data.string.convert_empty_to_null && !config->data.string.default_value) ? NULL : zstrdup(config->data.string.default_value); } -static int stringConfigSet(typeData data, sds *argv, int argc, const char **err) { +static int stringConfigSet(standardConfig *config, sds *argv, int argc, const char **err) { UNUSED(argc); - if (data.string.is_valid_fn && !data.string.is_valid_fn(argv[0], err)) + if (config->data.string.is_valid_fn && !config->data.string.is_valid_fn(argv[0], err)) return 0; - char *prev = *data.string.config; - char *new = (data.string.convert_empty_to_null && !argv[0][0]) ? NULL : argv[0]; + char *prev = *config->data.string.config; + char *new = (config->data.string.convert_empty_to_null && !argv[0][0]) ? NULL : argv[0]; if (new != prev && (new == NULL || prev == NULL || strcmp(prev, new))) { - *data.string.config = new != NULL ? zstrdup(new) : NULL; + *config->data.string.config = new != NULL ? zstrdup(new) : NULL; zfree(prev); return 1; } return 2; } -static sds stringConfigGet(typeData data) { - return sdsnew(*data.string.config ? *data.string.config : ""); +static sds stringConfigGet(standardConfig *config) { + return sdsnew(*config->data.string.config ? *config->data.string.config : ""); } -static void stringConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) { - rewriteConfigStringOption(state, name,*(data.string.config), data.string.default_value); +static void stringConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) { + rewriteConfigStringOption(state, name,*(config->data.string.config), config->data.string.default_value); } /* SDS Configs */ -static void sdsConfigInit(typeData data) { - *data.sds.config = (data.sds.convert_empty_to_null && !data.sds.default_value) ? NULL: sdsnew(data.sds.default_value); +static void sdsConfigInit(standardConfig *config) { + *config->data.sds.config = (config->data.sds.convert_empty_to_null && !config->data.sds.default_value) ? NULL : sdsnew(config->data.sds.default_value); } -static int sdsConfigSet(typeData data, sds *argv, int argc, const char **err) { +static int sdsConfigSet(standardConfig *config, sds *argv, int argc, const char **err) { UNUSED(argc); - if (data.sds.is_valid_fn && !data.sds.is_valid_fn(argv[0], err)) + if (config->data.sds.is_valid_fn && !config->data.sds.is_valid_fn(argv[0], err)) return 0; - sds prev = *data.sds.config; - sds new = (data.string.convert_empty_to_null && (sdslen(argv[0]) == 0)) ? NULL : argv[0]; + sds prev = config->flags & MODULE_CONFIG ? getModuleStringConfig(config->privdata) : *config->data.sds.config; + sds new = (config->data.string.convert_empty_to_null && (sdslen(argv[0]) == 0)) ? NULL : argv[0]; if (new != prev && (new == NULL || prev == NULL || sdscmp(prev, new))) { - *data.sds.config = new != NULL ? sdsdup(new) : NULL; sdsfree(prev); + if (config->flags & MODULE_CONFIG) { + return setModuleStringConfig(config->privdata, new, err); + } + *config->data.sds.config = new != NULL ? sdsdup(new) : NULL; return 1; } + if (config->flags & MODULE_CONFIG && prev) sdsfree(prev); return 2; } -static sds sdsConfigGet(typeData data) { - if (*data.sds.config) { - return sdsdup(*data.sds.config); +static sds sdsConfigGet(standardConfig *config) { + sds val = config->flags & MODULE_CONFIG ? getModuleStringConfig(config->privdata) : *config->data.sds.config; + if (val) { + if (config->flags & MODULE_CONFIG) return val; + return sdsdup(val); } else { return sdsnew(""); } } -static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) { - rewriteConfigSdsOption(state, name, *(data.sds.config), data.sds.default_value); +static void sdsConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) { + sds val = config->flags & MODULE_CONFIG ? getModuleStringConfig(config->privdata) : *config->data.sds.config; + rewriteConfigSdsOption(state, name, val, config->data.sds.default_value); + if (val) sdsfree(val); } @@ -1760,6 +1858,7 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf #define createStringConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, apply) { \ embedCommonConfig(name, alias, flags) \ embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite, apply) \ + .type = STRING_CONFIG, \ .data.string = { \ .config = &(config_addr), \ .default_value = (default), \ @@ -1771,6 +1870,7 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf #define createSDSConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, apply) { \ embedCommonConfig(name, alias, flags) \ embedConfigInterface(sdsConfigInit, sdsConfigSet, sdsConfigGet, sdsConfigRewrite, apply) \ + .type = SDS_CONFIG, \ .data.sds = { \ .config = &(config_addr), \ .default_value = (default), \ @@ -1780,16 +1880,16 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf } /* Enum configs */ -static void enumConfigInit(typeData data) { - *data.enumd.config = data.enumd.default_value; +static void enumConfigInit(standardConfig *config) { + *config->data.enumd.config = config->data.enumd.default_value; } -static int enumConfigSet(typeData data, sds *argv, int argc, const char **err) { +static int enumConfigSet(standardConfig *config, sds *argv, int argc, const char **err) { UNUSED(argc); - int enumval = configEnumGetValue(data.enumd.enum_value, argv[0]); + int enumval = configEnumGetValue(config->data.enumd.enum_value, argv[0]); if (enumval == INT_MIN) { sds enumerr = sdsnew("argument must be one of the following: "); - configEnum *enumNode = data.enumd.enum_value; + configEnum *enumNode = config->data.enumd.enum_value; while(enumNode->name != NULL) { enumerr = sdscatlen(enumerr, enumNode->name, strlen(enumNode->name)); @@ -1805,27 +1905,32 @@ static int enumConfigSet(typeData data, sds *argv, int argc, const char **err) { *err = loadbuf; return 0; } - if (data.enumd.is_valid_fn && !data.enumd.is_valid_fn(enumval, err)) + if (config->data.enumd.is_valid_fn && !config->data.enumd.is_valid_fn(enumval, err)) return 0; - int prev = *(data.enumd.config); + int prev = config->flags & MODULE_CONFIG ? getModuleEnumConfig(config->privdata) : *(config->data.enumd.config); if (prev != enumval) { - *(data.enumd.config) = enumval; + if (config->flags & MODULE_CONFIG) + return setModuleEnumConfig(config->privdata, enumval, err); + *(config->data.enumd.config) = enumval; return 1; } return 2; } -static sds enumConfigGet(typeData data) { - return sdsnew(configEnumGetNameOrUnknown(data.enumd.enum_value,*data.enumd.config)); +static sds enumConfigGet(standardConfig *config) { + int val = config->flags & MODULE_CONFIG ? getModuleEnumConfig(config->privdata) : *(config->data.enumd.config); + return sdsnew(configEnumGetNameOrUnknown(config->data.enumd.enum_value,val)); } -static void enumConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) { - rewriteConfigEnumOption(state, name,*(data.enumd.config), data.enumd.enum_value, data.enumd.default_value); +static void enumConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) { + int val = config->flags & MODULE_CONFIG ? getModuleEnumConfig(config->privdata) : *(config->data.enumd.config); + rewriteConfigEnumOption(state, name, val, config->data.enumd.enum_value, config->data.enumd.default_value); } #define createEnumConfig(name, alias, flags, enum, config_addr, default, is_valid, apply) { \ embedCommonConfig(name, alias, flags) \ embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite, apply) \ + .type = ENUM_CONFIG, \ .data.enumd = { \ .config = &(config_addr), \ .default_value = (default), \ @@ -1836,69 +1941,74 @@ static void enumConfigRewrite(typeData data, const char *name, struct rewriteCon /* Gets a 'long long val' and sets it into the union, using a macro to get * compile time type check. */ -#define SET_NUMERIC_TYPE(val) \ - if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \ - *(data.numeric.config.i) = (int) val; \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \ - *(data.numeric.config.ui) = (unsigned int) val; \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \ - *(data.numeric.config.l) = (long) val; \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \ - *(data.numeric.config.ul) = (unsigned long) val; \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \ - *(data.numeric.config.ll) = (long long) val; \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \ - *(data.numeric.config.ull) = (unsigned long long) val; \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \ - *(data.numeric.config.st) = (size_t) val; \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \ - *(data.numeric.config.sst) = (ssize_t) val; \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \ - *(data.numeric.config.ot) = (off_t) val; \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \ - *(data.numeric.config.tt) = (time_t) val; \ +int setNumericType(standardConfig *config, long long val, const char **err) { + if (config->data.numeric.numeric_type == NUMERIC_TYPE_INT) { + *(config->data.numeric.config.i) = (int) val; + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_UINT) { + *(config->data.numeric.config.ui) = (unsigned int) val; + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG) { + *(config->data.numeric.config.l) = (long) val; + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { + *(config->data.numeric.config.ul) = (unsigned long) val; + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { + if (config->flags & MODULE_CONFIG) + return setModuleNumericConfig(config->privdata, val, err); + else *(config->data.numeric.config.ll) = (long long) val; + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { + *(config->data.numeric.config.ull) = (unsigned long long) val; + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { + *(config->data.numeric.config.st) = (size_t) val; + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { + *(config->data.numeric.config.sst) = (ssize_t) val; + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { + *(config->data.numeric.config.ot) = (off_t) val; + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { + *(config->data.numeric.config.tt) = (time_t) val; } + return 1; +} /* Gets a 'long long val' and sets it with the value from the union, using a * macro to get compile time type check. */ #define GET_NUMERIC_TYPE(val) \ - if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \ - val = *(data.numeric.config.i); \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \ - val = *(data.numeric.config.ui); \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \ - val = *(data.numeric.config.l); \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \ - val = *(data.numeric.config.ul); \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \ - val = *(data.numeric.config.ll); \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \ - val = *(data.numeric.config.ull); \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \ - val = *(data.numeric.config.st); \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \ - val = *(data.numeric.config.sst); \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \ - val = *(data.numeric.config.ot); \ - } else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \ - val = *(data.numeric.config.tt); \ + if (config->data.numeric.numeric_type == NUMERIC_TYPE_INT) { \ + val = *(config->data.numeric.config.i); \ + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \ + val = *(config->data.numeric.config.ui); \ + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \ + val = *(config->data.numeric.config.l); \ + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \ + val = *(config->data.numeric.config.ul); \ + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \ + if (config->flags & MODULE_CONFIG) val = getModuleNumericConfig(config->privdata); \ + else val = *(config->data.numeric.config.ll); \ + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \ + val = *(config->data.numeric.config.ull); \ + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \ + val = *(config->data.numeric.config.st); \ + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \ + val = *(config->data.numeric.config.sst); \ + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \ + val = *(config->data.numeric.config.ot); \ + } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \ + val = *(config->data.numeric.config.tt); \ } /* Numeric configs */ -static void numericConfigInit(typeData data) { - SET_NUMERIC_TYPE(data.numeric.default_value) +static void numericConfigInit(standardConfig *config) { + setNumericType(config, config->data.numeric.default_value, NULL); } -static int numericBoundaryCheck(typeData data, long long ll, const char **err) { - if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG || - data.numeric.numeric_type == NUMERIC_TYPE_UINT || - data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { +static int numericBoundaryCheck(standardConfig *config, long long ll, const char **err) { + if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG || + config->data.numeric.numeric_type == NUMERIC_TYPE_UINT || + config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { /* Boundary check for unsigned types */ unsigned long long ull = ll; - unsigned long long upper_bound = data.numeric.upper_bound; - unsigned long long lower_bound = data.numeric.lower_bound; + unsigned long long upper_bound = config->data.numeric.upper_bound; + unsigned long long lower_bound = config->data.numeric.lower_bound; if (ull > upper_bound || ull < lower_bound) { - if (data.numeric.flags & OCTAL_CONFIG) { + if (config->data.numeric.flags & OCTAL_CONFIG) { snprintf(loadbuf, LOADBUF_SIZE, "argument must be between %llo and %llo inclusive", lower_bound, @@ -1914,21 +2024,21 @@ static int numericBoundaryCheck(typeData data, long long ll, const char **err) { } } else { /* Boundary check for percentages */ - if (data.numeric.flags & PERCENT_CONFIG && ll < 0) { - if (ll < data.numeric.lower_bound) { + if (config->data.numeric.flags & PERCENT_CONFIG && ll < 0) { + if (ll < config->data.numeric.lower_bound) { snprintf(loadbuf, LOADBUF_SIZE, "percentage argument must be less or equal to %lld", - -data.numeric.lower_bound); + -config->data.numeric.lower_bound); *err = loadbuf; return 0; } } /* Boundary check for signed types */ - else if (ll > data.numeric.upper_bound || ll < data.numeric.lower_bound) { + else if (ll > config->data.numeric.upper_bound || ll < config->data.numeric.lower_bound) { snprintf(loadbuf, LOADBUF_SIZE, "argument must be between %lld and %lld inclusive", - data.numeric.lower_bound, - data.numeric.upper_bound); + config->data.numeric.lower_bound, + config->data.numeric.upper_bound); *err = loadbuf; return 0; } @@ -1936,9 +2046,9 @@ static int numericBoundaryCheck(typeData data, long long ll, const char **err) { return 1; } -static int numericParseString(typeData data, sds value, const char **err, long long *res) { +static int numericParseString(standardConfig *config, sds value, const char **err, long long *res) { /* First try to parse as memory */ - if (data.numeric.flags & MEMORY_CONFIG) { + if (config->data.numeric.flags & MEMORY_CONFIG) { int memerr; *res = memtoull(value, &memerr); if (!memerr) @@ -1946,7 +2056,7 @@ static int numericParseString(typeData data, sds value, const char **err, long l } /* Attempt to parse as percent */ - if (data.numeric.flags & PERCENT_CONFIG && + if (config->data.numeric.flags & PERCENT_CONFIG && sdslen(value) > 1 && value[sdslen(value)-1] == '%' && string2ll(value, sdslen(value)-1, res) && *res >= 0) { @@ -1956,7 +2066,7 @@ static int numericParseString(typeData data, sds value, const char **err, long l } /* Attempt to parse as an octal number */ - if (data.numeric.flags & OCTAL_CONFIG) { + if (config->data.numeric.flags & OCTAL_CONFIG) { char *endptr; errno = 0; *res = strtoll(value, &endptr, 8); @@ -1965,58 +2075,57 @@ static int numericParseString(typeData data, sds value, const char **err, long l } /* Attempt a simple number (no special flags set) */ - if (!data.numeric.flags && string2ll(value, sdslen(value), res)) + if (!config->data.numeric.flags && string2ll(value, sdslen(value), res)) return 1; /* Select appropriate error string */ - if (data.numeric.flags & MEMORY_CONFIG && - data.numeric.flags & PERCENT_CONFIG) + if (config->data.numeric.flags & MEMORY_CONFIG && + config->data.numeric.flags & PERCENT_CONFIG) *err = "argument must be a memory or percent value" ; - else if (data.numeric.flags & MEMORY_CONFIG) + else if (config->data.numeric.flags & MEMORY_CONFIG) *err = "argument must be a memory value"; - else if (data.numeric.flags & OCTAL_CONFIG) + else if (config->data.numeric.flags & OCTAL_CONFIG) *err = "argument couldn't be parsed as an octal number"; else *err = "argument couldn't be parsed into an integer"; return 0; } -static int numericConfigSet(typeData data, sds *argv, int argc, const char **err) { +static int numericConfigSet(standardConfig *config, sds *argv, int argc, const char **err) { UNUSED(argc); long long ll, prev = 0; - if (!numericParseString(data, argv[0], err, &ll)) + if (!numericParseString(config, argv[0], err, &ll)) return 0; - if (!numericBoundaryCheck(data, ll, err)) + if (!numericBoundaryCheck(config, ll, err)) return 0; - if (data.numeric.is_valid_fn && !data.numeric.is_valid_fn(ll, err)) + if (config->data.numeric.is_valid_fn && !config->data.numeric.is_valid_fn(ll, err)) return 0; GET_NUMERIC_TYPE(prev) if (prev != ll) { - SET_NUMERIC_TYPE(ll) - return 1; + return setNumericType(config, ll, err); } return 2; } -static sds numericConfigGet(typeData data) { +static sds numericConfigGet(standardConfig *config) { char buf[128]; long long value = 0; GET_NUMERIC_TYPE(value) - if (data.numeric.flags & PERCENT_CONFIG && value < 0) { + if (config->data.numeric.flags & PERCENT_CONFIG && value < 0) { int len = ll2string(buf, sizeof(buf), -value); buf[len] = '%'; buf[len+1] = '\0'; } - else if (data.numeric.flags & MEMORY_CONFIG) { + else if (config->data.numeric.flags & MEMORY_CONFIG) { ull2string(buf, sizeof(buf), value); - } else if (data.numeric.flags & OCTAL_CONFIG) { + } else if (config->data.numeric.flags & OCTAL_CONFIG) { snprintf(buf, sizeof(buf), "%llo", value); } else { ll2string(buf, sizeof(buf), value); @@ -2024,25 +2133,26 @@ static sds numericConfigGet(typeData data) { return sdsnew(buf); } -static void numericConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) { +static void numericConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) { long long value = 0; GET_NUMERIC_TYPE(value) - if (data.numeric.flags & PERCENT_CONFIG && value < 0) { - rewriteConfigPercentOption(state, name, -value, data.numeric.default_value); - } else if (data.numeric.flags & MEMORY_CONFIG) { - rewriteConfigBytesOption(state, name, value, data.numeric.default_value); - } else if (data.numeric.flags & OCTAL_CONFIG) { - rewriteConfigOctalOption(state, name, value, data.numeric.default_value); + if (config->data.numeric.flags & PERCENT_CONFIG && value < 0) { + rewriteConfigPercentOption(state, name, -value, config->data.numeric.default_value); + } else if (config->data.numeric.flags & MEMORY_CONFIG) { + rewriteConfigBytesOption(state, name, value, config->data.numeric.default_value); + } else if (config->data.numeric.flags & OCTAL_CONFIG) { + rewriteConfigOctalOption(state, name, value, config->data.numeric.default_value); } else { - rewriteConfigNumericalOption(state, name, value, data.numeric.default_value); + rewriteConfigNumericalOption(state, name, value, config->data.numeric.default_value); } } #define embedCommonNumericalConfig(name, alias, _flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) { \ embedCommonConfig(name, alias, _flags) \ embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite, apply) \ + .type = NUMERIC_CONFIG, \ .data.numeric = { \ .lower_bound = (lower), \ .upper_bound = (upper), \ @@ -2121,6 +2231,7 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite } #define createSpecialConfig(name, alias, modifiable, setfn, getfn, rewritefn, applyfn) { \ + .type = SPECIAL_CONFIG, \ embedCommonConfig(name, alias, modifiable) \ embedConfigInterface(NULL, setfn, getfn, rewritefn, applyfn) \ } @@ -2397,8 +2508,8 @@ static int applyTLSPort(const char **err) { #endif /* USE_OPENSSL */ -static int setConfigDirOption(typeData data, sds *argv, int argc, const char **err) { - UNUSED(data); +static int setConfigDirOption(standardConfig *config, sds *argv, int argc, const char **err) { + UNUSED(config); if (argc != 1) { *err = "wrong number of arguments"; return 0; @@ -2410,8 +2521,8 @@ static int setConfigDirOption(typeData data, sds *argv, int argc, const char **e return 1; } -static sds getConfigDirOption(typeData data) { - UNUSED(data); +static sds getConfigDirOption(standardConfig *config) { + UNUSED(config); char buf[1024]; if (getcwd(buf,sizeof(buf)) == NULL) @@ -2420,8 +2531,8 @@ static sds getConfigDirOption(typeData data) { return sdsnew(buf); } -static int setConfigSaveOption(typeData data, sds *argv, int argc, const char **err) { - UNUSED(data); +static int setConfigSaveOption(standardConfig *config, sds *argv, int argc, const char **err) { + UNUSED(config); int j; /* Special case: treat single arg "" as zero args indicating empty save configuration */ @@ -2473,8 +2584,8 @@ static int setConfigSaveOption(typeData data, sds *argv, int argc, const char ** return 1; } -static sds getConfigSaveOption(typeData data) { - UNUSED(data); +static sds getConfigSaveOption(standardConfig *config) { + UNUSED(config); sds buf = sdsempty(); int j; @@ -2489,13 +2600,13 @@ static sds getConfigSaveOption(typeData data) { return buf; } -static int setConfigClientOutputBufferLimitOption(typeData data, sds *argv, int argc, const char **err) { - UNUSED(data); +static int setConfigClientOutputBufferLimitOption(standardConfig *config, sds *argv, int argc, const char **err) { + UNUSED(config); return updateClientOutputBufferLimit(argv, argc, err); } -static sds getConfigClientOutputBufferLimitOption(typeData data) { - UNUSED(data); +static sds getConfigClientOutputBufferLimitOption(standardConfig *config) { + UNUSED(config); sds buf = sdsempty(); int j; for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) { @@ -2513,11 +2624,11 @@ static sds getConfigClientOutputBufferLimitOption(typeData data) { /* Parse an array of CONFIG_OOM_COUNT sds strings, validate and populate * server.oom_score_adj_values if valid. */ -static int setConfigOOMScoreAdjValuesOption(typeData data, sds *argv, int argc, const char **err) { +static int setConfigOOMScoreAdjValuesOption(standardConfig *config, sds *argv, int argc, const char **err) { int i; int values[CONFIG_OOM_COUNT]; int change = 0; - UNUSED(data); + UNUSED(config); if (argc != CONFIG_OOM_COUNT) { *err = "wrong number of arguments"; @@ -2558,8 +2669,8 @@ static int setConfigOOMScoreAdjValuesOption(typeData data, sds *argv, int argc, return change ? 1 : 2; } -static sds getConfigOOMScoreAdjValuesOption(typeData data) { - UNUSED(data); +static sds getConfigOOMScoreAdjValuesOption(standardConfig *config) { + UNUSED(config); sds buf = sdsempty(); int j; @@ -2572,8 +2683,8 @@ static sds getConfigOOMScoreAdjValuesOption(typeData data) { return buf; } -static int setConfigNotifyKeyspaceEventsOption(typeData data, sds *argv, int argc, const char **err) { - UNUSED(data); +static int setConfigNotifyKeyspaceEventsOption(standardConfig *config, sds *argv, int argc, const char **err) { + UNUSED(config); if (argc != 1) { *err = "wrong number of arguments"; return 0; @@ -2587,13 +2698,13 @@ static int setConfigNotifyKeyspaceEventsOption(typeData data, sds *argv, int arg return 1; } -static sds getConfigNotifyKeyspaceEventsOption(typeData data) { - UNUSED(data); +static sds getConfigNotifyKeyspaceEventsOption(standardConfig *config) { + UNUSED(config); return keyspaceEventsFlagsToString(server.notify_keyspace_events); } -static int setConfigBindOption(typeData data, sds* argv, int argc, const char **err) { - UNUSED(data); +static int setConfigBindOption(standardConfig *config, sds* argv, int argc, const char **err) { + UNUSED(config); int j; if (argc > CONFIG_BINDADDR_MAX) { @@ -2615,8 +2726,8 @@ static int setConfigBindOption(typeData data, sds* argv, int argc, const char ** return 1; } -static int setConfigReplicaOfOption(typeData data, sds* argv, int argc, const char **err) { - UNUSED(data); +static int setConfigReplicaOfOption(standardConfig *config, sds* argv, int argc, const char **err) { + UNUSED(config); if (argc != 2) { *err = "wrong number of arguments"; @@ -2639,13 +2750,13 @@ static int setConfigReplicaOfOption(typeData data, sds* argv, int argc, const ch return 1; } -static sds getConfigBindOption(typeData data) { - UNUSED(data); +static sds getConfigBindOption(standardConfig *config) { + UNUSED(config); return sdsjoin(server.bindaddr,server.bindaddr_count," "); } -static sds getConfigReplicaOfOption(typeData data) { - UNUSED(data); +static sds getConfigReplicaOfOption(standardConfig *config) { + UNUSED(config); char buf[256]; if (server.masterhost) snprintf(buf,sizeof(buf),"%s %d", @@ -2661,8 +2772,8 @@ int allowProtectedAction(int config, client *c) { } -static int setConfigLatencyTrackingInfoPercentilesOutputOption(typeData data, sds *argv, int argc, const char **err) { - UNUSED(data); +static int setConfigLatencyTrackingInfoPercentilesOutputOption(standardConfig *config, sds *argv, int argc, const char **err) { + UNUSED(config); zfree(server.latency_tracking_info_percentiles); server.latency_tracking_info_percentiles = NULL; server.latency_tracking_info_percentiles_len = argc; @@ -2694,8 +2805,8 @@ configerr: return 0; } -static sds getConfigLatencyTrackingInfoPercentilesOutputOption(typeData data) { - UNUSED(data); +static sds getConfigLatencyTrackingInfoPercentilesOutputOption(standardConfig *config) { + UNUSED(config); sds buf = sdsempty(); for (int j = 0; j < server.latency_tracking_info_percentiles_len; j++) { char fbuf[128]; @@ -2709,8 +2820,8 @@ static sds getConfigLatencyTrackingInfoPercentilesOutputOption(typeData data) { } /* Rewrite the latency-tracking-info-percentiles option. */ -void rewriteConfigLatencyTrackingInfoPercentilesOutputOption(typeData data, const char *name, struct rewriteConfigState *state) { - UNUSED(data); +void rewriteConfigLatencyTrackingInfoPercentilesOutputOption(standardConfig *config, const char *name, struct rewriteConfigState *state) { + UNUSED(config); sds line = sdsnew(name); /* Rewrite latency-tracking-info-percentiles parameters, * or an empty 'latency-tracking-info-percentiles ""' line to avoid the @@ -2729,7 +2840,7 @@ void rewriteConfigLatencyTrackingInfoPercentilesOutputOption(typeData data, cons rewriteConfigRewriteLine(state,name,line,1); } -standardConfig configs[] = { +standardConfig static_configs[] = { /* Bool configs */ createBoolConfig("rdbchecksum", NULL, IMMUTABLE_CONFIG, server.rdb_checksum, 1, NULL, NULL), createBoolConfig("daemonize", NULL, IMMUTABLE_CONFIG, server.daemonize, 0, NULL, NULL), @@ -2771,7 +2882,7 @@ standardConfig configs[] = { createBoolConfig("crash-log-enabled", NULL, MODIFIABLE_CONFIG, server.crashlog_enabled, 1, NULL, updateSighandlerEnabled), createBoolConfig("crash-memcheck-enabled", NULL, MODIFIABLE_CONFIG, server.memcheck_enabled, 1, NULL, NULL), createBoolConfig("use-exit-on-panic", NULL, MODIFIABLE_CONFIG | HIDDEN_CONFIG, server.use_exit_on_panic, 0, NULL, NULL), - createBoolConfig("disable-thp", NULL, MODIFIABLE_CONFIG, server.disable_thp, 1, NULL, NULL), + createBoolConfig("disable-thp", NULL, IMMUTABLE_CONFIG, server.disable_thp, 1, NULL, NULL), createBoolConfig("cluster-allow-replica-migration", NULL, MODIFIABLE_CONFIG, server.cluster_allow_replica_migration, 1, NULL, NULL), createBoolConfig("replica-announced", NULL, MODIFIABLE_CONFIG, server.replica_announced, 1, NULL, NULL), createBoolConfig("latency-tracking", NULL, MODIFIABLE_CONFIG, server.latency_tracking_enabled, 1, NULL, NULL), @@ -2930,10 +3041,105 @@ standardConfig configs[] = { createSpecialConfig("replicaof", "slaveof", IMMUTABLE_CONFIG | MULTI_ARG_CONFIG, setConfigReplicaOfOption, getConfigReplicaOfOption, rewriteConfigReplicaOfOption, NULL), createSpecialConfig("latency-tracking-info-percentiles", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigLatencyTrackingInfoPercentilesOutputOption, getConfigLatencyTrackingInfoPercentilesOutputOption, rewriteConfigLatencyTrackingInfoPercentilesOutputOption, NULL), - /* NULL Terminator */ + /* NULL Terminator, this is dropped when we convert to the runtime array. */ {NULL} }; +/* Create a new config by copying the passed in config. Returns 1 on success + * or 0 when their was already a config with the same name.. */ +int registerConfigValue(const char *name, const standardConfig *config, int alias) { + standardConfig *new = zmalloc(sizeof(standardConfig)); + memcpy(new, config, sizeof(standardConfig)); + if (alias) { + new->flags |= ALIAS_CONFIG; + new->name = config->alias; + new->alias = config->name; + } + + return dictAdd(configs, sdsnew(name), new) == DICT_OK; +} + +/* Initialize configs to their default values and create and populate the + * runtime configuration dictionary. */ +void initConfigValues() { + configs = dictCreate(&sdsHashDictType); + dictExpand(configs, sizeof(static_configs) / sizeof(standardConfig)); + for (standardConfig *config = static_configs; config->name != NULL; config++) { + if (config->interface.init) config->interface.init(config); + /* Add the primary config to the dictionary. */ + int ret = registerConfigValue(config->name, config, 0); + serverAssert(ret); + + /* Aliases are the same as their primary counter parts, but they + * also have a flag indicating they are the alias. */ + if (config->alias) { + int ret = registerConfigValue(config->alias, config, ALIAS_CONFIG); + serverAssert(ret); + } + } +} + +/* Remove a config by name from the configs dict. */ +void removeConfig(sds name) { + standardConfig *config = lookupConfig(name); + if (!config) return; + if (config->flags & MODULE_CONFIG) { + sdsfree((sds) config->name); + if (config->type == ENUM_CONFIG) { + configEnum *enumNode = config->data.enumd.enum_value; + while(enumNode->name != NULL) { + zfree(enumNode->name); + enumNode++; + } + zfree(config->data.enumd.enum_value); + } else if (config->type == SDS_CONFIG) { + if (config->data.sds.default_value) sdsfree((sds)config->data.sds.default_value); + } + } + dictDelete(configs, name); +} + +/*----------------------------------------------------------------------------- + * Module Config + *----------------------------------------------------------------------------*/ + +/* Create a bool/string/enum/numeric standardConfig for a module config in the configs dictionary */ +void addModuleBoolConfig(const char *module_name, const char *name, int flags, void *privdata, int default_val) { + sds config_name = sdscatfmt(sdsempty(), "%s.%s", module_name, name); + int config_dummy_address; + standardConfig module_config = createBoolConfig(config_name, NULL, flags | MODULE_CONFIG, config_dummy_address, default_val, NULL, NULL); + module_config.data.yesno.config = NULL; + module_config.privdata = privdata; + registerConfigValue(config_name, &module_config, 0); +} + +void addModuleStringConfig(const char *module_name, const char *name, int flags, void *privdata, sds default_val) { + sds config_name = sdscatfmt(sdsempty(), "%s.%s", module_name, name); + sds config_dummy_address; + standardConfig module_config = createSDSConfig(config_name, NULL, flags | MODULE_CONFIG, 0, config_dummy_address, default_val, NULL, NULL); + module_config.data.sds.config = NULL; + module_config.privdata = privdata; + registerConfigValue(config_name, &module_config, 0); +} + +void addModuleEnumConfig(const char *module_name, const char *name, int flags, void *privdata, int default_val, configEnum *enum_vals) { + sds config_name = sdscatfmt(sdsempty(), "%s.%s", module_name, name); + int config_dummy_address; + standardConfig module_config = createEnumConfig(config_name, NULL, flags | MODULE_CONFIG, enum_vals, config_dummy_address, default_val, NULL, NULL); + module_config.data.enumd.config = NULL; + module_config.privdata = privdata; + registerConfigValue(config_name, &module_config, 0); +} + +void addModuleNumericConfig(const char *module_name, const char *name, int flags, void *privdata, long long default_val, int conf_flags, long long lower, long long upper) { + sds config_name = sdscatfmt(sdsempty(), "%s.%s", module_name, name); + long long config_dummy_address; + standardConfig module_config = createLongLongConfig(config_name, NULL, flags | MODULE_CONFIG, lower, upper, config_dummy_address, default_val, conf_flags, NULL, NULL); + module_config.data.numeric.config.ll = NULL; + module_config.privdata = privdata; + registerConfigValue(config_name, &module_config, 0); +} + /*----------------------------------------------------------------------------- * CONFIG HELP *----------------------------------------------------------------------------*/ @@ -2975,8 +3181,10 @@ void configRewriteCommand(client *c) { return; } if (rewriteConfig(server.configfile, 0) == -1) { - serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(errno)); - addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno)); + /* save errno in case of being tainted. */ + int err = errno; + serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(err)); + addReplyErrorFormat(c,"Rewriting config file: %s", strerror(err)); } else { serverLog(LL_WARNING,"CONFIG REWRITE executed with success."); addReply(c,shared.ok); |