summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryoav-steinberg <yoav@monfort.co.il>2021-12-01 10:15:11 +0200
committerGitHub <noreply@github.com>2021-12-01 10:15:11 +0200
commit0e5b813ef94b373f82bc75efcf3405f2c81af3dc (patch)
tree0e915635722ffbbca928bcc6cd4f023adac9c6c5
parent21aa1d4b9118bd42d254c7f9e6157c44d8b1a61d (diff)
downloadredis-0e5b813ef94b373f82bc75efcf3405f2c81af3dc.tar.gz
Multiparam config set (#9748)
We can now do: `config set maxmemory 10m repl-backlog-size 5m` ## Basic algorithm to support "transaction like" config sets: 1. Backup all relevant current values (via get). 2. Run "verify" and "set" on everything, if we fail run "restore". 3. Run "apply" on everything (optional optimization: skip functions already run). If we fail run "restore". 4. Return success. ### restore 1. Run set on everything in backup. If we fail log it and continue (this puts us in an undefined state but we decided it's better than the alternative of panicking). This indicates either a bug or some unsupported external state. 2. Run apply on everything in backup (optimization: skip functions already run). If we fail log it (see comment above). 3. Return error. ## Implementation/design changes: * Apply function are idempotent (have no effect if they are run more than once for the same config). * No indication in set functions if we're reading the config or running from the `CONFIG SET` command (removed `update` argument). * Set function should set some config variable and assume an (optional) apply function will use that later to apply. If we know this setting can be safely applied immediately and can always be reverted and doesn't depend on any other configuration we can apply immediately from within the set function (and not store the setting anywhere). This is the case of this `dir` config, for example, which has no apply function. No apply function is need also in the case that setting the variable in the `server` struct is all that needs to be done to make the configuration take effect. Note that the original concept of `update_fn`, which received the old and new values was removed and replaced by the optional apply function. * Apply functions use settings written to the `server` struct and don't receive any inputs. * I take care that for the generic (non-special) configs if there's no change I avoid calling the setter (possible optimization: avoid calling the apply function as well). * Passing the same config parameter more than once to `config set` will fail. You can't do `config set my-setting value1 my-setting value2`. Note that getting `save` in the context of the conf file parsing to work here as before was a pain. The conf file supports an aggregate `save` definition, where each `save` line is added to the server's save params. This is unlike any other line in the config file where each line overwrites any previous configuration. Since we now support passing multiple save params in a single line (see top comments about `save` in https://github.com/redis/redis/pull/9644) we should deprecate the aggregate nature of this config line and perhaps reduce this ugly code in the future.
-rw-r--r--redis.conf12
-rw-r--r--src/config.c762
-rw-r--r--src/replication.c16
-rw-r--r--src/server.c74
-rw-r--r--src/server.h5
-rw-r--r--src/tls.c6
-rw-r--r--tests/unit/introspection.tcl110
7 files changed, 532 insertions, 453 deletions
diff --git a/redis.conf b/redis.conf
index 47a393c7a..b7824c372 100644
--- a/redis.conf
+++ b/redis.conf
@@ -378,10 +378,10 @@ proc-title-template "{title} {listen-addr} {server-mode}"
# Save the DB to disk.
#
-# save <seconds> <changes>
+# save <seconds> <changes> [<seconds> <changes> ...]
#
-# Redis will save the DB if both the given number of seconds and the given
-# number of write operations against the DB occurred.
+# Redis will save the DB if the given number of seconds elapsed and it
+# surpassed the given number of write operations against the DB.
#
# Snapshotting can be completely disabled with a single empty string argument
# as in following example:
@@ -393,11 +393,9 @@ proc-title-template "{title} {listen-addr} {server-mode}"
# * After 300 seconds (5 minutes) if at least 100 keys changed
# * After 60 seconds if at least 10000 keys changed
#
-# You can set these explicitly by uncommenting the three following lines.
+# You can set these explicitly by uncommenting the following line.
#
-# save 3600 1
-# save 300 100
-# save 60 10000
+# save 3600 1 300 100 60 10000
# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
diff --git a/src/config.c b/src/config.c
index 3b97ea5b5..2b2ff737d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -142,12 +142,6 @@ int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT] = { 0, 200, 800 };
* int is_valid_fn(val, err)
* Return 1 when val is valid, and 0 when invalid.
* Optionally set err to a static error string.
- * int update_fn(val, prev, err)
- * This function is called only for CONFIG SET command (not at config file parsing)
- * It is called after the actual config is applied,
- * Return 1 for success, and 0 for failure.
- * Optionally set err to a static error string.
- * On failure the config change will be reverted.
*/
/* Configuration values that require no special handling to set, get, load or
@@ -156,14 +150,12 @@ 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 (*is_valid_fn)(int val, const char **err); /* Optional function to check validity of new value (generic doc above) */
- int (*update_fn)(int val, int prev, const char **err); /* Optional function to apply new value at runtime (generic doc above) */
} boolConfigData;
typedef struct stringConfigData {
char **config; /* Pointer to the server config this value is stored in. */
const char *default_value; /* Default value of the config on rewrite. */
int (*is_valid_fn)(char* val, const char **err); /* Optional function to check validity of new value (generic doc above) */
- int (*update_fn)(char* val, char* prev, const char **err); /* Optional function to apply new value at runtime (generic doc above) */
int convert_empty_to_null; /* Boolean indicating if empty strings should
be stored as a NULL value. */
} stringConfigData;
@@ -172,7 +164,6 @@ 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. */
int (*is_valid_fn)(sds val, const char **err); /* Optional function to check validity of new value (generic doc above) */
- int (*update_fn)(sds val, sds prev, const char **err); /* Optional function to apply new value at runtime (generic doc above) */
int convert_empty_to_null; /* Boolean indicating if empty SDS strings should
be stored as a NULL value. */
} sdsConfigData;
@@ -182,7 +173,6 @@ typedef struct enumConfigData {
configEnum *enum_value; /* The underlying enum type this data represents */
const 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) */
- int (*update_fn)(int val, int prev, const char **err); /* Optional function to apply new value at runtime (generic doc above) */
} enumConfigData;
typedef enum numericType {
@@ -222,7 +212,6 @@ typedef struct numericConfigData {
long long upper_bound; /* The upper bound of this numeric value */
const 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) */
- int (*update_fn)(long long val, long long prev, const char **err); /* Optional function to apply new value at runtime (generic doc above) */
} numericConfigData;
typedef union typeData {
@@ -233,14 +222,20 @@ typedef union typeData {
numericConfigData numeric;
} typeData;
+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);
- /* Called on server startup and CONFIG SET, returns 1 on success, 0 on error
- * and can set a verbose err string, update is true when called from CONFIG SET */
- int (*set)(typeData data, sds *argv, int argc, int update, const char **err);
- /* Called on CONFIG GET, required to add output to the client */
- void (*get)(client *c, typeData data);
+ /* 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);
+ /* 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);
/* Called on CONFIG REWRITE, required to rewrite the config state */
void (*rewrite)(typeData data, const char *name, struct rewriteConfigState *state);
} typeInterface;
@@ -333,63 +328,6 @@ void queueLoadModule(sds path, sds *argv, int argc) {
listAddNodeTail(server.loadmodule_queue,loadmod);
}
-/* Parse an array of CONFIG_OOM_COUNT sds strings, validate and populate
- * server.oom_score_adj_values if valid.
- */
-
-static int updateOOMScoreAdjValues(sds *args, const char **err, int apply) {
- int i;
- int values[CONFIG_OOM_COUNT];
-
- for (i = 0; i < CONFIG_OOM_COUNT; i++) {
- char *eptr;
- long long val = strtoll(args[i], &eptr, 10);
-
- if (*eptr != '\0' || val < -2000 || val > 2000) {
- if (err) *err = "Invalid oom-score-adj-values, elements must be between -2000 and 2000.";
- return C_ERR;
- }
-
- values[i] = val;
- }
-
- /* Verify that the values make sense. If they don't omit a warning but
- * keep the configuration, which may still be valid for privileged processes.
- */
-
- if (values[CONFIG_OOM_REPLICA] < values[CONFIG_OOM_MASTER] ||
- values[CONFIG_OOM_BGCHILD] < values[CONFIG_OOM_REPLICA]) {
- serverLog(LL_WARNING,
- "The oom-score-adj-values configuration may not work for non-privileged processes! "
- "Please consult the documentation.");
- }
-
- /* Store values, retain previous config for rollback in case we fail. */
- int old_values[CONFIG_OOM_COUNT];
- for (i = 0; i < CONFIG_OOM_COUNT; i++) {
- old_values[i] = server.oom_score_adj_values[i];
- server.oom_score_adj_values[i] = values[i];
- }
-
- /* When parsing the config file, we want to apply only when all is done. */
- if (!apply)
- return C_OK;
-
- /* Update */
- if (setOOMScoreAdj(-1) == C_ERR) {
- /* Roll back */
- for (i = 0; i < CONFIG_OOM_COUNT; i++)
- server.oom_score_adj_values[i] = old_values[i];
-
- if (err)
- *err = "Failed to apply oom-score-adj-values configuration, check server logs.";
-
- return C_ERR;
- }
-
- return C_OK;
-}
-
/* Parse an array of `arg_len` sds strings, validate and populate
* server.client_obuf_limits if valid.
* Used in CONFIG SET and configuration file parsing. */
@@ -452,12 +390,18 @@ void initConfigValues() {
}
}
+/* 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. */
+static int reading_config_file;
+
void loadServerConfigFromString(char *config) {
char buf[1024];
const char *err = NULL;
int linenum = 0, totlines, i;
sds *lines;
+ reading_config_file = 1;
lines = sdssplitlen(config,strlen(config),"\n",1,&totlines);
for (i = 0; i < totlines; i++) {
@@ -497,7 +441,7 @@ void loadServerConfigFromString(char *config) {
goto loaderr;
}
/* Set config using all arguments that follows */
- if (!config->interface.set(config->data, &argv[1], argc-1, 0, &err)) {
+ if (!config->interface.set(config->data, &argv[1], argc-1, &err)) {
goto loaderr;
}
@@ -594,6 +538,7 @@ void loadServerConfigFromString(char *config) {
if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;
sdsfreesplitres(lines,totlines);
+ reading_config_file = 0;
return;
loaderr:
@@ -687,63 +632,163 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) {
sdsfree(config);
}
+static int performInterfaceSet(standardConfig *config, sds value, const char **errstr) {
+ sds *argv;
+ int argc, res;
+
+ if (config->flags & MULTI_ARG_CONFIG) {
+ argv = sdssplitlen(value, sdslen(value), " ", 1, &argc);
+ } else {
+ argv = (char**)&value;
+ argc = 1;
+ }
+
+ /* Set the config */
+ res = config->interface.set(config->data, 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) {
+ int i;
+ const char *errstr = "unknown error";
+ /* Set all backup values */
+ for (i = 0; i < count; i++) {
+ if (!performInterfaceSet(set_configs[i], old_values[i], &errstr))
+ serverLog(LL_WARNING, "Failed restoring failed CONFIG SET command. Error setting %s to '%s': %s",
+ set_configs[i]->name, old_values[i], errstr);
+ }
+ /* Apply backup */
+ if (apply_fns) {
+ for (i = 0; i < count && apply_fns[i] != NULL; i++) {
+ if (!apply_fns[i](&errstr))
+ serverLog(LL_WARNING, "Failed applying restored failed CONFIG SET command: %s", errstr);
+ }
+ }
+}
+
/*-----------------------------------------------------------------------------
* CONFIG SET implementation
*----------------------------------------------------------------------------*/
+
void configSetCommand(client *c) {
- robj *o;
- sds *argv;
- int argc;
const char *errstr = NULL;
- serverAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));
- serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));
- o = c->argv[3];
+ standardConfig **set_configs; /* TODO: make this a dict for better performance */
+ sds *new_values;
+ sds *old_values = NULL;
+ apply_fn *apply_fns; /* TODO: make this a set for better performance */
+ int config_count, i, j;
+ int invalid_args = 0;
+
+ /* Make sure we have an even number of arguments: conf-val pairs */
+ if (c->argc & 1) {
+ addReplyErrorObject(c, shared.syntaxerr);
+ return;
+ }
+ config_count = (c->argc - 2) / 2;
- /* Iterate the configs that are standard */
- for (standardConfig *config = configs; config->name != NULL; config++) {
- if (!(config->flags & IMMUTABLE_CONFIG) &&
- (!strcasecmp(c->argv[2]->ptr,config->name) ||
- (config->alias && !strcasecmp(c->argv[2]->ptr,config->alias))))
- {
- if (config->flags & SENSITIVE_CONFIG) {
- redactClientCommandArgument(c,3);
- }
+ set_configs = zcalloc(sizeof(standardConfig*)*config_count);
+ new_values = zmalloc(sizeof(sds*)*config_count);
+ old_values = zcalloc(sizeof(sds*)*config_count);
+ apply_fns = zcalloc(sizeof(apply_fn)*config_count);
- if (config->flags & MULTI_ARG_CONFIG) {
- argv = sdssplitlen(o->ptr, sdslen(o->ptr), " ", 1, &argc);
- if (argv == NULL) {
- goto badfmt;
+ /* 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);
}
- } else {
- argv = (char**)&o->ptr;
- argc = 1;
- }
- if (!config->interface.set(config->data, argv, argc, 1, &errstr)) {
- if (config->flags & MULTI_ARG_CONFIG) sdsfreesplitres(argv, argc);
- goto badfmt;
+ if (!invalid_args) {
+ if (config->flags & IMMUTABLE_CONFIG) {
+ /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */
+ errstr = "can't set immutable config";
+ invalid_args = 1;
+ }
+
+ /* 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";
+ invalid_args = 1;
+ break;
+ }
+ }
+ set_configs[i] = config;
+ new_values[i] = c->argv[2+i*2+1]->ptr;
+ }
+ break;
+ }
+ }
+ /* 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]) {
+ errstr = "unrecognized parameter";
+ invalid_args = 1;
+ }
+ }
+
+ 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);
+
+ /* 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);
+ 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) {
+ /* Check if this apply function is already stored */
+ int exists = 0;
+ for (j = 0; apply_fns[j] != NULL && j <= i; j++) {
+ if (apply_fns[j] == set_configs[i]->interface.apply) {
+ exists = 1;
+ break;
+ }
+ }
+ /* Apply function not stored, store it */
+ if (!exists)
+ apply_fns[j] = set_configs[i]->interface.apply;
}
- if (config->flags & MULTI_ARG_CONFIG) sdsfreesplitres(argv, argc);
- addReply(c,shared.ok);
- return;
}
}
- addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
- (char*)c->argv[2]->ptr);
- return;
+ /* Apply all configs after being set */
+ for (i = 0; i < config_count && apply_fns[i] != NULL; i++) {
+ if (!apply_fns[i](&errstr)) {
+ serverLog(LL_WARNING, "Failed applying new %s configuration, restoring previous settings.", set_configs[i]->name);
+ restoreBackupConfig(set_configs, old_values, config_count, apply_fns);
+ goto err;
+ }
+ }
+ addReply(c,shared.ok);
+ goto end;
-badfmt: /* Bad format errors */
+err:
if (errstr) {
- addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s' - %s",
- (char*)o->ptr,
- (char*)c->argv[2]->ptr,
- errstr);
+ addReplyErrorFormat(c,"Config set failed - %s", errstr);
} else {
- addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
- (char*)o->ptr,
- (char*)c->argv[2]->ptr);
+ addReplyError(c,"Invalid arguments");
}
+end:
+ zfree(set_configs);
+ zfree(new_values);
+ for (i = 0; i < config_count; i++)
+ sdsfree(old_values[i]);
+ zfree(old_values);
+ zfree(apply_fns);
}
/*-----------------------------------------------------------------------------
@@ -760,12 +805,12 @@ void configGetCommand(client *c) {
for (standardConfig *config = configs; config->name != NULL; config++) {
if (stringmatch(pattern,config->name,1)) {
addReplyBulkCString(c,config->name);
- config->interface.get(c,config->data);
+ addReplyBulkSds(c, config->interface.get(config->data));
matches++;
}
if (config->alias && stringmatch(pattern,config->alias,1)) {
addReplyBulkCString(c,config->alias);
- config->interface.get(c,config->data);
+ addReplyBulkSds(c, config->interface.get(config->data));
matches++;
}
}
@@ -1516,11 +1561,12 @@ static char loadbuf[LOADBUF_SIZE];
.alias = (config_alias), \
.flags = (config_flags),
-#define embedConfigInterface(initfn, setfn, getfn, rewritefn) .interface = { \
+#define embedConfigInterface(initfn, setfn, getfn, rewritefn, applyfn) .interface = { \
.init = (initfn), \
.set = (setfn), \
.get = (getfn), \
- .rewrite = (rewritefn) \
+ .rewrite = (rewritefn), \
+ .apply = (applyfn) \
},
/* What follows is the generic config types that are supported. To add a new
@@ -1540,7 +1586,7 @@ static void boolConfigInit(typeData data) {
*data.yesno.config = data.yesno.default_value;
}
-static int boolConfigSet(typeData data, sds *argv, int argc, int update, const char **err) {
+static int boolConfigSet(typeData data, sds *argv, int argc, const char **err) {
UNUSED(argc);
int yn = yesnotoi(argv[0]);
if (yn == -1) {
@@ -1550,30 +1596,28 @@ static int boolConfigSet(typeData data, sds *argv, int argc, int update, const c
if (data.yesno.is_valid_fn && !data.yesno.is_valid_fn(yn, err))
return 0;
int prev = *(data.yesno.config);
- *(data.yesno.config) = yn;
- if (update && data.yesno.update_fn && !data.yesno.update_fn(yn, prev, err)) {
- *(data.yesno.config) = prev;
- return 0;
+ if (prev != yn) {
+ *(data.yesno.config) = yn;
+ return 1;
}
- return 1;
+ return 2;
}
-static void boolConfigGet(client *c, typeData data) {
- addReplyBulkCString(c, *data.yesno.config ? "yes" : "no");
+static sds boolConfigGet(typeData data) {
+ return sdsnew(*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);
}
-#define createBoolConfig(name, alias, flags, config_addr, default, is_valid, update) { \
+#define createBoolConfig(name, alias, flags, config_addr, default, is_valid, apply) { \
embedCommonConfig(name, alias, flags) \
- embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite) \
+ embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite, apply) \
.data.yesno = { \
.config = &(config_addr), \
.default_value = (default), \
.is_valid_fn = (is_valid), \
- .update_fn = (update), \
} \
}
@@ -1582,23 +1626,22 @@ 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 int stringConfigSet(typeData data, sds *argv, int argc, int update, const char **err) {
+static int stringConfigSet(typeData data, sds *argv, int argc, const char **err) {
UNUSED(argc);
if (data.string.is_valid_fn && !data.string.is_valid_fn(argv[0], err))
return 0;
char *prev = *data.string.config;
- *data.string.config = (data.string.convert_empty_to_null && !argv[0][0]) ? NULL : zstrdup(argv[0]);
- if (update && data.string.update_fn && !data.string.update_fn(*data.string.config, prev, err)) {
- zfree(*data.string.config);
- *data.string.config = prev;
- return 0;
+ char *new = (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;
+ zfree(prev);
+ return 1;
}
- zfree(prev);
- return 1;
+ return 2;
}
-static void stringConfigGet(client *c, typeData data) {
- addReplyBulkCString(c, *data.string.config ? *data.string.config : "");
+static sds stringConfigGet(typeData data) {
+ return sdsnew(*data.string.config ? *data.string.config : "");
}
static void stringConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
@@ -1610,26 +1653,25 @@ 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 int sdsConfigSet(typeData data, sds *argv, int argc, int update, const char **err) {
+static int sdsConfigSet(typeData data, sds *argv, int argc, const char **err) {
UNUSED(argc);
if (data.sds.is_valid_fn && !data.sds.is_valid_fn(argv[0], err))
return 0;
sds prev = *data.sds.config;
- *data.sds.config = (data.sds.convert_empty_to_null && (sdslen(argv[0]) == 0)) ? NULL : sdsdup(argv[0]);
- if (update && data.sds.update_fn && !data.sds.update_fn(*data.sds.config, prev, err)) {
- sdsfree(*data.sds.config);
- *data.sds.config = prev;
- return 0;
+ sds new = (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);
+ return 1;
}
- sdsfree(prev);
- return 1;
+ return 2;
}
-static void sdsConfigGet(client *c, typeData data) {
+static sds sdsConfigGet(typeData data) {
if (*data.sds.config) {
- addReplyBulkSds(c, sdsdup(*data.sds.config));
+ return sdsdup(*data.sds.config);
} else {
- addReplyBulkCString(c, "");
+ return sdsnew("");
}
}
@@ -1641,26 +1683,24 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf
#define ALLOW_EMPTY_STRING 0
#define EMPTY_STRING_IS_NULL 1
-#define createStringConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, update) { \
+#define createStringConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, apply) { \
embedCommonConfig(name, alias, flags) \
- embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite) \
+ embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite, apply) \
.data.string = { \
.config = &(config_addr), \
.default_value = (default), \
.is_valid_fn = (is_valid), \
- .update_fn = (update), \
.convert_empty_to_null = (empty_to_null), \
} \
}
-#define createSDSConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, update) { \
+#define createSDSConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, apply) { \
embedCommonConfig(name, alias, flags) \
- embedConfigInterface(sdsConfigInit, sdsConfigSet, sdsConfigGet, sdsConfigRewrite) \
+ embedConfigInterface(sdsConfigInit, sdsConfigSet, sdsConfigGet, sdsConfigRewrite, apply) \
.data.sds = { \
.config = &(config_addr), \
.default_value = (default), \
.is_valid_fn = (is_valid), \
- .update_fn = (update), \
.convert_empty_to_null = (empty_to_null), \
} \
}
@@ -1670,7 +1710,7 @@ static void enumConfigInit(typeData data) {
*data.enumd.config = data.enumd.default_value;
}
-static int enumConfigSet(typeData data, sds *argv, int argc, int update, const char **err) {
+static int enumConfigSet(typeData data, sds *argv, int argc, const char **err) {
UNUSED(argc);
int enumval = configEnumGetValue(data.enumd.enum_value, argv[0]);
if (enumval == INT_MIN) {
@@ -1694,30 +1734,28 @@ static int enumConfigSet(typeData data, sds *argv, int argc, int update, const c
if (data.enumd.is_valid_fn && !data.enumd.is_valid_fn(enumval, err))
return 0;
int prev = *(data.enumd.config);
- *(data.enumd.config) = enumval;
- if (update && data.enumd.update_fn && !data.enumd.update_fn(enumval, prev, err)) {
- *(data.enumd.config) = prev;
- return 0;
+ if (prev != enumval) {
+ *(data.enumd.config) = enumval;
+ return 1;
}
- return 1;
+ return 2;
}
-static void enumConfigGet(client *c, typeData data) {
- addReplyBulkCString(c, configEnumGetNameOrUnknown(data.enumd.enum_value,*data.enumd.config));
+static sds enumConfigGet(typeData data) {
+ return sdsnew(configEnumGetNameOrUnknown(data.enumd.enum_value,*data.enumd.config));
}
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);
}
-#define createEnumConfig(name, alias, flags, enum, config_addr, default, is_valid, update) { \
+#define createEnumConfig(name, alias, flags, enum, config_addr, default, is_valid, apply) { \
embedCommonConfig(name, alias, flags) \
- embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite) \
+ embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite, apply) \
.data.enumd = { \
.config = &(config_addr), \
.default_value = (default), \
.is_valid_fn = (is_valid), \
- .update_fn = (update), \
.enum_value = (enum), \
} \
}
@@ -1869,7 +1907,7 @@ static int numericParseString(typeData data, sds value, const char **err, long l
return 0;
}
-static int numericConfigSet(typeData data, sds *argv, int argc, int update, const char **err) {
+static int numericConfigSet(typeData data, sds *argv, int argc, const char **err) {
UNUSED(argc);
long long ll, prev = 0;
@@ -1883,16 +1921,15 @@ static int numericConfigSet(typeData data, sds *argv, int argc, int update, cons
return 0;
GET_NUMERIC_TYPE(prev)
- SET_NUMERIC_TYPE(ll)
-
- if (update && data.numeric.update_fn && !data.numeric.update_fn(ll, prev, err)) {
- SET_NUMERIC_TYPE(prev)
- return 0;
+ if (prev != ll) {
+ SET_NUMERIC_TYPE(ll)
+ return 1;
}
- return 1;
+
+ return 2;
}
-static void numericConfigGet(client *c, typeData data) {
+static sds numericConfigGet(typeData data) {
char buf[128];
long long value = 0;
@@ -1910,7 +1947,7 @@ static void numericConfigGet(client *c, typeData data) {
} else {
ll2string(buf, sizeof(buf), value);
}
- addReplyBulkCString(c, buf);
+ return sdsnew(buf);
}
static void numericConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
@@ -1929,90 +1966,89 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite
}
}
-#define embedCommonNumericalConfig(name, alias, _flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) { \
+#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) \
+ embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite, apply) \
.data.numeric = { \
.lower_bound = (lower), \
.upper_bound = (upper), \
.default_value = (default), \
.is_valid_fn = (is_valid), \
- .update_fn = (update), \
.flags = (num_conf_flags),
-#define createIntConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createIntConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_INT, \
.config.i = &(config_addr) \
} \
}
-#define createUIntConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createUIntConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_UINT, \
.config.ui = &(config_addr) \
} \
}
-#define createLongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createLongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_LONG, \
.config.l = &(config_addr) \
} \
}
-#define createULongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createULongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_ULONG, \
.config.ul = &(config_addr) \
} \
}
-#define createLongLongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createLongLongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_LONG_LONG, \
.config.ll = &(config_addr) \
} \
}
-#define createULongLongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createULongLongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_ULONG_LONG, \
.config.ull = &(config_addr) \
} \
}
-#define createSizeTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createSizeTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_SIZE_T, \
.config.st = &(config_addr) \
} \
}
-#define createSSizeTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createSSizeTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_SSIZE_T, \
.config.sst = &(config_addr) \
} \
}
-#define createTimeTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createTimeTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_TIME_T, \
.config.tt = &(config_addr) \
} \
}
-#define createOffTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
- embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, update) \
+#define createOffTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
+ embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
.numeric_type = NUMERIC_TYPE_OFF_T, \
.config.ot = &(config_addr) \
} \
}
-#define createSpecialConfig(name, alias, modifiable, setfn, getfn, rewritefn) { \
+#define createSpecialConfig(name, alias, modifiable, setfn, getfn, rewritefn, applyfn) { \
embedCommonConfig(name, alias, modifiable) \
- embedConfigInterface(NULL, setfn, getfn, rewritefn) \
+ embedConfigInterface(NULL, setfn, getfn, rewritefn, applyfn) \
}
static int isValidActiveDefrag(int val, const char **err) {
@@ -2056,9 +2092,7 @@ static int isValidProcTitleTemplate(char *val, const char **err) {
return 1;
}
-static int updateProcTitleTemplate(char *val, char *prev, const char **err) {
- UNUSED(val);
- UNUSED(prev);
+static int updateProcTitleTemplate(const char **err) {
if (redisSetProcTitle(NULL) == C_ERR) {
*err = "failed to set process title";
return 0;
@@ -2066,25 +2100,18 @@ static int updateProcTitleTemplate(char *val, char *prev, const char **err) {
return 1;
}
-static int updateHZ(long long val, long long prev, const char **err) {
- UNUSED(prev);
+static int updateHZ(const char **err) {
UNUSED(err);
/* Hz is more a hint from the user, so we accept values out of range
* but cap them to reasonable values. */
- server.config_hz = val;
if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;
if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;
server.hz = server.config_hz;
return 1;
}
-static int updatePort(long long val, long long prev, const char **err) {
- /* Do nothing if port is unchanged */
- if (val == prev) {
- return 1;
- }
-
- if (changeListenPort(val, &server.ipfd, acceptTcpHandler) == C_ERR) {
+static int updatePort(const char **err) {
+ if (changeListenPort(server.port, &server.ipfd, acceptTcpHandler) == C_ERR) {
*err = "Unable to listen on this port. Check server logs.";
return 0;
}
@@ -2092,28 +2119,23 @@ static int updatePort(long long val, long long prev, const char **err) {
return 1;
}
-static int updateJemallocBgThread(int val, int prev, const char **err) {
- UNUSED(prev);
+static int updateJemallocBgThread(const char **err) {
UNUSED(err);
- set_jemalloc_bg_thread(val);
+ set_jemalloc_bg_thread(server.jemalloc_bg_thread);
return 1;
}
-static int updateReplBacklogSize(long long val, long long prev, const char **err) {
- /* resizeReplicationBacklog sets server.repl_backlog_size, and relies on
- * being able to tell when the size changes, so restore prev before calling it. */
+static int updateReplBacklogSize(const char **err) {
UNUSED(err);
- server.repl_backlog_size = prev;
- resizeReplicationBacklog(val);
+ resizeReplicationBacklog();
return 1;
}
-static int updateMaxmemory(long long val, long long prev, const char **err) {
- UNUSED(prev);
+static int updateMaxmemory(const char **err) {
UNUSED(err);
- if (val) {
+ if (server.maxmemory) {
size_t used = zmalloc_used_memory()-freeMemoryGetNotCountedMemory();
- if ((unsigned long long)val < used) {
+ if (server.maxmemory < used) {
serverLog(LL_WARNING,"WARNING: the new maxmemory value set via CONFIG SET (%llu) is smaller than the current memory usage (%zu). This will result in key eviction and/or the inability to accept new write commands depending on the maxmemory-policy.", server.maxmemory, used);
}
performEvictions();
@@ -2121,27 +2143,22 @@ static int updateMaxmemory(long long val, long long prev, const char **err) {
return 1;
}
-static int updateGoodSlaves(long long val, long long prev, const char **err) {
- UNUSED(val);
- UNUSED(prev);
+static int updateGoodSlaves(const char **err) {
UNUSED(err);
refreshGoodSlavesCount();
return 1;
}
-static int updateWatchdogPeriod(long long val, long long prev, const char **err) {
- UNUSED(val);
- UNUSED(prev);
+static int updateWatchdogPeriod(const char **err) {
UNUSED(err);
applyWatchdogPeriod();
return 1;
}
-static int updateAppendonly(int val, int prev, const char **err) {
- UNUSED(prev);
- if (val == 0 && server.aof_state != AOF_OFF) {
+static int updateAppendonly(const char **err) {
+ if (!server.aof_enabled && server.aof_state != AOF_OFF) {
stopAppendOnly();
- } else if (val && server.aof_state == AOF_OFF) {
+ } else if (server.aof_enabled && server.aof_state == AOF_OFF) {
if (startAppendOnly() == C_ERR) {
*err = "Unable to turn on AOF. Check server logs.";
return 0;
@@ -2150,90 +2167,79 @@ static int updateAppendonly(int val, int prev, const char **err) {
return 1;
}
-static int updateSighandlerEnabled(int val, int prev, const char **err) {
+static int updateSighandlerEnabled(const char **err) {
UNUSED(err);
- UNUSED(prev);
- if (val)
+ if (server.crashlog_enabled)
setupSignalHandlers();
else
removeSignalHandlers();
return 1;
}
-static int updateMaxclients(long long val, long long prev, const char **err) {
- /* Try to check if the OS is capable of supporting so many FDs. */
- if (val > prev) {
- adjustOpenFilesLimit();
- if (server.maxclients != val) {
- static char msg[128];
- sprintf(msg, "The operating system is not able to handle the specified number of clients, try with %d", server.maxclients);
- *err = msg;
- if (server.maxclients > prev) {
- server.maxclients = prev;
- adjustOpenFilesLimit();
- }
- return 0;
- }
- if ((unsigned int) aeGetSetSize(server.el) <
- server.maxclients + CONFIG_FDSET_INCR)
+static int updateMaxclients(const char **err) {
+ unsigned int new_maxclients = server.maxclients;
+ adjustOpenFilesLimit();
+ if (server.maxclients != new_maxclients) {
+ static char msg[128];
+ sprintf(msg, "The operating system is not able to handle the specified number of clients, try with %d", server.maxclients);
+ *err = msg;
+ return 0;
+ }
+ if ((unsigned int) aeGetSetSize(server.el) <
+ server.maxclients + CONFIG_FDSET_INCR)
+ {
+ if (aeResizeSetSize(server.el,
+ server.maxclients + CONFIG_FDSET_INCR) == AE_ERR)
{
- if (aeResizeSetSize(server.el,
- server.maxclients + CONFIG_FDSET_INCR) == AE_ERR)
- {
- *err = "The event loop API used by Redis is not able to handle the specified number of clients";
- return 0;
- }
+ *err = "The event loop API used by Redis is not able to handle the specified number of clients";
+ return 0;
}
}
return 1;
}
-static int updateOOMScoreAdj(int val, int prev, const char **err) {
- UNUSED(prev);
-
- if (val) {
- if (setOOMScoreAdj(-1) == C_ERR) {
- *err = "Failed to set current oom_score_adj. Check server logs.";
- return 0;
- }
+static int updateOOMScoreAdj(const char **err) {
+ if (setOOMScoreAdj(-1) == C_ERR) {
+ *err = "Failed to set current oom_score_adj. Check server logs.";
+ return 0;
}
return 1;
}
-
-int updateRequirePass(sds val, sds prev, const char **err) {
- UNUSED(prev);
+int updateRequirePass(const char **err) {
UNUSED(err);
/* The old "requirepass" directive just translates to setting
* a password to the default user. The only thing we do
* additionally is to remember the cleartext password in this
* case, for backward compatibility with Redis <= 5. */
- ACLUpdateDefaultUserPassword(val);
+ ACLUpdateDefaultUserPassword(server.requirepass);
return 1;
}
+static int applyBind(const char **err) {
+ if (changeBindAddr() == C_ERR) {
+ *err = "Failed to bind to specified addresses.";
+ return 0;
+ }
-int updateClusterFlags(int val, int prev, const char **err) {
- UNUSED(val);
- UNUSED(prev);
+ return 1;
+}
+
+int updateClusterFlags(const char **err) {
UNUSED(err);
clusterUpdateMyselfFlags();
return 1;
}
-int updateClusterIp(char *val, char *prev, const char **err) {
- UNUSED(val);
- UNUSED(prev);
+static int updateClusterIp(const char **err) {
UNUSED(err);
clusterUpdateMyselfIp();
return 1;
}
#ifdef USE_OPENSSL
-static int updateTlsCfg(char *val, char *prev, const char **err) {
- UNUSED(val);
- UNUSED(prev);
+static int applyTlsCfg(const char **err) {
UNUSED(err);
/* If TLS is enabled, try to configure OpenSSL. */
@@ -2244,31 +2250,15 @@ static int updateTlsCfg(char *val, char *prev, const char **err) {
}
return 1;
}
-static int updateTlsCfgBool(int val, int prev, const char **err) {
- UNUSED(val);
- UNUSED(prev);
- return updateTlsCfg(NULL, NULL, err);
-}
-
-static int updateTlsCfgInt(long long val, long long prev, const char **err) {
- UNUSED(val);
- UNUSED(prev);
- return updateTlsCfg(NULL, NULL, err);
-}
-static int updateTLSPort(long long val, long long prev, const char **err) {
- /* Do nothing if port is unchanged */
- if (val == prev) {
- return 1;
- }
-
- /* Configure TLS if tls is enabled */
- if (prev == 0 && tlsConfigure(&server.tls_ctx_config) == C_ERR) {
+static int applyTLSPort(const char **err) {
+ /* Configure TLS in case it wasn't enabled */
+ if (!isTlsConfigured() && tlsConfigure(&server.tls_ctx_config) == C_ERR) {
*err = "Unable to update TLS configuration. Check server logs.";
return 0;
}
- if (changeListenPort(val, &server.tlsfd, acceptTLSHandler) == C_ERR) {
+ if (changeListenPort(server.tls_port, &server.tlsfd, acceptTLSHandler) == C_ERR) {
*err = "Unable to listen on this port. Check server logs.";
return 0;
}
@@ -2278,9 +2268,8 @@ static int updateTLSPort(long long val, long long prev, const char **err) {
#endif /* USE_OPENSSL */
-static int setConfigDirOption(typeData data, sds *argv, int argc, int update, const char **err) {
+static int setConfigDirOption(typeData data, sds *argv, int argc, const char **err) {
UNUSED(data);
- UNUSED(update);
if (argc != 1) {
*err = "wrong number of arguments";
return 0;
@@ -2292,17 +2281,17 @@ static int setConfigDirOption(typeData data, sds *argv, int argc, int update, co
return 1;
}
-static void getConfigDirOption(client *c, typeData data) {
+static sds getConfigDirOption(typeData data) {
UNUSED(data);
char buf[1024];
if (getcwd(buf,sizeof(buf)) == NULL)
buf[0] = '\0';
- addReplyBulkCString(c,buf);
+ return sdsnew(buf);
}
-static int setConfigSaveOption(typeData data, sds *argv, int argc, int update, const char **err) {
+static int setConfigSaveOption(typeData data, sds *argv, int argc, const char **err) {
UNUSED(data);
int j;
@@ -2330,7 +2319,7 @@ static int setConfigSaveOption(typeData data, sds *argv, int argc, int update, c
}
}
/* Finally set the new config */
- if (update) {
+ if (!reading_config_file) {
resetServerSaveParams();
} else {
/* We don't reset save params before loading, because if they're not part
@@ -2355,7 +2344,7 @@ static int setConfigSaveOption(typeData data, sds *argv, int argc, int update, c
return 1;
}
-static void getConfigSaveOption(client *c, typeData data) {
+static sds getConfigSaveOption(typeData data) {
UNUSED(data);
sds buf = sdsempty();
int j;
@@ -2368,17 +2357,15 @@ static void getConfigSaveOption(client *c, typeData data) {
buf = sdscatlen(buf," ",1);
}
- addReplyBulkCString(c,buf);
- sdsfree(buf);
+ return buf;
}
-static int setConfigClientOutputBufferLimitOption(typeData data, sds *argv, int argc, int update, const char **err) {
+static int setConfigClientOutputBufferLimitOption(typeData data, sds *argv, int argc, const char **err) {
UNUSED(data);
- UNUSED(update);
return updateClientOutputBufferLimit(argv, argc, err);
}
-static void getConfigClientOutputBufferLimitOption(client *c, typeData data) {
+static sds getConfigClientOutputBufferLimitOption(typeData data) {
UNUSED(data);
sds buf = sdsempty();
int j;
@@ -2391,21 +2378,54 @@ static void getConfigClientOutputBufferLimitOption(client *c, typeData data) {
if (j != CLIENT_TYPE_OBUF_COUNT-1)
buf = sdscatlen(buf," ",1);
}
- addReplyBulkCString(c,buf);
- sdsfree(buf);
+ return buf;
}
-static int setConfigOOMScoreAdjValuesOption(typeData data, sds *argv, int argc, int update, const char **err) {
+/* 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) {
+ int i;
+ int values[CONFIG_OOM_COUNT];
UNUSED(data);
+
if (argc != CONFIG_OOM_COUNT) {
*err = "wrong number of arguments";
return 0;
}
- if (updateOOMScoreAdjValues(argv,err,update) == C_ERR) return 0;
+
+ for (i = 0; i < CONFIG_OOM_COUNT; i++) {
+ char *eptr;
+ long long val = strtoll(argv[i], &eptr, 10);
+
+ if (*eptr != '\0' || val < -2000 || val > 2000) {
+ if (err) *err = "Invalid oom-score-adj-values, elements must be between -2000 and 2000.";
+ return -1;
+ }
+
+ values[i] = val;
+ }
+
+ /* Verify that the values make sense. If they don't omit a warning but
+ * keep the configuration, which may still be valid for privileged processes.
+ */
+
+ if (values[CONFIG_OOM_REPLICA] < values[CONFIG_OOM_MASTER] ||
+ values[CONFIG_OOM_BGCHILD] < values[CONFIG_OOM_REPLICA])
+ {
+ serverLog(LL_WARNING,
+ "The oom-score-adj-values configuration may not work for non-privileged processes! "
+ "Please consult the documentation.");
+ }
+
+ for (i = 0; i < CONFIG_OOM_COUNT; i++) {
+ server.oom_score_adj_values[i] = values[i];
+ }
+
return 1;
}
-static void getConfigOOMScoreAdjValuesOption(client *c, typeData data) {
+static sds getConfigOOMScoreAdjValuesOption(typeData data) {
UNUSED(data);
sds buf = sdsempty();
int j;
@@ -2416,13 +2436,11 @@ static void getConfigOOMScoreAdjValuesOption(client *c, typeData data) {
buf = sdscatlen(buf," ",1);
}
- addReplyBulkCString(c,buf);
- sdsfree(buf);
+ return buf;
}
-static int setConfigNotifyKeyspaceEventsOption(typeData data, sds *argv, int argc, int update, const char **err) {
+static int setConfigNotifyKeyspaceEventsOption(typeData data, sds *argv, int argc, const char **err) {
UNUSED(data);
- UNUSED(update);
if (argc != 1) {
*err = "wrong number of arguments";
return 0;
@@ -2436,46 +2454,36 @@ static int setConfigNotifyKeyspaceEventsOption(typeData data, sds *argv, int arg
return 1;
}
-static void getConfigNotifyKeyspaceEventsOption(client *c, typeData data) {
+static sds getConfigNotifyKeyspaceEventsOption(typeData data) {
UNUSED(data);
- sds flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);
- addReplyBulkSds(c,flags);
+ return keyspaceEventsFlagsToString(server.notify_keyspace_events);
}
-static int setConfigBindOption(typeData data, sds* argv, int argc, int update, const char **err) {
+static int setConfigBindOption(typeData data, sds* argv, int argc, const char **err) {
UNUSED(data);
+ int j;
if (argc > CONFIG_BINDADDR_MAX) {
*err = "Too many bind addresses specified.";
return 0;
}
- if (update) {
- if (changeBindAddr(argv, argc) == C_ERR) {
- *err = "Failed to bind to specified addresses.";
- return 0;
- }
- } else {
- int j;
+ /* A single empty argument is treated as a zero bindaddr count */
+ if (argc == 1 && sdslen(argv[0]) == 0) argc = 0;
- /* A single empty argument is treated as a zero bindaddr count */
- if (argc == 1 && sdslen(argv[0]) == 0) argc = 0;
-
- /* Free old bind addresses */
- for (j = 0; j < server.bindaddr_count; j++) {
- zfree(server.bindaddr[j]);
- }
- for (j = 0; j < argc; j++)
- server.bindaddr[j] = zstrdup(argv[j]);
- server.bindaddr_count = argc;
+ /* Free old bind addresses */
+ for (j = 0; j < server.bindaddr_count; j++) {
+ zfree(server.bindaddr[j]);
}
+ for (j = 0; j < argc; j++)
+ server.bindaddr[j] = zstrdup(argv[j]);
+ server.bindaddr_count = argc;
return 1;
}
-static int setConfigReplicaOfOption(typeData data, sds* argv, int argc, int update, const char **err) {
+static int setConfigReplicaOfOption(typeData data, sds* argv, int argc, const char **err) {
UNUSED(data);
- UNUSED(update);
if (argc != 2) {
*err = "wrong number of arguments";
@@ -2498,14 +2506,12 @@ static int setConfigReplicaOfOption(typeData data, sds* argv, int argc, int upda
return 1;
}
-static void getConfigBindOption(client *c, typeData data) {
+static sds getConfigBindOption(typeData data) {
UNUSED(data);
- sds aux = sdsjoin(server.bindaddr,server.bindaddr_count," ");
- addReplyBulkCString(c,aux);
- sdsfree(aux);
+ return sdsjoin(server.bindaddr,server.bindaddr_count," ");
}
-static void getConfigReplicaOfOption(client *c, typeData data) {
+static sds getConfigReplicaOfOption(typeData data) {
UNUSED(data);
char buf[256];
if (server.masterhost)
@@ -2513,7 +2519,7 @@ static void getConfigReplicaOfOption(client *c, typeData data) {
server.masterhost, server.masterport);
else
buf[0] = '\0';
- addReplyBulkCString(c,buf);
+ return sdsnew(buf);
}
standardConfig configs[] = {
@@ -2674,36 +2680,36 @@ standardConfig configs[] = {
createOffTConfig("loading-process-events-interval-bytes", NULL, MODIFIABLE_CONFIG, 1024, INT_MAX, server.loading_process_events_interval_bytes, 1024*1024*2, INTEGER_CONFIG, NULL, NULL),
#ifdef USE_OPENSSL
- createIntConfig("tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, updateTLSPort), /* TCP port. */
- createIntConfig("tls-session-cache-size", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_size, 20*1024, INTEGER_CONFIG, NULL, updateTlsCfgInt),
- createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, updateTlsCfgInt),
- createBoolConfig("tls-cluster", NULL, MODIFIABLE_CONFIG, server.tls_cluster, 0, NULL, updateTlsCfgBool),
- createBoolConfig("tls-replication", NULL, MODIFIABLE_CONFIG, server.tls_replication, 0, NULL, updateTlsCfgBool),
+ createIntConfig("tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, applyTLSPort), /* TCP port. */
+ createIntConfig("tls-session-cache-size", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_size, 20*1024, INTEGER_CONFIG, NULL, applyTlsCfg),
+ createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, applyTlsCfg),
+ createBoolConfig("tls-cluster", NULL, MODIFIABLE_CONFIG, server.tls_cluster, 0, NULL, applyTlsCfg),
+ createBoolConfig("tls-replication", NULL, MODIFIABLE_CONFIG, server.tls_replication, 0, NULL, applyTlsCfg),
createEnumConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, tls_auth_clients_enum, server.tls_auth_clients, TLS_CLIENT_AUTH_YES, NULL, NULL),
- createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.prefer_server_ciphers, 0, NULL, updateTlsCfgBool),
- createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.session_caching, 1, NULL, updateTlsCfgBool),
- createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file_pass, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-client-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_cert_file, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-client-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-client-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file_pass, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-dh-params-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-ca-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-ca-cert-dir", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-protocols", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.protocols, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-ciphers", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphers, NULL, NULL, updateTlsCfg),
- createStringConfig("tls-ciphersuites", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphersuites, NULL, NULL, updateTlsCfg),
+ createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.prefer_server_ciphers, 0, NULL, applyTlsCfg),
+ createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.session_caching, 1, NULL, applyTlsCfg),
+ createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file_pass, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-client-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_cert_file, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-client-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-client-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file_pass, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-dh-params-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-ca-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-ca-cert-dir", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-protocols", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.protocols, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-ciphers", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphers, NULL, NULL, applyTlsCfg),
+ createStringConfig("tls-ciphersuites", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphersuites, NULL, NULL, applyTlsCfg),
#endif
/* Special configs */
- createSpecialConfig("dir", NULL, MODIFIABLE_CONFIG, setConfigDirOption, getConfigDirOption, rewriteConfigDirOption),
- createSpecialConfig("save", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigSaveOption, getConfigSaveOption, rewriteConfigSaveOption),
- createSpecialConfig("client-output-buffer-limit", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigClientOutputBufferLimitOption, getConfigClientOutputBufferLimitOption, rewriteConfigClientOutputBufferLimitOption),
- createSpecialConfig("oom-score-adj-values", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigOOMScoreAdjValuesOption, getConfigOOMScoreAdjValuesOption, rewriteConfigOOMScoreAdjValuesOption),
- createSpecialConfig("notify-keyspace-events", NULL, MODIFIABLE_CONFIG, setConfigNotifyKeyspaceEventsOption, getConfigNotifyKeyspaceEventsOption, rewriteConfigNotifyKeyspaceEventsOption),
- createSpecialConfig("bind", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigBindOption, getConfigBindOption, rewriteConfigBindOption),
- createSpecialConfig("replicaof", "slaveof", IMMUTABLE_CONFIG | MULTI_ARG_CONFIG, setConfigReplicaOfOption, getConfigReplicaOfOption, rewriteConfigReplicaOfOption),
+ createSpecialConfig("dir", NULL, MODIFIABLE_CONFIG, setConfigDirOption, getConfigDirOption, rewriteConfigDirOption, NULL),
+ createSpecialConfig("save", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigSaveOption, getConfigSaveOption, rewriteConfigSaveOption, NULL),
+ createSpecialConfig("client-output-buffer-limit", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigClientOutputBufferLimitOption, getConfigClientOutputBufferLimitOption, rewriteConfigClientOutputBufferLimitOption, NULL),
+ createSpecialConfig("oom-score-adj-values", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigOOMScoreAdjValuesOption, getConfigOOMScoreAdjValuesOption, rewriteConfigOOMScoreAdjValuesOption, updateOOMScoreAdj),
+ createSpecialConfig("notify-keyspace-events", NULL, MODIFIABLE_CONFIG, setConfigNotifyKeyspaceEventsOption, getConfigNotifyKeyspaceEventsOption, rewriteConfigNotifyKeyspaceEventsOption, NULL),
+ createSpecialConfig("bind", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigBindOption, getConfigBindOption, rewriteConfigBindOption, applyBind),
+ createSpecialConfig("replicaof", "slaveof", IMMUTABLE_CONFIG | MULTI_ARG_CONFIG, setConfigReplicaOfOption, getConfigReplicaOfOption, rewriteConfigReplicaOfOption, NULL),
/* NULL Terminator */
{NULL}
diff --git a/src/replication.c b/src/replication.c
index 0f2512fcb..1a4aa4c2d 100644
--- a/src/replication.c
+++ b/src/replication.c
@@ -122,17 +122,13 @@ void createReplicationBacklog(void) {
}
/* This function is called when the user modifies the replication backlog
- * size at runtime. It is up to the function to both update the
- * server.repl_backlog_size and to resize the buffer and setup it so that
- * it contains the same data as the previous one (possibly less data, but
- * the most recent bytes, or the same data and more free space in case the
+ * size at runtime. It is up to the function to resize the buffer and setup it
+ * so that it contains the same data as the previous one (possibly less data,
+ * but the most recent bytes, or the same data and more free space in case the
* buffer is enlarged). */
-void resizeReplicationBacklog(long long newsize) {
- if (newsize < CONFIG_REPL_BACKLOG_MIN_SIZE)
- newsize = CONFIG_REPL_BACKLOG_MIN_SIZE;
- if (server.repl_backlog_size == newsize) return;
-
- server.repl_backlog_size = newsize;
+void resizeReplicationBacklog(void) {
+ if (server.repl_backlog_size < CONFIG_REPL_BACKLOG_MIN_SIZE)
+ server.repl_backlog_size = CONFIG_REPL_BACKLOG_MIN_SIZE;
if (server.repl_backlog)
incrementalTrimReplicationBacklog(REPL_BACKLOG_TRIM_BLOCKS_PER_CALL);
}
diff --git a/src/server.c b/src/server.c
index e4ee48d65..05ab6b93a 100644
--- a/src/server.c
+++ b/src/server.c
@@ -200,7 +200,7 @@ struct redisServer server; /* Server global state */
*/
struct redisCommand configSubcommands[] = {
- {"set",configSetCommand,4,
+ {"set",configSetCommand,-4,
"admin ok-stale no-script"},
{"get",configGetCommand,3,
@@ -3838,8 +3838,6 @@ static void readOOMScoreAdj(void) {
* depending on current role.
*/
int setOOMScoreAdj(int process_class) {
-
- if (server.oom_score_adj == OOM_SCORE_ADJ_NO) return C_OK;
if (process_class == -1)
process_class = (server.masterhost ? CONFIG_OOM_REPLICA : CONFIG_OOM_MASTER);
@@ -3850,11 +3848,14 @@ int setOOMScoreAdj(int process_class) {
int val;
char buf[64];
- val = server.oom_score_adj_values[process_class];
- if (server.oom_score_adj == OOM_SCORE_RELATIVE)
- val += server.oom_score_adj_base;
- if (val > 1000) val = 1000;
- if (val < -1000) val = -1000;
+ if (server.oom_score_adj != OOM_SCORE_ADJ_NO) {
+ val = server.oom_score_adj_values[process_class];
+ if (server.oom_score_adj == OOM_SCORE_RELATIVE)
+ val += server.oom_score_adj_base;
+ if (val > 1000) val = 1000;
+ if (val < -1000) val = -1000;
+ } else
+ val = server.oom_score_adj_base;
snprintf(buf, sizeof(buf) - 1, "%d\n", val);
@@ -7190,57 +7191,19 @@ void redisAsciiArt(void) {
zfree(buf);
}
-int changeBindAddr(sds *addrlist, int addrlist_len) {
- int i;
- int result = C_OK;
-
- char *prev_bindaddr[CONFIG_BINDADDR_MAX];
- int prev_bindaddr_count;
-
+int changeBindAddr(void) {
/* Close old TCP and TLS servers */
closeSocketListeners(&server.ipfd);
closeSocketListeners(&server.tlsfd);
- /* Keep previous settings */
- prev_bindaddr_count = server.bindaddr_count;
- memcpy(prev_bindaddr, server.bindaddr, sizeof(server.bindaddr));
-
- /* Copy new settings */
- memset(server.bindaddr, 0, sizeof(server.bindaddr));
- for (i = 0; i < addrlist_len; i++) {
- server.bindaddr[i] = zstrdup(addrlist[i]);
- }
- server.bindaddr_count = addrlist_len;
-
/* Bind to the new port */
if ((server.port != 0 && listenToPort(server.port, &server.ipfd) != C_OK) ||
(server.tls_port != 0 && listenToPort(server.tls_port, &server.tlsfd) != C_OK)) {
- serverLog(LL_WARNING, "Failed to bind, trying to restore old listening sockets.");
-
- /* Restore old bind addresses */
- for (i = 0; i < addrlist_len; i++) {
- zfree(server.bindaddr[i]);
- }
- memcpy(server.bindaddr, prev_bindaddr, sizeof(server.bindaddr));
- server.bindaddr_count = prev_bindaddr_count;
-
- /* Re-Listen TCP and TLS */
- server.ipfd.count = 0;
- if (server.port != 0 && listenToPort(server.port, &server.ipfd) != C_OK) {
- serverPanic("Failed to restore old listening TCP socket.");
- }
+ serverLog(LL_WARNING, "Failed to bind");
- server.tlsfd.count = 0;
- if (server.tls_port != 0 && listenToPort(server.tls_port, &server.tlsfd) != C_OK) {
- serverPanic("Failed to restore old listening TLS socket.");
- }
-
- result = C_ERR;
- } else {
- /* Free old bind addresses */
- for (i = 0; i < prev_bindaddr_count; i++) {
- zfree(prev_bindaddr[i]);
- }
+ closeSocketListeners(&server.ipfd);
+ closeSocketListeners(&server.tlsfd);
+ return C_ERR;
}
/* Create TCP and TLS event handlers */
@@ -7253,15 +7216,17 @@ int changeBindAddr(sds *addrlist, int addrlist_len) {
if (server.set_proc_title) redisSetProcTitle(NULL);
- return result;
+ return C_OK;
}
int changeListenPort(int port, socketFds *sfd, aeFileProc *accept_handler) {
socketFds new_sfd = {{0}};
+ /* Close old servers */
+ closeSocketListeners(sfd);
+
/* Just close the server if port disabled */
if (port == 0) {
- closeSocketListeners(sfd);
if (server.set_proc_title) redisSetProcTitle(NULL);
return C_OK;
}
@@ -7277,9 +7242,6 @@ int changeListenPort(int port, socketFds *sfd, aeFileProc *accept_handler) {
return C_ERR;
}
- /* Close old servers */
- closeSocketListeners(sfd);
-
/* Copy new descriptors */
sfd->count = new_sfd.count;
memcpy(sfd->fd, new_sfd.fd, sizeof(new_sfd.fd));
diff --git a/src/server.h b/src/server.h
index ad458652e..c06b23631 100644
--- a/src/server.h
+++ b/src/server.h
@@ -2302,7 +2302,7 @@ void replicationCron(void);
void replicationStartPendingFork(void);
void replicationHandleMasterDisconnection(void);
void replicationCacheMaster(client *c);
-void resizeReplicationBacklog(long long newsize);
+void resizeReplicationBacklog();
void replicationSetMaster(char *ip, int port);
void replicationUnsetMaster(void);
void refreshGoodSlavesCount(void);
@@ -2497,7 +2497,7 @@ void setupSignalHandlers(void);
void removeSignalHandlers(void);
int createSocketAcceptHandler(socketFds *sfd, aeFileProc *accept_handler);
int changeListenPort(int port, socketFds *sfd, aeFileProc *accept_handler);
-int changeBindAddr(sds *addrlist, int addrlist_len);
+int changeBindAddr(void);
struct redisCommand *lookupCommand(robj **argv ,int argc);
struct redisCommand *lookupCommandBySdsLogic(dict *commands, sds s);
struct redisCommand *lookupCommandBySds(sds s);
@@ -3064,6 +3064,7 @@ void swapMainDbWithTempDb(redisDb *tempDb);
void tlsInit(void);
void tlsCleanup(void);
int tlsConfigure(redisTLSContextConfig *ctx_config);
+int isTlsConfigured(void);
#define redisDebug(fmt, ...) \
printf("DEBUG %s:%d > " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
diff --git a/src/tls.c b/src/tls.c
index 2a78b6562..2fcf57b8f 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -372,6 +372,12 @@ error:
return C_ERR;
}
+/* Return 1 if TLS was already configured, 0 otherwise.
+ */
+int isTlsConfigured(void) {
+ return redis_tls_ctx != NULL;
+}
+
#ifdef TLS_DEBUGGING
#define TLSCONN_DEBUG(fmt, ...) \
serverLog(LL_DEBUG, "TLSCONN: " fmt, __VA_ARGS__)
diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl
index 480ada471..fc729e98b 100644
--- a/tests/unit/introspection.tcl
+++ b/tests/unit/introspection.tcl
@@ -288,6 +288,116 @@ start_server {tags {"introspection"}} {
assert_equal [r config get save] {save {}}
}
} {} {external:skip}
+
+ test {CONFIG SET with multiple args} {
+ set some_configs {maxmemory 10000001 repl-backlog-size 10000002 save {3000 5}}
+
+ # Backup
+ set backups {}
+ foreach c [dict keys $some_configs] {
+ lappend backups $c [lindex [r config get $c] 1]
+ }
+
+ # multi config set and veirfy
+ assert_equal [eval "r config set $some_configs"] "OK"
+ dict for {c val} $some_configs {
+ assert_equal [lindex [r config get $c] 1] $val
+ }
+
+ # Restore backup
+ assert_equal [eval "r config set $backups"] "OK"
+ }
+
+ test {CONFIG SET rollback on set error} {
+ # This test passes an invalid percent value to maxmemory-clients which should cause an
+ # input verification failure during the "set" phase before trying to apply the
+ # configuration. We want to make sure the correct failure happens and everything
+ # is rolled back.
+ # backup maxmemory config
+ set mm_backup [lindex [r config get maxmemory] 1]
+ set mmc_backup [lindex [r config get maxmemory-clients] 1]
+ set qbl_backup [lindex [r config get client-query-buffer-limit] 1]
+ # Set some value to maxmemory
+ assert_equal [r config set maxmemory 10000002] "OK"
+ # Set another value to maxmeory together with another invalid config
+ assert_error "ERR Config set failed - percentage argument must be less or equal to 100" {
+ r config set maxmemory 10000001 maxmemory-clients 200% client-query-buffer-limit invalid
+ }
+ # Validate we rolled back to original values
+ assert_equal [lindex [r config get maxmemory] 1] 10000002
+ assert_equal [lindex [r config get maxmemory-clients] 1] $mmc_backup
+ assert_equal [lindex [r config get client-query-buffer-limit] 1] $qbl_backup
+ # Make sure we revert back to the previous maxmemory
+ assert_equal [r config set maxmemory $mm_backup] "OK"
+ }
+
+ test {CONFIG SET rollback on apply error} {
+ # This test tries to configure a used port number in redis. This is expected
+ # to pass the `CONFIG SET` validity checking implementation but fail on
+ # actual "apply" of the setting. This will validate that after an "apply"
+ # failure we rollback to the previous values.
+ proc dummy_accept {chan addr port} {}
+
+ set some_configs {maxmemory 10000001 port 0 client-query-buffer-limit 10m}
+
+ # On Linux we also set the oom score adj which has an apply function. This is
+ # used to verify that even successful applies are rolled back if some other
+ # config's apply fails.
+ set oom_adj_avail [expr {!$::external && [exec uname] == "Linux"}]
+ if {$oom_adj_avail} {
+ proc get_oom_score_adj {} {
+ set pid [srv 0 pid]
+ set fd [open "/proc/$pid/oom_score_adj" "r"]
+ set val [gets $fd]
+ close $fd
+ return $val
+ }
+ set some_configs [linsert $some_configs 0 oom-score-adj yes oom-score-adj-values {1 1 1}]
+ set read_oom_adj [get_oom_score_adj]
+ }
+
+ # Backup
+ set backups {}
+ foreach c [dict keys $some_configs] {
+ lappend backups $c [lindex [r config get $c] 1]
+ }
+
+
+ set used_port [expr ([dict get $backups port]+1)%65536]
+ dict set some_configs port $used_port
+
+
+ # Run a dummy server on used_port so we know we can't configure redis to
+ # use it. It's ok for this to fail because that means used_port is invalid
+ # anyway
+ catch {socket -server dummy_accept $used_port}
+ # Try to listen on the used port, pass some more configs to make sure the
+ # returned failure message is for the first bad config and everything is rolled back.
+ assert_error "ERR Config set failed - Unable to listen on this port*" {
+ eval "r config set $some_configs"
+ }
+ # Make sure we reverted back to previous configs
+ dict for {conf val} $backups {
+ assert_equal [lindex [r config get $conf] 1] $val
+ }
+
+ if {$oom_adj_avail} {
+ assert_equal [get_oom_score_adj] $read_oom_adj
+ }
+
+ # Make sure we can still communicate with the server (on the original port)
+ set r1 [redis_client]
+ assert_equal [$r1 ping] "PONG"
+ $r1 close
+ }
+
+ test {CONFIG SET duplicate configs} {
+ assert_error "ERR*duplicate*" {r config set maxmemory 10000001 maxmemory 10000002}
+ }
+
+ test {CONFIG SET set immutable} {
+ assert_error "ERR*immutable*" {r config set daemonize yes}
+ }
# Config file at this point is at a weird state, and includes all
# known keywords. Might be a good idea to avoid adding tests here.