Commit d7971f96 authored by Oran Agra's avatar Oran Agra
Browse files

Merge remote-tracking branch 'origin/unstable' into 7.0

parents d2b5a579 acfb4f7a
...@@ -4,13 +4,20 @@ ...@@ -4,13 +4,20 @@
"complexity": "O(N) where N is the number of instances", "complexity": "O(N) where N is the number of instances",
"group": "sentinel", "group": "sentinel",
"since": "3.2.0", "since": "3.2.0",
"arity": 3, "arity": -3,
"container": "SENTINEL", "container": "SENTINEL",
"function": "sentinelCommand", "function": "sentinelCommand",
"command_flags": [ "command_flags": [
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "nodename",
"type": "string",
"multiple": true
}
] ]
} }
} }
...@@ -11,6 +11,24 @@ ...@@ -11,6 +11,24 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "ip",
"type": "string"
},
{
"name": "port",
"type": "integer"
},
{
"name": "current-epoch",
"type": "integer"
},
{
"name": "runid",
"type": "string"
}
] ]
} }
} }
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
] ]
} }
} }
...@@ -11,6 +11,24 @@ ...@@ -11,6 +11,24 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "name",
"type": "string"
},
{
"name": "ip",
"type": "string"
},
{
"name": "port",
"type": "integer"
},
{
"name": "quorum",
"type": "integer"
}
] ]
} }
} }
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
] ]
} }
} }
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
] ]
} }
} }
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "pattern",
"type": "pattern"
}
] ]
} }
} }
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
] ]
} }
} }
...@@ -11,6 +11,27 @@ ...@@ -11,6 +11,27 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
},
{
"name": "option_value",
"type": "block",
"multiple": true,
"arguments": [
{
"name": "option",
"type": "string"
},
{
"name": "value",
"type": "string"
}
]
}
] ]
} }
} }
...@@ -10,6 +10,28 @@ ...@@ -10,6 +10,28 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "mode",
"type": "oneof",
"optional":true,
"multiple":true,
"arguments": [
{
"name": "crash-after-election",
"type": "pure-token"
},
{
"name": "crash-after-promotion",
"type": "pure-token"
},
{
"name": "help",
"type": "pure-token"
}
]
}
] ]
} }
} }
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
"history": [ "history": [
[ [
"7.0.0", "7.0.0",
"Added the `NOW`, `FORCE` and `ABORT` modifiers. Introduced waiting for lagging replicas before exiting." "Added the `NOW`, `FORCE` and `ABORT` modifiers."
] ]
], ],
"command_flags": [ "command_flags": [
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
}, },
{ {
"name": "port", "name": "port",
"type": "string" "type": "integer"
} }
] ]
} }
......
...@@ -96,7 +96,7 @@ ...@@ -96,7 +96,7 @@
"token": "GET", "token": "GET",
"name": "pattern", "name": "pattern",
"key_spec_index": 1, "key_spec_index": 1,
"type": "string", "type": "pattern",
"optional": true, "optional": true,
"multiple": true, "multiple": true,
"multiple_token": true "multiple_token": true
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
"token": "GET", "token": "GET",
"name": "pattern", "name": "pattern",
"key_spec_index": 1, "key_spec_index": 1,
"type": "string", "type": "pattern",
"optional": true, "optional": true,
"multiple": true, "multiple": true,
"multiple_token": true "multiple_token": true
......
{ {
"SPUBLISH": { "SPUBLISH": {
"summary": "Post a message to a shard channel", "summary": "Post a message to a shard channel",
"complexity": "O(N) where N is the number of clients subscribed to the receiving shard channel.", "complexity": "O(N) where N is the number of clients subscribed to the receiving shard channel.",
"group": "pubsub", "group": "pubsub",
"since": "7.0.0", "since": "7.0.0",
"arity": 3, "arity": 3,
"function": "spublishCommand", "function": "spublishCommand",
"command_flags": [ "command_flags": [
"PUBSUB", "PUBSUB",
"LOADING", "LOADING",
"STALE", "STALE",
"FAST", "FAST",
"MAY_REPLICATE" "MAY_REPLICATE"
],
"arguments": [
{
"name": "channel",
"type": "string"
},
{
"name": "message",
"type": "string"
}
],
"key_specs": [
{
"flags": [
"NOT_KEY"
], ],
"begin_search": { "arguments": [
"index": { {
"pos": 1 "name": "channel",
} "type": "string"
}, },
"find_keys": { {
"range": { "name": "message",
"lastkey": 0, "type": "string"
"step": 1, }
"limit": 0 ],
} "key_specs": [
} {
} "flags": [
] "NOT_KEY"
} ],
"begin_search": {
"index": {
"pos": 1
}
},
"find_keys": {
"range": {
"lastkey": 0,
"step": 1,
"limit": 0
}
}
}
]
}
} }
{ {
"SSUBSCRIBE": { "SSUBSCRIBE": {
"summary": "Listen for messages published to the given shard channels", "summary": "Listen for messages published to the given shard channels",
"complexity": "O(N) where N is the number of shard channels to subscribe to.", "complexity": "O(N) where N is the number of shard channels to subscribe to.",
"group": "pubsub", "group": "pubsub",
"since": "7.0.0", "since": "7.0.0",
"arity": -2, "arity": -2,
"function": "ssubscribeCommand", "function": "ssubscribeCommand",
"command_flags": [ "command_flags": [
"PUBSUB", "PUBSUB",
"NOSCRIPT", "NOSCRIPT",
"LOADING", "LOADING",
"STALE" "STALE"
],
"arguments": [
{
"name": "channel",
"type": "string",
"multiple": true
}
],
"key_specs": [
{
"flags": [
"NOT_KEY"
], ],
"begin_search": { "arguments": [
"index": { {
"pos": 1 "name": "channel",
} "type": "string",
}, "multiple": true
"find_keys": { }
"range": { ],
"lastkey": -1, "key_specs": [
"step": 1, {
"limit": 0 "flags": [
} "NOT_KEY"
} ],
} "begin_search": {
] "index": {
} "pos": 1
}
},
"find_keys": {
"range": {
"lastkey": -1,
"step": 1,
"limit": 0
}
}
}
]
}
} }
...@@ -6,12 +6,7 @@ ...@@ -6,12 +6,7 @@
"since": "2.0.0", "since": "2.0.0",
"arity": -2, "arity": -2,
"function": "subscribeCommand", "function": "subscribeCommand",
"history": [ "history": [],
[
"6.2.0",
"`RESET` can be called to exit subscribed state."
]
],
"command_flags": [ "command_flags": [
"PUBSUB", "PUBSUB",
"NOSCRIPT", "NOSCRIPT",
......
{ {
"SUNSUBSCRIBE": { "SUNSUBSCRIBE": {
"summary": "Stop listening for messages posted to the given shard channels", "summary": "Stop listening for messages posted to the given shard channels",
"complexity": "O(N) where N is the number of clients already subscribed to a channel.", "complexity": "O(N) where N is the number of clients already subscribed to a channel.",
"group": "pubsub", "group": "pubsub",
"since": "7.0.0", "since": "7.0.0",
"arity": -1, "arity": -1,
"function": "sunsubscribeCommand", "function": "sunsubscribeCommand",
"command_flags": [ "command_flags": [
"PUBSUB", "PUBSUB",
"NOSCRIPT", "NOSCRIPT",
"LOADING", "LOADING",
"STALE" "STALE"
],
"arguments": [
{
"name": "channel",
"type": "string",
"optional": true,
"multiple": true
}
],
"key_specs": [
{
"flags": [
"NOT_KEY"
], ],
"begin_search": { "arguments": [
"index": { {
"pos": 1 "name": "channel",
} "type": "string",
}, "optional": true,
"find_keys": { "multiple": true
"range": { }
"lastkey": -1, ],
"step": 1, "key_specs": [
"limit": 0 {
} "flags": [
} "NOT_KEY"
} ],
] "begin_search": {
} "index": {
"pos": 1
}
},
"find_keys": {
"range": {
"lastkey": -1,
"step": 1,
"limit": 0
}
}
}
]
}
} }
...@@ -40,11 +40,6 @@ ...@@ -40,11 +40,6 @@
* Config file name-value maps. * Config file name-value maps.
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
typedef struct configEnum {
const char *name;
const int val;
} configEnum;
typedef struct deprecatedConfig { typedef struct deprecatedConfig {
const char *name; const char *name;
const int argc_min; const int argc_min;
...@@ -168,7 +163,7 @@ int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT] = { 0, 200, 800 }; ...@@ -168,7 +163,7 @@ int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT] = { 0, 200, 800 };
* rewrite. */ * rewrite. */
typedef struct boolConfigData { typedef struct boolConfigData {
int *config; /* The pointer to the server config this value is stored in */ 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) */ int (*is_valid_fn)(int val, const char **err); /* Optional function to check validity of new value (generic doc above) */
} boolConfigData; } boolConfigData;
...@@ -182,7 +177,7 @@ typedef struct stringConfigData { ...@@ -182,7 +177,7 @@ typedef struct stringConfigData {
typedef struct sdsConfigData { typedef struct sdsConfigData {
sds *config; /* Pointer to the server config this value is stored in. */ 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 (*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 int convert_empty_to_null; /* Boolean indicating if empty SDS strings should
be stored as a NULL value. */ be stored as a NULL value. */
...@@ -191,7 +186,7 @@ typedef struct sdsConfigData { ...@@ -191,7 +186,7 @@ typedef struct sdsConfigData {
typedef struct enumConfigData { typedef struct enumConfigData {
int *config; /* The pointer to the server config this value is stored in */ int *config; /* The pointer to the server config this value is stored in */
configEnum *enum_value; /* The underlying enum type this data represents */ 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) */ int (*is_valid_fn)(int val, const char **err); /* Optional function to check validity of new value (generic doc above) */
} enumConfigData; } enumConfigData;
...@@ -208,11 +203,6 @@ typedef enum numericType { ...@@ -208,11 +203,6 @@ typedef enum numericType {
NUMERIC_TYPE_TIME_T, NUMERIC_TYPE_TIME_T,
} numericType; } 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 { typedef struct numericConfigData {
union { union {
int *i; int *i;
...@@ -230,7 +220,7 @@ typedef struct numericConfigData { ...@@ -230,7 +220,7 @@ typedef struct numericConfigData {
numericType numeric_type; /* An enum indicating the type of this value */ numericType numeric_type; /* An enum indicating the type of this value */
long long lower_bound; /* The lower bound of this numeric value */ long long lower_bound; /* The lower bound of this numeric value */
long long upper_bound; /* The upper 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) */ int (*is_valid_fn)(long long val, const char **err); /* Optional function to check validity of new value (generic doc above) */
} numericConfigData; } numericConfigData;
...@@ -242,43 +232,44 @@ typedef union typeData { ...@@ -242,43 +232,44 @@ typedef union typeData {
numericConfigData numeric; numericConfigData numeric;
} typeData; } typeData;
typedef struct standardConfig standardConfig;
typedef int (*apply_fn)(const char **err); typedef int (*apply_fn)(const char **err);
typedef struct typeInterface { typedef struct typeInterface {
/* Called on server start, to init the server with default value */ /* 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, /* 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 * 2 meaning no actual change done, 0 on error and can set a verbose err
* string */ * 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 /* Optional: called after `set()` to apply the config change. Used only in
* the context of CONFIG SET. Returns 1 on success, 0 on failure. * the context of CONFIG SET. Returns 1 on success, 0 on failure.
* Optionally set err to a static error string. */ * Optionally set err to a static error string. */
apply_fn apply; apply_fn apply;
/* Called on CONFIG GET, returns sds to be used in reply */ /* 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 */ /* 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; } typeInterface;
typedef struct standardConfig { struct standardConfig {
const char *name; /* The user visible name of this config */ const char *name; /* The user visible name of this config */
const char *alias; /* An alias that can also be used for 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 */ typeInterface interface; /* The function pointers that define the type interface */
typeData data; /* The type specific data exposed used by the 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 dict *configs = NULL; /* Runtime config values */
* 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. */
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 * Enum access functions
...@@ -407,12 +398,6 @@ static int updateClientOutputBufferLimit(sds *args, int arg_len, const char **er ...@@ -407,12 +398,6 @@ static int updateClientOutputBufferLimit(sds *args, int arg_len, const char **er
return 1; 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 /* 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 * within conf file parsing. This is only needed to support the deprecated
* abnormal aggregate `save T C` functionality. Remove in the future. */ * abnormal aggregate `save T C` functionality. Remove in the future. */
...@@ -458,29 +443,23 @@ void loadServerConfigFromString(char *config) { ...@@ -458,29 +443,23 @@ void loadServerConfigFromString(char *config) {
sdstolower(argv[0]); sdstolower(argv[0]);
/* Iterate the configs that are standard */ /* Iterate the configs that are standard */
int match = 0; standardConfig *config = lookupConfig(argv[0]);
for (standardConfig *config = configs; config->name != NULL; config++) { if (config) {
if ((!strcasecmp(argv[0],config->name) || /* For normal single arg configs enforce we have a single argument.
(config->alias && !strcasecmp(argv[0],config->alias)))) * Note that MULTI_ARG_CONFIGs need to validate arg count on their own */
{ if (!(config->flags & MULTI_ARG_CONFIG) && argc != 2) {
/* For normal single arg configs enforce we have a single argument. err = "wrong number of arguments";
* Note that MULTI_ARG_CONFIGs need to validate arg count on their own */ goto loaderr;
if (!(config->flags & MULTI_ARG_CONFIG) && argc != 2) { }
err = "wrong number of arguments"; /* Set config using all arguments that follows */
goto loaderr; if (!config->interface.set(config, &argv[1], argc-1, &err)) {
} 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;
} }
}
/* If there's no matching above, we try matching them with deprecated configs */ sdsfreesplitres(argv,argc);
if (!match) { continue;
} else {
int match = 0;
for (deprecatedConfig *config = deprecated_configs; config->name != NULL; config++) { for (deprecatedConfig *config = deprecated_configs; config->name != NULL; config++) {
if (!strcasecmp(argv[0], config->name) && if (!strcasecmp(argv[0], config->name) &&
config->argc_min <= argc && config->argc_min <= argc &&
...@@ -490,11 +469,10 @@ void loadServerConfigFromString(char *config) { ...@@ -490,11 +469,10 @@ void loadServerConfigFromString(char *config) {
break; break;
} }
} }
} if (match) {
sdsfreesplitres(argv,argc);
if (match) { continue;
sdsfreesplitres(argv,argc); }
continue;
} }
/* Execute config directives */ /* Execute config directives */
...@@ -535,6 +513,13 @@ void loadServerConfigFromString(char *config) { ...@@ -535,6 +513,13 @@ void loadServerConfigFromString(char *config) {
} }
} else if (!strcasecmp(argv[0],"loadmodule") && argc >= 2) { } else if (!strcasecmp(argv[0],"loadmodule") && argc >= 2) {
queueLoadModule(argv[1],&argv[2],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")) { } else if (!strcasecmp(argv[0],"sentinel")) {
/* argc == 1 is handled by main() as we need to enter the sentinel /* argc == 1 is handled by main() as we need to enter the sentinel
* mode ASAP. */ * mode ASAP. */
...@@ -597,9 +582,10 @@ loaderr: ...@@ -597,9 +582,10 @@ loaderr:
* Both filename and options can be NULL, in such a case are considered * 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 * empty. This way loadServerConfig can be used to just load a file or
* just load a string. */ * just load a string. */
#define CONFIG_READ_LEN 1024
void loadServerConfig(char *filename, char config_from_stdin, char *options) { void loadServerConfig(char *filename, char config_from_stdin, char *options) {
sds config = sdsempty(); sds config = sdsempty();
char buf[CONFIG_MAX_LINE+1]; char buf[CONFIG_READ_LEN+1];
FILE *fp; FILE *fp;
glob_t globbuf; glob_t globbuf;
...@@ -631,7 +617,7 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) { ...@@ -631,7 +617,7 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) {
globbuf.gl_pathv[i], strerror(errno)); globbuf.gl_pathv[i], strerror(errno));
exit(1); exit(1);
} }
while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) while(fgets(buf,CONFIG_READ_LEN+1,fp) != NULL)
config = sdscat(config,buf); config = sdscat(config,buf);
fclose(fp); fclose(fp);
} }
...@@ -647,7 +633,7 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) { ...@@ -647,7 +633,7 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) {
filename, strerror(errno)); filename, strerror(errno));
exit(1); exit(1);
} }
while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) while(fgets(buf,CONFIG_READ_LEN+1,fp) != NULL)
config = sdscat(config,buf); config = sdscat(config,buf);
fclose(fp); fclose(fp);
} }
...@@ -657,7 +643,7 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) { ...@@ -657,7 +643,7 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) {
if (config_from_stdin) { if (config_from_stdin) {
serverLog(LL_WARNING,"Reading config from stdin"); serverLog(LL_WARNING,"Reading config from stdin");
fp = 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); config = sdscat(config,buf);
} }
...@@ -682,12 +668,45 @@ static int performInterfaceSet(standardConfig *config, sds value, const char **e ...@@ -682,12 +668,45 @@ static int performInterfaceSet(standardConfig *config, sds value, const char **e
} }
/* Set the config */ /* 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); if (config->flags & MULTI_ARG_CONFIG) sdsfreesplitres(argv, argc);
return res; 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; int i;
const char *errstr = "unknown error"; const char *errstr = "unknown error";
/* Set all backup values */ /* Set all backup values */
...@@ -703,6 +722,10 @@ static void restoreBackupConfig(standardConfig **set_configs, sds *old_values, i ...@@ -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); 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) { ...@@ -714,6 +737,8 @@ void configSetCommand(client *c) {
const char *invalid_arg_name = NULL; const char *invalid_arg_name = NULL;
const char *err_arg_name = NULL; const char *err_arg_name = NULL;
standardConfig **set_configs; /* TODO: make this a dict for better performance */ standardConfig **set_configs; /* TODO: make this a dict for better performance */
list *module_configs_apply;
const char **config_names;
sds *new_values; sds *new_values;
sds *old_values = NULL; sds *old_values = NULL;
apply_fn *apply_fns; /* TODO: make this a set for better performance */ apply_fn *apply_fns; /* TODO: make this a set for better performance */
...@@ -728,7 +753,9 @@ void configSetCommand(client *c) { ...@@ -728,7 +753,9 @@ void configSetCommand(client *c) {
} }
config_count = (c->argc - 2) / 2; config_count = (c->argc - 2) / 2;
module_configs_apply = listCreate();
set_configs = zcalloc(sizeof(standardConfig*)*config_count); set_configs = zcalloc(sizeof(standardConfig*)*config_count);
config_names = zcalloc(sizeof(char*)*config_count);
new_values = zmalloc(sizeof(sds*)*config_count); new_values = zmalloc(sizeof(sds*)*config_count);
old_values = zcalloc(sizeof(sds*)*config_count); old_values = zcalloc(sizeof(sds*)*config_count);
apply_fns = zcalloc(sizeof(apply_fn)*config_count); apply_fns = zcalloc(sizeof(apply_fn)*config_count);
...@@ -736,73 +763,76 @@ void configSetCommand(client *c) { ...@@ -736,73 +763,76 @@ void configSetCommand(client *c) {
/* Find all relevant configs */ /* Find all relevant configs */
for (i = 0; i < config_count; i++) { for (i = 0; i < config_count; i++) {
for (standardConfig *config = configs; config->name != NULL; config++) { standardConfig *config = lookupConfig(c->argv[2+i*2]->ptr);
if ((!strcasecmp(c->argv[2+i*2]->ptr,config->name) || /* Fail if we couldn't find this config */
(config->alias && !strcasecmp(c->argv[2]->ptr,config->alias)))) { if (!config) {
if (!invalid_args) {
/* Note: it's important we run over ALL passed configs and check if we need to call `redactClientCommandArgument()`. invalid_arg_name = c->argv[2+i*2]->ptr;
* This is in order to avoid anyone using this command for a log/slowlog/monitor/etc. displaying sensitive info. invalid_args = 1;
* So even if we encounter an error we still continue running over the remaining arguments. */ }
if (config->flags & SENSITIVE_CONFIG) { continue;
redactClientCommandArgument(c,2+i*2+1); }
}
if (!invalid_args) { /* Note: it's important we run over ALL passed configs and check if we need to call `redactClientCommandArgument()`.
if (config->flags & IMMUTABLE_CONFIG || * This is in order to avoid anyone using this command for a log/slowlog/monitor/etc. displaying sensitive info.
(config->flags & PROTECTED_CONFIG && !allowProtectedAction(server.enable_protected_configs, c))) * So even if we encounter an error we still continue running over the remaining arguments. */
{ if (config->flags & SENSITIVE_CONFIG) {
/* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ redactClientCommandArgument(c,2+i*2+1);
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;
}
if (server.loading && config->flags & DENY_LOADING_CONFIG) { /* We continue to make sure we redact all the configs */
/* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ if (invalid_args) continue;
deny_loading_error = 1;
invalid_args = 1;
}
/* If this config appears twice then fail */ if (config->flags & IMMUTABLE_CONFIG ||
for (j = 0; j < i; j++) { (config->flags & PROTECTED_CONFIG && !allowProtectedAction(server.enable_protected_configs, c)))
if (set_configs[j] == config) { {
/* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */ /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */
errstr = "duplicate parameter"; errstr = (config->flags & IMMUTABLE_CONFIG) ? "can't set immutable config" : "can't set protected config";
err_arg_name = c->argv[2+i*2]->ptr; err_arg_name = c->argv[2+i*2]->ptr;
invalid_args = 1; invalid_args = 1;
break; continue;
}
}
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 (server.loading && config->flags & DENY_LOADING_CONFIG) {
if (!invalid_args && !set_configs[i]) { /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */
invalid_arg_name = c->argv[2+i*2]->ptr; deny_loading_error = 1;
invalid_args = 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; if (invalid_args) goto err;
/* Backup old values before setting new ones */ /* Backup old values before setting new ones */
for (i = 0; i < config_count; i++) 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) */ /* Set all new values (don't apply yet) */
for (i = 0; i < config_count; i++) { for (i = 0; i < config_count; i++) {
int res = performInterfaceSet(set_configs[i], new_values[i], &errstr); int res = performInterfaceSet(set_configs[i], new_values[i], &errstr);
if (!res) { 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; err_arg_name = set_configs[i]->name;
goto err; goto err;
} else if (res == 1) { } else if (res == 1) {
/* A new value was set, if this config has an apply function then store it for execution later */ /* 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 */ /* Check if this apply function is already stored */
int exists = 0; int exists = 0;
for (j = 0; apply_fns[j] != NULL && j <= i; j++) { for (j = 0; apply_fns[j] != NULL && j <= i; j++) {
...@@ -824,11 +854,20 @@ void configSetCommand(client *c) { ...@@ -824,11 +854,20 @@ void configSetCommand(client *c) {
for (i = 0; i < config_count && apply_fns[i] != NULL; i++) { for (i = 0; i < config_count && apply_fns[i] != NULL; i++) {
if (!apply_fns[i](&errstr)) { 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); 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; err_arg_name = set_configs[config_map_fns[i]]->name;
goto err; 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); addReply(c,shared.ok);
goto end; goto end;
...@@ -845,12 +884,14 @@ err: ...@@ -845,12 +884,14 @@ err:
} }
end: end:
zfree(set_configs); zfree(set_configs);
zfree(config_names);
zfree(new_values); zfree(new_values);
for (i = 0; i < config_count; i++) for (i = 0; i < config_count; i++)
sdsfree(old_values[i]); sdsfree(old_values[i]);
zfree(old_values); zfree(old_values);
zfree(apply_fns); zfree(apply_fns);
zfree(config_map_fns); zfree(config_map_fns);
listRelease(module_configs_apply);
} }
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
...@@ -858,38 +899,51 @@ end: ...@@ -858,38 +899,51 @@ end:
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
void configGetCommand(client *c) { void configGetCommand(client *c) {
void *replylen = addReplyDeferredLen(c);
int matches = 0;
int i; 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++) { /* Otherwise, do a match against all items in the dictionary. */
int matched_conf = 0; di = dictGetIterator(configs);
int matched_alias = 0;
for (i = 0; i < c->argc - 2 && (!matched_conf || !matched_alias); i++) { while ((de = dictNext(di)) != NULL) {
robj *o = c->argv[2+i]; standardConfig *config = dictGetVal(de);
char *pattern = o->ptr;
/* Note that hidden configs require an exact match (not a pattern) */ /* Note that hidden configs require an exact match (not a pattern) */
if (!matched_conf && if (config->flags & HIDDEN_CONFIG) continue;
(((config->flags & HIDDEN_CONFIG) && !strcasecmp(pattern, config->name)) || if (dictFind(matches, config->name)) continue;
(!(config->flags & HIDDEN_CONFIG) && stringmatch(pattern, config->name, 1)))) { if (stringmatch(name, de->key, 1)) {
addReplyBulkCString(c, config->name); dictAdd(matches, de->key, config);
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;
} }
} }
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) { ...@@ -996,17 +1050,32 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
FILE *fp = fopen(path,"r"); FILE *fp = fopen(path,"r");
if (fp == NULL && errno != ENOENT) return NULL; 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; int linenum = -1;
struct rewriteConfigState *state = rewriteConfigCreateState(); 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. */ /* Load the file content */
while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) { 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; int argc;
sds *argv; 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 */ linenum++; /* Zero based, so we init at -1 */
...@@ -1020,12 +1089,13 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) { ...@@ -1020,12 +1089,13 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
/* Not a comment, split into arguments. */ /* Not a comment, split into arguments. */
argv = sdssplitargs(line,&argc); 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 /* Apparently the line is unparsable for some reason, for
* instance it may have unbalanced quotes. Load it as a * instance it may have unbalanced quotes, or may contain a
* comment. */ * config that doesn't exist anymore. Load it as a comment. */
sds aux = sdsnew("# ??? "); sds aux = sdsnew("# ??? ");
aux = sdscatsds(aux,line); aux = sdscatsds(aux,line);
if (argv) sdsfreesplitres(argv, argc);
sdsfree(line); sdsfree(line);
rewriteConfigAppendLine(state,aux); rewriteConfigAppendLine(state,aux);
continue; continue;
...@@ -1062,6 +1132,8 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) { ...@@ -1062,6 +1132,8 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
sdsfreesplitres(argv,argc); sdsfreesplitres(argv,argc);
} }
fclose(fp); fclose(fp);
sdsfreesplitres(lines,totlines);
sdsfree(config);
return state; return state;
} }
...@@ -1235,8 +1307,8 @@ void rewriteConfigEnumOption(struct rewriteConfigState *state, const char *optio ...@@ -1235,8 +1307,8 @@ void rewriteConfigEnumOption(struct rewriteConfigState *state, const char *optio
} }
/* Rewrite the save option. */ /* Rewrite the save option. */
void rewriteConfigSaveOption(typeData data, const char *name, struct rewriteConfigState *state) { void rewriteConfigSaveOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
UNUSED(data); UNUSED(config);
int j; int j;
sds line; sds line;
...@@ -1296,8 +1368,8 @@ void rewriteConfigUserOption(struct rewriteConfigState *state) { ...@@ -1296,8 +1368,8 @@ void rewriteConfigUserOption(struct rewriteConfigState *state) {
} }
/* Rewrite the dir option, always using absolute paths.*/ /* Rewrite the dir option, always using absolute paths.*/
void rewriteConfigDirOption(typeData data, const char *name, struct rewriteConfigState *state) { void rewriteConfigDirOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
UNUSED(data); UNUSED(config);
char cwd[1024]; char cwd[1024];
if (getcwd(cwd,sizeof(cwd)) == NULL) { if (getcwd(cwd,sizeof(cwd)) == NULL) {
...@@ -1308,8 +1380,8 @@ void rewriteConfigDirOption(typeData data, const char *name, struct rewriteConfi ...@@ -1308,8 +1380,8 @@ void rewriteConfigDirOption(typeData data, const char *name, struct rewriteConfi
} }
/* Rewrite the slaveof option. */ /* Rewrite the slaveof option. */
void rewriteConfigReplicaOfOption(typeData data, const char *name, struct rewriteConfigState *state) { void rewriteConfigReplicaOfOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
UNUSED(data); UNUSED(config);
sds line; sds line;
/* If this is a master, we want all the slaveof config options /* 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 ...@@ -1325,8 +1397,8 @@ void rewriteConfigReplicaOfOption(typeData data, const char *name, struct rewrit
} }
/* Rewrite the notify-keyspace-events option. */ /* Rewrite the notify-keyspace-events option. */
void rewriteConfigNotifyKeyspaceEventsOption(typeData data, const char *name, struct rewriteConfigState *state) { void rewriteConfigNotifyKeyspaceEventsOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
UNUSED(data); UNUSED(config);
int force = server.notify_keyspace_events != 0; int force = server.notify_keyspace_events != 0;
sds line, flags; sds line, flags;
...@@ -1339,8 +1411,8 @@ void rewriteConfigNotifyKeyspaceEventsOption(typeData data, const char *name, st ...@@ -1339,8 +1411,8 @@ void rewriteConfigNotifyKeyspaceEventsOption(typeData data, const char *name, st
} }
/* Rewrite the client-output-buffer-limit option. */ /* Rewrite the client-output-buffer-limit option. */
void rewriteConfigClientOutputBufferLimitOption(typeData data, const char *name, struct rewriteConfigState *state) { void rewriteConfigClientOutputBufferLimitOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
UNUSED(data); UNUSED(config);
int j; int j;
for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) { for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
int force = (server.client_obuf_limits[j].hard_limit_bytes != int force = (server.client_obuf_limits[j].hard_limit_bytes !=
...@@ -1367,8 +1439,8 @@ void rewriteConfigClientOutputBufferLimitOption(typeData data, const char *name, ...@@ -1367,8 +1439,8 @@ void rewriteConfigClientOutputBufferLimitOption(typeData data, const char *name,
} }
/* Rewrite the oom-score-adj-values option. */ /* Rewrite the oom-score-adj-values option. */
void rewriteConfigOOMScoreAdjValuesOption(typeData data, const char *name, struct rewriteConfigState *state) { void rewriteConfigOOMScoreAdjValuesOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
UNUSED(data); UNUSED(config);
int force = 0; int force = 0;
int j; int j;
sds line; sds line;
...@@ -1387,8 +1459,8 @@ void rewriteConfigOOMScoreAdjValuesOption(typeData data, const char *name, struc ...@@ -1387,8 +1459,8 @@ void rewriteConfigOOMScoreAdjValuesOption(typeData data, const char *name, struc
} }
/* Rewrite the bind option. */ /* Rewrite the bind option. */
void rewriteConfigBindOption(typeData data, const char *name, struct rewriteConfigState *state) { void rewriteConfigBindOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
UNUSED(data); UNUSED(config);
int force = 1; int force = 1;
sds line, addresses; sds line, addresses;
int is_default = 0; int is_default = 0;
...@@ -1508,10 +1580,14 @@ sds getConfigDebugInfo() { ...@@ -1508,10 +1580,14 @@ sds getConfigDebugInfo() {
/* Iterate the configs and "rewrite" the ones that have /* Iterate the configs and "rewrite" the ones that have
* the debug flag. */ * 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; 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); sds info = rewriteConfigGetContentFromState(state);
rewriteConfigReleaseState(state); rewriteConfigReleaseState(state);
return info; return info;
...@@ -1599,9 +1675,15 @@ int rewriteConfig(char *path, int force_write) { ...@@ -1599,9 +1675,15 @@ int rewriteConfig(char *path, int force_write) {
* the rewrite state. */ * the rewrite state. */
/* Iterate the configs that are standard */ /* Iterate the configs that are standard */
for (standardConfig *config = configs; config->name != NULL; config++) { dictIterator *di = dictGetIterator(configs);
if (config->interface.rewrite) config->interface.rewrite(config->data, config->name, state); 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); rewriteConfigUserOption(state);
rewriteConfigLoadmoduleOption(state); rewriteConfigLoadmoduleOption(state);
...@@ -1656,38 +1738,46 @@ static char loadbuf[LOADBUF_SIZE]; ...@@ -1656,38 +1738,46 @@ static char loadbuf[LOADBUF_SIZE];
*/ */
/* Bool Configs */ /* Bool Configs */
static void boolConfigInit(typeData data) { static void boolConfigInit(standardConfig *config) {
*data.yesno.config = data.yesno.default_value; *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); UNUSED(argc);
int yn = yesnotoi(argv[0]); int yn = yesnotoi(argv[0]);
if (yn == -1) { if (yn == -1) {
*err = "argument must be 'yes' or 'no'"; *err = "argument must be 'yes' or 'no'";
return 0; 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; return 0;
int prev = *(data.yesno.config); int prev = config->flags & MODULE_CONFIG ? getModuleBoolConfig(config->privdata) : *(config->data.yesno.config);
if (prev != yn) { 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 1;
} }
return 2; return 2;
} }
static sds boolConfigGet(typeData data) { static sds boolConfigGet(standardConfig *config) {
return sdsnew(*data.yesno.config ? "yes" : "no"); 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) { static void boolConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) {
rewriteConfigYesNoOption(state, name,*(data.yesno.config), data.yesno.default_value); 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) { \ #define createBoolConfig(name, alias, flags, config_addr, default, is_valid, apply) { \
embedCommonConfig(name, alias, flags) \ embedCommonConfig(name, alias, flags) \
embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite, apply) \ embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite, apply) \
.type = BOOL_CONFIG, \
.data.yesno = { \ .data.yesno = { \
.config = &(config_addr), \ .config = &(config_addr), \
.default_value = (default), \ .default_value = (default), \
...@@ -1696,61 +1786,69 @@ static void boolConfigRewrite(typeData data, const char *name, struct rewriteCon ...@@ -1696,61 +1786,69 @@ static void boolConfigRewrite(typeData data, const char *name, struct rewriteCon
} }
/* String Configs */ /* String Configs */
static void stringConfigInit(typeData data) { static void stringConfigInit(standardConfig *config) {
*data.string.config = (data.string.convert_empty_to_null && !data.string.default_value) ? NULL : zstrdup(data.string.default_value); *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); 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; return 0;
char *prev = *data.string.config; char *prev = *config->data.string.config;
char *new = (data.string.convert_empty_to_null && !argv[0][0]) ? NULL : argv[0]; 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))) { 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); zfree(prev);
return 1; return 1;
} }
return 2; return 2;
} }
static sds stringConfigGet(typeData data) { static sds stringConfigGet(standardConfig *config) {
return sdsnew(*data.string.config ? *data.string.config : ""); return sdsnew(*config->data.string.config ? *config->data.string.config : "");
} }
static void stringConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) { static void stringConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) {
rewriteConfigStringOption(state, name,*(data.string.config), data.string.default_value); rewriteConfigStringOption(state, name,*(config->data.string.config), config->data.string.default_value);
} }
/* SDS Configs */ /* SDS Configs */
static void sdsConfigInit(typeData data) { static void sdsConfigInit(standardConfig *config) {
*data.sds.config = (data.sds.convert_empty_to_null && !data.sds.default_value) ? NULL: sdsnew(data.sds.default_value); *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); 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; return 0;
sds prev = *data.sds.config; sds prev = config->flags & MODULE_CONFIG ? getModuleStringConfig(config->privdata) : *config->data.sds.config;
sds new = (data.string.convert_empty_to_null && (sdslen(argv[0]) == 0)) ? NULL : argv[0]; 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))) { if (new != prev && (new == NULL || prev == NULL || sdscmp(prev, new))) {
*data.sds.config = new != NULL ? sdsdup(new) : NULL;
sdsfree(prev); sdsfree(prev);
if (config->flags & MODULE_CONFIG) {
return setModuleStringConfig(config->privdata, new, err);
}
*config->data.sds.config = new != NULL ? sdsdup(new) : NULL;
return 1; return 1;
} }
if (config->flags & MODULE_CONFIG && prev) sdsfree(prev);
return 2; return 2;
} }
static sds sdsConfigGet(typeData data) { static sds sdsConfigGet(standardConfig *config) {
if (*data.sds.config) { sds val = config->flags & MODULE_CONFIG ? getModuleStringConfig(config->privdata) : *config->data.sds.config;
return sdsdup(*data.sds.config); if (val) {
if (config->flags & MODULE_CONFIG) return val;
return sdsdup(val);
} else { } else {
return sdsnew(""); return sdsnew("");
} }
} }
static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) { static void sdsConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) {
rewriteConfigSdsOption(state, name, *(data.sds.config), data.sds.default_value); 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 ...@@ -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) { \ #define createStringConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, apply) { \
embedCommonConfig(name, alias, flags) \ embedCommonConfig(name, alias, flags) \
embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite, apply) \ embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite, apply) \
.type = STRING_CONFIG, \
.data.string = { \ .data.string = { \
.config = &(config_addr), \ .config = &(config_addr), \
.default_value = (default), \ .default_value = (default), \
...@@ -1771,6 +1870,7 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf ...@@ -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) { \ #define createSDSConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, apply) { \
embedCommonConfig(name, alias, flags) \ embedCommonConfig(name, alias, flags) \
embedConfigInterface(sdsConfigInit, sdsConfigSet, sdsConfigGet, sdsConfigRewrite, apply) \ embedConfigInterface(sdsConfigInit, sdsConfigSet, sdsConfigGet, sdsConfigRewrite, apply) \
.type = SDS_CONFIG, \
.data.sds = { \ .data.sds = { \
.config = &(config_addr), \ .config = &(config_addr), \
.default_value = (default), \ .default_value = (default), \
...@@ -1780,16 +1880,16 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf ...@@ -1780,16 +1880,16 @@ static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConf
} }
/* Enum configs */ /* Enum configs */
static void enumConfigInit(typeData data) { static void enumConfigInit(standardConfig *config) {
*data.enumd.config = data.enumd.default_value; *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); 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) { if (enumval == INT_MIN) {
sds enumerr = sdsnew("argument must be one of the following: "); 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) { while(enumNode->name != NULL) {
enumerr = sdscatlen(enumerr, enumNode->name, enumerr = sdscatlen(enumerr, enumNode->name,
strlen(enumNode->name)); strlen(enumNode->name));
...@@ -1805,27 +1905,32 @@ static int enumConfigSet(typeData data, sds *argv, int argc, const char **err) { ...@@ -1805,27 +1905,32 @@ static int enumConfigSet(typeData data, sds *argv, int argc, const char **err) {
*err = loadbuf; *err = loadbuf;
return 0; 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; return 0;
int prev = *(data.enumd.config); int prev = config->flags & MODULE_CONFIG ? getModuleEnumConfig(config->privdata) : *(config->data.enumd.config);
if (prev != enumval) { 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 1;
} }
return 2; return 2;
} }
static sds enumConfigGet(typeData data) { static sds enumConfigGet(standardConfig *config) {
return sdsnew(configEnumGetNameOrUnknown(data.enumd.enum_value,*data.enumd.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) { static void enumConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) {
rewriteConfigEnumOption(state, name,*(data.enumd.config), data.enumd.enum_value, data.enumd.default_value); 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) { \ #define createEnumConfig(name, alias, flags, enum, config_addr, default, is_valid, apply) { \
embedCommonConfig(name, alias, flags) \ embedCommonConfig(name, alias, flags) \
embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite, apply) \ embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite, apply) \
.type = ENUM_CONFIG, \
.data.enumd = { \ .data.enumd = { \
.config = &(config_addr), \ .config = &(config_addr), \
.default_value = (default), \ .default_value = (default), \
...@@ -1836,69 +1941,74 @@ static void enumConfigRewrite(typeData data, const char *name, struct rewriteCon ...@@ -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 /* Gets a 'long long val' and sets it into the union, using a macro to get
* compile time type check. */ * compile time type check. */
#define SET_NUMERIC_TYPE(val) \ int setNumericType(standardConfig *config, long long val, const char **err) {
if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \ if (config->data.numeric.numeric_type == NUMERIC_TYPE_INT) {
*(data.numeric.config.i) = (int) val; \ *(config->data.numeric.config.i) = (int) val;
} else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_UINT) {
*(data.numeric.config.ui) = (unsigned int) val; \ *(config->data.numeric.config.ui) = (unsigned int) val;
} else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG) {
*(data.numeric.config.l) = (long) val; \ *(config->data.numeric.config.l) = (long) val;
} else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG) {
*(data.numeric.config.ul) = (unsigned long) val; \ *(config->data.numeric.config.ul) = (unsigned long) val;
} else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) {
*(data.numeric.config.ll) = (long long) val; \ if (config->flags & MODULE_CONFIG)
} else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \ return setModuleNumericConfig(config->privdata, val, err);
*(data.numeric.config.ull) = (unsigned long long) val; \ else *(config->data.numeric.config.ll) = (long long) val;
} else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) {
*(data.numeric.config.st) = (size_t) val; \ *(config->data.numeric.config.ull) = (unsigned long long) val;
} else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) {
*(data.numeric.config.sst) = (ssize_t) val; \ *(config->data.numeric.config.st) = (size_t) val;
} else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) {
*(data.numeric.config.ot) = (off_t) val; \ *(config->data.numeric.config.sst) = (ssize_t) val;
} else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) {
*(data.numeric.config.tt) = (time_t) val; \ *(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 /* Gets a 'long long val' and sets it with the value from the union, using a
* macro to get compile time type check. */ * macro to get compile time type check. */
#define GET_NUMERIC_TYPE(val) \ #define GET_NUMERIC_TYPE(val) \
if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \ if (config->data.numeric.numeric_type == NUMERIC_TYPE_INT) { \
val = *(data.numeric.config.i); \ val = *(config->data.numeric.config.i); \
} else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \
val = *(data.numeric.config.ui); \ val = *(config->data.numeric.config.ui); \
} else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \
val = *(data.numeric.config.l); \ val = *(config->data.numeric.config.l); \
} else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \
val = *(data.numeric.config.ul); \ val = *(config->data.numeric.config.ul); \
} else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \
val = *(data.numeric.config.ll); \ if (config->flags & MODULE_CONFIG) val = getModuleNumericConfig(config->privdata); \
} else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \ else val = *(config->data.numeric.config.ll); \
val = *(data.numeric.config.ull); \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \
} else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \ val = *(config->data.numeric.config.ull); \
val = *(data.numeric.config.st); \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \
} else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \ val = *(config->data.numeric.config.st); \
val = *(data.numeric.config.sst); \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \
} else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \ val = *(config->data.numeric.config.sst); \
val = *(data.numeric.config.ot); \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \
} else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \ val = *(config->data.numeric.config.ot); \
val = *(data.numeric.config.tt); \ } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \
val = *(config->data.numeric.config.tt); \
} }
/* Numeric configs */ /* Numeric configs */
static void numericConfigInit(typeData data) { static void numericConfigInit(standardConfig *config) {
SET_NUMERIC_TYPE(data.numeric.default_value) setNumericType(config, config->data.numeric.default_value, NULL);
} }
static int numericBoundaryCheck(typeData data, long long ll, const char **err) { static int numericBoundaryCheck(standardConfig *config, long long ll, const char **err) {
if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG || if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG ||
data.numeric.numeric_type == NUMERIC_TYPE_UINT || config->data.numeric.numeric_type == NUMERIC_TYPE_UINT ||
data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) {
/* Boundary check for unsigned types */ /* Boundary check for unsigned types */
unsigned long long ull = ll; unsigned long long ull = ll;
unsigned long long upper_bound = data.numeric.upper_bound; unsigned long long upper_bound = config->data.numeric.upper_bound;
unsigned long long lower_bound = data.numeric.lower_bound; unsigned long long lower_bound = config->data.numeric.lower_bound;
if (ull > upper_bound || ull < 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, snprintf(loadbuf, LOADBUF_SIZE,
"argument must be between %llo and %llo inclusive", "argument must be between %llo and %llo inclusive",
lower_bound, lower_bound,
...@@ -1914,21 +2024,21 @@ static int numericBoundaryCheck(typeData data, long long ll, const char **err) { ...@@ -1914,21 +2024,21 @@ static int numericBoundaryCheck(typeData data, long long ll, const char **err) {
} }
} else { } else {
/* Boundary check for percentages */ /* Boundary check for percentages */
if (data.numeric.flags & PERCENT_CONFIG && ll < 0) { if (config->data.numeric.flags & PERCENT_CONFIG && ll < 0) {
if (ll < data.numeric.lower_bound) { if (ll < config->data.numeric.lower_bound) {
snprintf(loadbuf, LOADBUF_SIZE, snprintf(loadbuf, LOADBUF_SIZE,
"percentage argument must be less or equal to %lld", "percentage argument must be less or equal to %lld",
-data.numeric.lower_bound); -config->data.numeric.lower_bound);
*err = loadbuf; *err = loadbuf;
return 0; return 0;
} }
} }
/* Boundary check for signed types */ /* 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, snprintf(loadbuf, LOADBUF_SIZE,
"argument must be between %lld and %lld inclusive", "argument must be between %lld and %lld inclusive",
data.numeric.lower_bound, config->data.numeric.lower_bound,
data.numeric.upper_bound); config->data.numeric.upper_bound);
*err = loadbuf; *err = loadbuf;
return 0; return 0;
} }
...@@ -1936,9 +2046,9 @@ static int numericBoundaryCheck(typeData data, long long ll, const char **err) { ...@@ -1936,9 +2046,9 @@ static int numericBoundaryCheck(typeData data, long long ll, const char **err) {
return 1; 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 */ /* First try to parse as memory */
if (data.numeric.flags & MEMORY_CONFIG) { if (config->data.numeric.flags & MEMORY_CONFIG) {
int memerr; int memerr;
*res = memtoull(value, &memerr); *res = memtoull(value, &memerr);
if (!memerr) if (!memerr)
...@@ -1946,7 +2056,7 @@ static int numericParseString(typeData data, sds value, const char **err, long l ...@@ -1946,7 +2056,7 @@ static int numericParseString(typeData data, sds value, const char **err, long l
} }
/* Attempt to parse as percent */ /* 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] == '%' && sdslen(value) > 1 && value[sdslen(value)-1] == '%' &&
string2ll(value, sdslen(value)-1, res) && string2ll(value, sdslen(value)-1, res) &&
*res >= 0) { *res >= 0) {
...@@ -1956,7 +2066,7 @@ static int numericParseString(typeData data, sds value, const char **err, long l ...@@ -1956,7 +2066,7 @@ static int numericParseString(typeData data, sds value, const char **err, long l
} }
/* Attempt to parse as an octal number */ /* Attempt to parse as an octal number */
if (data.numeric.flags & OCTAL_CONFIG) { if (config->data.numeric.flags & OCTAL_CONFIG) {
char *endptr; char *endptr;
errno = 0; errno = 0;
*res = strtoll(value, &endptr, 8); *res = strtoll(value, &endptr, 8);
...@@ -1965,58 +2075,57 @@ static int numericParseString(typeData data, sds value, const char **err, long l ...@@ -1965,58 +2075,57 @@ static int numericParseString(typeData data, sds value, const char **err, long l
} }
/* Attempt a simple number (no special flags set) */ /* 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; return 1;
/* Select appropriate error string */ /* Select appropriate error string */
if (data.numeric.flags & MEMORY_CONFIG && if (config->data.numeric.flags & MEMORY_CONFIG &&
data.numeric.flags & PERCENT_CONFIG) config->data.numeric.flags & PERCENT_CONFIG)
*err = "argument must be a memory or percent value" ; *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"; *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"; *err = "argument couldn't be parsed as an octal number";
else else
*err = "argument couldn't be parsed into an integer"; *err = "argument couldn't be parsed into an integer";
return 0; 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); UNUSED(argc);
long long ll, prev = 0; long long ll, prev = 0;
if (!numericParseString(data, argv[0], err, &ll)) if (!numericParseString(config, argv[0], err, &ll))
return 0; return 0;
if (!numericBoundaryCheck(data, ll, err)) if (!numericBoundaryCheck(config, ll, err))
return 0; 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; return 0;
GET_NUMERIC_TYPE(prev) GET_NUMERIC_TYPE(prev)
if (prev != ll) { if (prev != ll) {
SET_NUMERIC_TYPE(ll) return setNumericType(config, ll, err);
return 1;
} }
return 2; return 2;
} }
static sds numericConfigGet(typeData data) { static sds numericConfigGet(standardConfig *config) {
char buf[128]; char buf[128];
long long value = 0; long long value = 0;
GET_NUMERIC_TYPE(value) 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); int len = ll2string(buf, sizeof(buf), -value);
buf[len] = '%'; buf[len] = '%';
buf[len+1] = '\0'; buf[len+1] = '\0';
} }
else if (data.numeric.flags & MEMORY_CONFIG) { else if (config->data.numeric.flags & MEMORY_CONFIG) {
ull2string(buf, sizeof(buf), value); 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); snprintf(buf, sizeof(buf), "%llo", value);
} else { } else {
ll2string(buf, sizeof(buf), value); ll2string(buf, sizeof(buf), value);
...@@ -2024,25 +2133,26 @@ static sds numericConfigGet(typeData data) { ...@@ -2024,25 +2133,26 @@ static sds numericConfigGet(typeData data) {
return sdsnew(buf); 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; long long value = 0;
GET_NUMERIC_TYPE(value) GET_NUMERIC_TYPE(value)
if (data.numeric.flags & PERCENT_CONFIG && value < 0) { if (config->data.numeric.flags & PERCENT_CONFIG && value < 0) {
rewriteConfigPercentOption(state, name, -value, data.numeric.default_value); rewriteConfigPercentOption(state, name, -value, config->data.numeric.default_value);
} else if (data.numeric.flags & MEMORY_CONFIG) { } else if (config->data.numeric.flags & MEMORY_CONFIG) {
rewriteConfigBytesOption(state, name, value, data.numeric.default_value); rewriteConfigBytesOption(state, name, value, config->data.numeric.default_value);
} else if (data.numeric.flags & OCTAL_CONFIG) { } else if (config->data.numeric.flags & OCTAL_CONFIG) {
rewriteConfigOctalOption(state, name, value, data.numeric.default_value); rewriteConfigOctalOption(state, name, value, config->data.numeric.default_value);
} else { } 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) { \ #define embedCommonNumericalConfig(name, alias, _flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) { \
embedCommonConfig(name, alias, _flags) \ embedCommonConfig(name, alias, _flags) \
embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite, apply) \ embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite, apply) \
.type = NUMERIC_CONFIG, \
.data.numeric = { \ .data.numeric = { \
.lower_bound = (lower), \ .lower_bound = (lower), \
.upper_bound = (upper), \ .upper_bound = (upper), \
...@@ -2121,6 +2231,7 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite ...@@ -2121,6 +2231,7 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite
} }
#define createSpecialConfig(name, alias, modifiable, setfn, getfn, rewritefn, applyfn) { \ #define createSpecialConfig(name, alias, modifiable, setfn, getfn, rewritefn, applyfn) { \
.type = SPECIAL_CONFIG, \
embedCommonConfig(name, alias, modifiable) \ embedCommonConfig(name, alias, modifiable) \
embedConfigInterface(NULL, setfn, getfn, rewritefn, applyfn) \ embedConfigInterface(NULL, setfn, getfn, rewritefn, applyfn) \
} }
...@@ -2397,8 +2508,8 @@ static int applyTLSPort(const char **err) { ...@@ -2397,8 +2508,8 @@ static int applyTLSPort(const char **err) {
#endif /* USE_OPENSSL */ #endif /* USE_OPENSSL */
static int setConfigDirOption(typeData data, sds *argv, int argc, const char **err) { static int setConfigDirOption(standardConfig *config, sds *argv, int argc, const char **err) {
UNUSED(data); UNUSED(config);
if (argc != 1) { if (argc != 1) {
*err = "wrong number of arguments"; *err = "wrong number of arguments";
return 0; return 0;
...@@ -2410,8 +2521,8 @@ static int setConfigDirOption(typeData data, sds *argv, int argc, const char **e ...@@ -2410,8 +2521,8 @@ static int setConfigDirOption(typeData data, sds *argv, int argc, const char **e
return 1; return 1;
} }
static sds getConfigDirOption(typeData data) { static sds getConfigDirOption(standardConfig *config) {
UNUSED(data); UNUSED(config);
char buf[1024]; char buf[1024];
if (getcwd(buf,sizeof(buf)) == NULL) if (getcwd(buf,sizeof(buf)) == NULL)
...@@ -2420,8 +2531,8 @@ static sds getConfigDirOption(typeData data) { ...@@ -2420,8 +2531,8 @@ static sds getConfigDirOption(typeData data) {
return sdsnew(buf); return sdsnew(buf);
} }
static int setConfigSaveOption(typeData data, sds *argv, int argc, const char **err) { static int setConfigSaveOption(standardConfig *config, sds *argv, int argc, const char **err) {
UNUSED(data); UNUSED(config);
int j; int j;
/* Special case: treat single arg "" as zero args indicating empty save configuration */ /* 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 ** ...@@ -2473,8 +2584,8 @@ static int setConfigSaveOption(typeData data, sds *argv, int argc, const char **
return 1; return 1;
} }
static sds getConfigSaveOption(typeData data) { static sds getConfigSaveOption(standardConfig *config) {
UNUSED(data); UNUSED(config);
sds buf = sdsempty(); sds buf = sdsempty();
int j; int j;
...@@ -2489,13 +2600,13 @@ static sds getConfigSaveOption(typeData data) { ...@@ -2489,13 +2600,13 @@ static sds getConfigSaveOption(typeData data) {
return buf; return buf;
} }
static int setConfigClientOutputBufferLimitOption(typeData data, sds *argv, int argc, const char **err) { static int setConfigClientOutputBufferLimitOption(standardConfig *config, sds *argv, int argc, const char **err) {
UNUSED(data); UNUSED(config);
return updateClientOutputBufferLimit(argv, argc, err); return updateClientOutputBufferLimit(argv, argc, err);
} }
static sds getConfigClientOutputBufferLimitOption(typeData data) { static sds getConfigClientOutputBufferLimitOption(standardConfig *config) {
UNUSED(data); UNUSED(config);
sds buf = sdsempty(); sds buf = sdsempty();
int j; int j;
for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) { for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
...@@ -2513,11 +2624,11 @@ static sds getConfigClientOutputBufferLimitOption(typeData data) { ...@@ -2513,11 +2624,11 @@ static sds getConfigClientOutputBufferLimitOption(typeData data) {
/* Parse an array of CONFIG_OOM_COUNT sds strings, validate and populate /* Parse an array of CONFIG_OOM_COUNT sds strings, validate and populate
* server.oom_score_adj_values if valid. * 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 i;
int values[CONFIG_OOM_COUNT]; int values[CONFIG_OOM_COUNT];
int change = 0; int change = 0;
UNUSED(data); UNUSED(config);
if (argc != CONFIG_OOM_COUNT) { if (argc != CONFIG_OOM_COUNT) {
*err = "wrong number of arguments"; *err = "wrong number of arguments";
...@@ -2558,8 +2669,8 @@ static int setConfigOOMScoreAdjValuesOption(typeData data, sds *argv, int argc, ...@@ -2558,8 +2669,8 @@ static int setConfigOOMScoreAdjValuesOption(typeData data, sds *argv, int argc,
return change ? 1 : 2; return change ? 1 : 2;
} }
static sds getConfigOOMScoreAdjValuesOption(typeData data) { static sds getConfigOOMScoreAdjValuesOption(standardConfig *config) {
UNUSED(data); UNUSED(config);
sds buf = sdsempty(); sds buf = sdsempty();
int j; int j;
...@@ -2572,8 +2683,8 @@ static sds getConfigOOMScoreAdjValuesOption(typeData data) { ...@@ -2572,8 +2683,8 @@ static sds getConfigOOMScoreAdjValuesOption(typeData data) {
return buf; return buf;
} }
static int setConfigNotifyKeyspaceEventsOption(typeData data, sds *argv, int argc, const char **err) { static int setConfigNotifyKeyspaceEventsOption(standardConfig *config, sds *argv, int argc, const char **err) {
UNUSED(data); UNUSED(config);
if (argc != 1) { if (argc != 1) {
*err = "wrong number of arguments"; *err = "wrong number of arguments";
return 0; return 0;
...@@ -2587,13 +2698,13 @@ static int setConfigNotifyKeyspaceEventsOption(typeData data, sds *argv, int arg ...@@ -2587,13 +2698,13 @@ static int setConfigNotifyKeyspaceEventsOption(typeData data, sds *argv, int arg
return 1; return 1;
} }
static sds getConfigNotifyKeyspaceEventsOption(typeData data) { static sds getConfigNotifyKeyspaceEventsOption(standardConfig *config) {
UNUSED(data); UNUSED(config);
return keyspaceEventsFlagsToString(server.notify_keyspace_events); return keyspaceEventsFlagsToString(server.notify_keyspace_events);
} }
static int setConfigBindOption(typeData data, sds* argv, int argc, const char **err) { static int setConfigBindOption(standardConfig *config, sds* argv, int argc, const char **err) {
UNUSED(data); UNUSED(config);
int j; int j;
if (argc > CONFIG_BINDADDR_MAX) { if (argc > CONFIG_BINDADDR_MAX) {
...@@ -2615,8 +2726,8 @@ static int setConfigBindOption(typeData data, sds* argv, int argc, const char ** ...@@ -2615,8 +2726,8 @@ static int setConfigBindOption(typeData data, sds* argv, int argc, const char **
return 1; return 1;
} }
static int setConfigReplicaOfOption(typeData data, sds* argv, int argc, const char **err) { static int setConfigReplicaOfOption(standardConfig *config, sds* argv, int argc, const char **err) {
UNUSED(data); UNUSED(config);
if (argc != 2) { if (argc != 2) {
*err = "wrong number of arguments"; *err = "wrong number of arguments";
...@@ -2639,13 +2750,13 @@ static int setConfigReplicaOfOption(typeData data, sds* argv, int argc, const ch ...@@ -2639,13 +2750,13 @@ static int setConfigReplicaOfOption(typeData data, sds* argv, int argc, const ch
return 1; return 1;
} }
static sds getConfigBindOption(typeData data) { static sds getConfigBindOption(standardConfig *config) {
UNUSED(data); UNUSED(config);
return sdsjoin(server.bindaddr,server.bindaddr_count," "); return sdsjoin(server.bindaddr,server.bindaddr_count," ");
} }
static sds getConfigReplicaOfOption(typeData data) { static sds getConfigReplicaOfOption(standardConfig *config) {
UNUSED(data); UNUSED(config);
char buf[256]; char buf[256];
if (server.masterhost) if (server.masterhost)
snprintf(buf,sizeof(buf),"%s %d", snprintf(buf,sizeof(buf),"%s %d",
...@@ -2661,8 +2772,8 @@ int allowProtectedAction(int config, client *c) { ...@@ -2661,8 +2772,8 @@ int allowProtectedAction(int config, client *c) {
} }
static int setConfigLatencyTrackingInfoPercentilesOutputOption(typeData data, sds *argv, int argc, const char **err) { static int setConfigLatencyTrackingInfoPercentilesOutputOption(standardConfig *config, sds *argv, int argc, const char **err) {
UNUSED(data); UNUSED(config);
zfree(server.latency_tracking_info_percentiles); zfree(server.latency_tracking_info_percentiles);
server.latency_tracking_info_percentiles = NULL; server.latency_tracking_info_percentiles = NULL;
server.latency_tracking_info_percentiles_len = argc; server.latency_tracking_info_percentiles_len = argc;
...@@ -2694,8 +2805,8 @@ configerr: ...@@ -2694,8 +2805,8 @@ configerr:
return 0; return 0;
} }
static sds getConfigLatencyTrackingInfoPercentilesOutputOption(typeData data) { static sds getConfigLatencyTrackingInfoPercentilesOutputOption(standardConfig *config) {
UNUSED(data); UNUSED(config);
sds buf = sdsempty(); sds buf = sdsempty();
for (int j = 0; j < server.latency_tracking_info_percentiles_len; j++) { for (int j = 0; j < server.latency_tracking_info_percentiles_len; j++) {
char fbuf[128]; char fbuf[128];
...@@ -2709,8 +2820,8 @@ static sds getConfigLatencyTrackingInfoPercentilesOutputOption(typeData data) { ...@@ -2709,8 +2820,8 @@ static sds getConfigLatencyTrackingInfoPercentilesOutputOption(typeData data) {
} }
/* Rewrite the latency-tracking-info-percentiles option. */ /* Rewrite the latency-tracking-info-percentiles option. */
void rewriteConfigLatencyTrackingInfoPercentilesOutputOption(typeData data, const char *name, struct rewriteConfigState *state) { void rewriteConfigLatencyTrackingInfoPercentilesOutputOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
UNUSED(data); UNUSED(config);
sds line = sdsnew(name); sds line = sdsnew(name);
/* Rewrite latency-tracking-info-percentiles parameters, /* Rewrite latency-tracking-info-percentiles parameters,
* or an empty 'latency-tracking-info-percentiles ""' line to avoid the * or an empty 'latency-tracking-info-percentiles ""' line to avoid the
...@@ -2729,7 +2840,7 @@ void rewriteConfigLatencyTrackingInfoPercentilesOutputOption(typeData data, cons ...@@ -2729,7 +2840,7 @@ void rewriteConfigLatencyTrackingInfoPercentilesOutputOption(typeData data, cons
rewriteConfigRewriteLine(state,name,line,1); rewriteConfigRewriteLine(state,name,line,1);
} }
standardConfig configs[] = { standardConfig static_configs[] = {
/* Bool configs */ /* Bool configs */
createBoolConfig("rdbchecksum", NULL, IMMUTABLE_CONFIG, server.rdb_checksum, 1, NULL, NULL), createBoolConfig("rdbchecksum", NULL, IMMUTABLE_CONFIG, server.rdb_checksum, 1, NULL, NULL),
createBoolConfig("daemonize", NULL, IMMUTABLE_CONFIG, server.daemonize, 0, NULL, NULL), createBoolConfig("daemonize", NULL, IMMUTABLE_CONFIG, server.daemonize, 0, NULL, NULL),
...@@ -2771,7 +2882,7 @@ standardConfig configs[] = { ...@@ -2771,7 +2882,7 @@ standardConfig configs[] = {
createBoolConfig("crash-log-enabled", NULL, MODIFIABLE_CONFIG, server.crashlog_enabled, 1, NULL, updateSighandlerEnabled), 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("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("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("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("replica-announced", NULL, MODIFIABLE_CONFIG, server.replica_announced, 1, NULL, NULL),
createBoolConfig("latency-tracking", NULL, MODIFIABLE_CONFIG, server.latency_tracking_enabled, 1, NULL, NULL), createBoolConfig("latency-tracking", NULL, MODIFIABLE_CONFIG, server.latency_tracking_enabled, 1, NULL, NULL),
...@@ -2930,10 +3041,105 @@ standardConfig configs[] = { ...@@ -2930,10 +3041,105 @@ standardConfig configs[] = {
createSpecialConfig("replicaof", "slaveof", IMMUTABLE_CONFIG | MULTI_ARG_CONFIG, setConfigReplicaOfOption, getConfigReplicaOfOption, rewriteConfigReplicaOfOption, NULL), 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), 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} {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 * CONFIG HELP
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
...@@ -2975,8 +3181,10 @@ void configRewriteCommand(client *c) { ...@@ -2975,8 +3181,10 @@ void configRewriteCommand(client *c) {
return; return;
} }
if (rewriteConfig(server.configfile, 0) == -1) { if (rewriteConfig(server.configfile, 0) == -1) {
serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(errno)); /* save errno in case of being tainted. */
addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno)); int err = errno;
serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(err));
addReplyErrorFormat(c,"Rewriting config file: %s", strerror(err));
} else { } else {
serverLog(LL_WARNING,"CONFIG REWRITE executed with success."); serverLog(LL_WARNING,"CONFIG REWRITE executed with success.");
addReply(c,shared.ok); addReply(c,shared.ok);
......
...@@ -218,9 +218,12 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) { ...@@ -218,9 +218,12 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
val->lru = old->lru; val->lru = old->lru;
} }
/* Although the key is not really deleted from the database, we regard /* Although the key is not really deleted from the database, we regard
overwrite as two steps of unlink+add, so we still need to call the unlink * overwrite as two steps of unlink+add, so we still need to call the unlink
callback of the module. */ * callback of the module. */
moduleNotifyKeyUnlink(key,old,db->id); moduleNotifyKeyUnlink(key,old,db->id);
/* We want to try to unblock any client using a blocking XREADGROUP */
if (old->type == OBJ_STREAM)
signalKeyAsReady(db,key,old->type);
dictSetVal(db->dict, de, val); dictSetVal(db->dict, de, val);
if (server.lazyfree_lazy_server_del) { if (server.lazyfree_lazy_server_del) {
...@@ -311,6 +314,9 @@ static int dbGenericDelete(redisDb *db, robj *key, int async) { ...@@ -311,6 +314,9 @@ static int dbGenericDelete(redisDb *db, robj *key, int async) {
robj *val = dictGetVal(de); robj *val = dictGetVal(de);
/* Tells the module that the key has been unlinked from the database. */ /* Tells the module that the key has been unlinked from the database. */
moduleNotifyKeyUnlink(key,val,db->id); moduleNotifyKeyUnlink(key,val,db->id);
/* We want to try to unblock any client using a blocking XREADGROUP */
if (val->type == OBJ_STREAM)
signalKeyAsReady(db,key,val->type);
if (async) { if (async) {
freeObjAsync(key, val, db->id); freeObjAsync(key, val, db->id);
dictSetVal(db->dict, de, NULL); dictSetVal(db->dict, de, NULL);
...@@ -551,6 +557,7 @@ void signalFlushedDb(int dbid, int async) { ...@@ -551,6 +557,7 @@ void signalFlushedDb(int dbid, int async) {
} }
for (int j = startdb; j <= enddb; j++) { for (int j = startdb; j <= enddb; j++) {
scanDatabaseForDeletedStreams(&server.db[j], NULL);
touchAllWatchedKeysInDb(&server.db[j], NULL); touchAllWatchedKeysInDb(&server.db[j], NULL);
} }
...@@ -1311,7 +1318,7 @@ void copyCommand(client *c) { ...@@ -1311,7 +1318,7 @@ void copyCommand(client *c) {
* one or more blocked clients for B[LR]POP or other blocking commands * one or more blocked clients for B[LR]POP or other blocking commands
* and signal the keys as ready if they are of the right type. See the comment * and signal the keys as ready if they are of the right type. See the comment
* where the function is used for more info. */ * where the function is used for more info. */
void scanDatabaseForReadyLists(redisDb *db) { void scanDatabaseForReadyKeys(redisDb *db) {
dictEntry *de; dictEntry *de;
dictIterator *di = dictGetSafeIterator(db->blocking_keys); dictIterator *di = dictGetSafeIterator(db->blocking_keys);
while((de = dictNext(di)) != NULL) { while((de = dictNext(di)) != NULL) {
...@@ -1325,6 +1332,39 @@ void scanDatabaseForReadyLists(redisDb *db) { ...@@ -1325,6 +1332,39 @@ void scanDatabaseForReadyLists(redisDb *db) {
dictReleaseIterator(di); dictReleaseIterator(di);
} }
/* Since we are unblocking XREADGROUP clients in the event the
* key was deleted/overwritten we must do the same in case the
* database was flushed/swapped. */
void scanDatabaseForDeletedStreams(redisDb *emptied, redisDb *replaced_with) {
/* Optimization: If no clients are in type BLOCKED_STREAM,
* we can skip this loop. */
if (!server.blocked_clients_by_type[BLOCKED_STREAM]) return;
dictEntry *de;
dictIterator *di = dictGetSafeIterator(emptied->blocking_keys);
while((de = dictNext(di)) != NULL) {
robj *key = dictGetKey(de);
int was_stream = 0, is_stream = 0;
dictEntry *kde = dictFind(emptied->dict, key->ptr);
if (kde) {
robj *value = dictGetVal(kde);
was_stream = value->type == OBJ_STREAM;
}
if (replaced_with) {
dictEntry *kde = dictFind(replaced_with->dict, key->ptr);
if (kde) {
robj *value = dictGetVal(kde);
is_stream = value->type == OBJ_STREAM;
}
}
/* We want to try to unblock any client using a blocking XREADGROUP */
if (was_stream && !is_stream)
signalKeyAsReady(emptied, key, OBJ_STREAM);
}
dictReleaseIterator(di);
}
/* Swap two databases at runtime so that all clients will magically see /* Swap two databases at runtime so that all clients will magically see
* the new database even if already connected. Note that the client * the new database even if already connected. Note that the client
* structure c->db points to a given DB, so we need to be smarter and * structure c->db points to a given DB, so we need to be smarter and
...@@ -1345,6 +1385,10 @@ int dbSwapDatabases(int id1, int id2) { ...@@ -1345,6 +1385,10 @@ int dbSwapDatabases(int id1, int id2) {
touchAllWatchedKeysInDb(db1, db2); touchAllWatchedKeysInDb(db1, db2);
touchAllWatchedKeysInDb(db2, db1); touchAllWatchedKeysInDb(db2, db1);
/* Try to unblock any XREADGROUP clients if the key no longer exists. */
scanDatabaseForDeletedStreams(db1, db2);
scanDatabaseForDeletedStreams(db2, db1);
/* Swap hash tables. Note that we don't swap blocking_keys, /* Swap hash tables. Note that we don't swap blocking_keys,
* ready_keys and watched_keys, since we want clients to * ready_keys and watched_keys, since we want clients to
* remain in the same DB they were. */ * remain in the same DB they were. */
...@@ -1367,8 +1411,8 @@ int dbSwapDatabases(int id1, int id2) { ...@@ -1367,8 +1411,8 @@ int dbSwapDatabases(int id1, int id2) {
* in dbAdd() when a list is created. So here we need to rescan * in dbAdd() when a list is created. So here we need to rescan
* the list of clients blocked on lists and signal lists as ready * the list of clients blocked on lists and signal lists as ready
* if needed. */ * if needed. */
scanDatabaseForReadyLists(db1); scanDatabaseForReadyKeys(db1);
scanDatabaseForReadyLists(db2); scanDatabaseForReadyKeys(db2);
return C_OK; return C_OK;
} }
...@@ -1391,6 +1435,9 @@ void swapMainDbWithTempDb(redisDb *tempDb) { ...@@ -1391,6 +1435,9 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
* client watching keys. */ * client watching keys. */
touchAllWatchedKeysInDb(activedb, newdb); touchAllWatchedKeysInDb(activedb, newdb);
/* Try to unblock any XREADGROUP clients if the key no longer exists. */
scanDatabaseForDeletedStreams(activedb, newdb);
/* Swap hash tables. Note that we don't swap blocking_keys, /* Swap hash tables. Note that we don't swap blocking_keys,
* ready_keys and watched_keys, since clients * ready_keys and watched_keys, since clients
* remain in the same DB they were. */ * remain in the same DB they were. */
...@@ -1413,7 +1460,7 @@ void swapMainDbWithTempDb(redisDb *tempDb) { ...@@ -1413,7 +1460,7 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
* in dbAdd() when a list is created. So here we need to rescan * in dbAdd() when a list is created. So here we need to rescan
* the list of clients blocked on lists and signal lists as ready * the list of clients blocked on lists and signal lists as ready
* if needed. */ * if needed. */
scanDatabaseForReadyLists(activedb); scanDatabaseForReadyKeys(activedb);
} }
trackingInvalidateKeysOnFlush(1); trackingInvalidateKeysOnFlush(1);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment