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