1/* Configuration file parsing and CONFIG GET/SET commands implementation.
2 *
3 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "server.h"
32#include "cluster.h"
33
34#include <fcntl.h>
35#include <sys/stat.h>
36#include <glob.h>
37#include <string.h>
38
39/*-----------------------------------------------------------------------------
40 * Config file name-value maps.
41 *----------------------------------------------------------------------------*/
42
43typedef struct deprecatedConfig {
44 const char *name;
45 const int argc_min;
46 const int argc_max;
47} deprecatedConfig;
48
49configEnum maxmemory_policy_enum[] = {
50 {"volatile-lru", MAXMEMORY_VOLATILE_LRU},
51 {"volatile-lfu", MAXMEMORY_VOLATILE_LFU},
52 {"volatile-random",MAXMEMORY_VOLATILE_RANDOM},
53 {"volatile-ttl",MAXMEMORY_VOLATILE_TTL},
54 {"allkeys-lru",MAXMEMORY_ALLKEYS_LRU},
55 {"allkeys-lfu",MAXMEMORY_ALLKEYS_LFU},
56 {"allkeys-random",MAXMEMORY_ALLKEYS_RANDOM},
57 {"noeviction",MAXMEMORY_NO_EVICTION},
58 {NULL, 0}
59};
60
61configEnum syslog_facility_enum[] = {
62 {"user", LOG_USER},
63 {"local0", LOG_LOCAL0},
64 {"local1", LOG_LOCAL1},
65 {"local2", LOG_LOCAL2},
66 {"local3", LOG_LOCAL3},
67 {"local4", LOG_LOCAL4},
68 {"local5", LOG_LOCAL5},
69 {"local6", LOG_LOCAL6},
70 {"local7", LOG_LOCAL7},
71 {NULL, 0}
72};
73
74configEnum loglevel_enum[] = {
75 {"debug", LL_DEBUG},
76 {"verbose", LL_VERBOSE},
77 {"notice", LL_NOTICE},
78 {"warning", LL_WARNING},
79 {NULL,0}
80};
81
82configEnum supervised_mode_enum[] = {
83 {"upstart", SUPERVISED_UPSTART},
84 {"systemd", SUPERVISED_SYSTEMD},
85 {"auto", SUPERVISED_AUTODETECT},
86 {"no", SUPERVISED_NONE},
87 {NULL, 0}
88};
89
90configEnum aof_fsync_enum[] = {
91 {"everysec", AOF_FSYNC_EVERYSEC},
92 {"always", AOF_FSYNC_ALWAYS},
93 {"no", AOF_FSYNC_NO},
94 {NULL, 0}
95};
96
97configEnum shutdown_on_sig_enum[] = {
98 {"default", 0},
99 {"save", SHUTDOWN_SAVE},
100 {"nosave", SHUTDOWN_NOSAVE},
101 {"now", SHUTDOWN_NOW},
102 {"force", SHUTDOWN_FORCE},
103 {NULL, 0}
104};
105
106configEnum repl_diskless_load_enum[] = {
107 {"disabled", REPL_DISKLESS_LOAD_DISABLED},
108 {"on-empty-db", REPL_DISKLESS_LOAD_WHEN_DB_EMPTY},
109 {"swapdb", REPL_DISKLESS_LOAD_SWAPDB},
110 {NULL, 0}
111};
112
113configEnum tls_auth_clients_enum[] = {
114 {"no", TLS_CLIENT_AUTH_NO},
115 {"yes", TLS_CLIENT_AUTH_YES},
116 {"optional", TLS_CLIENT_AUTH_OPTIONAL},
117 {NULL, 0}
118};
119
120configEnum oom_score_adj_enum[] = {
121 {"no", OOM_SCORE_ADJ_NO},
122 {"yes", OOM_SCORE_RELATIVE},
123 {"relative", OOM_SCORE_RELATIVE},
124 {"absolute", OOM_SCORE_ADJ_ABSOLUTE},
125 {NULL, 0}
126};
127
128configEnum acl_pubsub_default_enum[] = {
129 {"allchannels", SELECTOR_FLAG_ALLCHANNELS},
130 {"resetchannels", 0},
131 {NULL, 0}
132};
133
134configEnum sanitize_dump_payload_enum[] = {
135 {"no", SANITIZE_DUMP_NO},
136 {"yes", SANITIZE_DUMP_YES},
137 {"clients", SANITIZE_DUMP_CLIENTS},
138 {NULL, 0}
139};
140
141configEnum protected_action_enum[] = {
142 {"no", PROTECTED_ACTION_ALLOWED_NO},
143 {"yes", PROTECTED_ACTION_ALLOWED_YES},
144 {"local", PROTECTED_ACTION_ALLOWED_LOCAL},
145 {NULL, 0}
146};
147
148configEnum cluster_preferred_endpoint_type_enum[] = {
149 {"ip", CLUSTER_ENDPOINT_TYPE_IP},
150 {"hostname", CLUSTER_ENDPOINT_TYPE_HOSTNAME},
151 {"unknown-endpoint", CLUSTER_ENDPOINT_TYPE_UNKNOWN_ENDPOINT},
152 {NULL, 0}
153};
154
155configEnum propagation_error_behavior_enum[] = {
156 {"ignore", PROPAGATION_ERR_BEHAVIOR_IGNORE},
157 {"panic", PROPAGATION_ERR_BEHAVIOR_PANIC},
158 {"panic-on-replicas", PROPAGATION_ERR_BEHAVIOR_PANIC_ON_REPLICAS},
159 {NULL, 0}
160};
161
162/* Output buffer limits presets. */
163clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
164 {0, 0, 0}, /* normal */
165 {1024*1024*256, 1024*1024*64, 60}, /* slave */
166 {1024*1024*32, 1024*1024*8, 60} /* pubsub */
167};
168
169/* OOM Score defaults */
170int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT] = { 0, 200, 800 };
171
172/* Generic config infrastructure function pointers
173 * int is_valid_fn(val, err)
174 * Return 1 when val is valid, and 0 when invalid.
175 * Optionally set err to a static error string.
176 */
177
178/* Configuration values that require no special handling to set, get, load or
179 * rewrite. */
180typedef struct boolConfigData {
181 int *config; /* The pointer to the server config this value is stored in */
182 int default_value; /* The default value of the config on rewrite */
183 int (*is_valid_fn)(int val, const char **err); /* Optional function to check validity of new value (generic doc above) */
184} boolConfigData;
185
186typedef struct stringConfigData {
187 char **config; /* Pointer to the server config this value is stored in. */
188 const char *default_value; /* Default value of the config on rewrite. */
189 int (*is_valid_fn)(char* val, const char **err); /* Optional function to check validity of new value (generic doc above) */
190 int convert_empty_to_null; /* Boolean indicating if empty strings should
191 be stored as a NULL value. */
192} stringConfigData;
193
194typedef struct sdsConfigData {
195 sds *config; /* Pointer to the server config this value is stored in. */
196 char *default_value; /* Default value of the config on rewrite. */
197 int (*is_valid_fn)(sds val, const char **err); /* Optional function to check validity of new value (generic doc above) */
198 int convert_empty_to_null; /* Boolean indicating if empty SDS strings should
199 be stored as a NULL value. */
200} sdsConfigData;
201
202typedef struct enumConfigData {
203 int *config; /* The pointer to the server config this value is stored in */
204 configEnum *enum_value; /* The underlying enum type this data represents */
205 int default_value; /* The default value of the config on rewrite */
206 int (*is_valid_fn)(int val, const char **err); /* Optional function to check validity of new value (generic doc above) */
207} enumConfigData;
208
209typedef enum numericType {
210 NUMERIC_TYPE_INT,
211 NUMERIC_TYPE_UINT,
212 NUMERIC_TYPE_LONG,
213 NUMERIC_TYPE_ULONG,
214 NUMERIC_TYPE_LONG_LONG,
215 NUMERIC_TYPE_ULONG_LONG,
216 NUMERIC_TYPE_SIZE_T,
217 NUMERIC_TYPE_SSIZE_T,
218 NUMERIC_TYPE_OFF_T,
219 NUMERIC_TYPE_TIME_T,
220} numericType;
221
222typedef struct numericConfigData {
223 union {
224 int *i;
225 unsigned int *ui;
226 long *l;
227 unsigned long *ul;
228 long long *ll;
229 unsigned long long *ull;
230 size_t *st;
231 ssize_t *sst;
232 off_t *ot;
233 time_t *tt;
234 } config; /* The pointer to the numeric config this value is stored in */
235 unsigned int flags;
236 numericType numeric_type; /* An enum indicating the type of this value */
237 long long lower_bound; /* The lower bound of this numeric value */
238 long long upper_bound; /* The upper bound of this numeric value */
239 long long default_value; /* The default value of the config on rewrite */
240 int (*is_valid_fn)(long long val, const char **err); /* Optional function to check validity of new value (generic doc above) */
241} numericConfigData;
242
243typedef union typeData {
244 boolConfigData yesno;
245 stringConfigData string;
246 sdsConfigData sds;
247 enumConfigData enumd;
248 numericConfigData numeric;
249} typeData;
250
251typedef struct standardConfig standardConfig;
252
253typedef int (*apply_fn)(const char **err);
254typedef struct typeInterface {
255 /* Called on server start, to init the server with default value */
256 void (*init)(standardConfig *config);
257 /* Called on server startup and CONFIG SET, returns 1 on success,
258 * 2 meaning no actual change done, 0 on error and can set a verbose err
259 * string */
260 int (*set)(standardConfig *config, sds *argv, int argc, const char **err);
261 /* Optional: called after `set()` to apply the config change. Used only in
262 * the context of CONFIG SET. Returns 1 on success, 0 on failure.
263 * Optionally set err to a static error string. */
264 apply_fn apply;
265 /* Called on CONFIG GET, returns sds to be used in reply */
266 sds (*get)(standardConfig *config);
267 /* Called on CONFIG REWRITE, required to rewrite the config state */
268 void (*rewrite)(standardConfig *config, const char *name, struct rewriteConfigState *state);
269} typeInterface;
270
271struct standardConfig {
272 const char *name; /* The user visible name of this config */
273 const char *alias; /* An alias that can also be used for this config */
274 unsigned int flags; /* Flags for this specific config */
275 typeInterface interface; /* The function pointers that define the type interface */
276 typeData data; /* The type specific data exposed used by the interface */
277 configType type; /* The type of config this is. */
278 void *privdata; /* privdata for this config, for module configs this is a ModuleConfig struct */
279};
280
281dict *configs = NULL; /* Runtime config values */
282
283/* Lookup a config by the provided sds string name, or return NULL
284 * if the config does not exist */
285static standardConfig *lookupConfig(sds name) {
286 dictEntry *de = dictFind(configs, name);
287 return de ? dictGetVal(de) : NULL;
288}
289
290/*-----------------------------------------------------------------------------
291 * Enum access functions
292 *----------------------------------------------------------------------------*/
293
294/* Get enum value from name. If there is no match INT_MIN is returned. */
295int configEnumGetValue(configEnum *ce, sds *argv, int argc, int bitflags) {
296 if (argc == 0 || (!bitflags && argc != 1)) return INT_MIN;
297 int values = 0;
298 for (int i = 0; i < argc; i++) {
299 int matched = 0;
300 for (configEnum *ceItem = ce; ceItem->name != NULL; ceItem++) {
301 if (!strcasecmp(argv[i],ceItem->name)) {
302 values |= ceItem->val;
303 matched = 1;
304 }
305 }
306 if (!matched) return INT_MIN;
307 }
308 return values;
309}
310
311/* Get enum name/s from value. If no matches are found "unknown" is returned. */
312static sds configEnumGetName(configEnum *ce, int values, int bitflags) {
313 sds names = NULL;
314 int unmatched = values;
315 for( ; ce->name != NULL; ce++) {
316 if (values == ce->val) { /* Short path for perfect match */
317 sdsfree(names);
318 return sdsnew(ce->name);
319 }
320
321 /* Note: for bitflags, we want them sorted from high to low, so that if there are several / partially
322 * overlapping entries, we'll prefer the ones matching more bits. */
323 if (bitflags && ce->val && ce->val == (unmatched & ce->val)) {
324 names = names ? sdscatfmt(names, " %s", ce->name) : sdsnew(ce->name);
325 unmatched &= ~ce->val;
326 }
327 }
328 if (!names || unmatched) {
329 sdsfree(names);
330 return sdsnew("unknown");
331 }
332 return names;
333}
334
335/* Used for INFO generation. */
336const char *evictPolicyToString(void) {
337 for (configEnum *ce = maxmemory_policy_enum; ce->name != NULL; ce++) {
338 if (server.maxmemory_policy == ce->val)
339 return ce->name;
340 }
341 serverPanic("unknown eviction policy");
342}
343
344/*-----------------------------------------------------------------------------
345 * Config file parsing
346 *----------------------------------------------------------------------------*/
347
348int yesnotoi(char *s) {
349 if (!strcasecmp(s,"yes")) return 1;
350 else if (!strcasecmp(s,"no")) return 0;
351 else return -1;
352}
353
354void appendServerSaveParams(time_t seconds, int changes) {
355 server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));
356 server.saveparams[server.saveparamslen].seconds = seconds;
357 server.saveparams[server.saveparamslen].changes = changes;
358 server.saveparamslen++;
359}
360
361void resetServerSaveParams(void) {
362 zfree(server.saveparams);
363 server.saveparams = NULL;
364 server.saveparamslen = 0;
365}
366
367void queueLoadModule(sds path, sds *argv, int argc) {
368 int i;
369 struct moduleLoadQueueEntry *loadmod;
370
371 loadmod = zmalloc(sizeof(struct moduleLoadQueueEntry));
372 loadmod->argv = argc ? zmalloc(sizeof(robj*)*argc) : NULL;
373 loadmod->path = sdsnew(path);
374 loadmod->argc = argc;
375 for (i = 0; i < argc; i++) {
376 loadmod->argv[i] = createRawStringObject(argv[i],sdslen(argv[i]));
377 }
378 listAddNodeTail(server.loadmodule_queue,loadmod);
379}
380
381/* Parse an array of `arg_len` sds strings, validate and populate
382 * server.client_obuf_limits if valid.
383 * Used in CONFIG SET and configuration file parsing. */
384static int updateClientOutputBufferLimit(sds *args, int arg_len, const char **err) {
385 int j;
386 int class;
387 unsigned long long hard, soft;
388 int hard_err, soft_err;
389 int soft_seconds;
390 char *soft_seconds_eptr;
391 clientBufferLimitsConfig values[CLIENT_TYPE_OBUF_COUNT];
392 int classes[CLIENT_TYPE_OBUF_COUNT] = {0};
393
394 /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */
395 if (arg_len % 4) {
396 if (err) *err = "Wrong number of arguments in "
397 "buffer limit configuration.";
398 return 0;
399 }
400
401 /* Sanity check of single arguments, so that we either refuse the
402 * whole configuration string or accept it all, even if a single
403 * error in a single client class is present. */
404 for (j = 0; j < arg_len; j += 4) {
405 class = getClientTypeByName(args[j]);
406 if (class == -1 || class == CLIENT_TYPE_MASTER) {
407 if (err) *err = "Invalid client class specified in "
408 "buffer limit configuration.";
409 return 0;
410 }
411
412 hard = memtoull(args[j+1], &hard_err);
413 soft = memtoull(args[j+2], &soft_err);
414 soft_seconds = strtoll(args[j+3], &soft_seconds_eptr, 10);
415 if (hard_err || soft_err ||
416 soft_seconds < 0 || *soft_seconds_eptr != '\0')
417 {
418 if (err) *err = "Error in hard, soft or soft_seconds setting in "
419 "buffer limit configuration.";
420 return 0;
421 }
422
423 values[class].hard_limit_bytes = hard;
424 values[class].soft_limit_bytes = soft;
425 values[class].soft_limit_seconds = soft_seconds;
426 classes[class] = 1;
427 }
428
429 /* Finally set the new config. */
430 for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
431 if (classes[j]) server.client_obuf_limits[j] = values[j];
432 }
433
434 return 1;
435}
436
437/* Note this is here to support detecting we're running a config set from
438 * within conf file parsing. This is only needed to support the deprecated
439 * abnormal aggregate `save T C` functionality. Remove in the future. */
440static int reading_config_file;
441
442void loadServerConfigFromString(char *config) {
443 deprecatedConfig deprecated_configs[] = {
444 {"list-max-ziplist-entries", 2, 2},
445 {"list-max-ziplist-value", 2, 2},
446 {"lua-replicate-commands", 2, 2},
447 {NULL, 0},
448 };
449 char buf[1024];
450 const char *err = NULL;
451 int linenum = 0, totlines, i;
452 sds *lines;
453
454 reading_config_file = 1;
455 lines = sdssplitlen(config,strlen(config),"\n",1,&totlines);
456
457 for (i = 0; i < totlines; i++) {
458 sds *argv;
459 int argc;
460
461 linenum = i+1;
462 lines[i] = sdstrim(lines[i]," \t\r\n");
463
464 /* Skip comments and blank lines */
465 if (lines[i][0] == '#' || lines[i][0] == '\0') continue;
466
467 /* Split into arguments */
468 argv = sdssplitargs(lines[i],&argc);
469 if (argv == NULL) {
470 err = "Unbalanced quotes in configuration line";
471 goto loaderr;
472 }
473
474 /* Skip this line if the resulting command vector is empty. */
475 if (argc == 0) {
476 sdsfreesplitres(argv,argc);
477 continue;
478 }
479 sdstolower(argv[0]);
480
481 /* Iterate the configs that are standard */
482 standardConfig *config = lookupConfig(argv[0]);
483 if (config) {
484 /* For normal single arg configs enforce we have a single argument.
485 * Note that MULTI_ARG_CONFIGs need to validate arg count on their own */
486 if (!(config->flags & MULTI_ARG_CONFIG) && argc != 2) {
487 err = "wrong number of arguments";
488 goto loaderr;
489 }
490
491 if ((config->flags & MULTI_ARG_CONFIG) && argc == 2 && sdslen(argv[1])) {
492 /* For MULTI_ARG_CONFIGs, if we only have one argument, try to split it by spaces.
493 * Only if the argument is not empty, otherwise something like --save "" will fail.
494 * So that we can support something like --config "arg1 arg2 arg3". */
495 sds *new_argv;
496 int new_argc;
497 new_argv = sdssplitargs(argv[1], &new_argc);
498 if (!config->interface.set(config, new_argv, new_argc, &err)) {
499 goto loaderr;
500 }
501 sdsfreesplitres(new_argv, new_argc);
502 } else {
503 /* Set config using all arguments that follows */
504 if (!config->interface.set(config, &argv[1], argc-1, &err)) {
505 goto loaderr;
506 }
507 }
508
509 sdsfreesplitres(argv,argc);
510 continue;
511 } else {
512 int match = 0;
513 for (deprecatedConfig *config = deprecated_configs; config->name != NULL; config++) {
514 if (!strcasecmp(argv[0], config->name) &&
515 config->argc_min <= argc &&
516 argc <= config->argc_max)
517 {
518 match = 1;
519 break;
520 }
521 }
522 if (match) {
523 sdsfreesplitres(argv,argc);
524 continue;
525 }
526 }
527
528 /* Execute config directives */
529 if (!strcasecmp(argv[0],"include") && argc == 2) {
530 loadServerConfig(argv[1], 0, NULL);
531 } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) {
532 struct redisCommand *cmd = lookupCommandBySds(argv[1]);
533 int retval;
534
535 if (!cmd) {
536 err = "No such command in rename-command";
537 goto loaderr;
538 }
539
540 /* If the target command name is the empty string we just
541 * remove it from the command table. */
542 retval = dictDelete(server.commands, argv[1]);
543 serverAssert(retval == DICT_OK);
544
545 /* Otherwise we re-add the command under a different name. */
546 if (sdslen(argv[2]) != 0) {
547 sds copy = sdsdup(argv[2]);
548
549 retval = dictAdd(server.commands, copy, cmd);
550 if (retval != DICT_OK) {
551 sdsfree(copy);
552 err = "Target command name already exists"; goto loaderr;
553 }
554 }
555 } else if (!strcasecmp(argv[0],"user") && argc >= 2) {
556 int argc_err;
557 if (ACLAppendUserForLoading(argv,argc,&argc_err) == C_ERR) {
558 const char *errmsg = ACLSetUserStringError();
559 snprintf(buf,sizeof(buf),"Error in user declaration '%s': %s",
560 argv[argc_err],errmsg);
561 err = buf;
562 goto loaderr;
563 }
564 } else if (!strcasecmp(argv[0],"loadmodule") && argc >= 2) {
565 queueLoadModule(argv[1],&argv[2],argc-2);
566 } else if (strchr(argv[0], '.')) {
567 if (argc < 2) {
568 err = "Module config specified without value";
569 goto loaderr;
570 }
571 sds name = sdsdup(argv[0]);
572 sds val = sdsdup(argv[1]);
573 for (int i = 2; i < argc; i++)
574 val = sdscatfmt(val, " %S", argv[i]);
575 if (!dictReplace(server.module_configs_queue, name, val)) sdsfree(name);
576 } else if (!strcasecmp(argv[0],"sentinel")) {
577 /* argc == 1 is handled by main() as we need to enter the sentinel
578 * mode ASAP. */
579 if (argc != 1) {
580 if (!server.sentinel_mode) {
581 err = "sentinel directive while not in sentinel mode";
582 goto loaderr;
583 }
584 queueSentinelConfig(argv+1,argc-1,linenum,lines[i]);
585 }
586 } else {
587 err = "Bad directive or wrong number of arguments"; goto loaderr;
588 }
589 sdsfreesplitres(argv,argc);
590 }
591
592 if (server.logfile[0] != '\0') {
593 FILE *logfp;
594
595 /* Test if we are able to open the file. The server will not
596 * be able to abort just for this problem later... */
597 logfp = fopen(server.logfile,"a");
598 if (logfp == NULL) {
599 err = sdscatprintf(sdsempty(),
600 "Can't open the log file: %s", strerror(errno));
601 goto loaderr;
602 }
603 fclose(logfp);
604 }
605
606 /* Sanity checks. */
607 if (server.cluster_enabled && server.masterhost) {
608 err = "replicaof directive not allowed in cluster mode";
609 goto loaderr;
610 }
611
612 /* To ensure backward compatibility and work while hz is out of range */
613 if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;
614 if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;
615
616 sdsfreesplitres(lines,totlines);
617 reading_config_file = 0;
618 return;
619
620loaderr:
621 fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR (Redis %s) ***\n",
622 REDIS_VERSION);
623 if (i < totlines) {
624 fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
625 fprintf(stderr, ">>> '%s'\n", lines[i]);
626 }
627 fprintf(stderr, "%s\n", err);
628 exit(1);
629}
630
631/* Load the server configuration from the specified filename.
632 * The function appends the additional configuration directives stored
633 * in the 'options' string to the config file before loading.
634 *
635 * Both filename and options can be NULL, in such a case are considered
636 * empty. This way loadServerConfig can be used to just load a file or
637 * just load a string. */
638#define CONFIG_READ_LEN 1024
639void loadServerConfig(char *filename, char config_from_stdin, char *options) {
640 sds config = sdsempty();
641 char buf[CONFIG_READ_LEN+1];
642 FILE *fp;
643 glob_t globbuf;
644
645 /* Load the file content */
646 if (filename) {
647
648 /* The logic for handling wildcards has slightly different behavior in cases where
649 * there is a failure to locate the included file.
650 * Whether or not a wildcard is specified, we should ALWAYS log errors when attempting
651 * to open included config files.
652 *
653 * However, we desire a behavioral difference between instances where a wildcard was
654 * specified and those where it hasn't:
655 * no wildcards : attempt to open the specified file and fail with a logged error
656 * if the file cannot be found and opened.
657 * with wildcards : attempt to glob the specified pattern; if no files match the
658 * pattern, then gracefully continue on to the next entry in the
659 * config file, as if the current entry was never encountered.
660 * This will allow for empty conf.d directories to be included. */
661
662 if (strchr(filename, '*') || strchr(filename, '?') || strchr(filename, '[')) {
663 /* A wildcard character detected in filename, so let us use glob */
664 if (glob(filename, 0, NULL, &globbuf) == 0) {
665
666 for (size_t i = 0; i < globbuf.gl_pathc; i++) {
667 if ((fp = fopen(globbuf.gl_pathv[i], "r")) == NULL) {
668 serverLog(LL_WARNING,
669 "Fatal error, can't open config file '%s': %s",
670 globbuf.gl_pathv[i], strerror(errno));
671 exit(1);
672 }
673 while(fgets(buf,CONFIG_READ_LEN+1,fp) != NULL)
674 config = sdscat(config,buf);
675 fclose(fp);
676 }
677
678 globfree(&globbuf);
679 }
680 } else {
681 /* No wildcard in filename means we can use the original logic to read and
682 * potentially fail traditionally */
683 if ((fp = fopen(filename, "r")) == NULL) {
684 serverLog(LL_WARNING,
685 "Fatal error, can't open config file '%s': %s",
686 filename, strerror(errno));
687 exit(1);
688 }
689 while(fgets(buf,CONFIG_READ_LEN+1,fp) != NULL)
690 config = sdscat(config,buf);
691 fclose(fp);
692 }
693 }
694
695 /* Append content from stdin */
696 if (config_from_stdin) {
697 serverLog(LL_WARNING,"Reading config from stdin");
698 fp = stdin;
699 while(fgets(buf,CONFIG_READ_LEN+1,fp) != NULL)
700 config = sdscat(config,buf);
701 }
702
703 /* Append the additional options */
704 if (options) {
705 config = sdscat(config,"\n");
706 config = sdscat(config,options);
707 }
708 loadServerConfigFromString(config);
709 sdsfree(config);
710}
711
712static int performInterfaceSet(standardConfig *config, sds value, const char **errstr) {
713 sds *argv;
714 int argc, res;
715
716 if (config->flags & MULTI_ARG_CONFIG) {
717 argv = sdssplitlen(value, sdslen(value), " ", 1, &argc);
718 } else {
719 argv = (char**)&value;
720 argc = 1;
721 }
722
723 /* Set the config */
724 res = config->interface.set(config, argv, argc, errstr);
725 if (config->flags & MULTI_ARG_CONFIG) sdsfreesplitres(argv, argc);
726 return res;
727}
728
729/* Find the config by name and attempt to set it to value. */
730int performModuleConfigSetFromName(sds name, sds value, const char **err) {
731 standardConfig *config = lookupConfig(name);
732 if (!config || !(config->flags & MODULE_CONFIG)) {
733 *err = "Config name not found";
734 return 0;
735 }
736 return performInterfaceSet(config, value, err);
737}
738
739/* Find config by name and attempt to set it to its default value. */
740int performModuleConfigSetDefaultFromName(sds name, const char **err) {
741 standardConfig *config = lookupConfig(name);
742 serverAssert(config);
743 if (!(config->flags & MODULE_CONFIG)) {
744 *err = "Config name not found";
745 return 0;
746 }
747 switch (config->type) {
748 case BOOL_CONFIG:
749 return setModuleBoolConfig(config->privdata, config->data.yesno.default_value, err);
750 case SDS_CONFIG:
751 return setModuleStringConfig(config->privdata, config->data.sds.default_value, err);
752 case NUMERIC_CONFIG:
753 return setModuleNumericConfig(config->privdata, config->data.numeric.default_value, err);
754 case ENUM_CONFIG:
755 return setModuleEnumConfig(config->privdata, config->data.enumd.default_value, err);
756 default:
757 serverPanic("Config type of module config is not allowed.");
758 }
759 return 0;
760}
761
762static void restoreBackupConfig(standardConfig **set_configs, sds *old_values, int count, apply_fn *apply_fns, list *module_configs) {
763 int i;
764 const char *errstr = "unknown error";
765 /* Set all backup values */
766 for (i = 0; i < count; i++) {
767 if (!performInterfaceSet(set_configs[i], old_values[i], &errstr))
768 serverLog(LL_WARNING, "Failed restoring failed CONFIG SET command. Error setting %s to '%s': %s",
769 set_configs[i]->name, old_values[i], errstr);
770 }
771 /* Apply backup */
772 if (apply_fns) {
773 for (i = 0; i < count && apply_fns[i] != NULL; i++) {
774 if (!apply_fns[i](&errstr))
775 serverLog(LL_WARNING, "Failed applying restored failed CONFIG SET command: %s", errstr);
776 }
777 }
778 if (module_configs) {
779 if (!moduleConfigApplyConfig(module_configs, &errstr, NULL))
780 serverLog(LL_WARNING, "Failed applying restored failed CONFIG SET command: %s", errstr);
781 }
782}
783
784/*-----------------------------------------------------------------------------
785 * CONFIG SET implementation
786 *----------------------------------------------------------------------------*/
787
788void configSetCommand(client *c) {
789 const char *errstr = NULL;
790 const char *invalid_arg_name = NULL;
791 const char *err_arg_name = NULL;
792 standardConfig **set_configs; /* TODO: make this a dict for better performance */
793 list *module_configs_apply;
794 const char **config_names;
795 sds *new_values;
796 sds *old_values = NULL;
797 apply_fn *apply_fns; /* TODO: make this a set for better performance */
798 int config_count, i, j;
799 int invalid_args = 0, deny_loading_error = 0;
800 int *config_map_fns;
801
802 /* Make sure we have an even number of arguments: conf-val pairs */
803 if (c->argc & 1) {
804 addReplyErrorObject(c, shared.syntaxerr);
805 return;
806 }
807 config_count = (c->argc - 2) / 2;
808
809 module_configs_apply = listCreate();
810 set_configs = zcalloc(sizeof(standardConfig*)*config_count);
811 config_names = zcalloc(sizeof(char*)*config_count);
812 new_values = zmalloc(sizeof(sds*)*config_count);
813 old_values = zcalloc(sizeof(sds*)*config_count);
814 apply_fns = zcalloc(sizeof(apply_fn)*config_count);
815 config_map_fns = zmalloc(sizeof(int)*config_count);
816
817 /* Find all relevant configs */
818 for (i = 0; i < config_count; i++) {
819 standardConfig *config = lookupConfig(c->argv[2+i*2]->ptr);
820 /* Fail if we couldn't find this config */
821 if (!config) {
822 if (!invalid_args) {
823 invalid_arg_name = c->argv[2+i*2]->ptr;
824 invalid_args = 1;
825 }
826 continue;
827 }
828
829 /* Note: it's important we run over ALL passed configs and check if we need to call `redactClientCommandArgument()`.
830 * This is in order to avoid anyone using this command for a log/slowlog/monitor/etc. displaying sensitive info.
831 * So even if we encounter an error we still continue running over the remaining arguments. */
832 if (config->flags & SENSITIVE_CONFIG) {
833 redactClientCommandArgument(c,2+i*2+1);
834 }
835
836 /* We continue to make sure we redact all the configs */
837 if (invalid_args) continue;
838
839 if (config->flags & IMMUTABLE_CONFIG ||
840 (config->flags & PROTECTED_CONFIG && !allowProtectedAction(server.enable_protected_configs, c)))
841 {
842 /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */
843 errstr = (config->flags & IMMUTABLE_CONFIG) ? "can't set immutable config" : "can't set protected config";
844 err_arg_name = c->argv[2+i*2]->ptr;
845 invalid_args = 1;
846 continue;
847 }
848
849 if (server.loading && config->flags & DENY_LOADING_CONFIG) {
850 /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */
851 deny_loading_error = 1;
852 invalid_args = 1;
853 continue;
854 }
855
856 /* If this config appears twice then fail */
857 for (j = 0; j < i; j++) {
858 if (set_configs[j] == config) {
859 /* Note: we don't abort the loop since we still want to handle redacting sensitive configs (above) */
860 errstr = "duplicate parameter";
861 err_arg_name = c->argv[2+i*2]->ptr;
862 invalid_args = 1;
863 break;
864 }
865 }
866 set_configs[i] = config;
867 config_names[i] = config->name;
868 new_values[i] = c->argv[2+i*2+1]->ptr;
869 }
870
871 if (invalid_args) goto err;
872
873 /* Backup old values before setting new ones */
874 for (i = 0; i < config_count; i++)
875 old_values[i] = set_configs[i]->interface.get(set_configs[i]);
876
877 /* Set all new values (don't apply yet) */
878 for (i = 0; i < config_count; i++) {
879 int res = performInterfaceSet(set_configs[i], new_values[i], &errstr);
880 if (!res) {
881 restoreBackupConfig(set_configs, old_values, i+1, NULL, NULL);
882 err_arg_name = set_configs[i]->name;
883 goto err;
884 } else if (res == 1) {
885 /* A new value was set, if this config has an apply function then store it for execution later */
886 if (set_configs[i]->flags & MODULE_CONFIG) {
887 addModuleConfigApply(module_configs_apply, set_configs[i]->privdata);
888 } else if (set_configs[i]->interface.apply) {
889 /* Check if this apply function is already stored */
890 int exists = 0;
891 for (j = 0; apply_fns[j] != NULL && j <= i; j++) {
892 if (apply_fns[j] == set_configs[i]->interface.apply) {
893 exists = 1;
894 break;
895 }
896 }
897 /* Apply function not stored, store it */
898 if (!exists) {
899 apply_fns[j] = set_configs[i]->interface.apply;
900 config_map_fns[j] = i;
901 }
902 }
903 }
904 }
905
906 /* Apply all configs after being set */
907 for (i = 0; i < config_count && apply_fns[i] != NULL; i++) {
908 if (!apply_fns[i](&errstr)) {
909 serverLog(LL_WARNING, "Failed applying new configuration. Possibly related to new %s setting. Restoring previous settings.", set_configs[config_map_fns[i]]->name);
910 restoreBackupConfig(set_configs, old_values, config_count, apply_fns, NULL);
911 err_arg_name = set_configs[config_map_fns[i]]->name;
912 goto err;
913 }
914 }
915 /* Apply all module configs that were set. */
916 if (!moduleConfigApplyConfig(module_configs_apply, &errstr, &err_arg_name)) {
917 serverLogRaw(LL_WARNING, "Failed applying new module configuration. Restoring previous settings.");
918 restoreBackupConfig(set_configs, old_values, config_count, apply_fns, module_configs_apply);
919 goto err;
920 }
921
922 RedisModuleConfigChangeV1 cc = {.num_changes = config_count, .config_names = config_names};
923 moduleFireServerEvent(REDISMODULE_EVENT_CONFIG, REDISMODULE_SUBEVENT_CONFIG_CHANGE, &cc);
924 addReply(c,shared.ok);
925 goto end;
926
927err:
928 if (deny_loading_error) {
929 /* We give the loading error precedence because it may be handled by clients differently, unlike a plain -ERR. */
930 addReplyErrorObject(c,shared.loadingerr);
931 } else if (invalid_arg_name) {
932 addReplyErrorFormat(c,"Unknown option or number of arguments for CONFIG SET - '%s'", invalid_arg_name);
933 } else if (errstr) {
934 addReplyErrorFormat(c,"CONFIG SET failed (possibly related to argument '%s') - %s", err_arg_name, errstr);
935 } else {
936 addReplyErrorFormat(c,"CONFIG SET failed (possibly related to argument '%s')", err_arg_name);
937 }
938end:
939 zfree(set_configs);
940 zfree(config_names);
941 zfree(new_values);
942 for (i = 0; i < config_count; i++)
943 sdsfree(old_values[i]);
944 zfree(old_values);
945 zfree(apply_fns);
946 zfree(config_map_fns);
947 listRelease(module_configs_apply);
948}
949
950/*-----------------------------------------------------------------------------
951 * CONFIG GET implementation
952 *----------------------------------------------------------------------------*/
953
954void configGetCommand(client *c) {
955 int i;
956 dictEntry *de;
957 dictIterator *di;
958 /* Create a dictionary to store the matched configs */
959 dict *matches = dictCreate(&externalStringType);
960 for (i = 0; i < c->argc - 2; i++) {
961 robj *o = c->argv[2+i];
962 sds name = o->ptr;
963
964 /* If the string doesn't contain glob patterns, just directly
965 * look up the key in the dictionary. */
966 if (!strpbrk(name, "[*?")) {
967 if (dictFind(matches, name)) continue;
968 standardConfig *config = lookupConfig(name);
969
970 if (config) {
971 dictAdd(matches, name, config);
972 }
973 continue;
974 }
975
976 /* Otherwise, do a match against all items in the dictionary. */
977 di = dictGetIterator(configs);
978
979 while ((de = dictNext(di)) != NULL) {
980 standardConfig *config = dictGetVal(de);
981 /* Note that hidden configs require an exact match (not a pattern) */
982 if (config->flags & HIDDEN_CONFIG) continue;
983 if (dictFind(matches, config->name)) continue;
984 if (stringmatch(name, de->key, 1)) {
985 dictAdd(matches, de->key, config);
986 }
987 }
988 dictReleaseIterator(di);
989 }
990
991 di = dictGetIterator(matches);
992 addReplyMapLen(c, dictSize(matches));
993 while ((de = dictNext(di)) != NULL) {
994 standardConfig *config = (standardConfig *) dictGetVal(de);
995 addReplyBulkCString(c, de->key);
996 addReplyBulkSds(c, config->interface.get(config));
997 }
998 dictReleaseIterator(di);
999 dictRelease(matches);
1000}
1001
1002/*-----------------------------------------------------------------------------
1003 * CONFIG REWRITE implementation
1004 *----------------------------------------------------------------------------*/
1005
1006#define REDIS_CONFIG_REWRITE_SIGNATURE "# Generated by CONFIG REWRITE"
1007
1008/* We use the following dictionary type to store where a configuration
1009 * option is mentioned in the old configuration file, so it's
1010 * like "maxmemory" -> list of line numbers (first line is zero). */
1011void dictListDestructor(dict *d, void *val);
1012
1013/* Sentinel config rewriting is implemented inside sentinel.c by
1014 * rewriteConfigSentinelOption(). */
1015void rewriteConfigSentinelOption(struct rewriteConfigState *state);
1016
1017dictType optionToLineDictType = {
1018 dictSdsCaseHash, /* hash function */
1019 NULL, /* key dup */
1020 NULL, /* val dup */
1021 dictSdsKeyCaseCompare, /* key compare */
1022 dictSdsDestructor, /* key destructor */
1023 dictListDestructor, /* val destructor */
1024 NULL /* allow to expand */
1025};
1026
1027dictType optionSetDictType = {
1028 dictSdsCaseHash, /* hash function */
1029 NULL, /* key dup */
1030 NULL, /* val dup */
1031 dictSdsKeyCaseCompare, /* key compare */
1032 dictSdsDestructor, /* key destructor */
1033 NULL, /* val destructor */
1034 NULL /* allow to expand */
1035};
1036
1037/* The config rewrite state. */
1038struct rewriteConfigState {
1039 dict *option_to_line; /* Option -> list of config file lines map */
1040 dict *rewritten; /* Dictionary of already processed options */
1041 int numlines; /* Number of lines in current config */
1042 sds *lines; /* Current lines as an array of sds strings */
1043 int needs_signature; /* True if we need to append the rewrite
1044 signature. */
1045 int force_write; /* True if we want all keywords to be force
1046 written. Currently only used for testing
1047 and debug information. */
1048};
1049
1050/* Free the configuration rewrite state. */
1051void rewriteConfigReleaseState(struct rewriteConfigState *state) {
1052 sdsfreesplitres(state->lines,state->numlines);
1053 dictRelease(state->option_to_line);
1054 dictRelease(state->rewritten);
1055 zfree(state);
1056}
1057
1058/* Create the configuration rewrite state */
1059struct rewriteConfigState *rewriteConfigCreateState() {
1060 struct rewriteConfigState *state = zmalloc(sizeof(*state));
1061 state->option_to_line = dictCreate(&optionToLineDictType);
1062 state->rewritten = dictCreate(&optionSetDictType);
1063 state->numlines = 0;
1064 state->lines = NULL;
1065 state->needs_signature = 1;
1066 state->force_write = 0;
1067 return state;
1068}
1069
1070/* Append the new line to the current configuration state. */
1071void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) {
1072 state->lines = zrealloc(state->lines, sizeof(char*) * (state->numlines+1));
1073 state->lines[state->numlines++] = line;
1074}
1075
1076/* Populate the option -> list of line numbers map. */
1077void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) {
1078 list *l = dictFetchValue(state->option_to_line,option);
1079
1080 if (l == NULL) {
1081 l = listCreate();
1082 dictAdd(state->option_to_line,sdsdup(option),l);
1083 }
1084 listAddNodeTail(l,(void*)(long)linenum);
1085}
1086
1087/* Add the specified option to the set of processed options.
1088 * This is useful as only unused lines of processed options will be blanked
1089 * in the config file, while options the rewrite process does not understand
1090 * remain untouched. */
1091void rewriteConfigMarkAsProcessed(struct rewriteConfigState *state, const char *option) {
1092 sds opt = sdsnew(option);
1093
1094 if (dictAdd(state->rewritten,opt,NULL) != DICT_OK) sdsfree(opt);
1095}
1096
1097/* Read the old file, split it into lines to populate a newly created
1098 * config rewrite state, and return it to the caller.
1099 *
1100 * If it is impossible to read the old file, NULL is returned.
1101 * If the old file does not exist at all, an empty state is returned. */
1102struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
1103 FILE *fp = fopen(path,"r");
1104 if (fp == NULL && errno != ENOENT) return NULL;
1105
1106 struct redis_stat sb;
1107 if (fp && redis_fstat(fileno(fp),&sb) == -1) return NULL;
1108
1109 int linenum = -1;
1110 struct rewriteConfigState *state = rewriteConfigCreateState();
1111
1112 if (fp == NULL || sb.st_size == 0) return state;
1113
1114 /* Load the file content */
1115 sds config = sdsnewlen(SDS_NOINIT,sb.st_size);
1116 if (fread(config,1,sb.st_size,fp) == 0) {
1117 sdsfree(config);
1118 rewriteConfigReleaseState(state);
1119 fclose(fp);
1120 return NULL;
1121 }
1122
1123 int i, totlines;
1124 sds *lines = sdssplitlen(config,sdslen(config),"\n",1,&totlines);
1125
1126 /* Read the old content line by line, populate the state. */
1127 for (i = 0; i < totlines; i++) {
1128 int argc;
1129 sds *argv;
1130 sds line = sdstrim(lines[i],"\r\n\t ");
1131 lines[i] = NULL;
1132
1133 linenum++; /* Zero based, so we init at -1 */
1134
1135 /* Handle comments and empty lines. */
1136 if (line[0] == '#' || line[0] == '\0') {
1137 if (state->needs_signature && !strcmp(line,REDIS_CONFIG_REWRITE_SIGNATURE))
1138 state->needs_signature = 0;
1139 rewriteConfigAppendLine(state,line);
1140 continue;
1141 }
1142
1143 /* Not a comment, split into arguments. */
1144 argv = sdssplitargs(line,&argc);
1145
1146 if (argv == NULL ||
1147 (!lookupConfig(argv[0]) &&
1148 /* The following is a list of config features that are only supported in
1149 * config file parsing and are not recognized by lookupConfig */
1150 strcasecmp(argv[0],"include") &&
1151 strcasecmp(argv[0],"rename-command") &&
1152 strcasecmp(argv[0],"user") &&
1153 strcasecmp(argv[0],"loadmodule") &&
1154 strcasecmp(argv[0],"sentinel")))
1155 {
1156 /* The line is either unparsable for some reason, for
1157 * instance it may have unbalanced quotes, may contain a
1158 * config that doesn't exist anymore, for instance a module that got
1159 * unloaded. Load it as a comment. */
1160 sds aux = sdsnew("# ??? ");
1161 aux = sdscatsds(aux,line);
1162 if (argv) sdsfreesplitres(argv, argc);
1163 sdsfree(line);
1164 rewriteConfigAppendLine(state,aux);
1165 continue;
1166 }
1167
1168 sdstolower(argv[0]); /* We only want lowercase config directives. */
1169
1170 /* Now we populate the state according to the content of this line.
1171 * Append the line and populate the option -> line numbers map. */
1172 rewriteConfigAppendLine(state,line);
1173
1174 /* If this is a alias config, replace it with the original name. */
1175 standardConfig *s_conf = lookupConfig(argv[0]);
1176 if (s_conf && s_conf->flags & ALIAS_CONFIG) {
1177 sdsfree(argv[0]);
1178 argv[0] = sdsnew(s_conf->alias);
1179 }
1180
1181 /* If this is sentinel config, we use sentinel "sentinel <config>" as option
1182 to avoid messing up the sequence. */
1183 if (server.sentinel_mode && argc > 1 && !strcasecmp(argv[0],"sentinel")) {
1184 sds sentinelOption = sdsempty();
1185 sentinelOption = sdscatfmt(sentinelOption,"%S %S",argv[0],argv[1]);
1186 rewriteConfigAddLineNumberToOption(state,sentinelOption,linenum);
1187 sdsfree(sentinelOption);
1188 } else {
1189 rewriteConfigAddLineNumberToOption(state,argv[0],linenum);
1190 }
1191 sdsfreesplitres(argv,argc);
1192 }
1193 fclose(fp);
1194 sdsfreesplitres(lines,totlines);
1195 sdsfree(config);
1196 return state;
1197}
1198
1199/* Rewrite the specified configuration option with the new "line".
1200 * It progressively uses lines of the file that were already used for the same
1201 * configuration option in the old version of the file, removing that line from
1202 * the map of options -> line numbers.
1203 *
1204 * If there are lines associated with a given configuration option and
1205 * "force" is non-zero, the line is appended to the configuration file.
1206 * Usually "force" is true when an option has not its default value, so it
1207 * must be rewritten even if not present previously.
1208 *
1209 * The first time a line is appended into a configuration file, a comment
1210 * is added to show that starting from that point the config file was generated
1211 * by CONFIG REWRITE.
1212 *
1213 * "line" is either used, or freed, so the caller does not need to free it
1214 * in any way. */
1215void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force) {
1216 sds o = sdsnew(option);
1217 list *l = dictFetchValue(state->option_to_line,o);
1218
1219 rewriteConfigMarkAsProcessed(state,option);
1220
1221 if (!l && !force && !state->force_write) {
1222 /* Option not used previously, and we are not forced to use it. */
1223 sdsfree(line);
1224 sdsfree(o);
1225 return;
1226 }
1227
1228 if (l) {
1229 listNode *ln = listFirst(l);
1230 int linenum = (long) ln->value;
1231
1232 /* There are still lines in the old configuration file we can reuse
1233 * for this option. Replace the line with the new one. */
1234 listDelNode(l,ln);
1235 if (listLength(l) == 0) dictDelete(state->option_to_line,o);
1236 sdsfree(state->lines[linenum]);
1237 state->lines[linenum] = line;
1238 } else {
1239 /* Append a new line. */
1240 if (state->needs_signature) {
1241 rewriteConfigAppendLine(state,
1242 sdsnew(REDIS_CONFIG_REWRITE_SIGNATURE));
1243 state->needs_signature = 0;
1244 }
1245 rewriteConfigAppendLine(state,line);
1246 }
1247 sdsfree(o);
1248}
1249
1250/* Write the long long 'bytes' value as a string in a way that is parsable
1251 * inside redis.conf. If possible uses the GB, MB, KB notation. */
1252int rewriteConfigFormatMemory(char *buf, size_t len, long long bytes) {
1253 int gb = 1024*1024*1024;
1254 int mb = 1024*1024;
1255 int kb = 1024;
1256
1257 if (bytes && (bytes % gb) == 0) {
1258 return snprintf(buf,len,"%lldgb",bytes/gb);
1259 } else if (bytes && (bytes % mb) == 0) {
1260 return snprintf(buf,len,"%lldmb",bytes/mb);
1261 } else if (bytes && (bytes % kb) == 0) {
1262 return snprintf(buf,len,"%lldkb",bytes/kb);
1263 } else {
1264 return snprintf(buf,len,"%lld",bytes);
1265 }
1266}
1267
1268/* Rewrite a simple "option-name <bytes>" configuration option. */
1269void rewriteConfigBytesOption(struct rewriteConfigState *state, const char *option, long long value, long long defvalue) {
1270 char buf[64];
1271 int force = value != defvalue;
1272 sds line;
1273
1274 rewriteConfigFormatMemory(buf,sizeof(buf),value);
1275 line = sdscatprintf(sdsempty(),"%s %s",option,buf);
1276 rewriteConfigRewriteLine(state,option,line,force);
1277}
1278
1279/* Rewrite a simple "option-name n%" configuration option. */
1280void rewriteConfigPercentOption(struct rewriteConfigState *state, const char *option, long long value, long long defvalue) {
1281 int force = value != defvalue;
1282 sds line = sdscatprintf(sdsempty(),"%s %lld%%",option,value);
1283
1284 rewriteConfigRewriteLine(state,option,line,force);
1285}
1286
1287/* Rewrite a yes/no option. */
1288void rewriteConfigYesNoOption(struct rewriteConfigState *state, const char *option, int value, int defvalue) {
1289 int force = value != defvalue;
1290 sds line = sdscatprintf(sdsempty(),"%s %s",option,
1291 value ? "yes" : "no");
1292
1293 rewriteConfigRewriteLine(state,option,line,force);
1294}
1295
1296/* Rewrite a string option. */
1297void rewriteConfigStringOption(struct rewriteConfigState *state, const char *option, char *value, const char *defvalue) {
1298 int force = 1;
1299 sds line;
1300
1301 /* String options set to NULL need to be not present at all in the
1302 * configuration file to be set to NULL again at the next reboot. */
1303 if (value == NULL) {
1304 rewriteConfigMarkAsProcessed(state,option);
1305 return;
1306 }
1307
1308 /* Set force to zero if the value is set to its default. */
1309 if (defvalue && strcmp(value,defvalue) == 0) force = 0;
1310
1311 line = sdsnew(option);
1312 line = sdscatlen(line, " ", 1);
1313 line = sdscatrepr(line, value, strlen(value));
1314
1315 rewriteConfigRewriteLine(state,option,line,force);
1316}
1317
1318/* Rewrite a SDS string option. */
1319void rewriteConfigSdsOption(struct rewriteConfigState *state, const char *option, sds value, const char *defvalue) {
1320 int force = 1;
1321 sds line;
1322
1323 /* If there is no value set, we don't want the SDS option
1324 * to be present in the configuration at all. */
1325 if (value == NULL) {
1326 rewriteConfigMarkAsProcessed(state, option);
1327 return;
1328 }
1329
1330 /* Set force to zero if the value is set to its default. */
1331 if (defvalue && strcmp(value, defvalue) == 0) force = 0;
1332
1333 line = sdsnew(option);
1334 line = sdscatlen(line, " ", 1);
1335 line = sdscatrepr(line, value, sdslen(value));
1336
1337 rewriteConfigRewriteLine(state, option, line, force);
1338}
1339
1340/* Rewrite a numerical (long long range) option. */
1341void rewriteConfigNumericalOption(struct rewriteConfigState *state, const char *option, long long value, long long defvalue) {
1342 int force = value != defvalue;
1343 sds line = sdscatprintf(sdsempty(),"%s %lld",option,value);
1344
1345 rewriteConfigRewriteLine(state,option,line,force);
1346}
1347
1348/* Rewrite an octal option. */
1349void rewriteConfigOctalOption(struct rewriteConfigState *state, const char *option, long long value, long long defvalue) {
1350 int force = value != defvalue;
1351 sds line = sdscatprintf(sdsempty(),"%s %llo",option,value);
1352
1353 rewriteConfigRewriteLine(state,option,line,force);
1354}
1355
1356/* Rewrite an enumeration option. It takes as usually state and option name,
1357 * and in addition the enumeration array and the default value for the
1358 * option. */
1359void rewriteConfigEnumOption(struct rewriteConfigState *state, const char *option, int value, standardConfig *config) {
1360 int multiarg = config->flags & MULTI_ARG_CONFIG;
1361 sds names = configEnumGetName(config->data.enumd.enum_value,value,multiarg);
1362 sds line = sdscatfmt(sdsempty(),"%s %s",option,names);
1363 sdsfree(names);
1364 int force = value != config->data.enumd.default_value;
1365
1366 rewriteConfigRewriteLine(state,option,line,force);
1367}
1368
1369/* Rewrite the save option. */
1370void rewriteConfigSaveOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1371 UNUSED(config);
1372 int j;
1373 sds line;
1374
1375 /* In Sentinel mode we don't need to rewrite the save parameters */
1376 if (server.sentinel_mode) {
1377 rewriteConfigMarkAsProcessed(state,name);
1378 return;
1379 }
1380
1381 /* Rewrite save parameters, or an empty 'save ""' line to avoid the
1382 * defaults from being used.
1383 */
1384 if (!server.saveparamslen) {
1385 rewriteConfigRewriteLine(state,name,sdsnew("save \"\""),1);
1386 } else {
1387 for (j = 0; j < server.saveparamslen; j++) {
1388 line = sdscatprintf(sdsempty(),"save %ld %d",
1389 (long) server.saveparams[j].seconds, server.saveparams[j].changes);
1390 rewriteConfigRewriteLine(state,name,line,1);
1391 }
1392 }
1393
1394 /* Mark "save" as processed in case server.saveparamslen is zero. */
1395 rewriteConfigMarkAsProcessed(state,name);
1396}
1397
1398/* Rewrite the user option. */
1399void rewriteConfigUserOption(struct rewriteConfigState *state) {
1400 /* If there is a user file defined we just mark this configuration
1401 * directive as processed, so that all the lines containing users
1402 * inside the config file gets discarded. */
1403 if (server.acl_filename[0] != '\0') {
1404 rewriteConfigMarkAsProcessed(state,"user");
1405 return;
1406 }
1407
1408 /* Otherwise scan the list of users and rewrite every line. Note that
1409 * in case the list here is empty, the effect will just be to comment
1410 * all the users directive inside the config file. */
1411 raxIterator ri;
1412 raxStart(&ri,Users);
1413 raxSeek(&ri,"^",NULL,0);
1414 while(raxNext(&ri)) {
1415 user *u = ri.data;
1416 sds line = sdsnew("user ");
1417 line = sdscatsds(line,u->name);
1418 line = sdscatlen(line," ",1);
1419 sds descr = ACLDescribeUser(u);
1420 line = sdscatsds(line,descr);
1421 sdsfree(descr);
1422 rewriteConfigRewriteLine(state,"user",line,1);
1423 }
1424 raxStop(&ri);
1425
1426 /* Mark "user" as processed in case there are no defined users. */
1427 rewriteConfigMarkAsProcessed(state,"user");
1428}
1429
1430/* Rewrite the dir option, always using absolute paths.*/
1431void rewriteConfigDirOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1432 UNUSED(config);
1433 char cwd[1024];
1434
1435 if (getcwd(cwd,sizeof(cwd)) == NULL) {
1436 rewriteConfigMarkAsProcessed(state,name);
1437 return; /* no rewrite on error. */
1438 }
1439 rewriteConfigStringOption(state,name,cwd,NULL);
1440}
1441
1442/* Rewrite the slaveof option. */
1443void rewriteConfigReplicaOfOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1444 UNUSED(config);
1445 sds line;
1446
1447 /* If this is a master, we want all the slaveof config options
1448 * in the file to be removed. Note that if this is a cluster instance
1449 * we don't want a slaveof directive inside redis.conf. */
1450 if (server.cluster_enabled || server.masterhost == NULL) {
1451 rewriteConfigMarkAsProcessed(state, name);
1452 return;
1453 }
1454 line = sdscatprintf(sdsempty(),"%s %s %d", name,
1455 server.masterhost, server.masterport);
1456 rewriteConfigRewriteLine(state,name,line,1);
1457}
1458
1459/* Rewrite the notify-keyspace-events option. */
1460void rewriteConfigNotifyKeyspaceEventsOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1461 UNUSED(config);
1462 int force = server.notify_keyspace_events != 0;
1463 sds line, flags;
1464
1465 flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);
1466 line = sdsnew(name);
1467 line = sdscatlen(line, " ", 1);
1468 line = sdscatrepr(line, flags, sdslen(flags));
1469 sdsfree(flags);
1470 rewriteConfigRewriteLine(state,name,line,force);
1471}
1472
1473/* Rewrite the client-output-buffer-limit option. */
1474void rewriteConfigClientOutputBufferLimitOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1475 UNUSED(config);
1476 int j;
1477 for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
1478 int force = (server.client_obuf_limits[j].hard_limit_bytes !=
1479 clientBufferLimitsDefaults[j].hard_limit_bytes) ||
1480 (server.client_obuf_limits[j].soft_limit_bytes !=
1481 clientBufferLimitsDefaults[j].soft_limit_bytes) ||
1482 (server.client_obuf_limits[j].soft_limit_seconds !=
1483 clientBufferLimitsDefaults[j].soft_limit_seconds);
1484 sds line;
1485 char hard[64], soft[64];
1486
1487 rewriteConfigFormatMemory(hard,sizeof(hard),
1488 server.client_obuf_limits[j].hard_limit_bytes);
1489 rewriteConfigFormatMemory(soft,sizeof(soft),
1490 server.client_obuf_limits[j].soft_limit_bytes);
1491
1492 char *typename = getClientTypeName(j);
1493 if (!strcmp(typename,"slave")) typename = "replica";
1494 line = sdscatprintf(sdsempty(),"%s %s %s %s %ld",
1495 name, typename, hard, soft,
1496 (long) server.client_obuf_limits[j].soft_limit_seconds);
1497 rewriteConfigRewriteLine(state,name,line,force);
1498 }
1499}
1500
1501/* Rewrite the oom-score-adj-values option. */
1502void rewriteConfigOOMScoreAdjValuesOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1503 UNUSED(config);
1504 int force = 0;
1505 int j;
1506 sds line;
1507
1508 line = sdsnew(name);
1509 line = sdscatlen(line, " ", 1);
1510 for (j = 0; j < CONFIG_OOM_COUNT; j++) {
1511 if (server.oom_score_adj_values[j] != configOOMScoreAdjValuesDefaults[j])
1512 force = 1;
1513
1514 line = sdscatprintf(line, "%d", server.oom_score_adj_values[j]);
1515 if (j+1 != CONFIG_OOM_COUNT)
1516 line = sdscatlen(line, " ", 1);
1517 }
1518 rewriteConfigRewriteLine(state,name,line,force);
1519}
1520
1521/* Rewrite the bind option. */
1522void rewriteConfigBindOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1523 UNUSED(config);
1524 int force = 1;
1525 sds line, addresses;
1526 int is_default = 0;
1527
1528 /* Compare server.bindaddr with CONFIG_DEFAULT_BINDADDR */
1529 if (server.bindaddr_count == CONFIG_DEFAULT_BINDADDR_COUNT) {
1530 is_default = 1;
1531 char *default_bindaddr[CONFIG_DEFAULT_BINDADDR_COUNT] = CONFIG_DEFAULT_BINDADDR;
1532 for (int j = 0; j < CONFIG_DEFAULT_BINDADDR_COUNT; j++) {
1533 if (strcmp(server.bindaddr[j], default_bindaddr[j]) != 0) {
1534 is_default = 0;
1535 break;
1536 }
1537 }
1538 }
1539
1540 if (is_default) {
1541 rewriteConfigMarkAsProcessed(state,name);
1542 return;
1543 }
1544
1545 /* Rewrite as bind <addr1> <addr2> ... <addrN> */
1546 if (server.bindaddr_count > 0)
1547 addresses = sdsjoin(server.bindaddr,server.bindaddr_count," ");
1548 else
1549 addresses = sdsnew("\"\"");
1550 line = sdsnew(name);
1551 line = sdscatlen(line, " ", 1);
1552 line = sdscatsds(line, addresses);
1553 sdsfree(addresses);
1554
1555 rewriteConfigRewriteLine(state,name,line,force);
1556}
1557
1558/* Rewrite the loadmodule option. */
1559void rewriteConfigLoadmoduleOption(struct rewriteConfigState *state) {
1560 sds line;
1561
1562 dictIterator *di = dictGetIterator(modules);
1563 dictEntry *de;
1564 while ((de = dictNext(di)) != NULL) {
1565 struct RedisModule *module = dictGetVal(de);
1566 line = sdsnew("loadmodule ");
1567 line = sdscatsds(line, module->loadmod->path);
1568 for (int i = 0; i < module->loadmod->argc; i++) {
1569 line = sdscatlen(line, " ", 1);
1570 line = sdscatsds(line, module->loadmod->argv[i]->ptr);
1571 }
1572 rewriteConfigRewriteLine(state,"loadmodule",line,1);
1573 }
1574 dictReleaseIterator(di);
1575 /* Mark "loadmodule" as processed in case modules is empty. */
1576 rewriteConfigMarkAsProcessed(state,"loadmodule");
1577}
1578
1579/* Glue together the configuration lines in the current configuration
1580 * rewrite state into a single string, stripping multiple empty lines. */
1581sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {
1582 sds content = sdsempty();
1583 int j, was_empty = 0;
1584
1585 for (j = 0; j < state->numlines; j++) {
1586 /* Every cluster of empty lines is turned into a single empty line. */
1587 if (sdslen(state->lines[j]) == 0) {
1588 if (was_empty) continue;
1589 was_empty = 1;
1590 } else {
1591 was_empty = 0;
1592 }
1593 content = sdscatsds(content,state->lines[j]);
1594 content = sdscatlen(content,"\n",1);
1595 }
1596 return content;
1597}
1598
1599/* At the end of the rewrite process the state contains the remaining
1600 * map between "option name" => "lines in the original config file".
1601 * Lines used by the rewrite process were removed by the function
1602 * rewriteConfigRewriteLine(), all the other lines are "orphaned" and
1603 * should be replaced by empty lines.
1604 *
1605 * This function does just this, iterating all the option names and
1606 * blanking all the lines still associated. */
1607void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
1608 dictIterator *di = dictGetIterator(state->option_to_line);
1609 dictEntry *de;
1610
1611 while((de = dictNext(di)) != NULL) {
1612 list *l = dictGetVal(de);
1613 sds option = dictGetKey(de);
1614
1615 /* Don't blank lines about options the rewrite process
1616 * don't understand. */
1617 if (dictFind(state->rewritten,option) == NULL) {
1618 serverLog(LL_DEBUG,"Not rewritten option: %s", option);
1619 continue;
1620 }
1621
1622 while(listLength(l)) {
1623 listNode *ln = listFirst(l);
1624 int linenum = (long) ln->value;
1625
1626 sdsfree(state->lines[linenum]);
1627 state->lines[linenum] = sdsempty();
1628 listDelNode(l,ln);
1629 }
1630 }
1631 dictReleaseIterator(di);
1632}
1633
1634/* This function returns a string representation of all the config options
1635 * marked with DEBUG_CONFIG, which can be used to help with debugging. */
1636sds getConfigDebugInfo() {
1637 struct rewriteConfigState *state = rewriteConfigCreateState();
1638 state->force_write = 1; /* Force the output */
1639 state->needs_signature = 0; /* Omit the rewrite signature */
1640
1641 /* Iterate the configs and "rewrite" the ones that have
1642 * the debug flag. */
1643 dictIterator *di = dictGetIterator(configs);
1644 dictEntry *de;
1645 while ((de = dictNext(di)) != NULL) {
1646 standardConfig *config = dictGetVal(de);
1647 if (!(config->flags & DEBUG_CONFIG)) continue;
1648 config->interface.rewrite(config, config->name, state);
1649 }
1650 dictReleaseIterator(di);
1651 sds info = rewriteConfigGetContentFromState(state);
1652 rewriteConfigReleaseState(state);
1653 return info;
1654}
1655
1656/* This function replaces the old configuration file with the new content
1657 * in an atomic manner.
1658 *
1659 * The function returns 0 on success, otherwise -1 is returned and errno
1660 * is set accordingly. */
1661int rewriteConfigOverwriteFile(char *configfile, sds content) {
1662 int fd = -1;
1663 int retval = -1;
1664 char tmp_conffile[PATH_MAX];
1665 const char *tmp_suffix = ".XXXXXX";
1666 size_t offset = 0;
1667 ssize_t written_bytes = 0;
1668 int old_errno;
1669
1670 int tmp_path_len = snprintf(tmp_conffile, sizeof(tmp_conffile), "%s%s", configfile, tmp_suffix);
1671 if (tmp_path_len <= 0 || (unsigned int)tmp_path_len >= sizeof(tmp_conffile)) {
1672 serverLog(LL_WARNING, "Config file full path is too long");
1673 errno = ENAMETOOLONG;
1674 return retval;
1675 }
1676
1677#ifdef _GNU_SOURCE
1678 fd = mkostemp(tmp_conffile, O_CLOEXEC);
1679#else
1680 /* There's a theoretical chance here to leak the FD if a module thread forks & execv in the middle */
1681 fd = mkstemp(tmp_conffile);
1682#endif
1683
1684 if (fd == -1) {
1685 serverLog(LL_WARNING, "Could not create tmp config file (%s)", strerror(errno));
1686 return retval;
1687 }
1688
1689 while (offset < sdslen(content)) {
1690 written_bytes = write(fd, content + offset, sdslen(content) - offset);
1691 if (written_bytes <= 0) {
1692 if (errno == EINTR) continue; /* FD is blocking, no other retryable errors */
1693 serverLog(LL_WARNING, "Failed after writing (%zd) bytes to tmp config file (%s)", offset, strerror(errno));
1694 goto cleanup;
1695 }
1696 offset+=written_bytes;
1697 }
1698
1699 if (fsync(fd))
1700 serverLog(LL_WARNING, "Could not sync tmp config file to disk (%s)", strerror(errno));
1701 else if (fchmod(fd, 0644 & ~server.umask) == -1)
1702 serverLog(LL_WARNING, "Could not chmod config file (%s)", strerror(errno));
1703 else if (rename(tmp_conffile, configfile) == -1)
1704 serverLog(LL_WARNING, "Could not rename tmp config file (%s)", strerror(errno));
1705 else if (fsyncFileDir(configfile) == -1)
1706 serverLog(LL_WARNING, "Could not sync config file dir (%s)", strerror(errno));
1707 else {
1708 retval = 0;
1709 serverLog(LL_DEBUG, "Rewritten config file (%s) successfully", configfile);
1710 }
1711
1712cleanup:
1713 old_errno = errno;
1714 close(fd);
1715 if (retval) unlink(tmp_conffile);
1716 errno = old_errno;
1717 return retval;
1718}
1719
1720/* Rewrite the configuration file at "path".
1721 * If the configuration file already exists, we try at best to retain comments
1722 * and overall structure.
1723 *
1724 * Configuration parameters that are at their default value, unless already
1725 * explicitly included in the old configuration file, are not rewritten.
1726 * The force_write flag overrides this behavior and forces everything to be
1727 * written. This is currently only used for testing purposes.
1728 *
1729 * On error -1 is returned and errno is set accordingly, otherwise 0. */
1730int rewriteConfig(char *path, int force_write) {
1731 struct rewriteConfigState *state;
1732 sds newcontent;
1733 int retval;
1734
1735 /* Step 1: read the old config into our rewrite state. */
1736 if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;
1737 if (force_write) state->force_write = 1;
1738
1739 /* Step 2: rewrite every single option, replacing or appending it inside
1740 * the rewrite state. */
1741
1742 /* Iterate the configs that are standard */
1743 dictIterator *di = dictGetIterator(configs);
1744 dictEntry *de;
1745 while ((de = dictNext(di)) != NULL) {
1746 standardConfig *config = dictGetVal(de);
1747 /* Only rewrite the primary names */
1748 if (config->flags & ALIAS_CONFIG) continue;
1749 if (config->interface.rewrite) config->interface.rewrite(config, de->key, state);
1750 }
1751 dictReleaseIterator(di);
1752
1753 rewriteConfigUserOption(state);
1754 rewriteConfigLoadmoduleOption(state);
1755
1756 /* Rewrite Sentinel config if in Sentinel mode. */
1757 if (server.sentinel_mode) rewriteConfigSentinelOption(state);
1758
1759 /* Step 3: remove all the orphaned lines in the old file, that is, lines
1760 * that were used by a config option and are no longer used, like in case
1761 * of multiple "save" options or duplicated options. */
1762 rewriteConfigRemoveOrphaned(state);
1763
1764 /* Step 4: generate a new configuration file from the modified state
1765 * and write it into the original file. */
1766 newcontent = rewriteConfigGetContentFromState(state);
1767 retval = rewriteConfigOverwriteFile(server.configfile,newcontent);
1768
1769 sdsfree(newcontent);
1770 rewriteConfigReleaseState(state);
1771 return retval;
1772}
1773
1774/*-----------------------------------------------------------------------------
1775 * Configs that fit one of the major types and require no special handling
1776 *----------------------------------------------------------------------------*/
1777#define LOADBUF_SIZE 256
1778static char loadbuf[LOADBUF_SIZE];
1779
1780#define embedCommonConfig(config_name, config_alias, config_flags) \
1781 .name = (config_name), \
1782 .alias = (config_alias), \
1783 .flags = (config_flags),
1784
1785#define embedConfigInterface(initfn, setfn, getfn, rewritefn, applyfn) .interface = { \
1786 .init = (initfn), \
1787 .set = (setfn), \
1788 .get = (getfn), \
1789 .rewrite = (rewritefn), \
1790 .apply = (applyfn) \
1791},
1792
1793/* What follows is the generic config types that are supported. To add a new
1794 * config with one of these types, add it to the standardConfig table with
1795 * the creation macro for each type.
1796 *
1797 * Each type contains the following:
1798 * * A function defining how to load this type on startup.
1799 * * A function defining how to update this type on CONFIG SET.
1800 * * A function defining how to serialize this type on CONFIG SET.
1801 * * A function defining how to rewrite this type on CONFIG REWRITE.
1802 * * A Macro defining how to create this type.
1803 */
1804
1805/* Bool Configs */
1806static void boolConfigInit(standardConfig *config) {
1807 *config->data.yesno.config = config->data.yesno.default_value;
1808}
1809
1810static int boolConfigSet(standardConfig *config, sds *argv, int argc, const char **err) {
1811 UNUSED(argc);
1812 int yn = yesnotoi(argv[0]);
1813 if (yn == -1) {
1814 *err = "argument must be 'yes' or 'no'";
1815 return 0;
1816 }
1817 if (config->data.yesno.is_valid_fn && !config->data.yesno.is_valid_fn(yn, err))
1818 return 0;
1819 int prev = config->flags & MODULE_CONFIG ? getModuleBoolConfig(config->privdata) : *(config->data.yesno.config);
1820 if (prev != yn) {
1821 if (config->flags & MODULE_CONFIG) {
1822 return setModuleBoolConfig(config->privdata, yn, err);
1823 }
1824 *(config->data.yesno.config) = yn;
1825 return 1;
1826 }
1827 return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
1828}
1829
1830static sds boolConfigGet(standardConfig *config) {
1831 if (config->flags & MODULE_CONFIG) {
1832 return sdsnew(getModuleBoolConfig(config->privdata) ? "yes" : "no");
1833 }
1834 return sdsnew(*config->data.yesno.config ? "yes" : "no");
1835}
1836
1837static void boolConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1838 int val = config->flags & MODULE_CONFIG ? getModuleBoolConfig(config->privdata) : *(config->data.yesno.config);
1839 rewriteConfigYesNoOption(state, name, val, config->data.yesno.default_value);
1840}
1841
1842#define createBoolConfig(name, alias, flags, config_addr, default, is_valid, apply) { \
1843 embedCommonConfig(name, alias, flags) \
1844 embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite, apply) \
1845 .type = BOOL_CONFIG, \
1846 .data.yesno = { \
1847 .config = &(config_addr), \
1848 .default_value = (default), \
1849 .is_valid_fn = (is_valid), \
1850 } \
1851}
1852
1853/* String Configs */
1854static void stringConfigInit(standardConfig *config) {
1855 *config->data.string.config = (config->data.string.convert_empty_to_null && !config->data.string.default_value) ? NULL : zstrdup(config->data.string.default_value);
1856}
1857
1858static int stringConfigSet(standardConfig *config, sds *argv, int argc, const char **err) {
1859 UNUSED(argc);
1860 if (config->data.string.is_valid_fn && !config->data.string.is_valid_fn(argv[0], err))
1861 return 0;
1862 char *prev = *config->data.string.config;
1863 char *new = (config->data.string.convert_empty_to_null && !argv[0][0]) ? NULL : argv[0];
1864 if (new != prev && (new == NULL || prev == NULL || strcmp(prev, new))) {
1865 *config->data.string.config = new != NULL ? zstrdup(new) : NULL;
1866 zfree(prev);
1867 return 1;
1868 }
1869 return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
1870}
1871
1872static sds stringConfigGet(standardConfig *config) {
1873 return sdsnew(*config->data.string.config ? *config->data.string.config : "");
1874}
1875
1876static void stringConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1877 rewriteConfigStringOption(state, name,*(config->data.string.config), config->data.string.default_value);
1878}
1879
1880/* SDS Configs */
1881static void sdsConfigInit(standardConfig *config) {
1882 *config->data.sds.config = (config->data.sds.convert_empty_to_null && !config->data.sds.default_value) ? NULL : sdsnew(config->data.sds.default_value);
1883}
1884
1885static int sdsConfigSet(standardConfig *config, sds *argv, int argc, const char **err) {
1886 UNUSED(argc);
1887 if (config->data.sds.is_valid_fn && !config->data.sds.is_valid_fn(argv[0], err))
1888 return 0;
1889
1890 sds prev = config->flags & MODULE_CONFIG ? getModuleStringConfig(config->privdata) : *config->data.sds.config;
1891 sds new = (config->data.string.convert_empty_to_null && (sdslen(argv[0]) == 0)) ? NULL : argv[0];
1892
1893 /* if prev and new configuration are not equal, set the new one */
1894 if (new != prev && (new == NULL || prev == NULL || sdscmp(prev, new))) {
1895 /* If MODULE_CONFIG flag is set, then free temporary prev getModuleStringConfig returned.
1896 * Otherwise, free the actual previous config value Redis held (Same action, different reasons) */
1897 sdsfree(prev);
1898
1899 if (config->flags & MODULE_CONFIG) {
1900 return setModuleStringConfig(config->privdata, new, err);
1901 }
1902 *config->data.sds.config = new != NULL ? sdsdup(new) : NULL;
1903 return 1;
1904 }
1905 if (config->flags & MODULE_CONFIG && prev) sdsfree(prev);
1906 return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
1907}
1908
1909static sds sdsConfigGet(standardConfig *config) {
1910 sds val = config->flags & MODULE_CONFIG ? getModuleStringConfig(config->privdata) : *config->data.sds.config;
1911 if (val) {
1912 if (config->flags & MODULE_CONFIG) return val;
1913 return sdsdup(val);
1914 } else {
1915 return sdsnew("");
1916 }
1917}
1918
1919static void sdsConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) {
1920 sds val = config->flags & MODULE_CONFIG ? getModuleStringConfig(config->privdata) : *config->data.sds.config;
1921 rewriteConfigSdsOption(state, name, val, config->data.sds.default_value);
1922 if ((val) && (config->flags & MODULE_CONFIG)) sdsfree(val);
1923}
1924
1925
1926#define ALLOW_EMPTY_STRING 0
1927#define EMPTY_STRING_IS_NULL 1
1928
1929#define createStringConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, apply) { \
1930 embedCommonConfig(name, alias, flags) \
1931 embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite, apply) \
1932 .type = STRING_CONFIG, \
1933 .data.string = { \
1934 .config = &(config_addr), \
1935 .default_value = (default), \
1936 .is_valid_fn = (is_valid), \
1937 .convert_empty_to_null = (empty_to_null), \
1938 } \
1939}
1940
1941#define createSDSConfig(name, alias, flags, empty_to_null, config_addr, default, is_valid, apply) { \
1942 embedCommonConfig(name, alias, flags) \
1943 embedConfigInterface(sdsConfigInit, sdsConfigSet, sdsConfigGet, sdsConfigRewrite, apply) \
1944 .type = SDS_CONFIG, \
1945 .data.sds = { \
1946 .config = &(config_addr), \
1947 .default_value = (default), \
1948 .is_valid_fn = (is_valid), \
1949 .convert_empty_to_null = (empty_to_null), \
1950 } \
1951}
1952
1953/* Enum configs */
1954static void enumConfigInit(standardConfig *config) {
1955 *config->data.enumd.config = config->data.enumd.default_value;
1956}
1957
1958static int enumConfigSet(standardConfig *config, sds *argv, int argc, const char **err) {
1959 int enumval;
1960 int bitflags = !!(config->flags & MULTI_ARG_CONFIG);
1961 enumval = configEnumGetValue(config->data.enumd.enum_value, argv, argc, bitflags);
1962
1963 if (enumval == INT_MIN) {
1964 sds enumerr = sdsnew("argument(s) must be one of the following: ");
1965 configEnum *enumNode = config->data.enumd.enum_value;
1966 while(enumNode->name != NULL) {
1967 enumerr = sdscatlen(enumerr, enumNode->name,
1968 strlen(enumNode->name));
1969 enumerr = sdscatlen(enumerr, ", ", 2);
1970 enumNode++;
1971 }
1972 sdsrange(enumerr,0,-3); /* Remove final ", ". */
1973
1974 strncpy(loadbuf, enumerr, LOADBUF_SIZE);
1975 loadbuf[LOADBUF_SIZE - 1] = '\0';
1976
1977 sdsfree(enumerr);
1978 *err = loadbuf;
1979 return 0;
1980 }
1981 if (config->data.enumd.is_valid_fn && !config->data.enumd.is_valid_fn(enumval, err))
1982 return 0;
1983 int prev = config->flags & MODULE_CONFIG ? getModuleEnumConfig(config->privdata) : *(config->data.enumd.config);
1984 if (prev != enumval) {
1985 if (config->flags & MODULE_CONFIG)
1986 return setModuleEnumConfig(config->privdata, enumval, err);
1987 *(config->data.enumd.config) = enumval;
1988 return 1;
1989 }
1990 return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
1991}
1992
1993static sds enumConfigGet(standardConfig *config) {
1994 int val = config->flags & MODULE_CONFIG ? getModuleEnumConfig(config->privdata) : *(config->data.enumd.config);
1995 int bitflags = !!(config->flags & MULTI_ARG_CONFIG);
1996 return configEnumGetName(config->data.enumd.enum_value,val,bitflags);
1997}
1998
1999static void enumConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) {
2000 int val = config->flags & MODULE_CONFIG ? getModuleEnumConfig(config->privdata) : *(config->data.enumd.config);
2001 rewriteConfigEnumOption(state, name, val, config);
2002}
2003
2004#define createEnumConfig(name, alias, flags, enum, config_addr, default, is_valid, apply) { \
2005 embedCommonConfig(name, alias, flags) \
2006 embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite, apply) \
2007 .type = ENUM_CONFIG, \
2008 .data.enumd = { \
2009 .config = &(config_addr), \
2010 .default_value = (default), \
2011 .is_valid_fn = (is_valid), \
2012 .enum_value = (enum), \
2013 } \
2014}
2015
2016/* Gets a 'long long val' and sets it into the union, using a macro to get
2017 * compile time type check. */
2018int setNumericType(standardConfig *config, long long val, const char **err) {
2019 if (config->data.numeric.numeric_type == NUMERIC_TYPE_INT) {
2020 *(config->data.numeric.config.i) = (int) val;
2021 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_UINT) {
2022 *(config->data.numeric.config.ui) = (unsigned int) val;
2023 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG) {
2024 *(config->data.numeric.config.l) = (long) val;
2025 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG) {
2026 *(config->data.numeric.config.ul) = (unsigned long) val;
2027 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) {
2028 if (config->flags & MODULE_CONFIG)
2029 return setModuleNumericConfig(config->privdata, val, err);
2030 else *(config->data.numeric.config.ll) = (long long) val;
2031 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) {
2032 *(config->data.numeric.config.ull) = (unsigned long long) val;
2033 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) {
2034 *(config->data.numeric.config.st) = (size_t) val;
2035 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) {
2036 *(config->data.numeric.config.sst) = (ssize_t) val;
2037 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) {
2038 *(config->data.numeric.config.ot) = (off_t) val;
2039 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) {
2040 *(config->data.numeric.config.tt) = (time_t) val;
2041 }
2042 return 1;
2043}
2044
2045/* Gets a 'long long val' and sets it with the value from the union, using a
2046 * macro to get compile time type check. */
2047#define GET_NUMERIC_TYPE(val) \
2048 if (config->data.numeric.numeric_type == NUMERIC_TYPE_INT) { \
2049 val = *(config->data.numeric.config.i); \
2050 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \
2051 val = *(config->data.numeric.config.ui); \
2052 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \
2053 val = *(config->data.numeric.config.l); \
2054 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \
2055 val = *(config->data.numeric.config.ul); \
2056 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \
2057 if (config->flags & MODULE_CONFIG) val = getModuleNumericConfig(config->privdata); \
2058 else val = *(config->data.numeric.config.ll); \
2059 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \
2060 val = *(config->data.numeric.config.ull); \
2061 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \
2062 val = *(config->data.numeric.config.st); \
2063 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \
2064 val = *(config->data.numeric.config.sst); \
2065 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \
2066 val = *(config->data.numeric.config.ot); \
2067 } else if (config->data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \
2068 val = *(config->data.numeric.config.tt); \
2069 }
2070
2071/* Numeric configs */
2072static void numericConfigInit(standardConfig *config) {
2073 setNumericType(config, config->data.numeric.default_value, NULL);
2074}
2075
2076static int numericBoundaryCheck(standardConfig *config, long long ll, const char **err) {
2077 if (config->data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG ||
2078 config->data.numeric.numeric_type == NUMERIC_TYPE_UINT ||
2079 config->data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) {
2080 /* Boundary check for unsigned types */
2081 unsigned long long ull = ll;
2082 unsigned long long upper_bound = config->data.numeric.upper_bound;
2083 unsigned long long lower_bound = config->data.numeric.lower_bound;
2084 if (ull > upper_bound || ull < lower_bound) {
2085 if (config->data.numeric.flags & OCTAL_CONFIG) {
2086 snprintf(loadbuf, LOADBUF_SIZE,
2087 "argument must be between %llo and %llo inclusive",
2088 lower_bound,
2089 upper_bound);
2090 } else {
2091 snprintf(loadbuf, LOADBUF_SIZE,
2092 "argument must be between %llu and %llu inclusive",
2093 lower_bound,
2094 upper_bound);
2095 }
2096 *err = loadbuf;
2097 return 0;
2098 }
2099 } else {
2100 /* Boundary check for percentages */
2101 if (config->data.numeric.flags & PERCENT_CONFIG && ll < 0) {
2102 if (ll < config->data.numeric.lower_bound) {
2103 snprintf(loadbuf, LOADBUF_SIZE,
2104 "percentage argument must be less or equal to %lld",
2105 -config->data.numeric.lower_bound);
2106 *err = loadbuf;
2107 return 0;
2108 }
2109 }
2110 /* Boundary check for signed types */
2111 else if (ll > config->data.numeric.upper_bound || ll < config->data.numeric.lower_bound) {
2112 snprintf(loadbuf, LOADBUF_SIZE,
2113 "argument must be between %lld and %lld inclusive",
2114 config->data.numeric.lower_bound,
2115 config->data.numeric.upper_bound);
2116 *err = loadbuf;
2117 return 0;
2118 }
2119 }
2120 return 1;
2121}
2122
2123static int numericParseString(standardConfig *config, sds value, const char **err, long long *res) {
2124 /* First try to parse as memory */
2125 if (config->data.numeric.flags & MEMORY_CONFIG) {
2126 int memerr;
2127 *res = memtoull(value, &memerr);
2128 if (!memerr)
2129 return 1;
2130 }
2131
2132 /* Attempt to parse as percent */
2133 if (config->data.numeric.flags & PERCENT_CONFIG &&
2134 sdslen(value) > 1 && value[sdslen(value)-1] == '%' &&
2135 string2ll(value, sdslen(value)-1, res) &&
2136 *res >= 0) {
2137 /* We store percentage as negative value */
2138 *res = -*res;
2139 return 1;
2140 }
2141
2142 /* Attempt to parse as an octal number */
2143 if (config->data.numeric.flags & OCTAL_CONFIG) {
2144 char *endptr;
2145 errno = 0;
2146 *res = strtoll(value, &endptr, 8);
2147 if (errno == 0 && *endptr == '\0')
2148 return 1; /* No overflow or invalid characters */
2149 }
2150
2151 /* Attempt a simple number (no special flags set) */
2152 if (!config->data.numeric.flags && string2ll(value, sdslen(value), res))
2153 return 1;
2154
2155 /* Select appropriate error string */
2156 if (config->data.numeric.flags & MEMORY_CONFIG &&
2157 config->data.numeric.flags & PERCENT_CONFIG)
2158 *err = "argument must be a memory or percent value" ;
2159 else if (config->data.numeric.flags & MEMORY_CONFIG)
2160 *err = "argument must be a memory value";
2161 else if (config->data.numeric.flags & OCTAL_CONFIG)
2162 *err = "argument couldn't be parsed as an octal number";
2163 else
2164 *err = "argument couldn't be parsed into an integer";
2165 return 0;
2166}
2167
2168static int numericConfigSet(standardConfig *config, sds *argv, int argc, const char **err) {
2169 UNUSED(argc);
2170 long long ll, prev = 0;
2171
2172 if (!numericParseString(config, argv[0], err, &ll))
2173 return 0;
2174
2175 if (!numericBoundaryCheck(config, ll, err))
2176 return 0;
2177
2178 if (config->data.numeric.is_valid_fn && !config->data.numeric.is_valid_fn(ll, err))
2179 return 0;
2180
2181 GET_NUMERIC_TYPE(prev)
2182 if (prev != ll) {
2183 return setNumericType(config, ll, err);
2184 }
2185
2186 return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
2187}
2188
2189static sds numericConfigGet(standardConfig *config) {
2190 char buf[128];
2191
2192 long long value = 0;
2193 GET_NUMERIC_TYPE(value)
2194
2195 if (config->data.numeric.flags & PERCENT_CONFIG && value < 0) {
2196 int len = ll2string(buf, sizeof(buf), -value);
2197 buf[len] = '%';
2198 buf[len+1] = '\0';
2199 }
2200 else if (config->data.numeric.flags & MEMORY_CONFIG) {
2201 ull2string(buf, sizeof(buf), value);
2202 } else if (config->data.numeric.flags & OCTAL_CONFIG) {
2203 snprintf(buf, sizeof(buf), "%llo", value);
2204 } else {
2205 ll2string(buf, sizeof(buf), value);
2206 }
2207 return sdsnew(buf);
2208}
2209
2210static void numericConfigRewrite(standardConfig *config, const char *name, struct rewriteConfigState *state) {
2211 long long value = 0;
2212
2213 GET_NUMERIC_TYPE(value)
2214
2215 if (config->data.numeric.flags & PERCENT_CONFIG && value < 0) {
2216 rewriteConfigPercentOption(state, name, -value, config->data.numeric.default_value);
2217 } else if (config->data.numeric.flags & MEMORY_CONFIG) {
2218 rewriteConfigBytesOption(state, name, value, config->data.numeric.default_value);
2219 } else if (config->data.numeric.flags & OCTAL_CONFIG) {
2220 rewriteConfigOctalOption(state, name, value, config->data.numeric.default_value);
2221 } else {
2222 rewriteConfigNumericalOption(state, name, value, config->data.numeric.default_value);
2223 }
2224}
2225
2226#define embedCommonNumericalConfig(name, alias, _flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) { \
2227 embedCommonConfig(name, alias, _flags) \
2228 embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite, apply) \
2229 .type = NUMERIC_CONFIG, \
2230 .data.numeric = { \
2231 .lower_bound = (lower), \
2232 .upper_bound = (upper), \
2233 .default_value = (default), \
2234 .is_valid_fn = (is_valid), \
2235 .flags = (num_conf_flags),
2236
2237#define createIntConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2238 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2239 .numeric_type = NUMERIC_TYPE_INT, \
2240 .config.i = &(config_addr) \
2241 } \
2242}
2243
2244#define createUIntConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2245 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2246 .numeric_type = NUMERIC_TYPE_UINT, \
2247 .config.ui = &(config_addr) \
2248 } \
2249}
2250
2251#define createLongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2252 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2253 .numeric_type = NUMERIC_TYPE_LONG, \
2254 .config.l = &(config_addr) \
2255 } \
2256}
2257
2258#define createULongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2259 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2260 .numeric_type = NUMERIC_TYPE_ULONG, \
2261 .config.ul = &(config_addr) \
2262 } \
2263}
2264
2265#define createLongLongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2266 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2267 .numeric_type = NUMERIC_TYPE_LONG_LONG, \
2268 .config.ll = &(config_addr) \
2269 } \
2270}
2271
2272#define createULongLongConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2273 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2274 .numeric_type = NUMERIC_TYPE_ULONG_LONG, \
2275 .config.ull = &(config_addr) \
2276 } \
2277}
2278
2279#define createSizeTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2280 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2281 .numeric_type = NUMERIC_TYPE_SIZE_T, \
2282 .config.st = &(config_addr) \
2283 } \
2284}
2285
2286#define createSSizeTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2287 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2288 .numeric_type = NUMERIC_TYPE_SSIZE_T, \
2289 .config.sst = &(config_addr) \
2290 } \
2291}
2292
2293#define createTimeTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2294 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2295 .numeric_type = NUMERIC_TYPE_TIME_T, \
2296 .config.tt = &(config_addr) \
2297 } \
2298}
2299
2300#define createOffTConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2301 embedCommonNumericalConfig(name, alias, flags, lower, upper, config_addr, default, num_conf_flags, is_valid, apply) \
2302 .numeric_type = NUMERIC_TYPE_OFF_T, \
2303 .config.ot = &(config_addr) \
2304 } \
2305}
2306
2307#define createSpecialConfig(name, alias, modifiable, setfn, getfn, rewritefn, applyfn) { \
2308 .type = SPECIAL_CONFIG, \
2309 embedCommonConfig(name, alias, modifiable) \
2310 embedConfigInterface(NULL, setfn, getfn, rewritefn, applyfn) \
2311}
2312
2313static int isValidActiveDefrag(int val, const char **err) {
2314#ifndef HAVE_DEFRAG
2315 if (val) {
2316 *err = "Active defragmentation cannot be enabled: it "
2317 "requires a Redis server compiled with a modified Jemalloc "
2318 "like the one shipped by default with the Redis source "
2319 "distribution";
2320 return 0;
2321 }
2322#else
2323 UNUSED(val);
2324 UNUSED(err);
2325#endif
2326 return 1;
2327}
2328
2329static int isValidDBfilename(char *val, const char **err) {
2330 if (!pathIsBaseName(val)) {
2331 *err = "dbfilename can't be a path, just a filename";
2332 return 0;
2333 }
2334 return 1;
2335}
2336
2337static int isValidAOFfilename(char *val, const char **err) {
2338 if (!strcmp(val, "")) {
2339 *err = "appendfilename can't be empty";
2340 return 0;
2341 }
2342 if (!pathIsBaseName(val)) {
2343 *err = "appendfilename can't be a path, just a filename";
2344 return 0;
2345 }
2346 return 1;
2347}
2348
2349static int isValidAOFdirname(char *val, const char **err) {
2350 if (!strcmp(val, "")) {
2351 *err = "appenddirname can't be empty";
2352 return 0;
2353 }
2354 if (!pathIsBaseName(val)) {
2355 *err = "appenddirname can't be a path, just a dirname";
2356 return 0;
2357 }
2358 return 1;
2359}
2360
2361static int isValidShutdownOnSigFlags(int val, const char **err) {
2362 /* Individual arguments are validated by createEnumConfig logic.
2363 * We just need to ensure valid combinations here. */
2364 if (val & SHUTDOWN_NOSAVE && val & SHUTDOWN_SAVE) {
2365 *err = "shutdown options SAVE and NOSAVE can't be used simultaneously";
2366 return 0;
2367 }
2368 return 1;
2369}
2370
2371static int isValidAnnouncedHostname(char *val, const char **err) {
2372 if (strlen(val) >= NET_HOST_STR_LEN) {
2373 *err = "Hostnames must be less than "
2374 STRINGIFY(NET_HOST_STR_LEN) " characters";
2375 return 0;
2376 }
2377
2378 int i = 0;
2379 char c;
2380 while ((c = val[i])) {
2381 /* We just validate the character set to make sure that everything
2382 * is parsed and handled correctly. */
2383 if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
2384 || (c >= '0' && c <= '9') || (c == '-') || (c == '.')))
2385 {
2386 *err = "Hostnames may only contain alphanumeric characters, "
2387 "hyphens or dots";
2388 return 0;
2389 }
2390 c = val[i++];
2391 }
2392 return 1;
2393}
2394
2395/* Validate specified string is a valid proc-title-template */
2396static int isValidProcTitleTemplate(char *val, const char **err) {
2397 if (!validateProcTitleTemplate(val)) {
2398 *err = "template format is invalid or contains unknown variables";
2399 return 0;
2400 }
2401 return 1;
2402}
2403
2404static int updateProcTitleTemplate(const char **err) {
2405 if (redisSetProcTitle(NULL) == C_ERR) {
2406 *err = "failed to set process title";
2407 return 0;
2408 }
2409 return 1;
2410}
2411
2412static int updateHZ(const char **err) {
2413 UNUSED(err);
2414 /* Hz is more a hint from the user, so we accept values out of range
2415 * but cap them to reasonable values. */
2416 if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;
2417 if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;
2418 server.hz = server.config_hz;
2419 return 1;
2420}
2421
2422static int updatePort(const char **err) {
2423 if (changeListenPort(server.port, &server.ipfd, acceptTcpHandler) == C_ERR) {
2424 *err = "Unable to listen on this port. Check server logs.";
2425 return 0;
2426 }
2427
2428 return 1;
2429}
2430
2431static int updateJemallocBgThread(const char **err) {
2432 UNUSED(err);
2433 set_jemalloc_bg_thread(server.jemalloc_bg_thread);
2434 return 1;
2435}
2436
2437static int updateReplBacklogSize(const char **err) {
2438 UNUSED(err);
2439 resizeReplicationBacklog();
2440 return 1;
2441}
2442
2443static int updateMaxmemory(const char **err) {
2444 UNUSED(err);
2445 if (server.maxmemory) {
2446 size_t used = zmalloc_used_memory()-freeMemoryGetNotCountedMemory();
2447 if (server.maxmemory < used) {
2448 serverLog(LL_WARNING,"WARNING: the new maxmemory value set via CONFIG SET (%llu) is smaller than the current memory usage (%zu). This will result in key eviction and/or the inability to accept new write commands depending on the maxmemory-policy.", server.maxmemory, used);
2449 }
2450 startEvictionTimeProc();
2451 }
2452 return 1;
2453}
2454
2455static int updateGoodSlaves(const char **err) {
2456 UNUSED(err);
2457 refreshGoodSlavesCount();
2458 return 1;
2459}
2460
2461static int updateWatchdogPeriod(const char **err) {
2462 UNUSED(err);
2463 applyWatchdogPeriod();
2464 return 1;
2465}
2466
2467static int updateAppendonly(const char **err) {
2468 if (!server.aof_enabled && server.aof_state != AOF_OFF) {
2469 stopAppendOnly();
2470 } else if (server.aof_enabled && server.aof_state == AOF_OFF) {
2471 if (startAppendOnly() == C_ERR) {
2472 *err = "Unable to turn on AOF. Check server logs.";
2473 return 0;
2474 }
2475 }
2476 return 1;
2477}
2478
2479static int updateAofAutoGCEnabled(const char **err) {
2480 UNUSED(err);
2481 if (!server.aof_disable_auto_gc) {
2482 aofDelHistoryFiles();
2483 }
2484
2485 return 1;
2486}
2487
2488static int updateSighandlerEnabled(const char **err) {
2489 UNUSED(err);
2490 if (server.crashlog_enabled)
2491 setupSignalHandlers();
2492 else
2493 removeSignalHandlers();
2494 return 1;
2495}
2496
2497static int updateMaxclients(const char **err) {
2498 unsigned int new_maxclients = server.maxclients;
2499 adjustOpenFilesLimit();
2500 if (server.maxclients != new_maxclients) {
2501 static char msg[128];
2502 sprintf(msg, "The operating system is not able to handle the specified number of clients, try with %d", server.maxclients);
2503 *err = msg;
2504 return 0;
2505 }
2506 if ((unsigned int) aeGetSetSize(server.el) <
2507 server.maxclients + CONFIG_FDSET_INCR)
2508 {
2509 if (aeResizeSetSize(server.el,
2510 server.maxclients + CONFIG_FDSET_INCR) == AE_ERR)
2511 {
2512 *err = "The event loop API used by Redis is not able to handle the specified number of clients";
2513 return 0;
2514 }
2515 }
2516 return 1;
2517}
2518
2519static int updateOOMScoreAdj(const char **err) {
2520 if (setOOMScoreAdj(-1) == C_ERR) {
2521 *err = "Failed to set current oom_score_adj. Check server logs.";
2522 return 0;
2523 }
2524
2525 return 1;
2526}
2527
2528int updateRequirePass(const char **err) {
2529 UNUSED(err);
2530 /* The old "requirepass" directive just translates to setting
2531 * a password to the default user. The only thing we do
2532 * additionally is to remember the cleartext password in this
2533 * case, for backward compatibility with Redis <= 5. */
2534 ACLUpdateDefaultUserPassword(server.requirepass);
2535 return 1;
2536}
2537
2538static int applyBind(const char **err) {
2539 if (changeBindAddr() == C_ERR) {
2540 *err = "Failed to bind to specified addresses.";
2541 return 0;
2542 }
2543
2544 return 1;
2545}
2546
2547int updateClusterFlags(const char **err) {
2548 UNUSED(err);
2549 clusterUpdateMyselfFlags();
2550 return 1;
2551}
2552
2553static int updateClusterIp(const char **err) {
2554 UNUSED(err);
2555 clusterUpdateMyselfIp();
2556 return 1;
2557}
2558
2559int updateClusterHostname(const char **err) {
2560 UNUSED(err);
2561 clusterUpdateMyselfHostname();
2562 return 1;
2563}
2564
2565#ifdef USE_OPENSSL
2566static int applyTlsCfg(const char **err) {
2567 UNUSED(err);
2568
2569 /* If TLS is enabled, try to configure OpenSSL. */
2570 if ((server.tls_port || server.tls_replication || server.tls_cluster)
2571 && tlsConfigure(&server.tls_ctx_config) == C_ERR) {
2572 *err = "Unable to update TLS configuration. Check server logs.";
2573 return 0;
2574 }
2575 return 1;
2576}
2577
2578static int applyTLSPort(const char **err) {
2579 /* Configure TLS in case it wasn't enabled */
2580 if (!isTlsConfigured() && tlsConfigure(&server.tls_ctx_config) == C_ERR) {
2581 *err = "Unable to update TLS configuration. Check server logs.";
2582 return 0;
2583 }
2584
2585 if (changeListenPort(server.tls_port, &server.tlsfd, acceptTLSHandler) == C_ERR) {
2586 *err = "Unable to listen on this port. Check server logs.";
2587 return 0;
2588 }
2589
2590 return 1;
2591}
2592
2593#endif /* USE_OPENSSL */
2594
2595static int setConfigDirOption(standardConfig *config, sds *argv, int argc, const char **err) {
2596 UNUSED(config);
2597 if (argc != 1) {
2598 *err = "wrong number of arguments";
2599 return 0;
2600 }
2601 if (chdir(argv[0]) == -1) {
2602 *err = strerror(errno);
2603 return 0;
2604 }
2605 return 1;
2606}
2607
2608static sds getConfigDirOption(standardConfig *config) {
2609 UNUSED(config);
2610 char buf[1024];
2611
2612 if (getcwd(buf,sizeof(buf)) == NULL)
2613 buf[0] = '\0';
2614
2615 return sdsnew(buf);
2616}
2617
2618static int setConfigSaveOption(standardConfig *config, sds *argv, int argc, const char **err) {
2619 UNUSED(config);
2620 int j;
2621
2622 /* Special case: treat single arg "" as zero args indicating empty save configuration */
2623 if (argc == 1 && !strcasecmp(argv[0],"")) {
2624 resetServerSaveParams();
2625 argc = 0;
2626 }
2627
2628 /* Perform sanity check before setting the new config:
2629 * - Even number of args
2630 * - Seconds >= 1, changes >= 0 */
2631 if (argc & 1) {
2632 *err = "Invalid save parameters";
2633 return 0;
2634 }
2635 for (j = 0; j < argc; j++) {
2636 char *eptr;
2637 long val;
2638
2639 val = strtoll(argv[j], &eptr, 10);
2640 if (eptr[0] != '\0' ||
2641 ((j & 1) == 0 && val < 1) ||
2642 ((j & 1) == 1 && val < 0)) {
2643 *err = "Invalid save parameters";
2644 return 0;
2645 }
2646 }
2647 /* Finally set the new config */
2648 if (!reading_config_file) {
2649 resetServerSaveParams();
2650 } else {
2651 /* We don't reset save params before loading, because if they're not part
2652 * of the file the defaults should be used.
2653 */
2654 static int save_loaded = 0;
2655 if (!save_loaded) {
2656 save_loaded = 1;
2657 resetServerSaveParams();
2658 }
2659 }
2660
2661 for (j = 0; j < argc; j += 2) {
2662 time_t seconds;
2663 int changes;
2664
2665 seconds = strtoll(argv[j],NULL,10);
2666 changes = strtoll(argv[j+1],NULL,10);
2667 appendServerSaveParams(seconds, changes);
2668 }
2669
2670 return 1;
2671}
2672
2673static sds getConfigSaveOption(standardConfig *config) {
2674 UNUSED(config);
2675 sds buf = sdsempty();
2676 int j;
2677
2678 for (j = 0; j < server.saveparamslen; j++) {
2679 buf = sdscatprintf(buf,"%jd %d",
2680 (intmax_t)server.saveparams[j].seconds,
2681 server.saveparams[j].changes);
2682 if (j != server.saveparamslen-1)
2683 buf = sdscatlen(buf," ",1);
2684 }
2685
2686 return buf;
2687}
2688
2689static int setConfigClientOutputBufferLimitOption(standardConfig *config, sds *argv, int argc, const char **err) {
2690 UNUSED(config);
2691 return updateClientOutputBufferLimit(argv, argc, err);
2692}
2693
2694static sds getConfigClientOutputBufferLimitOption(standardConfig *config) {
2695 UNUSED(config);
2696 sds buf = sdsempty();
2697 int j;
2698 for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
2699 buf = sdscatprintf(buf,"%s %llu %llu %ld",
2700 getClientTypeName(j),
2701 server.client_obuf_limits[j].hard_limit_bytes,
2702 server.client_obuf_limits[j].soft_limit_bytes,
2703 (long) server.client_obuf_limits[j].soft_limit_seconds);
2704 if (j != CLIENT_TYPE_OBUF_COUNT-1)
2705 buf = sdscatlen(buf," ",1);
2706 }
2707 return buf;
2708}
2709
2710/* Parse an array of CONFIG_OOM_COUNT sds strings, validate and populate
2711 * server.oom_score_adj_values if valid.
2712 */
2713static int setConfigOOMScoreAdjValuesOption(standardConfig *config, sds *argv, int argc, const char **err) {
2714 int i;
2715 int values[CONFIG_OOM_COUNT];
2716 int change = 0;
2717 UNUSED(config);
2718
2719 if (argc != CONFIG_OOM_COUNT) {
2720 *err = "wrong number of arguments";
2721 return 0;
2722 }
2723
2724 for (i = 0; i < CONFIG_OOM_COUNT; i++) {
2725 char *eptr;
2726 long long val = strtoll(argv[i], &eptr, 10);
2727
2728 if (*eptr != '\0' || val < -2000 || val > 2000) {
2729 if (err) *err = "Invalid oom-score-adj-values, elements must be between -2000 and 2000.";
2730 return 0;
2731 }
2732
2733 values[i] = val;
2734 }
2735
2736 /* Verify that the values make sense. If they don't omit a warning but
2737 * keep the configuration, which may still be valid for privileged processes.
2738 */
2739
2740 if (values[CONFIG_OOM_REPLICA] < values[CONFIG_OOM_MASTER] ||
2741 values[CONFIG_OOM_BGCHILD] < values[CONFIG_OOM_REPLICA])
2742 {
2743 serverLog(LL_WARNING,
2744 "The oom-score-adj-values configuration may not work for non-privileged processes! "
2745 "Please consult the documentation.");
2746 }
2747
2748 for (i = 0; i < CONFIG_OOM_COUNT; i++) {
2749 if (server.oom_score_adj_values[i] != values[i]) {
2750 server.oom_score_adj_values[i] = values[i];
2751 change = 1;
2752 }
2753 }
2754
2755 return change ? 1 : 2;
2756}
2757
2758static sds getConfigOOMScoreAdjValuesOption(standardConfig *config) {
2759 UNUSED(config);
2760 sds buf = sdsempty();
2761 int j;
2762
2763 for (j = 0; j < CONFIG_OOM_COUNT; j++) {
2764 buf = sdscatprintf(buf,"%d", server.oom_score_adj_values[j]);
2765 if (j != CONFIG_OOM_COUNT-1)
2766 buf = sdscatlen(buf," ",1);
2767 }
2768
2769 return buf;
2770}
2771
2772static int setConfigNotifyKeyspaceEventsOption(standardConfig *config, sds *argv, int argc, const char **err) {
2773 UNUSED(config);
2774 if (argc != 1) {
2775 *err = "wrong number of arguments";
2776 return 0;
2777 }
2778 int flags = keyspaceEventsStringToFlags(argv[0]);
2779 if (flags == -1) {
2780 *err = "Invalid event class character. Use 'Ag$lshzxeKEtmdn'.";
2781 return 0;
2782 }
2783 server.notify_keyspace_events = flags;
2784 return 1;
2785}
2786
2787static sds getConfigNotifyKeyspaceEventsOption(standardConfig *config) {
2788 UNUSED(config);
2789 return keyspaceEventsFlagsToString(server.notify_keyspace_events);
2790}
2791
2792static int setConfigBindOption(standardConfig *config, sds* argv, int argc, const char **err) {
2793 UNUSED(config);
2794 int j;
2795
2796 if (argc > CONFIG_BINDADDR_MAX) {
2797 *err = "Too many bind addresses specified.";
2798 return 0;
2799 }
2800
2801 /* A single empty argument is treated as a zero bindaddr count */
2802 if (argc == 1 && sdslen(argv[0]) == 0) argc = 0;
2803
2804 /* Free old bind addresses */
2805 for (j = 0; j < server.bindaddr_count; j++) {
2806 zfree(server.bindaddr[j]);
2807 }
2808 for (j = 0; j < argc; j++)
2809 server.bindaddr[j] = zstrdup(argv[j]);
2810 server.bindaddr_count = argc;
2811
2812 return 1;
2813}
2814
2815static int setConfigReplicaOfOption(standardConfig *config, sds* argv, int argc, const char **err) {
2816 UNUSED(config);
2817
2818 if (argc != 2) {
2819 *err = "wrong number of arguments";
2820 return 0;
2821 }
2822
2823 sdsfree(server.masterhost);
2824 server.masterhost = NULL;
2825 if (!strcasecmp(argv[0], "no") && !strcasecmp(argv[1], "one")) {
2826 return 1;
2827 }
2828 char *ptr;
2829 server.masterport = strtol(argv[1], &ptr, 10);
2830 if (server.masterport < 0 || server.masterport > 65535 || *ptr != '\0') {
2831 *err = "Invalid master port";
2832 return 0;
2833 }
2834 server.masterhost = sdsnew(argv[0]);
2835 server.repl_state = REPL_STATE_CONNECT;
2836 return 1;
2837}
2838
2839static sds getConfigBindOption(standardConfig *config) {
2840 UNUSED(config);
2841 return sdsjoin(server.bindaddr,server.bindaddr_count," ");
2842}
2843
2844static sds getConfigReplicaOfOption(standardConfig *config) {
2845 UNUSED(config);
2846 char buf[256];
2847 if (server.masterhost)
2848 snprintf(buf,sizeof(buf),"%s %d",
2849 server.masterhost, server.masterport);
2850 else
2851 buf[0] = '\0';
2852 return sdsnew(buf);
2853}
2854
2855int allowProtectedAction(int config, client *c) {
2856 return (config == PROTECTED_ACTION_ALLOWED_YES) ||
2857 (config == PROTECTED_ACTION_ALLOWED_LOCAL && islocalClient(c));
2858}
2859
2860
2861static int setConfigLatencyTrackingInfoPercentilesOutputOption(standardConfig *config, sds *argv, int argc, const char **err) {
2862 UNUSED(config);
2863 zfree(server.latency_tracking_info_percentiles);
2864 server.latency_tracking_info_percentiles = NULL;
2865 server.latency_tracking_info_percentiles_len = argc;
2866
2867 /* Special case: treat single arg "" as zero args indicating empty percentile configuration */
2868 if (argc == 1 && sdslen(argv[0]) == 0)
2869 server.latency_tracking_info_percentiles_len = 0;
2870 else
2871 server.latency_tracking_info_percentiles = zmalloc(sizeof(double)*argc);
2872
2873 for (int j = 0; j < server.latency_tracking_info_percentiles_len; j++) {
2874 double percentile;
2875 if (!string2d(argv[j], sdslen(argv[j]), &percentile)) {
2876 *err = "Invalid latency-tracking-info-percentiles parameters";
2877 goto configerr;
2878 }
2879 if (percentile > 100.0 || percentile < 0.0) {
2880 *err = "latency-tracking-info-percentiles parameters should sit between [0.0,100.0]";
2881 goto configerr;
2882 }
2883 server.latency_tracking_info_percentiles[j] = percentile;
2884 }
2885
2886 return 1;
2887configerr:
2888 zfree(server.latency_tracking_info_percentiles);
2889 server.latency_tracking_info_percentiles = NULL;
2890 server.latency_tracking_info_percentiles_len = 0;
2891 return 0;
2892}
2893
2894static sds getConfigLatencyTrackingInfoPercentilesOutputOption(standardConfig *config) {
2895 UNUSED(config);
2896 sds buf = sdsempty();
2897 for (int j = 0; j < server.latency_tracking_info_percentiles_len; j++) {
2898 char fbuf[128];
2899 size_t len = sprintf(fbuf, "%f", server.latency_tracking_info_percentiles[j]);
2900 len = trimDoubleString(fbuf, len);
2901 buf = sdscatlen(buf, fbuf, len);
2902 if (j != server.latency_tracking_info_percentiles_len-1)
2903 buf = sdscatlen(buf," ",1);
2904 }
2905 return buf;
2906}
2907
2908/* Rewrite the latency-tracking-info-percentiles option. */
2909void rewriteConfigLatencyTrackingInfoPercentilesOutputOption(standardConfig *config, const char *name, struct rewriteConfigState *state) {
2910 UNUSED(config);
2911 sds line = sdsnew(name);
2912 /* Rewrite latency-tracking-info-percentiles parameters,
2913 * or an empty 'latency-tracking-info-percentiles ""' line to avoid the
2914 * defaults from being used.
2915 */
2916 if (!server.latency_tracking_info_percentiles_len) {
2917 line = sdscat(line," \"\"");
2918 } else {
2919 for (int j = 0; j < server.latency_tracking_info_percentiles_len; j++) {
2920 char fbuf[128];
2921 size_t len = sprintf(fbuf, " %f", server.latency_tracking_info_percentiles[j]);
2922 len = trimDoubleString(fbuf, len);
2923 line = sdscatlen(line, fbuf, len);
2924 }
2925 }
2926 rewriteConfigRewriteLine(state,name,line,1);
2927}
2928
2929standardConfig static_configs[] = {
2930 /* Bool configs */
2931 createBoolConfig("rdbchecksum", NULL, IMMUTABLE_CONFIG, server.rdb_checksum, 1, NULL, NULL),
2932 createBoolConfig("daemonize", NULL, IMMUTABLE_CONFIG, server.daemonize, 0, NULL, NULL),
2933 createBoolConfig("io-threads-do-reads", NULL, DEBUG_CONFIG | IMMUTABLE_CONFIG, server.io_threads_do_reads, 0,NULL, NULL), /* Read + parse from threads? */
2934 createBoolConfig("always-show-logo", NULL, IMMUTABLE_CONFIG, server.always_show_logo, 0, NULL, NULL),
2935 createBoolConfig("protected-mode", NULL, MODIFIABLE_CONFIG, server.protected_mode, 1, NULL, NULL),
2936 createBoolConfig("rdbcompression", NULL, MODIFIABLE_CONFIG, server.rdb_compression, 1, NULL, NULL),
2937 createBoolConfig("rdb-del-sync-files", NULL, MODIFIABLE_CONFIG, server.rdb_del_sync_files, 0, NULL, NULL),
2938 createBoolConfig("activerehashing", NULL, MODIFIABLE_CONFIG, server.activerehashing, 1, NULL, NULL),
2939 createBoolConfig("stop-writes-on-bgsave-error", NULL, MODIFIABLE_CONFIG, server.stop_writes_on_bgsave_err, 1, NULL, NULL),
2940 createBoolConfig("set-proc-title", NULL, IMMUTABLE_CONFIG, server.set_proc_title, 1, NULL, NULL), /* Should setproctitle be used? */
2941 createBoolConfig("dynamic-hz", NULL, MODIFIABLE_CONFIG, server.dynamic_hz, 1, NULL, NULL), /* Adapt hz to # of clients.*/
2942 createBoolConfig("lazyfree-lazy-eviction", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, server.lazyfree_lazy_eviction, 0, NULL, NULL),
2943 createBoolConfig("lazyfree-lazy-expire", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, server.lazyfree_lazy_expire, 0, NULL, NULL),
2944 createBoolConfig("lazyfree-lazy-server-del", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, server.lazyfree_lazy_server_del, 0, NULL, NULL),
2945 createBoolConfig("lazyfree-lazy-user-del", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, server.lazyfree_lazy_user_del , 0, NULL, NULL),
2946 createBoolConfig("lazyfree-lazy-user-flush", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, server.lazyfree_lazy_user_flush , 0, NULL, NULL),
2947 createBoolConfig("repl-disable-tcp-nodelay", NULL, MODIFIABLE_CONFIG, server.repl_disable_tcp_nodelay, 0, NULL, NULL),
2948 createBoolConfig("repl-diskless-sync", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, server.repl_diskless_sync, 1, NULL, NULL),
2949 createBoolConfig("aof-rewrite-incremental-fsync", NULL, MODIFIABLE_CONFIG, server.aof_rewrite_incremental_fsync, 1, NULL, NULL),
2950 createBoolConfig("no-appendfsync-on-rewrite", NULL, MODIFIABLE_CONFIG, server.aof_no_fsync_on_rewrite, 0, NULL, NULL),
2951 createBoolConfig("cluster-require-full-coverage", NULL, MODIFIABLE_CONFIG, server.cluster_require_full_coverage, 1, NULL, NULL),
2952 createBoolConfig("rdb-save-incremental-fsync", NULL, MODIFIABLE_CONFIG, server.rdb_save_incremental_fsync, 1, NULL, NULL),
2953 createBoolConfig("aof-load-truncated", NULL, MODIFIABLE_CONFIG, server.aof_load_truncated, 1, NULL, NULL),
2954 createBoolConfig("aof-use-rdb-preamble", NULL, MODIFIABLE_CONFIG, server.aof_use_rdb_preamble, 1, NULL, NULL),
2955 createBoolConfig("aof-timestamp-enabled", NULL, MODIFIABLE_CONFIG, server.aof_timestamp_enabled, 0, NULL, NULL),
2956 createBoolConfig("cluster-replica-no-failover", "cluster-slave-no-failover", MODIFIABLE_CONFIG, server.cluster_slave_no_failover, 0, NULL, updateClusterFlags), /* Failover by default. */
2957 createBoolConfig("replica-lazy-flush", "slave-lazy-flush", MODIFIABLE_CONFIG, server.repl_slave_lazy_flush, 0, NULL, NULL),
2958 createBoolConfig("replica-serve-stale-data", "slave-serve-stale-data", MODIFIABLE_CONFIG, server.repl_serve_stale_data, 1, NULL, NULL),
2959 createBoolConfig("replica-read-only", "slave-read-only", DEBUG_CONFIG | MODIFIABLE_CONFIG, server.repl_slave_ro, 1, NULL, NULL),
2960 createBoolConfig("replica-ignore-maxmemory", "slave-ignore-maxmemory", MODIFIABLE_CONFIG, server.repl_slave_ignore_maxmemory, 1, NULL, NULL),
2961 createBoolConfig("jemalloc-bg-thread", NULL, MODIFIABLE_CONFIG, server.jemalloc_bg_thread, 1, NULL, updateJemallocBgThread),
2962 createBoolConfig("activedefrag", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, server.active_defrag_enabled, 0, isValidActiveDefrag, NULL),
2963 createBoolConfig("syslog-enabled", NULL, IMMUTABLE_CONFIG, server.syslog_enabled, 0, NULL, NULL),
2964 createBoolConfig("cluster-enabled", NULL, IMMUTABLE_CONFIG, server.cluster_enabled, 0, NULL, NULL),
2965 createBoolConfig("appendonly", NULL, MODIFIABLE_CONFIG | DENY_LOADING_CONFIG, server.aof_enabled, 0, NULL, updateAppendonly),
2966 createBoolConfig("cluster-allow-reads-when-down", NULL, MODIFIABLE_CONFIG, server.cluster_allow_reads_when_down, 0, NULL, NULL),
2967 createBoolConfig("cluster-allow-pubsubshard-when-down", NULL, MODIFIABLE_CONFIG, server.cluster_allow_pubsubshard_when_down, 1, NULL, NULL),
2968 createBoolConfig("crash-log-enabled", NULL, MODIFIABLE_CONFIG, server.crashlog_enabled, 1, NULL, updateSighandlerEnabled),
2969 createBoolConfig("crash-memcheck-enabled", NULL, MODIFIABLE_CONFIG, server.memcheck_enabled, 1, NULL, NULL),
2970 createBoolConfig("use-exit-on-panic", NULL, MODIFIABLE_CONFIG | HIDDEN_CONFIG, server.use_exit_on_panic, 0, NULL, NULL),
2971 createBoolConfig("disable-thp", NULL, IMMUTABLE_CONFIG, server.disable_thp, 1, NULL, NULL),
2972 createBoolConfig("cluster-allow-replica-migration", NULL, MODIFIABLE_CONFIG, server.cluster_allow_replica_migration, 1, NULL, NULL),
2973 createBoolConfig("replica-announced", NULL, MODIFIABLE_CONFIG, server.replica_announced, 1, NULL, NULL),
2974 createBoolConfig("latency-tracking", NULL, MODIFIABLE_CONFIG, server.latency_tracking_enabled, 1, NULL, NULL),
2975 createBoolConfig("aof-disable-auto-gc", NULL, MODIFIABLE_CONFIG, server.aof_disable_auto_gc, 0, NULL, updateAofAutoGCEnabled),
2976 createBoolConfig("replica-ignore-disk-write-errors", NULL, MODIFIABLE_CONFIG, server.repl_ignore_disk_write_error, 0, NULL, NULL),
2977
2978 /* String Configs */
2979 createStringConfig("aclfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.acl_filename, "", NULL, NULL),
2980 createStringConfig("unixsocket", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.unixsocket, NULL, NULL, NULL),
2981 createStringConfig("pidfile", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.pidfile, NULL, NULL, NULL),
2982 createStringConfig("replica-announce-ip", "slave-announce-ip", MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.slave_announce_ip, NULL, NULL, NULL),
2983 createStringConfig("masteruser", NULL, MODIFIABLE_CONFIG | SENSITIVE_CONFIG, EMPTY_STRING_IS_NULL, server.masteruser, NULL, NULL, NULL),
2984 createStringConfig("cluster-announce-ip", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.cluster_announce_ip, NULL, NULL, updateClusterIp),
2985 createStringConfig("cluster-config-file", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.cluster_configfile, "nodes.conf", NULL, NULL),
2986 createStringConfig("cluster-announce-hostname", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.cluster_announce_hostname, NULL, isValidAnnouncedHostname, updateClusterHostname),
2987 createStringConfig("syslog-ident", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.syslog_ident, "redis", NULL, NULL),
2988 createStringConfig("dbfilename", NULL, MODIFIABLE_CONFIG | PROTECTED_CONFIG, ALLOW_EMPTY_STRING, server.rdb_filename, "dump.rdb", isValidDBfilename, NULL),
2989 createStringConfig("appendfilename", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.aof_filename, "appendonly.aof", isValidAOFfilename, NULL),
2990 createStringConfig("appenddirname", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.aof_dirname, "appendonlydir", isValidAOFdirname, NULL),
2991 createStringConfig("server_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.server_cpulist, NULL, NULL, NULL),
2992 createStringConfig("bio_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bio_cpulist, NULL, NULL, NULL),
2993 createStringConfig("aof_rewrite_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.aof_rewrite_cpulist, NULL, NULL, NULL),
2994 createStringConfig("bgsave_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bgsave_cpulist, NULL, NULL, NULL),
2995 createStringConfig("ignore-warnings", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.ignore_warnings, "", NULL, NULL),
2996 createStringConfig("proc-title-template", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.proc_title_template, CONFIG_DEFAULT_PROC_TITLE_TEMPLATE, isValidProcTitleTemplate, updateProcTitleTemplate),
2997 createStringConfig("bind-source-addr", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bind_source_addr, NULL, NULL, NULL),
2998 createStringConfig("logfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.logfile, "", NULL, NULL),
2999
3000 /* SDS Configs */
3001 createSDSConfig("masterauth", NULL, MODIFIABLE_CONFIG | SENSITIVE_CONFIG, EMPTY_STRING_IS_NULL, server.masterauth, NULL, NULL, NULL),
3002 createSDSConfig("requirepass", NULL, MODIFIABLE_CONFIG | SENSITIVE_CONFIG, EMPTY_STRING_IS_NULL, server.requirepass, NULL, NULL, updateRequirePass),
3003
3004 /* Enum Configs */
3005 createEnumConfig("supervised", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, server.supervised_mode, SUPERVISED_NONE, NULL, NULL),
3006 createEnumConfig("syslog-facility", NULL, IMMUTABLE_CONFIG, syslog_facility_enum, server.syslog_facility, LOG_LOCAL0, NULL, NULL),
3007 createEnumConfig("repl-diskless-load", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG | DENY_LOADING_CONFIG, repl_diskless_load_enum, server.repl_diskless_load, REPL_DISKLESS_LOAD_DISABLED, NULL, NULL),
3008 createEnumConfig("loglevel", NULL, MODIFIABLE_CONFIG, loglevel_enum, server.verbosity, LL_NOTICE, NULL, NULL),
3009 createEnumConfig("maxmemory-policy", NULL, MODIFIABLE_CONFIG, maxmemory_policy_enum, server.maxmemory_policy, MAXMEMORY_NO_EVICTION, NULL, NULL),
3010 createEnumConfig("appendfsync", NULL, MODIFIABLE_CONFIG, aof_fsync_enum, server.aof_fsync, AOF_FSYNC_EVERYSEC, NULL, NULL),
3011 createEnumConfig("oom-score-adj", NULL, MODIFIABLE_CONFIG, oom_score_adj_enum, server.oom_score_adj, OOM_SCORE_ADJ_NO, NULL, updateOOMScoreAdj),
3012 createEnumConfig("acl-pubsub-default", NULL, MODIFIABLE_CONFIG, acl_pubsub_default_enum, server.acl_pubsub_default, 0, NULL, NULL),
3013 createEnumConfig("sanitize-dump-payload", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, sanitize_dump_payload_enum, server.sanitize_dump_payload, SANITIZE_DUMP_NO, NULL, NULL),
3014 createEnumConfig("enable-protected-configs", NULL, IMMUTABLE_CONFIG, protected_action_enum, server.enable_protected_configs, PROTECTED_ACTION_ALLOWED_NO, NULL, NULL),
3015 createEnumConfig("enable-debug-command", NULL, IMMUTABLE_CONFIG, protected_action_enum, server.enable_debug_cmd, PROTECTED_ACTION_ALLOWED_NO, NULL, NULL),
3016 createEnumConfig("enable-module-command", NULL, IMMUTABLE_CONFIG, protected_action_enum, server.enable_module_cmd, PROTECTED_ACTION_ALLOWED_NO, NULL, NULL),
3017 createEnumConfig("cluster-preferred-endpoint-type", NULL, MODIFIABLE_CONFIG, cluster_preferred_endpoint_type_enum, server.cluster_preferred_endpoint_type, CLUSTER_ENDPOINT_TYPE_IP, NULL, NULL),
3018 createEnumConfig("propagation-error-behavior", NULL, MODIFIABLE_CONFIG, propagation_error_behavior_enum, server.propagation_error_behavior, PROPAGATION_ERR_BEHAVIOR_IGNORE, NULL, NULL),
3019 createEnumConfig("shutdown-on-sigint", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, shutdown_on_sig_enum, server.shutdown_on_sigint, 0, isValidShutdownOnSigFlags, NULL),
3020 createEnumConfig("shutdown-on-sigterm", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, shutdown_on_sig_enum, server.shutdown_on_sigterm, 0, isValidShutdownOnSigFlags, NULL),
3021
3022 /* Integer configs */
3023 createIntConfig("databases", NULL, IMMUTABLE_CONFIG, 1, INT_MAX, server.dbnum, 16, INTEGER_CONFIG, NULL, NULL),
3024 createIntConfig("port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.port, 6379, INTEGER_CONFIG, NULL, updatePort), /* TCP port. */
3025 createIntConfig("io-threads", NULL, DEBUG_CONFIG | IMMUTABLE_CONFIG, 1, 128, server.io_threads_num, 1, INTEGER_CONFIG, NULL, NULL), /* Single threaded by default */
3026 createIntConfig("auto-aof-rewrite-percentage", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.aof_rewrite_perc, 100, INTEGER_CONFIG, NULL, NULL),
3027 createIntConfig("cluster-replica-validity-factor", "cluster-slave-validity-factor", MODIFIABLE_CONFIG, 0, INT_MAX, server.cluster_slave_validity_factor, 10, INTEGER_CONFIG, NULL, NULL), /* Slave max data age factor. */
3028 createIntConfig("list-max-listpack-size", "list-max-ziplist-size", MODIFIABLE_CONFIG, INT_MIN, INT_MAX, server.list_max_listpack_size, -2, INTEGER_CONFIG, NULL, NULL),
3029 createIntConfig("tcp-keepalive", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tcpkeepalive, 300, INTEGER_CONFIG, NULL, NULL),
3030 createIntConfig("cluster-migration-barrier", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.cluster_migration_barrier, 1, INTEGER_CONFIG, NULL, NULL),
3031 createIntConfig("active-defrag-cycle-min", NULL, MODIFIABLE_CONFIG, 1, 99, server.active_defrag_cycle_min, 1, INTEGER_CONFIG, NULL, NULL), /* Default: 1% CPU min (at lower threshold) */
3032 createIntConfig("active-defrag-cycle-max", NULL, MODIFIABLE_CONFIG, 1, 99, server.active_defrag_cycle_max, 25, INTEGER_CONFIG, NULL, NULL), /* Default: 25% CPU max (at upper threshold) */
3033 createIntConfig("active-defrag-threshold-lower", NULL, MODIFIABLE_CONFIG, 0, 1000, server.active_defrag_threshold_lower, 10, INTEGER_CONFIG, NULL, NULL), /* Default: don't defrag when fragmentation is below 10% */
3034 createIntConfig("active-defrag-threshold-upper", NULL, MODIFIABLE_CONFIG, 0, 1000, server.active_defrag_threshold_upper, 100, INTEGER_CONFIG, NULL, NULL), /* Default: maximum defrag force at 100% fragmentation */
3035 createIntConfig("lfu-log-factor", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.lfu_log_factor, 10, INTEGER_CONFIG, NULL, NULL),
3036 createIntConfig("lfu-decay-time", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.lfu_decay_time, 1, INTEGER_CONFIG, NULL, NULL),
3037 createIntConfig("replica-priority", "slave-priority", MODIFIABLE_CONFIG, 0, INT_MAX, server.slave_priority, 100, INTEGER_CONFIG, NULL, NULL),
3038 createIntConfig("repl-diskless-sync-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_diskless_sync_delay, 5, INTEGER_CONFIG, NULL, NULL),
3039 createIntConfig("maxmemory-samples", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.maxmemory_samples, 5, INTEGER_CONFIG, NULL, NULL),
3040 createIntConfig("maxmemory-eviction-tenacity", NULL, MODIFIABLE_CONFIG, 0, 100, server.maxmemory_eviction_tenacity, 10, INTEGER_CONFIG, NULL, NULL),
3041 createIntConfig("timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.maxidletime, 0, INTEGER_CONFIG, NULL, NULL), /* Default client timeout: infinite */
3042 createIntConfig("replica-announce-port", "slave-announce-port", MODIFIABLE_CONFIG, 0, 65535, server.slave_announce_port, 0, INTEGER_CONFIG, NULL, NULL),
3043 createIntConfig("tcp-backlog", NULL, IMMUTABLE_CONFIG, 0, INT_MAX, server.tcp_backlog, 511, INTEGER_CONFIG, NULL, NULL), /* TCP listen backlog. */
3044 createIntConfig("cluster-port", NULL, IMMUTABLE_CONFIG, 0, 65535, server.cluster_port, 0, INTEGER_CONFIG, NULL, NULL),
3045 createIntConfig("cluster-announce-bus-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_bus_port, 0, INTEGER_CONFIG, NULL, NULL), /* Default: Use +10000 offset. */
3046 createIntConfig("cluster-announce-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_port, 0, INTEGER_CONFIG, NULL, NULL), /* Use server.port */
3047 createIntConfig("cluster-announce-tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_tls_port, 0, INTEGER_CONFIG, NULL, NULL), /* Use server.tls_port */
3048 createIntConfig("repl-timeout", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_timeout, 60, INTEGER_CONFIG, NULL, NULL),
3049 createIntConfig("repl-ping-replica-period", "repl-ping-slave-period", MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_ping_slave_period, 10, INTEGER_CONFIG, NULL, NULL),
3050 createIntConfig("list-compress-depth", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, 0, INT_MAX, server.list_compress_depth, 0, INTEGER_CONFIG, NULL, NULL),
3051 createIntConfig("rdb-key-save-delay", NULL, MODIFIABLE_CONFIG | HIDDEN_CONFIG, INT_MIN, INT_MAX, server.rdb_key_save_delay, 0, INTEGER_CONFIG, NULL, NULL),
3052 createIntConfig("key-load-delay", NULL, MODIFIABLE_CONFIG | HIDDEN_CONFIG, INT_MIN, INT_MAX, server.key_load_delay, 0, INTEGER_CONFIG, NULL, NULL),
3053 createIntConfig("active-expire-effort", NULL, MODIFIABLE_CONFIG, 1, 10, server.active_expire_effort, 1, INTEGER_CONFIG, NULL, NULL), /* From 1 to 10. */
3054 createIntConfig("hz", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ),
3055 createIntConfig("min-replicas-to-write", "min-slaves-to-write", MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_min_slaves_to_write, 0, INTEGER_CONFIG, NULL, updateGoodSlaves),
3056 createIntConfig("min-replicas-max-lag", "min-slaves-max-lag", MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_min_slaves_max_lag, 10, INTEGER_CONFIG, NULL, updateGoodSlaves),
3057 createIntConfig("watchdog-period", NULL, MODIFIABLE_CONFIG | HIDDEN_CONFIG, 0, INT_MAX, server.watchdog_period, 0, INTEGER_CONFIG, NULL, updateWatchdogPeriod),
3058 createIntConfig("shutdown-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.shutdown_timeout, 10, INTEGER_CONFIG, NULL, NULL),
3059 createIntConfig("repl-diskless-sync-max-replicas", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_diskless_sync_max_replicas, 0, INTEGER_CONFIG, NULL, NULL),
3060
3061 /* Unsigned int configs */
3062 createUIntConfig("maxclients", NULL, MODIFIABLE_CONFIG, 1, UINT_MAX, server.maxclients, 10000, INTEGER_CONFIG, NULL, updateMaxclients),
3063 createUIntConfig("unixsocketperm", NULL, IMMUTABLE_CONFIG, 0, 0777, server.unixsocketperm, 0, OCTAL_CONFIG, NULL, NULL),
3064 createUIntConfig("socket-mark-id", NULL, IMMUTABLE_CONFIG, 0, UINT_MAX, server.socket_mark_id, 0, INTEGER_CONFIG, NULL, NULL),
3065
3066 /* Unsigned Long configs */
3067 createULongConfig("active-defrag-max-scan-fields", NULL, MODIFIABLE_CONFIG, 1, LONG_MAX, server.active_defrag_max_scan_fields, 1000, INTEGER_CONFIG, NULL, NULL), /* Default: keys with more than 1000 fields will be processed separately */
3068 createULongConfig("slowlog-max-len", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.slowlog_max_len, 128, INTEGER_CONFIG, NULL, NULL),
3069 createULongConfig("acllog-max-len", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.acllog_max_len, 128, INTEGER_CONFIG, NULL, NULL),
3070
3071 /* Long Long configs */
3072 createLongLongConfig("busy-reply-threshold", "lua-time-limit", MODIFIABLE_CONFIG, 0, LONG_MAX, server.busy_reply_threshold, 5000, INTEGER_CONFIG, NULL, NULL),/* milliseconds */
3073 createLongLongConfig("cluster-node-timeout", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.cluster_node_timeout, 15000, INTEGER_CONFIG, NULL, NULL),
3074 createLongLongConfig("slowlog-log-slower-than", NULL, MODIFIABLE_CONFIG, -1, LLONG_MAX, server.slowlog_log_slower_than, 10000, INTEGER_CONFIG, NULL, NULL),
3075 createLongLongConfig("latency-monitor-threshold", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.latency_monitor_threshold, 0, INTEGER_CONFIG, NULL, NULL),
3076 createLongLongConfig("proto-max-bulk-len", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, 1024*1024, LONG_MAX, server.proto_max_bulk_len, 512ll*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */
3077 createLongLongConfig("stream-node-max-entries", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.stream_node_max_entries, 100, INTEGER_CONFIG, NULL, NULL),
3078 createLongLongConfig("repl-backlog-size", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, server.repl_backlog_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */
3079
3080 /* Unsigned Long Long configs */
3081 createULongLongConfig("maxmemory", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),
3082 createULongLongConfig("cluster-link-sendbuf-limit", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.cluster_link_sendbuf_limit_bytes, 0, MEMORY_CONFIG, NULL, NULL),
3083
3084 /* Size_t configs */
3085 createSizeTConfig("hash-max-listpack-entries", "hash-max-ziplist-entries", MODIFIABLE_CONFIG, 0, LONG_MAX, server.hash_max_listpack_entries, 512, INTEGER_CONFIG, NULL, NULL),
3086 createSizeTConfig("set-max-intset-entries", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.set_max_intset_entries, 512, INTEGER_CONFIG, NULL, NULL),
3087 createSizeTConfig("zset-max-listpack-entries", "zset-max-ziplist-entries", MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_listpack_entries, 128, INTEGER_CONFIG, NULL, NULL),
3088 createSizeTConfig("active-defrag-ignore-bytes", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, server.active_defrag_ignore_bytes, 100<<20, MEMORY_CONFIG, NULL, NULL), /* Default: don't defrag if frag overhead is below 100mb */
3089 createSizeTConfig("hash-max-listpack-value", "hash-max-ziplist-value", MODIFIABLE_CONFIG, 0, LONG_MAX, server.hash_max_listpack_value, 64, MEMORY_CONFIG, NULL, NULL),
3090 createSizeTConfig("stream-node-max-bytes", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.stream_node_max_bytes, 4096, MEMORY_CONFIG, NULL, NULL),
3091 createSizeTConfig("zset-max-listpack-value", "zset-max-ziplist-value", MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_listpack_value, 64, MEMORY_CONFIG, NULL, NULL),
3092 createSizeTConfig("hll-sparse-max-bytes", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hll_sparse_max_bytes, 3000, MEMORY_CONFIG, NULL, NULL),
3093 createSizeTConfig("tracking-table-max-keys", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.tracking_table_max_keys, 1000000, INTEGER_CONFIG, NULL, NULL), /* Default: 1 million keys max. */
3094 createSizeTConfig("client-query-buffer-limit", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, 1024*1024, LONG_MAX, server.client_max_querybuf_len, 1024*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Default: 1GB max query buffer. */
3095 createSSizeTConfig("maxmemory-clients", NULL, MODIFIABLE_CONFIG, -100, SSIZE_MAX, server.maxmemory_clients, 0, MEMORY_CONFIG | PERCENT_CONFIG, NULL, NULL),
3096
3097 /* Other configs */
3098 createTimeTConfig("repl-backlog-ttl", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.repl_backlog_time_limit, 60*60, INTEGER_CONFIG, NULL, NULL), /* Default: 1 hour */
3099 createOffTConfig("auto-aof-rewrite-min-size", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.aof_rewrite_min_size, 64*1024*1024, MEMORY_CONFIG, NULL, NULL),
3100 createOffTConfig("loading-process-events-interval-bytes", NULL, MODIFIABLE_CONFIG | HIDDEN_CONFIG, 1024, INT_MAX, server.loading_process_events_interval_bytes, 1024*1024*2, INTEGER_CONFIG, NULL, NULL),
3101
3102#ifdef USE_OPENSSL
3103 createIntConfig("tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, applyTLSPort), /* TCP port. */
3104 createIntConfig("tls-session-cache-size", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_size, 20*1024, INTEGER_CONFIG, NULL, applyTlsCfg),
3105 createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, applyTlsCfg),
3106 createBoolConfig("tls-cluster", NULL, MODIFIABLE_CONFIG, server.tls_cluster, 0, NULL, applyTlsCfg),
3107 createBoolConfig("tls-replication", NULL, MODIFIABLE_CONFIG, server.tls_replication, 0, NULL, applyTlsCfg),
3108 createEnumConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, tls_auth_clients_enum, server.tls_auth_clients, TLS_CLIENT_AUTH_YES, NULL, NULL),
3109 createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.prefer_server_ciphers, 0, NULL, applyTlsCfg),
3110 createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.session_caching, 1, NULL, applyTlsCfg),
3111 createStringConfig("tls-cert-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, applyTlsCfg),
3112 createStringConfig("tls-key-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, applyTlsCfg),
3113 createStringConfig("tls-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file_pass, NULL, NULL, applyTlsCfg),
3114 createStringConfig("tls-client-cert-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_cert_file, NULL, NULL, applyTlsCfg),
3115 createStringConfig("tls-client-key-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file, NULL, NULL, applyTlsCfg),
3116 createStringConfig("tls-client-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file_pass, NULL, NULL, applyTlsCfg),
3117 createStringConfig("tls-dh-params-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, applyTlsCfg),
3118 createStringConfig("tls-ca-cert-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, applyTlsCfg),
3119 createStringConfig("tls-ca-cert-dir", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, applyTlsCfg),
3120 createStringConfig("tls-protocols", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.protocols, NULL, NULL, applyTlsCfg),
3121 createStringConfig("tls-ciphers", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphers, NULL, NULL, applyTlsCfg),
3122 createStringConfig("tls-ciphersuites", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphersuites, NULL, NULL, applyTlsCfg),
3123#endif
3124
3125 /* Special configs */
3126 createSpecialConfig("dir", NULL, MODIFIABLE_CONFIG | PROTECTED_CONFIG | DENY_LOADING_CONFIG, setConfigDirOption, getConfigDirOption, rewriteConfigDirOption, NULL),
3127 createSpecialConfig("save", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigSaveOption, getConfigSaveOption, rewriteConfigSaveOption, NULL),
3128 createSpecialConfig("client-output-buffer-limit", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigClientOutputBufferLimitOption, getConfigClientOutputBufferLimitOption, rewriteConfigClientOutputBufferLimitOption, NULL),
3129 createSpecialConfig("oom-score-adj-values", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigOOMScoreAdjValuesOption, getConfigOOMScoreAdjValuesOption, rewriteConfigOOMScoreAdjValuesOption, updateOOMScoreAdj),
3130 createSpecialConfig("notify-keyspace-events", NULL, MODIFIABLE_CONFIG, setConfigNotifyKeyspaceEventsOption, getConfigNotifyKeyspaceEventsOption, rewriteConfigNotifyKeyspaceEventsOption, NULL),
3131 createSpecialConfig("bind", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigBindOption, getConfigBindOption, rewriteConfigBindOption, applyBind),
3132 createSpecialConfig("replicaof", "slaveof", IMMUTABLE_CONFIG | MULTI_ARG_CONFIG, setConfigReplicaOfOption, getConfigReplicaOfOption, rewriteConfigReplicaOfOption, NULL),
3133 createSpecialConfig("latency-tracking-info-percentiles", NULL, MODIFIABLE_CONFIG | MULTI_ARG_CONFIG, setConfigLatencyTrackingInfoPercentilesOutputOption, getConfigLatencyTrackingInfoPercentilesOutputOption, rewriteConfigLatencyTrackingInfoPercentilesOutputOption, NULL),
3134
3135 /* NULL Terminator, this is dropped when we convert to the runtime array. */
3136 {NULL}
3137};
3138
3139/* Create a new config by copying the passed in config. Returns 1 on success
3140 * or 0 when their was already a config with the same name.. */
3141int registerConfigValue(const char *name, const standardConfig *config, int alias) {
3142 standardConfig *new = zmalloc(sizeof(standardConfig));
3143 memcpy(new, config, sizeof(standardConfig));
3144 if (alias) {
3145 new->flags |= ALIAS_CONFIG;
3146 new->name = config->alias;
3147 new->alias = config->name;
3148 }
3149
3150 return dictAdd(configs, sdsnew(name), new) == DICT_OK;
3151}
3152
3153/* Initialize configs to their default values and create and populate the
3154 * runtime configuration dictionary. */
3155void initConfigValues() {
3156 configs = dictCreate(&sdsHashDictType);
3157 dictExpand(configs, sizeof(static_configs) / sizeof(standardConfig));
3158 for (standardConfig *config = static_configs; config->name != NULL; config++) {
3159 if (config->interface.init) config->interface.init(config);
3160 /* Add the primary config to the dictionary. */
3161 int ret = registerConfigValue(config->name, config, 0);
3162 serverAssert(ret);
3163
3164 /* Aliases are the same as their primary counter parts, but they
3165 * also have a flag indicating they are the alias. */
3166 if (config->alias) {
3167 int ret = registerConfigValue(config->alias, config, ALIAS_CONFIG);
3168 serverAssert(ret);
3169 }
3170 }
3171}
3172
3173/* Remove a config by name from the configs dict. */
3174void removeConfig(sds name) {
3175 standardConfig *config = lookupConfig(name);
3176 if (!config) return;
3177 if (config->flags & MODULE_CONFIG) {
3178 sdsfree((sds) config->name);
3179 if (config->type == ENUM_CONFIG) {
3180 configEnum *enumNode = config->data.enumd.enum_value;
3181 while(enumNode->name != NULL) {
3182 zfree(enumNode->name);
3183 enumNode++;
3184 }
3185 zfree(config->data.enumd.enum_value);
3186 } else if (config->type == SDS_CONFIG) {
3187 if (config->data.sds.default_value) sdsfree((sds)config->data.sds.default_value);
3188 }
3189 }
3190 dictDelete(configs, name);
3191}
3192
3193/*-----------------------------------------------------------------------------
3194 * Module Config
3195 *----------------------------------------------------------------------------*/
3196
3197/* Create a bool/string/enum/numeric standardConfig for a module config in the configs dictionary */
3198void addModuleBoolConfig(const char *module_name, const char *name, int flags, void *privdata, int default_val) {
3199 sds config_name = sdscatfmt(sdsempty(), "%s.%s", module_name, name);
3200 int config_dummy_address;
3201 standardConfig module_config = createBoolConfig(config_name, NULL, flags | MODULE_CONFIG, config_dummy_address, default_val, NULL, NULL);
3202 module_config.data.yesno.config = NULL;
3203 module_config.privdata = privdata;
3204 registerConfigValue(config_name, &module_config, 0);
3205}
3206
3207void addModuleStringConfig(const char *module_name, const char *name, int flags, void *privdata, sds default_val) {
3208 sds config_name = sdscatfmt(sdsempty(), "%s.%s", module_name, name);
3209 sds config_dummy_address;
3210 standardConfig module_config = createSDSConfig(config_name, NULL, flags | MODULE_CONFIG, 0, config_dummy_address, default_val, NULL, NULL);
3211 module_config.data.sds.config = NULL;
3212 module_config.privdata = privdata;
3213 registerConfigValue(config_name, &module_config, 0);
3214}
3215
3216void addModuleEnumConfig(const char *module_name, const char *name, int flags, void *privdata, int default_val, configEnum *enum_vals) {
3217 sds config_name = sdscatfmt(sdsempty(), "%s.%s", module_name, name);
3218 int config_dummy_address;
3219 standardConfig module_config = createEnumConfig(config_name, NULL, flags | MODULE_CONFIG, enum_vals, config_dummy_address, default_val, NULL, NULL);
3220 module_config.data.enumd.config = NULL;
3221 module_config.privdata = privdata;
3222 registerConfigValue(config_name, &module_config, 0);
3223}
3224
3225void 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) {
3226 sds config_name = sdscatfmt(sdsempty(), "%s.%s", module_name, name);
3227 long long config_dummy_address;
3228 standardConfig module_config = createLongLongConfig(config_name, NULL, flags | MODULE_CONFIG, lower, upper, config_dummy_address, default_val, conf_flags, NULL, NULL);
3229 module_config.data.numeric.config.ll = NULL;
3230 module_config.privdata = privdata;
3231 registerConfigValue(config_name, &module_config, 0);
3232}
3233
3234/*-----------------------------------------------------------------------------
3235 * CONFIG HELP
3236 *----------------------------------------------------------------------------*/
3237
3238void configHelpCommand(client *c) {
3239 const char *help[] = {
3240"GET <pattern>",
3241" Return parameters matching the glob-like <pattern> and their values.",
3242"SET <directive> <value>",
3243" Set the configuration <directive> to <value>.",
3244"RESETSTAT",
3245" Reset statistics reported by the INFO command.",
3246"REWRITE",
3247" Rewrite the configuration file.",
3248NULL
3249 };
3250
3251 addReplyHelp(c, help);
3252}
3253
3254/*-----------------------------------------------------------------------------
3255 * CONFIG RESETSTAT
3256 *----------------------------------------------------------------------------*/
3257
3258void configResetStatCommand(client *c) {
3259 resetServerStats();
3260 resetCommandTableStats(server.commands);
3261 resetErrorTableStats();
3262 addReply(c,shared.ok);
3263}
3264
3265/*-----------------------------------------------------------------------------
3266 * CONFIG REWRITE
3267 *----------------------------------------------------------------------------*/
3268
3269void configRewriteCommand(client *c) {
3270 if (server.configfile == NULL) {
3271 addReplyError(c,"The server is running without a config file");
3272 return;
3273 }
3274 if (rewriteConfig(server.configfile, 0) == -1) {
3275 /* save errno in case of being tainted. */
3276 int err = errno;
3277 serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(err));
3278 addReplyErrorFormat(c,"Rewriting config file: %s", strerror(err));
3279 } else {
3280 serverLog(LL_WARNING,"CONFIG REWRITE executed with success.");
3281 addReply(c,shared.ok);
3282 }
3283}
3284