summaryrefslogtreecommitdiff
path: root/src/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/config.c')
-rw-r--r--src/config.c878
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);