"Fossies" - the Fresh Open Source Software Archive

Member "redis-6.0.8/src/config.c" (10 Sep 2020, 102445 Bytes) of package /linux/misc/redis-6.0.8.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "config.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.0.7_vs_6.0.8.

    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 
   37 /*-----------------------------------------------------------------------------
   38  * Config file name-value maps.
   39  *----------------------------------------------------------------------------*/
   40 
   41 typedef struct configEnum {
   42     const char *name;
   43     const int val;
   44 } configEnum;
   45 
   46 configEnum maxmemory_policy_enum[] = {
   47     {"volatile-lru", MAXMEMORY_VOLATILE_LRU},
   48     {"volatile-lfu", MAXMEMORY_VOLATILE_LFU},
   49     {"volatile-random",MAXMEMORY_VOLATILE_RANDOM},
   50     {"volatile-ttl",MAXMEMORY_VOLATILE_TTL},
   51     {"allkeys-lru",MAXMEMORY_ALLKEYS_LRU},
   52     {"allkeys-lfu",MAXMEMORY_ALLKEYS_LFU},
   53     {"allkeys-random",MAXMEMORY_ALLKEYS_RANDOM},
   54     {"noeviction",MAXMEMORY_NO_EVICTION},
   55     {NULL, 0}
   56 };
   57 
   58 configEnum syslog_facility_enum[] = {
   59     {"user",    LOG_USER},
   60     {"local0",  LOG_LOCAL0},
   61     {"local1",  LOG_LOCAL1},
   62     {"local2",  LOG_LOCAL2},
   63     {"local3",  LOG_LOCAL3},
   64     {"local4",  LOG_LOCAL4},
   65     {"local5",  LOG_LOCAL5},
   66     {"local6",  LOG_LOCAL6},
   67     {"local7",  LOG_LOCAL7},
   68     {NULL, 0}
   69 };
   70 
   71 configEnum loglevel_enum[] = {
   72     {"debug", LL_DEBUG},
   73     {"verbose", LL_VERBOSE},
   74     {"notice", LL_NOTICE},
   75     {"warning", LL_WARNING},
   76     {NULL,0}
   77 };
   78 
   79 configEnum supervised_mode_enum[] = {
   80     {"upstart", SUPERVISED_UPSTART},
   81     {"systemd", SUPERVISED_SYSTEMD},
   82     {"auto", SUPERVISED_AUTODETECT},
   83     {"no", SUPERVISED_NONE},
   84     {NULL, 0}
   85 };
   86 
   87 configEnum aof_fsync_enum[] = {
   88     {"everysec", AOF_FSYNC_EVERYSEC},
   89     {"always", AOF_FSYNC_ALWAYS},
   90     {"no", AOF_FSYNC_NO},
   91     {NULL, 0}
   92 };
   93 
   94 configEnum repl_diskless_load_enum[] = {
   95     {"disabled", REPL_DISKLESS_LOAD_DISABLED},
   96     {"on-empty-db", REPL_DISKLESS_LOAD_WHEN_DB_EMPTY},
   97     {"swapdb", REPL_DISKLESS_LOAD_SWAPDB},
   98     {NULL, 0}
   99 };
  100 
  101 configEnum tls_auth_clients_enum[] = {
  102     {"no", TLS_CLIENT_AUTH_NO},
  103     {"yes", TLS_CLIENT_AUTH_YES},
  104     {"optional", TLS_CLIENT_AUTH_OPTIONAL},
  105     {NULL, 0}
  106 };
  107 /* Output buffer limits presets. */
  108 clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
  109     {0, 0, 0}, /* normal */
  110     {1024*1024*256, 1024*1024*64, 60}, /* slave */
  111     {1024*1024*32, 1024*1024*8, 60}  /* pubsub */
  112 };
  113 
  114 /* OOM Score defaults */
  115 int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT] = { 0, 200, 800 };
  116 
  117 /* Generic config infrastructure function pointers
  118  * int is_valid_fn(val, err)
  119  *     Return 1 when val is valid, and 0 when invalid.
  120  *     Optionally set err to a static error string.
  121  * int update_fn(val, prev, err)
  122  *     This function is called only for CONFIG SET command (not at config file parsing)
  123  *     It is called after the actual config is applied,
  124  *     Return 1 for success, and 0 for failure.
  125  *     Optionally set err to a static error string.
  126  *     On failure the config change will be reverted.
  127  */
  128 
  129 /* Configuration values that require no special handling to set, get, load or
  130  * rewrite. */
  131 typedef struct boolConfigData {
  132     int *config; /* The pointer to the server config this value is stored in */
  133     const int default_value; /* The default value of the config on rewrite */
  134     int (*is_valid_fn)(int val, char **err); /* Optional function to check validity of new value (generic doc above) */
  135     int (*update_fn)(int val, int prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */
  136 } boolConfigData;
  137 
  138 typedef struct stringConfigData {
  139     char **config; /* Pointer to the server config this value is stored in. */
  140     const char *default_value; /* Default value of the config on rewrite. */
  141     int (*is_valid_fn)(char* val, char **err); /* Optional function to check validity of new value (generic doc above) */
  142     int (*update_fn)(char* val, char* prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */
  143     int convert_empty_to_null; /* Boolean indicating if empty strings should
  144                                   be stored as a NULL value. */
  145 } stringConfigData;
  146 
  147 typedef struct enumConfigData {
  148     int *config; /* The pointer to the server config this value is stored in */
  149     configEnum *enum_value; /* The underlying enum type this data represents */
  150     const int default_value; /* The default value of the config on rewrite */
  151     int (*is_valid_fn)(int val, char **err); /* Optional function to check validity of new value (generic doc above) */
  152     int (*update_fn)(int val, int prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */
  153 } enumConfigData;
  154 
  155 typedef enum numericType {
  156     NUMERIC_TYPE_INT,
  157     NUMERIC_TYPE_UINT,
  158     NUMERIC_TYPE_LONG,
  159     NUMERIC_TYPE_ULONG,
  160     NUMERIC_TYPE_LONG_LONG,
  161     NUMERIC_TYPE_ULONG_LONG,
  162     NUMERIC_TYPE_SIZE_T,
  163     NUMERIC_TYPE_SSIZE_T,
  164     NUMERIC_TYPE_OFF_T,
  165     NUMERIC_TYPE_TIME_T,
  166 } numericType;
  167 
  168 typedef struct numericConfigData {
  169     union {
  170         int *i;
  171         unsigned int *ui;
  172         long *l;
  173         unsigned long *ul;
  174         long long *ll;
  175         unsigned long long *ull;
  176         size_t *st;
  177         ssize_t *sst;
  178         off_t *ot;
  179         time_t *tt;
  180     } config; /* The pointer to the numeric config this value is stored in */
  181     int is_memory; /* Indicates if this value can be loaded as a memory value */
  182     numericType numeric_type; /* An enum indicating the type of this value */
  183     long long lower_bound; /* The lower bound of this numeric value */
  184     long long upper_bound; /* The upper bound of this numeric value */
  185     const long long default_value; /* The default value of the config on rewrite */
  186     int (*is_valid_fn)(long long val, char **err); /* Optional function to check validity of new value (generic doc above) */
  187     int (*update_fn)(long long val, long long prev, char **err); /* Optional function to apply new value at runtime (generic doc above) */
  188 } numericConfigData;
  189 
  190 typedef union typeData {
  191     boolConfigData yesno;
  192     stringConfigData string;
  193     enumConfigData enumd;
  194     numericConfigData numeric;
  195 } typeData;
  196 
  197 typedef struct typeInterface {
  198     /* Called on server start, to init the server with default value */
  199     void (*init)(typeData data);
  200     /* Called on server start, should return 1 on success, 0 on error and should set err */
  201     int (*load)(typeData data, sds *argc, int argv, char **err);
  202     /* Called on server startup and CONFIG SET, returns 1 on success, 0 on error
  203      * and can set a verbose err string, update is true when called from CONFIG SET */
  204     int (*set)(typeData data, sds value, int update, char **err);
  205     /* Called on CONFIG GET, required to add output to the client */
  206     void (*get)(client *c, typeData data);
  207     /* Called on CONFIG REWRITE, required to rewrite the config state */
  208     void (*rewrite)(typeData data, const char *name, struct rewriteConfigState *state);
  209 } typeInterface;
  210 
  211 typedef struct standardConfig {
  212     const char *name; /* The user visible name of this config */
  213     const char *alias; /* An alias that can also be used for this config */
  214     const int modifiable; /* Can this value be updated by CONFIG SET? */
  215     typeInterface interface; /* The function pointers that define the type interface */
  216     typeData data; /* The type specific data exposed used by the interface */
  217 } standardConfig;
  218 
  219 standardConfig configs[];
  220 
  221 /*-----------------------------------------------------------------------------
  222  * Enum access functions
  223  *----------------------------------------------------------------------------*/
  224 
  225 /* Get enum value from name. If there is no match INT_MIN is returned. */
  226 int configEnumGetValue(configEnum *ce, char *name) {
  227     while(ce->name != NULL) {
  228         if (!strcasecmp(ce->name,name)) return ce->val;
  229         ce++;
  230     }
  231     return INT_MIN;
  232 }
  233 
  234 /* Get enum name from value. If no match is found NULL is returned. */
  235 const char *configEnumGetName(configEnum *ce, int val) {
  236     while(ce->name != NULL) {
  237         if (ce->val == val) return ce->name;
  238         ce++;
  239     }
  240     return NULL;
  241 }
  242 
  243 /* Wrapper for configEnumGetName() returning "unknown" instead of NULL if
  244  * there is no match. */
  245 const char *configEnumGetNameOrUnknown(configEnum *ce, int val) {
  246     const char *name = configEnumGetName(ce,val);
  247     return name ? name : "unknown";
  248 }
  249 
  250 /* Used for INFO generation. */
  251 const char *evictPolicyToString(void) {
  252     return configEnumGetNameOrUnknown(maxmemory_policy_enum,server.maxmemory_policy);
  253 }
  254 
  255 /*-----------------------------------------------------------------------------
  256  * Config file parsing
  257  *----------------------------------------------------------------------------*/
  258 
  259 int yesnotoi(char *s) {
  260     if (!strcasecmp(s,"yes")) return 1;
  261     else if (!strcasecmp(s,"no")) return 0;
  262     else return -1;
  263 }
  264 
  265 void appendServerSaveParams(time_t seconds, int changes) {
  266     server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));
  267     server.saveparams[server.saveparamslen].seconds = seconds;
  268     server.saveparams[server.saveparamslen].changes = changes;
  269     server.saveparamslen++;
  270 }
  271 
  272 void resetServerSaveParams(void) {
  273     zfree(server.saveparams);
  274     server.saveparams = NULL;
  275     server.saveparamslen = 0;
  276 }
  277 
  278 void queueLoadModule(sds path, sds *argv, int argc) {
  279     int i;
  280     struct moduleLoadQueueEntry *loadmod;
  281 
  282     loadmod = zmalloc(sizeof(struct moduleLoadQueueEntry));
  283     loadmod->argv = zmalloc(sizeof(robj*)*argc);
  284     loadmod->path = sdsnew(path);
  285     loadmod->argc = argc;
  286     for (i = 0; i < argc; i++) {
  287         loadmod->argv[i] = createRawStringObject(argv[i],sdslen(argv[i]));
  288     }
  289     listAddNodeTail(server.loadmodule_queue,loadmod);
  290 }
  291 
  292 /* Parse an array of CONFIG_OOM_COUNT sds strings, validate and populate
  293  * server.oom_score_adj_values if valid.
  294  */
  295 
  296 static int updateOOMScoreAdjValues(sds *args, char **err) {
  297     int i;
  298     int values[CONFIG_OOM_COUNT];
  299 
  300     for (i = 0; i < CONFIG_OOM_COUNT; i++) {
  301         char *eptr;
  302         long long val = strtoll(args[i], &eptr, 10);
  303 
  304         if (*eptr != '\0' || val < -1000 || val > 1000) {
  305             if (err) *err = "Invalid oom-score-adj-values, elements must be between -1000 and 1000.";
  306             return C_ERR;
  307         }
  308 
  309         values[i] = val;
  310     }
  311 
  312     /* Verify that the values make sense. If they don't omit a warning but
  313      * keep the configuration, which may still be valid for privileged processes.
  314      */
  315 
  316     if (values[CONFIG_OOM_REPLICA] < values[CONFIG_OOM_MASTER] ||
  317         values[CONFIG_OOM_BGCHILD] < values[CONFIG_OOM_REPLICA]) {
  318             serverLog(LOG_WARNING,
  319                     "The oom-score-adj-values configuration may not work for non-privileged processes! "
  320                     "Please consult the documentation.");
  321     }
  322 
  323     /* Store values, retain previous config for rollback in case we fail. */
  324     int old_values[CONFIG_OOM_COUNT];
  325     for (i = 0; i < CONFIG_OOM_COUNT; i++) {
  326         old_values[i] = server.oom_score_adj_values[i];
  327         server.oom_score_adj_values[i] = values[i];
  328     }
  329 
  330     /* Update */
  331     if (setOOMScoreAdj(-1) == C_ERR) {
  332         /* Roll back */
  333         for (i = 0; i < CONFIG_OOM_COUNT; i++)
  334             server.oom_score_adj_values[i] = old_values[i];
  335 
  336         if (err)
  337             *err = "Failed to apply oom-score-adj-values configuration, check server logs.";
  338 
  339         return C_ERR;
  340     }
  341 
  342     return C_OK;
  343 }
  344 
  345 void initConfigValues() {
  346     for (standardConfig *config = configs; config->name != NULL; config++) {
  347         config->interface.init(config->data);
  348     }
  349 }
  350 
  351 void loadServerConfigFromString(char *config) {
  352     char *err = NULL;
  353     int linenum = 0, totlines, i;
  354     int slaveof_linenum = 0;
  355     sds *lines;
  356 
  357     lines = sdssplitlen(config,strlen(config),"\n",1,&totlines);
  358 
  359     for (i = 0; i < totlines; i++) {
  360         sds *argv;
  361         int argc;
  362 
  363         linenum = i+1;
  364         lines[i] = sdstrim(lines[i]," \t\r\n");
  365 
  366         /* Skip comments and blank lines */
  367         if (lines[i][0] == '#' || lines[i][0] == '\0') continue;
  368 
  369         /* Split into arguments */
  370         argv = sdssplitargs(lines[i],&argc);
  371         if (argv == NULL) {
  372             err = "Unbalanced quotes in configuration line";
  373             goto loaderr;
  374         }
  375 
  376         /* Skip this line if the resulting command vector is empty. */
  377         if (argc == 0) {
  378             sdsfreesplitres(argv,argc);
  379             continue;
  380         }
  381         sdstolower(argv[0]);
  382 
  383         /* Iterate the configs that are standard */
  384         int match = 0;
  385         for (standardConfig *config = configs; config->name != NULL; config++) {
  386             if ((!strcasecmp(argv[0],config->name) ||
  387                 (config->alias && !strcasecmp(argv[0],config->alias))))
  388             {
  389                 if (argc != 2) {
  390                     err = "wrong number of arguments";
  391                     goto loaderr;
  392                 }
  393                 if (!config->interface.set(config->data, argv[1], 0, &err)) {
  394                     goto loaderr;
  395                 }
  396 
  397                 match = 1;
  398                 break;
  399             }
  400         }
  401 
  402         if (match) {
  403             sdsfreesplitres(argv,argc);
  404             continue;
  405         }
  406 
  407         /* Execute config directives */
  408         if (!strcasecmp(argv[0],"bind") && argc >= 2) {
  409             int j, addresses = argc-1;
  410 
  411             if (addresses > CONFIG_BINDADDR_MAX) {
  412                 err = "Too many bind addresses specified"; goto loaderr;
  413             }
  414             /* Free old bind addresses */
  415             for (j = 0; j < server.bindaddr_count; j++) {
  416                 zfree(server.bindaddr[j]);
  417             }
  418             for (j = 0; j < addresses; j++)
  419                 server.bindaddr[j] = zstrdup(argv[j+1]);
  420             server.bindaddr_count = addresses;
  421         } else if (!strcasecmp(argv[0],"unixsocketperm") && argc == 2) {
  422             errno = 0;
  423             server.unixsocketperm = (mode_t)strtol(argv[1], NULL, 8);
  424             if (errno || server.unixsocketperm > 0777) {
  425                 err = "Invalid socket file permissions"; goto loaderr;
  426             }
  427         } else if (!strcasecmp(argv[0],"save")) {
  428             if (argc == 3) {
  429                 int seconds = atoi(argv[1]);
  430                 int changes = atoi(argv[2]);
  431                 if (seconds < 1 || changes < 0) {
  432                     err = "Invalid save parameters"; goto loaderr;
  433                 }
  434                 appendServerSaveParams(seconds,changes);
  435             } else if (argc == 2 && !strcasecmp(argv[1],"")) {
  436                 resetServerSaveParams();
  437             }
  438         } else if (!strcasecmp(argv[0],"dir") && argc == 2) {
  439             if (chdir(argv[1]) == -1) {
  440                 serverLog(LL_WARNING,"Can't chdir to '%s': %s",
  441                     argv[1], strerror(errno));
  442                 exit(1);
  443             }
  444         } else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
  445             FILE *logfp;
  446 
  447             zfree(server.logfile);
  448             server.logfile = zstrdup(argv[1]);
  449             if (server.logfile[0] != '\0') {
  450                 /* Test if we are able to open the file. The server will not
  451                  * be able to abort just for this problem later... */
  452                 logfp = fopen(server.logfile,"a");
  453                 if (logfp == NULL) {
  454                     err = sdscatprintf(sdsempty(),
  455                         "Can't open the log file: %s", strerror(errno));
  456                     goto loaderr;
  457                 }
  458                 fclose(logfp);
  459             }
  460         } else if (!strcasecmp(argv[0],"include") && argc == 2) {
  461             loadServerConfig(argv[1],NULL);
  462         } else if ((!strcasecmp(argv[0],"client-query-buffer-limit")) && argc == 2) {
  463              server.client_max_querybuf_len = memtoll(argv[1],NULL);
  464         } else if ((!strcasecmp(argv[0],"slaveof") ||
  465                     !strcasecmp(argv[0],"replicaof")) && argc == 3) {
  466             slaveof_linenum = linenum;
  467             server.masterhost = sdsnew(argv[1]);
  468             server.masterport = atoi(argv[2]);
  469             server.repl_state = REPL_STATE_CONNECT;
  470         } else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
  471             if (strlen(argv[1]) > CONFIG_AUTHPASS_MAX_LEN) {
  472                 err = "Password is longer than CONFIG_AUTHPASS_MAX_LEN";
  473                 goto loaderr;
  474             }
  475             /* The old "requirepass" directive just translates to setting
  476              * a password to the default user. The only thing we do
  477              * additionally is to remember the cleartext password in this
  478              * case, for backward compatibility with Redis <= 5. */
  479             ACLSetUser(DefaultUser,"resetpass",-1);
  480             sds aclop = sdscatprintf(sdsempty(),">%s",argv[1]);
  481             ACLSetUser(DefaultUser,aclop,sdslen(aclop));
  482             sdsfree(aclop);
  483             sdsfree(server.requirepass);
  484             server.requirepass = sdsnew(argv[1]);
  485         } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
  486             /* DEAD OPTION */
  487         } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
  488             /* DEAD OPTION */
  489         } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) {
  490             struct redisCommand *cmd = lookupCommand(argv[1]);
  491             int retval;
  492 
  493             if (!cmd) {
  494                 err = "No such command in rename-command";
  495                 goto loaderr;
  496             }
  497 
  498             /* If the target command name is the empty string we just
  499              * remove it from the command table. */
  500             retval = dictDelete(server.commands, argv[1]);
  501             serverAssert(retval == DICT_OK);
  502 
  503             /* Otherwise we re-add the command under a different name. */
  504             if (sdslen(argv[2]) != 0) {
  505                 sds copy = sdsdup(argv[2]);
  506 
  507                 retval = dictAdd(server.commands, copy, cmd);
  508                 if (retval != DICT_OK) {
  509                     sdsfree(copy);
  510                     err = "Target command name already exists"; goto loaderr;
  511                 }
  512             }
  513         } else if (!strcasecmp(argv[0],"cluster-config-file") && argc == 2) {
  514             zfree(server.cluster_configfile);
  515             server.cluster_configfile = zstrdup(argv[1]);
  516         } else if (!strcasecmp(argv[0],"client-output-buffer-limit") &&
  517                    argc == 5)
  518         {
  519             int class = getClientTypeByName(argv[1]);
  520             unsigned long long hard, soft;
  521             int soft_seconds;
  522 
  523             if (class == -1 || class == CLIENT_TYPE_MASTER) {
  524                 err = "Unrecognized client limit class: the user specified "
  525                 "an invalid one, or 'master' which has no buffer limits.";
  526                 goto loaderr;
  527             }
  528             hard = memtoll(argv[2],NULL);
  529             soft = memtoll(argv[3],NULL);
  530             soft_seconds = atoi(argv[4]);
  531             if (soft_seconds < 0) {
  532                 err = "Negative number of seconds in soft limit is invalid";
  533                 goto loaderr;
  534             }
  535             server.client_obuf_limits[class].hard_limit_bytes = hard;
  536             server.client_obuf_limits[class].soft_limit_bytes = soft;
  537             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
  538         } else if (!strcasecmp(argv[0],"oom-score-adj-values") && argc == 1 + CONFIG_OOM_COUNT) {
  539             if (updateOOMScoreAdjValues(&argv[1], &err) == C_ERR) goto loaderr;
  540         } else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) {
  541             int flags = keyspaceEventsStringToFlags(argv[1]);
  542 
  543             if (flags == -1) {
  544                 err = "Invalid event class character. Use 'g$lshzxeA'.";
  545                 goto loaderr;
  546             }
  547             server.notify_keyspace_events = flags;
  548         } else if (!strcasecmp(argv[0],"user") && argc >= 2) {
  549             int argc_err;
  550             if (ACLAppendUserForLoading(argv,argc,&argc_err) == C_ERR) {
  551                 char buf[1024];
  552                 char *errmsg = ACLSetUserStringError();
  553                 snprintf(buf,sizeof(buf),"Error in user declaration '%s': %s",
  554                     argv[argc_err],errmsg);
  555                 err = buf;
  556                 goto loaderr;
  557             }
  558         } else if (!strcasecmp(argv[0],"loadmodule") && argc >= 2) {
  559             queueLoadModule(argv[1],&argv[2],argc-2);
  560         } else if (!strcasecmp(argv[0],"sentinel")) {
  561             /* argc == 1 is handled by main() as we need to enter the sentinel
  562              * mode ASAP. */
  563             if (argc != 1) {
  564                 if (!server.sentinel_mode) {
  565                     err = "sentinel directive while not in sentinel mode";
  566                     goto loaderr;
  567                 }
  568                 err = sentinelHandleConfiguration(argv+1,argc-1);
  569                 if (err) goto loaderr;
  570             }
  571         } else {
  572             err = "Bad directive or wrong number of arguments"; goto loaderr;
  573         }
  574         sdsfreesplitres(argv,argc);
  575     }
  576 
  577     /* Sanity checks. */
  578     if (server.cluster_enabled && server.masterhost) {
  579         linenum = slaveof_linenum;
  580         i = linenum-1;
  581         err = "replicaof directive not allowed in cluster mode";
  582         goto loaderr;
  583     }
  584 
  585     sdsfreesplitres(lines,totlines);
  586     return;
  587 
  588 loaderr:
  589     fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR (Redis %s) ***\n",
  590         REDIS_VERSION);
  591     fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
  592     fprintf(stderr, ">>> '%s'\n", lines[i]);
  593     fprintf(stderr, "%s\n", err);
  594     exit(1);
  595 }
  596 
  597 /* Load the server configuration from the specified filename.
  598  * The function appends the additional configuration directives stored
  599  * in the 'options' string to the config file before loading.
  600  *
  601  * Both filename and options can be NULL, in such a case are considered
  602  * empty. This way loadServerConfig can be used to just load a file or
  603  * just load a string. */
  604 void loadServerConfig(char *filename, char *options) {
  605     sds config = sdsempty();
  606     char buf[CONFIG_MAX_LINE+1];
  607 
  608     /* Load the file content */
  609     if (filename) {
  610         FILE *fp;
  611 
  612         if (filename[0] == '-' && filename[1] == '\0') {
  613             fp = stdin;
  614         } else {
  615             if ((fp = fopen(filename,"r")) == NULL) {
  616                 serverLog(LL_WARNING,
  617                     "Fatal error, can't open config file '%s': %s",
  618                     filename, strerror(errno));
  619                 exit(1);
  620             }
  621         }
  622         while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL)
  623             config = sdscat(config,buf);
  624         if (fp != stdin) fclose(fp);
  625     }
  626     /* Append the additional options */
  627     if (options) {
  628         config = sdscat(config,"\n");
  629         config = sdscat(config,options);
  630     }
  631     loadServerConfigFromString(config);
  632     sdsfree(config);
  633 }
  634 
  635 /*-----------------------------------------------------------------------------
  636  * CONFIG SET implementation
  637  *----------------------------------------------------------------------------*/
  638 
  639 #define config_set_bool_field(_name,_var) \
  640     } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \
  641         int yn = yesnotoi(o->ptr); \
  642         if (yn == -1) goto badfmt; \
  643         _var = yn;
  644 
  645 #define config_set_numerical_field(_name,_var,min,max) \
  646     } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \
  647         if (getLongLongFromObject(o,&ll) == C_ERR) goto badfmt; \
  648         if (min != LLONG_MIN && ll < min) goto badfmt; \
  649         if (max != LLONG_MAX && ll > max) goto badfmt; \
  650         _var = ll;
  651 
  652 #define config_set_memory_field(_name,_var) \
  653     } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \
  654         ll = memtoll(o->ptr,&err); \
  655         if (err || ll < 0) goto badfmt; \
  656         _var = ll;
  657 
  658 #define config_set_special_field(_name) \
  659     } else if (!strcasecmp(c->argv[2]->ptr,_name)) {
  660 
  661 #define config_set_special_field_with_alias(_name1,_name2) \
  662     } else if (!strcasecmp(c->argv[2]->ptr,_name1) || \
  663                !strcasecmp(c->argv[2]->ptr,_name2)) {
  664 
  665 #define config_set_else } else
  666 
  667 void configSetCommand(client *c) {
  668     robj *o;
  669     long long ll;
  670     int err;
  671     char *errstr = NULL;
  672     serverAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));
  673     serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));
  674     o = c->argv[3];
  675 
  676     /* Iterate the configs that are standard */
  677     for (standardConfig *config = configs; config->name != NULL; config++) {
  678         if(config->modifiable && (!strcasecmp(c->argv[2]->ptr,config->name) ||
  679             (config->alias && !strcasecmp(c->argv[2]->ptr,config->alias))))
  680         {
  681             if (!config->interface.set(config->data,o->ptr,1,&errstr)) {
  682                 goto badfmt;
  683             }
  684             addReply(c,shared.ok);
  685             return;
  686         }
  687     }
  688 
  689     if (0) { /* this starts the config_set macros else-if chain. */
  690 
  691     /* Special fields that can't be handled with general macros. */
  692     config_set_special_field("requirepass") {
  693         if (sdslen(o->ptr) > CONFIG_AUTHPASS_MAX_LEN) goto badfmt;
  694         /* The old "requirepass" directive just translates to setting
  695          * a password to the default user. The only thing we do
  696          * additionally is to remember the cleartext password in this
  697          * case, for backward compatibility with Redis <= 5. */
  698         ACLSetUser(DefaultUser,"resetpass",-1);
  699         sds aclop = sdscatprintf(sdsempty(),">%s",(char*)o->ptr);
  700         ACLSetUser(DefaultUser,aclop,sdslen(aclop));
  701         sdsfree(aclop);
  702         sdsfree(server.requirepass);
  703         server.requirepass = sdsnew(o->ptr);
  704     } config_set_special_field("save") {
  705         int vlen, j;
  706         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
  707 
  708         /* Perform sanity check before setting the new config:
  709          * - Even number of args
  710          * - Seconds >= 1, changes >= 0 */
  711         if (vlen & 1) {
  712             sdsfreesplitres(v,vlen);
  713             goto badfmt;
  714         }
  715         for (j = 0; j < vlen; j++) {
  716             char *eptr;
  717             long val;
  718 
  719             val = strtoll(v[j], &eptr, 10);
  720             if (eptr[0] != '\0' ||
  721                 ((j & 1) == 0 && val < 1) ||
  722                 ((j & 1) == 1 && val < 0)) {
  723                 sdsfreesplitres(v,vlen);
  724                 goto badfmt;
  725             }
  726         }
  727         /* Finally set the new config */
  728         resetServerSaveParams();
  729         for (j = 0; j < vlen; j += 2) {
  730             time_t seconds;
  731             int changes;
  732 
  733             seconds = strtoll(v[j],NULL,10);
  734             changes = strtoll(v[j+1],NULL,10);
  735             appendServerSaveParams(seconds, changes);
  736         }
  737         sdsfreesplitres(v,vlen);
  738     } config_set_special_field("dir") {
  739         if (chdir((char*)o->ptr) == -1) {
  740             addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
  741             return;
  742         }
  743     } config_set_special_field("client-output-buffer-limit") {
  744         int vlen, j;
  745         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
  746 
  747         /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */
  748         if (vlen % 4) {
  749             sdsfreesplitres(v,vlen);
  750             goto badfmt;
  751         }
  752 
  753         /* Sanity check of single arguments, so that we either refuse the
  754          * whole configuration string or accept it all, even if a single
  755          * error in a single client class is present. */
  756         for (j = 0; j < vlen; j++) {
  757             long val;
  758 
  759             if ((j % 4) == 0) {
  760                 int class = getClientTypeByName(v[j]);
  761                 if (class == -1 || class == CLIENT_TYPE_MASTER) {
  762                     sdsfreesplitres(v,vlen);
  763                     goto badfmt;
  764                 }
  765             } else {
  766                 val = memtoll(v[j], &err);
  767                 if (err || val < 0) {
  768                     sdsfreesplitres(v,vlen);
  769                     goto badfmt;
  770                 }
  771             }
  772         }
  773         /* Finally set the new config */
  774         for (j = 0; j < vlen; j += 4) {
  775             int class;
  776             unsigned long long hard, soft;
  777             int soft_seconds;
  778 
  779             class = getClientTypeByName(v[j]);
  780             hard = memtoll(v[j+1],NULL);
  781             soft = memtoll(v[j+2],NULL);
  782             soft_seconds = strtoll(v[j+3],NULL,10);
  783 
  784             server.client_obuf_limits[class].hard_limit_bytes = hard;
  785             server.client_obuf_limits[class].soft_limit_bytes = soft;
  786             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
  787         }
  788         sdsfreesplitres(v,vlen);
  789     } config_set_special_field("oom-score-adj-values") {
  790         int vlen;
  791         int success = 1;
  792 
  793         sds *v = sdssplitlen(o->ptr, sdslen(o->ptr), " ", 1, &vlen);
  794         if (vlen != CONFIG_OOM_COUNT || updateOOMScoreAdjValues(v, &errstr) == C_ERR)
  795             success = 0;
  796 
  797         sdsfreesplitres(v, vlen);
  798         if (!success)
  799             goto badfmt;
  800     } config_set_special_field("notify-keyspace-events") {
  801         int flags = keyspaceEventsStringToFlags(o->ptr);
  802 
  803         if (flags == -1) goto badfmt;
  804         server.notify_keyspace_events = flags;
  805     /* Numerical fields.
  806      * config_set_numerical_field(name,var,min,max) */
  807     } config_set_numerical_field(
  808       "watchdog-period",ll,0,INT_MAX) {
  809         if (ll)
  810             enableWatchdog(ll);
  811         else
  812             disableWatchdog();
  813     /* Memory fields.
  814      * config_set_memory_field(name,var) */
  815     } config_set_memory_field(
  816       "client-query-buffer-limit",server.client_max_querybuf_len) {
  817     /* Everything else is an error... */
  818     } config_set_else {
  819         addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
  820             (char*)c->argv[2]->ptr);
  821         return;
  822     }
  823 
  824     /* On success we just return a generic OK for all the options. */
  825     addReply(c,shared.ok);
  826     return;
  827 
  828 badfmt: /* Bad format errors */
  829     if (errstr) {
  830         addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s' - %s",
  831                 (char*)o->ptr,
  832                 (char*)c->argv[2]->ptr,
  833                 errstr);
  834     } else {
  835         addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
  836                 (char*)o->ptr,
  837                 (char*)c->argv[2]->ptr);
  838     }
  839 }
  840 
  841 /*-----------------------------------------------------------------------------
  842  * CONFIG GET implementation
  843  *----------------------------------------------------------------------------*/
  844 
  845 #define config_get_string_field(_name,_var) do { \
  846     if (stringmatch(pattern,_name,1)) { \
  847         addReplyBulkCString(c,_name); \
  848         addReplyBulkCString(c,_var ? _var : ""); \
  849         matches++; \
  850     } \
  851 } while(0);
  852 
  853 #define config_get_bool_field(_name,_var) do { \
  854     if (stringmatch(pattern,_name,1)) { \
  855         addReplyBulkCString(c,_name); \
  856         addReplyBulkCString(c,_var ? "yes" : "no"); \
  857         matches++; \
  858     } \
  859 } while(0);
  860 
  861 #define config_get_numerical_field(_name,_var) do { \
  862     if (stringmatch(pattern,_name,1)) { \
  863         ll2string(buf,sizeof(buf),_var); \
  864         addReplyBulkCString(c,_name); \
  865         addReplyBulkCString(c,buf); \
  866         matches++; \
  867     } \
  868 } while(0);
  869 
  870 
  871 void configGetCommand(client *c) {
  872     robj *o = c->argv[2];
  873     void *replylen = addReplyDeferredLen(c);
  874     char *pattern = o->ptr;
  875     char buf[128];
  876     int matches = 0;
  877     serverAssertWithInfo(c,o,sdsEncodedObject(o));
  878 
  879     /* Iterate the configs that are standard */
  880     for (standardConfig *config = configs; config->name != NULL; config++) {
  881         if (stringmatch(pattern,config->name,1)) {
  882             addReplyBulkCString(c,config->name);
  883             config->interface.get(c,config->data);
  884             matches++;
  885         }
  886         if (config->alias && stringmatch(pattern,config->alias,1)) {
  887             addReplyBulkCString(c,config->alias);
  888             config->interface.get(c,config->data);
  889             matches++;
  890         }
  891     }
  892 
  893     /* String values */
  894     config_get_string_field("logfile",server.logfile);
  895 
  896     /* Numerical values */
  897     config_get_numerical_field("client-query-buffer-limit",server.client_max_querybuf_len);
  898     config_get_numerical_field("watchdog-period",server.watchdog_period);
  899 
  900     /* Everything we can't handle with macros follows. */
  901 
  902     if (stringmatch(pattern,"dir",1)) {
  903         char buf[1024];
  904 
  905         if (getcwd(buf,sizeof(buf)) == NULL)
  906             buf[0] = '\0';
  907 
  908         addReplyBulkCString(c,"dir");
  909         addReplyBulkCString(c,buf);
  910         matches++;
  911     }
  912     if (stringmatch(pattern,"save",1)) {
  913         sds buf = sdsempty();
  914         int j;
  915 
  916         for (j = 0; j < server.saveparamslen; j++) {
  917             buf = sdscatprintf(buf,"%jd %d",
  918                     (intmax_t)server.saveparams[j].seconds,
  919                     server.saveparams[j].changes);
  920             if (j != server.saveparamslen-1)
  921                 buf = sdscatlen(buf," ",1);
  922         }
  923         addReplyBulkCString(c,"save");
  924         addReplyBulkCString(c,buf);
  925         sdsfree(buf);
  926         matches++;
  927     }
  928     if (stringmatch(pattern,"client-output-buffer-limit",1)) {
  929         sds buf = sdsempty();
  930         int j;
  931 
  932         for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
  933             buf = sdscatprintf(buf,"%s %llu %llu %ld",
  934                     getClientTypeName(j),
  935                     server.client_obuf_limits[j].hard_limit_bytes,
  936                     server.client_obuf_limits[j].soft_limit_bytes,
  937                     (long) server.client_obuf_limits[j].soft_limit_seconds);
  938             if (j != CLIENT_TYPE_OBUF_COUNT-1)
  939                 buf = sdscatlen(buf," ",1);
  940         }
  941         addReplyBulkCString(c,"client-output-buffer-limit");
  942         addReplyBulkCString(c,buf);
  943         sdsfree(buf);
  944         matches++;
  945     }
  946     if (stringmatch(pattern,"unixsocketperm",1)) {
  947         char buf[32];
  948         snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
  949         addReplyBulkCString(c,"unixsocketperm");
  950         addReplyBulkCString(c,buf);
  951         matches++;
  952     }
  953     if (stringmatch(pattern,"slaveof",1) ||
  954         stringmatch(pattern,"replicaof",1))
  955     {
  956         char *optname = stringmatch(pattern,"slaveof",1) ?
  957                         "slaveof" : "replicaof";
  958         char buf[256];
  959 
  960         addReplyBulkCString(c,optname);
  961         if (server.masterhost)
  962             snprintf(buf,sizeof(buf),"%s %d",
  963                 server.masterhost, server.masterport);
  964         else
  965             buf[0] = '\0';
  966         addReplyBulkCString(c,buf);
  967         matches++;
  968     }
  969     if (stringmatch(pattern,"notify-keyspace-events",1)) {
  970         sds flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);
  971 
  972         addReplyBulkCString(c,"notify-keyspace-events");
  973         addReplyBulkSds(c,flags);
  974         matches++;
  975     }
  976     if (stringmatch(pattern,"bind",1)) {
  977         sds aux = sdsjoin(server.bindaddr,server.bindaddr_count," ");
  978 
  979         addReplyBulkCString(c,"bind");
  980         addReplyBulkCString(c,aux);
  981         sdsfree(aux);
  982         matches++;
  983     }
  984     if (stringmatch(pattern,"requirepass",1)) {
  985         addReplyBulkCString(c,"requirepass");
  986         sds password = server.requirepass;
  987         if (password) {
  988             addReplyBulkCBuffer(c,password,sdslen(password));
  989         } else {
  990             addReplyBulkCString(c,"");
  991         }
  992         matches++;
  993     }
  994 
  995     if (stringmatch(pattern,"oom-score-adj-values",0)) {
  996         sds buf = sdsempty();
  997         int j;
  998 
  999         for (j = 0; j < CONFIG_OOM_COUNT; j++) {
 1000             buf = sdscatprintf(buf,"%d", server.oom_score_adj_values[j]);
 1001             if (j != CONFIG_OOM_COUNT-1)
 1002                 buf = sdscatlen(buf," ",1);
 1003         }
 1004 
 1005         addReplyBulkCString(c,"oom-score-adj-values");
 1006         addReplyBulkCString(c,buf);
 1007         sdsfree(buf);
 1008         matches++;
 1009     }
 1010 
 1011     setDeferredMapLen(c,replylen,matches);
 1012 }
 1013 
 1014 /*-----------------------------------------------------------------------------
 1015  * CONFIG REWRITE implementation
 1016  *----------------------------------------------------------------------------*/
 1017 
 1018 #define REDIS_CONFIG_REWRITE_SIGNATURE "# Generated by CONFIG REWRITE"
 1019 
 1020 /* We use the following dictionary type to store where a configuration
 1021  * option is mentioned in the old configuration file, so it's
 1022  * like "maxmemory" -> list of line numbers (first line is zero). */
 1023 uint64_t dictSdsCaseHash(const void *key);
 1024 int dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2);
 1025 void dictSdsDestructor(void *privdata, void *val);
 1026 void dictListDestructor(void *privdata, void *val);
 1027 
 1028 /* Sentinel config rewriting is implemented inside sentinel.c by
 1029  * rewriteConfigSentinelOption(). */
 1030 void rewriteConfigSentinelOption(struct rewriteConfigState *state);
 1031 
 1032 dictType optionToLineDictType = {
 1033     dictSdsCaseHash,            /* hash function */
 1034     NULL,                       /* key dup */
 1035     NULL,                       /* val dup */
 1036     dictSdsKeyCaseCompare,      /* key compare */
 1037     dictSdsDestructor,          /* key destructor */
 1038     dictListDestructor          /* val destructor */
 1039 };
 1040 
 1041 dictType optionSetDictType = {
 1042     dictSdsCaseHash,            /* hash function */
 1043     NULL,                       /* key dup */
 1044     NULL,                       /* val dup */
 1045     dictSdsKeyCaseCompare,      /* key compare */
 1046     dictSdsDestructor,          /* key destructor */
 1047     NULL                        /* val destructor */
 1048 };
 1049 
 1050 /* The config rewrite state. */
 1051 struct rewriteConfigState {
 1052     dict *option_to_line; /* Option -> list of config file lines map */
 1053     dict *rewritten;      /* Dictionary of already processed options */
 1054     int numlines;         /* Number of lines in current config */
 1055     sds *lines;           /* Current lines as an array of sds strings */
 1056     int has_tail;         /* True if we already added directives that were
 1057                              not present in the original config file. */
 1058     int force_all;        /* True if we want all keywords to be force
 1059                              written. Currently only used for testing. */
 1060 };
 1061 
 1062 /* Append the new line to the current configuration state. */
 1063 void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) {
 1064     state->lines = zrealloc(state->lines, sizeof(char*) * (state->numlines+1));
 1065     state->lines[state->numlines++] = line;
 1066 }
 1067 
 1068 /* Populate the option -> list of line numbers map. */
 1069 void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) {
 1070     list *l = dictFetchValue(state->option_to_line,option);
 1071 
 1072     if (l == NULL) {
 1073         l = listCreate();
 1074         dictAdd(state->option_to_line,sdsdup(option),l);
 1075     }
 1076     listAddNodeTail(l,(void*)(long)linenum);
 1077 }
 1078 
 1079 /* Add the specified option to the set of processed options.
 1080  * This is useful as only unused lines of processed options will be blanked
 1081  * in the config file, while options the rewrite process does not understand
 1082  * remain untouched. */
 1083 void rewriteConfigMarkAsProcessed(struct rewriteConfigState *state, const char *option) {
 1084     sds opt = sdsnew(option);
 1085 
 1086     if (dictAdd(state->rewritten,opt,NULL) != DICT_OK) sdsfree(opt);
 1087 }
 1088 
 1089 /* Read the old file, split it into lines to populate a newly created
 1090  * config rewrite state, and return it to the caller.
 1091  *
 1092  * If it is impossible to read the old file, NULL is returned.
 1093  * If the old file does not exist at all, an empty state is returned. */
 1094 struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
 1095     FILE *fp = fopen(path,"r");
 1096     if (fp == NULL && errno != ENOENT) return NULL;
 1097 
 1098     char buf[CONFIG_MAX_LINE+1];
 1099     int linenum = -1;
 1100     struct rewriteConfigState *state = zmalloc(sizeof(*state));
 1101     state->option_to_line = dictCreate(&optionToLineDictType,NULL);
 1102     state->rewritten = dictCreate(&optionSetDictType,NULL);
 1103     state->numlines = 0;
 1104     state->lines = NULL;
 1105     state->has_tail = 0;
 1106     state->force_all = 0;
 1107     if (fp == NULL) return state;
 1108 
 1109     /* Read the old file line by line, populate the state. */
 1110     while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) {
 1111         int argc;
 1112         sds *argv;
 1113         sds line = sdstrim(sdsnew(buf),"\r\n\t ");
 1114 
 1115         linenum++; /* Zero based, so we init at -1 */
 1116 
 1117         /* Handle comments and empty lines. */
 1118         if (line[0] == '#' || line[0] == '\0') {
 1119             if (!state->has_tail && !strcmp(line,REDIS_CONFIG_REWRITE_SIGNATURE))
 1120                 state->has_tail = 1;
 1121             rewriteConfigAppendLine(state,line);
 1122             continue;
 1123         }
 1124 
 1125         /* Not a comment, split into arguments. */
 1126         argv = sdssplitargs(line,&argc);
 1127         if (argv == NULL) {
 1128             /* Apparently the line is unparsable for some reason, for
 1129              * instance it may have unbalanced quotes. Load it as a
 1130              * comment. */
 1131             sds aux = sdsnew("# ??? ");
 1132             aux = sdscatsds(aux,line);
 1133             sdsfree(line);
 1134             rewriteConfigAppendLine(state,aux);
 1135             continue;
 1136         }
 1137 
 1138         sdstolower(argv[0]); /* We only want lowercase config directives. */
 1139 
 1140         /* Now we populate the state according to the content of this line.
 1141          * Append the line and populate the option -> line numbers map. */
 1142         rewriteConfigAppendLine(state,line);
 1143 
 1144         /* Translate options using the word "slave" to the corresponding name
 1145          * "replica", before adding such option to the config name -> lines
 1146          * mapping. */
 1147         char *p = strstr(argv[0],"slave");
 1148         if (p) {
 1149             sds alt = sdsempty();
 1150             alt = sdscatlen(alt,argv[0],p-argv[0]);;
 1151             alt = sdscatlen(alt,"replica",7);
 1152             alt = sdscatlen(alt,p+5,strlen(p+5));
 1153             sdsfree(argv[0]);
 1154             argv[0] = alt;
 1155         }
 1156         rewriteConfigAddLineNumberToOption(state,argv[0],linenum);
 1157         sdsfreesplitres(argv,argc);
 1158     }
 1159     fclose(fp);
 1160     return state;
 1161 }
 1162 
 1163 /* Rewrite the specified configuration option with the new "line".
 1164  * It progressively uses lines of the file that were already used for the same
 1165  * configuration option in the old version of the file, removing that line from
 1166  * the map of options -> line numbers.
 1167  *
 1168  * If there are lines associated with a given configuration option and
 1169  * "force" is non-zero, the line is appended to the configuration file.
 1170  * Usually "force" is true when an option has not its default value, so it
 1171  * must be rewritten even if not present previously.
 1172  *
 1173  * The first time a line is appended into a configuration file, a comment
 1174  * is added to show that starting from that point the config file was generated
 1175  * by CONFIG REWRITE.
 1176  *
 1177  * "line" is either used, or freed, so the caller does not need to free it
 1178  * in any way. */
 1179 void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force) {
 1180     sds o = sdsnew(option);
 1181     list *l = dictFetchValue(state->option_to_line,o);
 1182 
 1183     rewriteConfigMarkAsProcessed(state,option);
 1184 
 1185     if (!l && !force && !state->force_all) {
 1186         /* Option not used previously, and we are not forced to use it. */
 1187         sdsfree(line);
 1188         sdsfree(o);
 1189         return;
 1190     }
 1191 
 1192     if (l) {
 1193         listNode *ln = listFirst(l);
 1194         int linenum = (long) ln->value;
 1195 
 1196         /* There are still lines in the old configuration file we can reuse
 1197          * for this option. Replace the line with the new one. */
 1198         listDelNode(l,ln);
 1199         if (listLength(l) == 0) dictDelete(state->option_to_line,o);
 1200         sdsfree(state->lines[linenum]);
 1201         state->lines[linenum] = line;
 1202     } else {
 1203         /* Append a new line. */
 1204         if (!state->has_tail) {
 1205             rewriteConfigAppendLine(state,
 1206                 sdsnew(REDIS_CONFIG_REWRITE_SIGNATURE));
 1207             state->has_tail = 1;
 1208         }
 1209         rewriteConfigAppendLine(state,line);
 1210     }
 1211     sdsfree(o);
 1212 }
 1213 
 1214 /* Write the long long 'bytes' value as a string in a way that is parsable
 1215  * inside redis.conf. If possible uses the GB, MB, KB notation. */
 1216 int rewriteConfigFormatMemory(char *buf, size_t len, long long bytes) {
 1217     int gb = 1024*1024*1024;
 1218     int mb = 1024*1024;
 1219     int kb = 1024;
 1220 
 1221     if (bytes && (bytes % gb) == 0) {
 1222         return snprintf(buf,len,"%lldgb",bytes/gb);
 1223     } else if (bytes && (bytes % mb) == 0) {
 1224         return snprintf(buf,len,"%lldmb",bytes/mb);
 1225     } else if (bytes && (bytes % kb) == 0) {
 1226         return snprintf(buf,len,"%lldkb",bytes/kb);
 1227     } else {
 1228         return snprintf(buf,len,"%lld",bytes);
 1229     }
 1230 }
 1231 
 1232 /* Rewrite a simple "option-name <bytes>" configuration option. */
 1233 void rewriteConfigBytesOption(struct rewriteConfigState *state, const char *option, long long value, long long defvalue) {
 1234     char buf[64];
 1235     int force = value != defvalue;
 1236     sds line;
 1237 
 1238     rewriteConfigFormatMemory(buf,sizeof(buf),value);
 1239     line = sdscatprintf(sdsempty(),"%s %s",option,buf);
 1240     rewriteConfigRewriteLine(state,option,line,force);
 1241 }
 1242 
 1243 /* Rewrite a yes/no option. */
 1244 void rewriteConfigYesNoOption(struct rewriteConfigState *state, const char *option, int value, int defvalue) {
 1245     int force = value != defvalue;
 1246     sds line = sdscatprintf(sdsempty(),"%s %s",option,
 1247         value ? "yes" : "no");
 1248 
 1249     rewriteConfigRewriteLine(state,option,line,force);
 1250 }
 1251 
 1252 /* Rewrite a string option. */
 1253 void rewriteConfigStringOption(struct rewriteConfigState *state, const char *option, char *value, const char *defvalue) {
 1254     int force = 1;
 1255     sds line;
 1256 
 1257     /* String options set to NULL need to be not present at all in the
 1258      * configuration file to be set to NULL again at the next reboot. */
 1259     if (value == NULL) {
 1260         rewriteConfigMarkAsProcessed(state,option);
 1261         return;
 1262     }
 1263 
 1264     /* Set force to zero if the value is set to its default. */
 1265     if (defvalue && strcmp(value,defvalue) == 0) force = 0;
 1266 
 1267     line = sdsnew(option);
 1268     line = sdscatlen(line, " ", 1);
 1269     line = sdscatrepr(line, value, strlen(value));
 1270 
 1271     rewriteConfigRewriteLine(state,option,line,force);
 1272 }
 1273 
 1274 /* Rewrite a numerical (long long range) option. */
 1275 void rewriteConfigNumericalOption(struct rewriteConfigState *state, const char *option, long long value, long long defvalue) {
 1276     int force = value != defvalue;
 1277     sds line = sdscatprintf(sdsempty(),"%s %lld",option,value);
 1278 
 1279     rewriteConfigRewriteLine(state,option,line,force);
 1280 }
 1281 
 1282 /* Rewrite a octal option. */
 1283 void rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
 1284     int force = value != defvalue;
 1285     sds line = sdscatprintf(sdsempty(),"%s %o",option,value);
 1286 
 1287     rewriteConfigRewriteLine(state,option,line,force);
 1288 }
 1289 
 1290 /* Rewrite an enumeration option. It takes as usually state and option name,
 1291  * and in addition the enumeration array and the default value for the
 1292  * option. */
 1293 void rewriteConfigEnumOption(struct rewriteConfigState *state, const char *option, int value, configEnum *ce, int defval) {
 1294     sds line;
 1295     const char *name = configEnumGetNameOrUnknown(ce,value);
 1296     int force = value != defval;
 1297 
 1298     line = sdscatprintf(sdsempty(),"%s %s",option,name);
 1299     rewriteConfigRewriteLine(state,option,line,force);
 1300 }
 1301 
 1302 /* Rewrite the save option. */
 1303 void rewriteConfigSaveOption(struct rewriteConfigState *state) {
 1304     int j;
 1305     sds line;
 1306 
 1307     /* Note that if there are no save parameters at all, all the current
 1308      * config line with "save" will be detected as orphaned and deleted,
 1309      * resulting into no RDB persistence as expected. */
 1310     for (j = 0; j < server.saveparamslen; j++) {
 1311         line = sdscatprintf(sdsempty(),"save %ld %d",
 1312             (long) server.saveparams[j].seconds, server.saveparams[j].changes);
 1313         rewriteConfigRewriteLine(state,"save",line,1);
 1314     }
 1315     /* Mark "save" as processed in case server.saveparamslen is zero. */
 1316     rewriteConfigMarkAsProcessed(state,"save");
 1317 }
 1318 
 1319 /* Rewrite the user option. */
 1320 void rewriteConfigUserOption(struct rewriteConfigState *state) {
 1321     /* If there is a user file defined we just mark this configuration
 1322      * directive as processed, so that all the lines containing users
 1323      * inside the config file gets discarded. */
 1324     if (server.acl_filename[0] != '\0') {
 1325         rewriteConfigMarkAsProcessed(state,"user");
 1326         return;
 1327     }
 1328 
 1329     /* Otherwise scan the list of users and rewrite every line. Note that
 1330      * in case the list here is empty, the effect will just be to comment
 1331      * all the users directive inside the config file. */
 1332     raxIterator ri;
 1333     raxStart(&ri,Users);
 1334     raxSeek(&ri,"^",NULL,0);
 1335     while(raxNext(&ri)) {
 1336         user *u = ri.data;
 1337         sds line = sdsnew("user ");
 1338         line = sdscatsds(line,u->name);
 1339         line = sdscatlen(line," ",1);
 1340         sds descr = ACLDescribeUser(u);
 1341         line = sdscatsds(line,descr);
 1342         sdsfree(descr);
 1343         rewriteConfigRewriteLine(state,"user",line,1);
 1344     }
 1345     raxStop(&ri);
 1346 
 1347     /* Mark "user" as processed in case there are no defined users. */
 1348     rewriteConfigMarkAsProcessed(state,"user");
 1349 }
 1350 
 1351 /* Rewrite the dir option, always using absolute paths.*/
 1352 void rewriteConfigDirOption(struct rewriteConfigState *state) {
 1353     char cwd[1024];
 1354 
 1355     if (getcwd(cwd,sizeof(cwd)) == NULL) {
 1356         rewriteConfigMarkAsProcessed(state,"dir");
 1357         return; /* no rewrite on error. */
 1358     }
 1359     rewriteConfigStringOption(state,"dir",cwd,NULL);
 1360 }
 1361 
 1362 /* Rewrite the slaveof option. */
 1363 void rewriteConfigSlaveofOption(struct rewriteConfigState *state, char *option) {
 1364     sds line;
 1365 
 1366     /* If this is a master, we want all the slaveof config options
 1367      * in the file to be removed. Note that if this is a cluster instance
 1368      * we don't want a slaveof directive inside redis.conf. */
 1369     if (server.cluster_enabled || server.masterhost == NULL) {
 1370         rewriteConfigMarkAsProcessed(state,option);
 1371         return;
 1372     }
 1373     line = sdscatprintf(sdsempty(),"%s %s %d", option,
 1374         server.masterhost, server.masterport);
 1375     rewriteConfigRewriteLine(state,option,line,1);
 1376 }
 1377 
 1378 /* Rewrite the notify-keyspace-events option. */
 1379 void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) {
 1380     int force = server.notify_keyspace_events != 0;
 1381     char *option = "notify-keyspace-events";
 1382     sds line, flags;
 1383 
 1384     flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);
 1385     line = sdsnew(option);
 1386     line = sdscatlen(line, " ", 1);
 1387     line = sdscatrepr(line, flags, sdslen(flags));
 1388     sdsfree(flags);
 1389     rewriteConfigRewriteLine(state,option,line,force);
 1390 }
 1391 
 1392 /* Rewrite the client-output-buffer-limit option. */
 1393 void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) {
 1394     int j;
 1395     char *option = "client-output-buffer-limit";
 1396 
 1397     for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
 1398         int force = (server.client_obuf_limits[j].hard_limit_bytes !=
 1399                     clientBufferLimitsDefaults[j].hard_limit_bytes) ||
 1400                     (server.client_obuf_limits[j].soft_limit_bytes !=
 1401                     clientBufferLimitsDefaults[j].soft_limit_bytes) ||
 1402                     (server.client_obuf_limits[j].soft_limit_seconds !=
 1403                     clientBufferLimitsDefaults[j].soft_limit_seconds);
 1404         sds line;
 1405         char hard[64], soft[64];
 1406 
 1407         rewriteConfigFormatMemory(hard,sizeof(hard),
 1408                 server.client_obuf_limits[j].hard_limit_bytes);
 1409         rewriteConfigFormatMemory(soft,sizeof(soft),
 1410                 server.client_obuf_limits[j].soft_limit_bytes);
 1411 
 1412         char *typename = getClientTypeName(j);
 1413         if (!strcmp(typename,"slave")) typename = "replica";
 1414         line = sdscatprintf(sdsempty(),"%s %s %s %s %ld",
 1415                 option, typename, hard, soft,
 1416                 (long) server.client_obuf_limits[j].soft_limit_seconds);
 1417         rewriteConfigRewriteLine(state,option,line,force);
 1418     }
 1419 }
 1420 
 1421 /* Rewrite the oom-score-adj-values option. */
 1422 void rewriteConfigOOMScoreAdjValuesOption(struct rewriteConfigState *state) {
 1423     int force = 0;
 1424     int j;
 1425     char *option = "oom-score-adj-values";
 1426     sds line;
 1427 
 1428     line = sdsnew(option);
 1429     line = sdscatlen(line, " ", 1);
 1430     for (j = 0; j < CONFIG_OOM_COUNT; j++) {
 1431         if (server.oom_score_adj_values[j] != configOOMScoreAdjValuesDefaults[j])
 1432             force = 1;
 1433 
 1434         line = sdscatprintf(line, "%d", server.oom_score_adj_values[j]);
 1435         if (j+1 != CONFIG_OOM_COUNT)
 1436             line = sdscatlen(line, " ", 1);
 1437     }
 1438     rewriteConfigRewriteLine(state,option,line,force);
 1439 }
 1440 
 1441 /* Rewrite the bind option. */
 1442 void rewriteConfigBindOption(struct rewriteConfigState *state) {
 1443     int force = 1;
 1444     sds line, addresses;
 1445     char *option = "bind";
 1446 
 1447     /* Nothing to rewrite if we don't have bind addresses. */
 1448     if (server.bindaddr_count == 0) {
 1449         rewriteConfigMarkAsProcessed(state,option);
 1450         return;
 1451     }
 1452 
 1453     /* Rewrite as bind <addr1> <addr2> ... <addrN> */
 1454     addresses = sdsjoin(server.bindaddr,server.bindaddr_count," ");
 1455     line = sdsnew(option);
 1456     line = sdscatlen(line, " ", 1);
 1457     line = sdscatsds(line, addresses);
 1458     sdsfree(addresses);
 1459 
 1460     rewriteConfigRewriteLine(state,option,line,force);
 1461 }
 1462 
 1463 /* Rewrite the requirepass option. */
 1464 void rewriteConfigRequirepassOption(struct rewriteConfigState *state, char *option) {
 1465     int force = 1;
 1466     sds line;
 1467     sds password = server.requirepass;
 1468 
 1469     /* If there is no password set, we don't want the requirepass option
 1470      * to be present in the configuration at all. */
 1471     if (password == NULL) {
 1472         rewriteConfigMarkAsProcessed(state,option);
 1473         return;
 1474     }
 1475 
 1476     line = sdsnew(option);
 1477     line = sdscatlen(line, " ", 1);
 1478     line = sdscatsds(line, password);
 1479 
 1480     rewriteConfigRewriteLine(state,option,line,force);
 1481 }
 1482 
 1483 /* Glue together the configuration lines in the current configuration
 1484  * rewrite state into a single string, stripping multiple empty lines. */
 1485 sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {
 1486     sds content = sdsempty();
 1487     int j, was_empty = 0;
 1488 
 1489     for (j = 0; j < state->numlines; j++) {
 1490         /* Every cluster of empty lines is turned into a single empty line. */
 1491         if (sdslen(state->lines[j]) == 0) {
 1492             if (was_empty) continue;
 1493             was_empty = 1;
 1494         } else {
 1495             was_empty = 0;
 1496         }
 1497         content = sdscatsds(content,state->lines[j]);
 1498         content = sdscatlen(content,"\n",1);
 1499     }
 1500     return content;
 1501 }
 1502 
 1503 /* Free the configuration rewrite state. */
 1504 void rewriteConfigReleaseState(struct rewriteConfigState *state) {
 1505     sdsfreesplitres(state->lines,state->numlines);
 1506     dictRelease(state->option_to_line);
 1507     dictRelease(state->rewritten);
 1508     zfree(state);
 1509 }
 1510 
 1511 /* At the end of the rewrite process the state contains the remaining
 1512  * map between "option name" => "lines in the original config file".
 1513  * Lines used by the rewrite process were removed by the function
 1514  * rewriteConfigRewriteLine(), all the other lines are "orphaned" and
 1515  * should be replaced by empty lines.
 1516  *
 1517  * This function does just this, iterating all the option names and
 1518  * blanking all the lines still associated. */
 1519 void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
 1520     dictIterator *di = dictGetIterator(state->option_to_line);
 1521     dictEntry *de;
 1522 
 1523     while((de = dictNext(di)) != NULL) {
 1524         list *l = dictGetVal(de);
 1525         sds option = dictGetKey(de);
 1526 
 1527         /* Don't blank lines about options the rewrite process
 1528          * don't understand. */
 1529         if (dictFind(state->rewritten,option) == NULL) {
 1530             serverLog(LL_DEBUG,"Not rewritten option: %s", option);
 1531             continue;
 1532         }
 1533 
 1534         while(listLength(l)) {
 1535             listNode *ln = listFirst(l);
 1536             int linenum = (long) ln->value;
 1537 
 1538             sdsfree(state->lines[linenum]);
 1539             state->lines[linenum] = sdsempty();
 1540             listDelNode(l,ln);
 1541         }
 1542     }
 1543     dictReleaseIterator(di);
 1544 }
 1545 
 1546 /* This function overwrites the old configuration file with the new content.
 1547  *
 1548  * 1) The old file length is obtained.
 1549  * 2) If the new content is smaller, padding is added.
 1550  * 3) A single write(2) call is used to replace the content of the file.
 1551  * 4) Later the file is truncated to the length of the new content.
 1552  *
 1553  * This way we are sure the file is left in a consistent state even if the
 1554  * process is stopped between any of the four operations.
 1555  *
 1556  * The function returns 0 on success, otherwise -1 is returned and errno
 1557  * set accordingly. */
 1558 int rewriteConfigOverwriteFile(char *configfile, sds content) {
 1559     int retval = 0;
 1560     int fd = open(configfile,O_RDWR|O_CREAT,0644);
 1561     int content_size = sdslen(content), padding = 0;
 1562     struct stat sb;
 1563     sds content_padded;
 1564 
 1565     /* 1) Open the old file (or create a new one if it does not
 1566      *    exist), get the size. */
 1567     if (fd == -1) return -1; /* errno set by open(). */
 1568     if (fstat(fd,&sb) == -1) {
 1569         close(fd);
 1570         return -1; /* errno set by fstat(). */
 1571     }
 1572 
 1573     /* 2) Pad the content at least match the old file size. */
 1574     content_padded = sdsdup(content);
 1575     if (content_size < sb.st_size) {
 1576         /* If the old file was bigger, pad the content with
 1577          * a newline plus as many "#" chars as required. */
 1578         padding = sb.st_size - content_size;
 1579         content_padded = sdsgrowzero(content_padded,sb.st_size);
 1580         content_padded[content_size] = '\n';
 1581         memset(content_padded+content_size+1,'#',padding-1);
 1582     }
 1583 
 1584     /* 3) Write the new content using a single write(2). */
 1585     if (write(fd,content_padded,strlen(content_padded)) == -1) {
 1586         retval = -1;
 1587         goto cleanup;
 1588     }
 1589 
 1590     /* 4) Truncate the file to the right length if we used padding. */
 1591     if (padding) {
 1592         if (ftruncate(fd,content_size) == -1) {
 1593             /* Non critical error... */
 1594         }
 1595     }
 1596 
 1597 cleanup:
 1598     sdsfree(content_padded);
 1599     close(fd);
 1600     return retval;
 1601 }
 1602 
 1603 /* Rewrite the configuration file at "path".
 1604  * If the configuration file already exists, we try at best to retain comments
 1605  * and overall structure.
 1606  *
 1607  * Configuration parameters that are at their default value, unless already
 1608  * explicitly included in the old configuration file, are not rewritten.
 1609  * The force_all flag overrides this behavior and forces everything to be
 1610  * written. This is currently only used for testing purposes.
 1611  *
 1612  * On error -1 is returned and errno is set accordingly, otherwise 0. */
 1613 int rewriteConfig(char *path, int force_all) {
 1614     struct rewriteConfigState *state;
 1615     sds newcontent;
 1616     int retval;
 1617 
 1618     /* Step 1: read the old config into our rewrite state. */
 1619     if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;
 1620     if (force_all) state->force_all = 1;
 1621 
 1622     /* Step 2: rewrite every single option, replacing or appending it inside
 1623      * the rewrite state. */
 1624 
 1625     /* Iterate the configs that are standard */
 1626     for (standardConfig *config = configs; config->name != NULL; config++) {
 1627         config->interface.rewrite(config->data, config->name, state);
 1628     }
 1629 
 1630     rewriteConfigBindOption(state);
 1631     rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,CONFIG_DEFAULT_UNIX_SOCKET_PERM);
 1632     rewriteConfigStringOption(state,"logfile",server.logfile,CONFIG_DEFAULT_LOGFILE);
 1633     rewriteConfigSaveOption(state);
 1634     rewriteConfigUserOption(state);
 1635     rewriteConfigDirOption(state);
 1636     rewriteConfigSlaveofOption(state,"replicaof");
 1637     rewriteConfigRequirepassOption(state,"requirepass");
 1638     rewriteConfigBytesOption(state,"client-query-buffer-limit",server.client_max_querybuf_len,PROTO_MAX_QUERYBUF_LEN);
 1639     rewriteConfigStringOption(state,"cluster-config-file",server.cluster_configfile,CONFIG_DEFAULT_CLUSTER_CONFIG_FILE);
 1640     rewriteConfigNotifykeyspaceeventsOption(state);
 1641     rewriteConfigClientoutputbufferlimitOption(state);
 1642     rewriteConfigOOMScoreAdjValuesOption(state);
 1643 
 1644     /* Rewrite Sentinel config if in Sentinel mode. */
 1645     if (server.sentinel_mode) rewriteConfigSentinelOption(state);
 1646 
 1647     /* Step 3: remove all the orphaned lines in the old file, that is, lines
 1648      * that were used by a config option and are no longer used, like in case
 1649      * of multiple "save" options or duplicated options. */
 1650     rewriteConfigRemoveOrphaned(state);
 1651 
 1652     /* Step 4: generate a new configuration file from the modified state
 1653      * and write it into the original file. */
 1654     newcontent = rewriteConfigGetContentFromState(state);
 1655     retval = rewriteConfigOverwriteFile(server.configfile,newcontent);
 1656 
 1657     sdsfree(newcontent);
 1658     rewriteConfigReleaseState(state);
 1659     return retval;
 1660 }
 1661 
 1662 /*-----------------------------------------------------------------------------
 1663  * Configs that fit one of the major types and require no special handling
 1664  *----------------------------------------------------------------------------*/
 1665 #define LOADBUF_SIZE 256
 1666 static char loadbuf[LOADBUF_SIZE];
 1667 
 1668 #define MODIFIABLE_CONFIG 1
 1669 #define IMMUTABLE_CONFIG 0
 1670 
 1671 #define embedCommonConfig(config_name, config_alias, is_modifiable) \
 1672     .name = (config_name), \
 1673     .alias = (config_alias), \
 1674     .modifiable = (is_modifiable),
 1675 
 1676 #define embedConfigInterface(initfn, setfn, getfn, rewritefn) .interface = { \
 1677     .init = (initfn), \
 1678     .set = (setfn), \
 1679     .get = (getfn), \
 1680     .rewrite = (rewritefn) \
 1681 },
 1682 
 1683 /* What follows is the generic config types that are supported. To add a new
 1684  * config with one of these types, add it to the standardConfig table with
 1685  * the creation macro for each type.
 1686  *
 1687  * Each type contains the following:
 1688  * * A function defining how to load this type on startup.
 1689  * * A function defining how to update this type on CONFIG SET.
 1690  * * A function defining how to serialize this type on CONFIG SET.
 1691  * * A function defining how to rewrite this type on CONFIG REWRITE.
 1692  * * A Macro defining how to create this type.
 1693  */
 1694 
 1695 /* Bool Configs */
 1696 static void boolConfigInit(typeData data) {
 1697     *data.yesno.config = data.yesno.default_value;
 1698 }
 1699 
 1700 static int boolConfigSet(typeData data, sds value, int update, char **err) {
 1701     int yn = yesnotoi(value);
 1702     if (yn == -1) {
 1703         *err = "argument must be 'yes' or 'no'";
 1704         return 0;
 1705     }
 1706     if (data.yesno.is_valid_fn && !data.yesno.is_valid_fn(yn, err))
 1707         return 0;
 1708     int prev = *(data.yesno.config);
 1709     *(data.yesno.config) = yn;
 1710     if (update && data.yesno.update_fn && !data.yesno.update_fn(yn, prev, err)) {
 1711         *(data.yesno.config) = prev;
 1712         return 0;
 1713     }
 1714     return 1;
 1715 }
 1716 
 1717 static void boolConfigGet(client *c, typeData data) {
 1718     addReplyBulkCString(c, *data.yesno.config ? "yes" : "no");
 1719 }
 1720 
 1721 static void boolConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
 1722     rewriteConfigYesNoOption(state, name,*(data.yesno.config), data.yesno.default_value);
 1723 }
 1724 
 1725 #define createBoolConfig(name, alias, modifiable, config_addr, default, is_valid, update) { \
 1726     embedCommonConfig(name, alias, modifiable) \
 1727     embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite) \
 1728     .data.yesno = { \
 1729         .config = &(config_addr), \
 1730         .default_value = (default), \
 1731         .is_valid_fn = (is_valid), \
 1732         .update_fn = (update), \
 1733     } \
 1734 }
 1735 
 1736 /* String Configs */
 1737 static void stringConfigInit(typeData data) {
 1738     if (data.string.convert_empty_to_null) {
 1739         *data.string.config = data.string.default_value ? zstrdup(data.string.default_value) : NULL;
 1740     } else {
 1741         *data.string.config = zstrdup(data.string.default_value);
 1742     }
 1743 }
 1744 
 1745 static int stringConfigSet(typeData data, sds value, int update, char **err) {
 1746     if (data.string.is_valid_fn && !data.string.is_valid_fn(value, err))
 1747         return 0;
 1748     char *prev = *data.string.config;
 1749     if (data.string.convert_empty_to_null) {
 1750         *data.string.config = value[0] ? zstrdup(value) : NULL;
 1751     } else {
 1752         *data.string.config = zstrdup(value);
 1753     }
 1754     if (update && data.string.update_fn && !data.string.update_fn(*data.string.config, prev, err)) {
 1755         zfree(*data.string.config);
 1756         *data.string.config = prev;
 1757         return 0;
 1758     }
 1759     zfree(prev);
 1760     return 1;
 1761 }
 1762 
 1763 static void stringConfigGet(client *c, typeData data) {
 1764     addReplyBulkCString(c, *data.string.config ? *data.string.config : "");
 1765 }
 1766 
 1767 static void stringConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
 1768     rewriteConfigStringOption(state, name,*(data.string.config), data.string.default_value);
 1769 }
 1770 
 1771 #define ALLOW_EMPTY_STRING 0
 1772 #define EMPTY_STRING_IS_NULL 1
 1773 
 1774 #define createStringConfig(name, alias, modifiable, empty_to_null, config_addr, default, is_valid, update) { \
 1775     embedCommonConfig(name, alias, modifiable) \
 1776     embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite) \
 1777     .data.string = { \
 1778         .config = &(config_addr), \
 1779         .default_value = (default), \
 1780         .is_valid_fn = (is_valid), \
 1781         .update_fn = (update), \
 1782         .convert_empty_to_null = (empty_to_null), \
 1783     } \
 1784 }
 1785 
 1786 /* Enum configs */
 1787 static void enumConfigInit(typeData data) {
 1788     *data.enumd.config = data.enumd.default_value;
 1789 }
 1790 
 1791 static int enumConfigSet(typeData data, sds value, int update, char **err) {
 1792     int enumval = configEnumGetValue(data.enumd.enum_value, value);
 1793     if (enumval == INT_MIN) {
 1794         sds enumerr = sdsnew("argument must be one of the following: ");
 1795         configEnum *enumNode = data.enumd.enum_value;
 1796         while(enumNode->name != NULL) {
 1797             enumerr = sdscatlen(enumerr, enumNode->name,
 1798                                 strlen(enumNode->name));
 1799             enumerr = sdscatlen(enumerr, ", ", 2);
 1800             enumNode++;
 1801         }
 1802         sdsrange(enumerr,0,-3); /* Remove final ", ". */
 1803 
 1804         strncpy(loadbuf, enumerr, LOADBUF_SIZE);
 1805         loadbuf[LOADBUF_SIZE - 1] = '\0';
 1806 
 1807         sdsfree(enumerr);
 1808         *err = loadbuf;
 1809         return 0;
 1810     }
 1811     if (data.enumd.is_valid_fn && !data.enumd.is_valid_fn(enumval, err))
 1812         return 0;
 1813     int prev = *(data.enumd.config);
 1814     *(data.enumd.config) = enumval;
 1815     if (update && data.enumd.update_fn && !data.enumd.update_fn(enumval, prev, err)) {
 1816         *(data.enumd.config) = prev;
 1817         return 0;
 1818     }
 1819     return 1;
 1820 }
 1821 
 1822 static void enumConfigGet(client *c, typeData data) {
 1823     addReplyBulkCString(c, configEnumGetNameOrUnknown(data.enumd.enum_value,*data.enumd.config));
 1824 }
 1825 
 1826 static void enumConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
 1827     rewriteConfigEnumOption(state, name,*(data.enumd.config), data.enumd.enum_value, data.enumd.default_value);
 1828 }
 1829 
 1830 #define createEnumConfig(name, alias, modifiable, enum, config_addr, default, is_valid, update) { \
 1831     embedCommonConfig(name, alias, modifiable) \
 1832     embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite) \
 1833     .data.enumd = { \
 1834         .config = &(config_addr), \
 1835         .default_value = (default), \
 1836         .is_valid_fn = (is_valid), \
 1837         .update_fn = (update), \
 1838         .enum_value = (enum), \
 1839     } \
 1840 }
 1841 
 1842 /* Gets a 'long long val' and sets it into the union, using a macro to get
 1843  * compile time type check. */
 1844 #define SET_NUMERIC_TYPE(val) \
 1845     if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \
 1846         *(data.numeric.config.i) = (int) val; \
 1847     } else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \
 1848         *(data.numeric.config.ui) = (unsigned int) val; \
 1849     } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \
 1850         *(data.numeric.config.l) = (long) val; \
 1851     } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \
 1852         *(data.numeric.config.ul) = (unsigned long) val; \
 1853     } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \
 1854         *(data.numeric.config.ll) = (long long) val; \
 1855     } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \
 1856         *(data.numeric.config.ull) = (unsigned long long) val; \
 1857     } else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \
 1858         *(data.numeric.config.st) = (size_t) val; \
 1859     } else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \
 1860         *(data.numeric.config.sst) = (ssize_t) val; \
 1861     } else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \
 1862         *(data.numeric.config.ot) = (off_t) val; \
 1863     } else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \
 1864         *(data.numeric.config.tt) = (time_t) val; \
 1865     }
 1866 
 1867 /* Gets a 'long long val' and sets it with the value from the union, using a
 1868  * macro to get compile time type check. */
 1869 #define GET_NUMERIC_TYPE(val) \
 1870     if (data.numeric.numeric_type == NUMERIC_TYPE_INT) { \
 1871         val = *(data.numeric.config.i); \
 1872     } else if (data.numeric.numeric_type == NUMERIC_TYPE_UINT) { \
 1873         val = *(data.numeric.config.ui); \
 1874     } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG) { \
 1875         val = *(data.numeric.config.l); \
 1876     } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG) { \
 1877         val = *(data.numeric.config.ul); \
 1878     } else if (data.numeric.numeric_type == NUMERIC_TYPE_LONG_LONG) { \
 1879         val = *(data.numeric.config.ll); \
 1880     } else if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG) { \
 1881         val = *(data.numeric.config.ull); \
 1882     } else if (data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) { \
 1883         val = *(data.numeric.config.st); \
 1884     } else if (data.numeric.numeric_type == NUMERIC_TYPE_SSIZE_T) { \
 1885         val = *(data.numeric.config.sst); \
 1886     } else if (data.numeric.numeric_type == NUMERIC_TYPE_OFF_T) { \
 1887         val = *(data.numeric.config.ot); \
 1888     } else if (data.numeric.numeric_type == NUMERIC_TYPE_TIME_T) { \
 1889         val = *(data.numeric.config.tt); \
 1890     }
 1891 
 1892 /* Numeric configs */
 1893 static void numericConfigInit(typeData data) {
 1894     SET_NUMERIC_TYPE(data.numeric.default_value)
 1895 }
 1896 
 1897 static int numericBoundaryCheck(typeData data, long long ll, char **err) {
 1898     if (data.numeric.numeric_type == NUMERIC_TYPE_ULONG_LONG ||
 1899         data.numeric.numeric_type == NUMERIC_TYPE_UINT ||
 1900         data.numeric.numeric_type == NUMERIC_TYPE_SIZE_T) {
 1901         /* Boundary check for unsigned types */
 1902         unsigned long long ull = ll;
 1903         unsigned long long upper_bound = data.numeric.upper_bound;
 1904         unsigned long long lower_bound = data.numeric.lower_bound;
 1905         if (ull > upper_bound || ull < lower_bound) {
 1906             snprintf(loadbuf, LOADBUF_SIZE,
 1907                 "argument must be between %llu and %llu inclusive",
 1908                 lower_bound,
 1909                 upper_bound);
 1910             *err = loadbuf;
 1911             return 0;
 1912         }
 1913     } else {
 1914         /* Boundary check for signed types */
 1915         if (ll > data.numeric.upper_bound || ll < data.numeric.lower_bound) {
 1916             snprintf(loadbuf, LOADBUF_SIZE,
 1917                 "argument must be between %lld and %lld inclusive",
 1918                 data.numeric.lower_bound,
 1919                 data.numeric.upper_bound);
 1920             *err = loadbuf;
 1921             return 0;
 1922         }
 1923     }
 1924     return 1;
 1925 }
 1926 
 1927 static int numericConfigSet(typeData data, sds value, int update, char **err) {
 1928     long long ll, prev = 0;
 1929     if (data.numeric.is_memory) {
 1930         int memerr;
 1931         ll = memtoll(value, &memerr);
 1932         if (memerr || ll < 0) {
 1933             *err = "argument must be a memory value";
 1934             return 0;
 1935         }
 1936     } else {
 1937         if (!string2ll(value, sdslen(value),&ll)) {
 1938             *err = "argument couldn't be parsed into an integer" ;
 1939             return 0;
 1940         }
 1941     }
 1942 
 1943     if (!numericBoundaryCheck(data, ll, err))
 1944         return 0;
 1945 
 1946     if (data.numeric.is_valid_fn && !data.numeric.is_valid_fn(ll, err))
 1947         return 0;
 1948 
 1949     GET_NUMERIC_TYPE(prev)
 1950     SET_NUMERIC_TYPE(ll)
 1951 
 1952     if (update && data.numeric.update_fn && !data.numeric.update_fn(ll, prev, err)) {
 1953         SET_NUMERIC_TYPE(prev)
 1954         return 0;
 1955     }
 1956     return 1;
 1957 }
 1958 
 1959 static void numericConfigGet(client *c, typeData data) {
 1960     char buf[128];
 1961     long long value = 0;
 1962 
 1963     GET_NUMERIC_TYPE(value)
 1964 
 1965     ll2string(buf, sizeof(buf), value);
 1966     addReplyBulkCString(c, buf);
 1967 }
 1968 
 1969 static void numericConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
 1970     long long value = 0;
 1971 
 1972     GET_NUMERIC_TYPE(value)
 1973 
 1974     if (data.numeric.is_memory) {
 1975         rewriteConfigBytesOption(state, name, value, data.numeric.default_value);
 1976     } else {
 1977         rewriteConfigNumericalOption(state, name, value, data.numeric.default_value);
 1978     }
 1979 }
 1980 
 1981 #define INTEGER_CONFIG 0
 1982 #define MEMORY_CONFIG 1
 1983 
 1984 #define embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) { \
 1985     embedCommonConfig(name, alias, modifiable) \
 1986     embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite) \
 1987     .data.numeric = { \
 1988         .lower_bound = (lower), \
 1989         .upper_bound = (upper), \
 1990         .default_value = (default), \
 1991         .is_valid_fn = (is_valid), \
 1992         .update_fn = (update), \
 1993         .is_memory = (memory),
 1994 
 1995 #define createIntConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 1996     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 1997         .numeric_type = NUMERIC_TYPE_INT, \
 1998         .config.i = &(config_addr) \
 1999     } \
 2000 }
 2001 
 2002 #define createUIntConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2003     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2004         .numeric_type = NUMERIC_TYPE_UINT, \
 2005         .config.ui = &(config_addr) \
 2006     } \
 2007 }
 2008 
 2009 #define createLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2010     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2011         .numeric_type = NUMERIC_TYPE_LONG, \
 2012         .config.l = &(config_addr) \
 2013     } \
 2014 }
 2015 
 2016 #define createULongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2017     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2018         .numeric_type = NUMERIC_TYPE_ULONG, \
 2019         .config.ul = &(config_addr) \
 2020     } \
 2021 }
 2022 
 2023 #define createLongLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2024     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2025         .numeric_type = NUMERIC_TYPE_LONG_LONG, \
 2026         .config.ll = &(config_addr) \
 2027     } \
 2028 }
 2029 
 2030 #define createULongLongConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2031     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2032         .numeric_type = NUMERIC_TYPE_ULONG_LONG, \
 2033         .config.ull = &(config_addr) \
 2034     } \
 2035 }
 2036 
 2037 #define createSizeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2038     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2039         .numeric_type = NUMERIC_TYPE_SIZE_T, \
 2040         .config.st = &(config_addr) \
 2041     } \
 2042 }
 2043 
 2044 #define createSSizeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2045     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2046         .numeric_type = NUMERIC_TYPE_SSIZE_T, \
 2047         .config.sst = &(config_addr) \
 2048     } \
 2049 }
 2050 
 2051 #define createTimeTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2052     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2053         .numeric_type = NUMERIC_TYPE_TIME_T, \
 2054         .config.tt = &(config_addr) \
 2055     } \
 2056 }
 2057 
 2058 #define createOffTConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2059     embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) \
 2060         .numeric_type = NUMERIC_TYPE_OFF_T, \
 2061         .config.ot = &(config_addr) \
 2062     } \
 2063 }
 2064 
 2065 static int isValidActiveDefrag(int val, char **err) {
 2066 #ifndef HAVE_DEFRAG
 2067     if (val) {
 2068         *err = "Active defragmentation cannot be enabled: it "
 2069                "requires a Redis server compiled with a modified Jemalloc "
 2070                "like the one shipped by default with the Redis source "
 2071                "distribution";
 2072         return 0;
 2073     }
 2074 #else
 2075     UNUSED(val);
 2076     UNUSED(err);
 2077 #endif
 2078     return 1;
 2079 }
 2080 
 2081 static int isValidDBfilename(char *val, char **err) {
 2082     if (!pathIsBaseName(val)) {
 2083         *err = "dbfilename can't be a path, just a filename";
 2084         return 0;
 2085     }
 2086     return 1;
 2087 }
 2088 
 2089 static int isValidAOFfilename(char *val, char **err) {
 2090     if (!pathIsBaseName(val)) {
 2091         *err = "appendfilename can't be a path, just a filename";
 2092         return 0;
 2093     }
 2094     return 1;
 2095 }
 2096 
 2097 static int updateHZ(long long val, long long prev, char **err) {
 2098     UNUSED(prev);
 2099     UNUSED(err);
 2100     /* Hz is more an hint from the user, so we accept values out of range
 2101      * but cap them to reasonable values. */
 2102     server.config_hz = val;
 2103     if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;
 2104     if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;
 2105     server.hz = server.config_hz;
 2106     return 1;
 2107 }
 2108 
 2109 static int updateJemallocBgThread(int val, int prev, char **err) {
 2110     UNUSED(prev);
 2111     UNUSED(err);
 2112     set_jemalloc_bg_thread(val);
 2113     return 1;
 2114 }
 2115 
 2116 static int updateReplBacklogSize(long long val, long long prev, char **err) {
 2117     /* resizeReplicationBacklog sets server.repl_backlog_size, and relies on
 2118      * being able to tell when the size changes, so restore prev becore calling it. */
 2119     UNUSED(err);
 2120     server.repl_backlog_size = prev;
 2121     resizeReplicationBacklog(val);
 2122     return 1;
 2123 }
 2124 
 2125 static int updateMaxmemory(long long val, long long prev, char **err) {
 2126     UNUSED(prev);
 2127     UNUSED(err);
 2128     if (val) {
 2129         size_t used = zmalloc_used_memory()-freeMemoryGetNotCountedMemory();
 2130         if ((unsigned long long)val < used) {
 2131             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);
 2132         }
 2133         freeMemoryIfNeededAndSafe();
 2134     }
 2135     return 1;
 2136 }
 2137 
 2138 static int updateGoodSlaves(long long val, long long prev, char **err) {
 2139     UNUSED(val);
 2140     UNUSED(prev);
 2141     UNUSED(err);
 2142     refreshGoodSlavesCount();
 2143     return 1;
 2144 }
 2145 
 2146 static int updateAppendonly(int val, int prev, char **err) {
 2147     UNUSED(prev);
 2148     if (val == 0 && server.aof_state != AOF_OFF) {
 2149         stopAppendOnly();
 2150     } else if (val && server.aof_state == AOF_OFF) {
 2151         if (startAppendOnly() == C_ERR) {
 2152             *err = "Unable to turn on AOF. Check server logs.";
 2153             return 0;
 2154         }
 2155     }
 2156     return 1;
 2157 }
 2158 
 2159 static int updateMaxclients(long long val, long long prev, char **err) {
 2160     /* Try to check if the OS is capable of supporting so many FDs. */
 2161     if (val > prev) {
 2162         adjustOpenFilesLimit();
 2163         if (server.maxclients != val) {
 2164             static char msg[128];
 2165             sprintf(msg, "The operating system is not able to handle the specified number of clients, try with %d", server.maxclients);
 2166             *err = msg;
 2167             if (server.maxclients > prev) {
 2168                 server.maxclients = prev;
 2169                 adjustOpenFilesLimit();
 2170             }
 2171             return 0;
 2172         }
 2173         if ((unsigned int) aeGetSetSize(server.el) <
 2174             server.maxclients + CONFIG_FDSET_INCR)
 2175         {
 2176             if (aeResizeSetSize(server.el,
 2177                 server.maxclients + CONFIG_FDSET_INCR) == AE_ERR)
 2178             {
 2179                 *err = "The event loop API used by Redis is not able to handle the specified number of clients";
 2180                 return 0;
 2181             }
 2182         }
 2183     }
 2184     return 1;
 2185 }
 2186 
 2187 static int updateOOMScoreAdj(int val, int prev, char **err) {
 2188     UNUSED(prev);
 2189 
 2190     if (val) {
 2191         if (setOOMScoreAdj(-1) == C_ERR) {
 2192             *err = "Failed to set current oom_score_adj. Check server logs.";
 2193             return 0;
 2194         }
 2195     }
 2196 
 2197     return 1;
 2198 }
 2199 
 2200 #ifdef USE_OPENSSL
 2201 static int updateTlsCfg(char *val, char *prev, char **err) {
 2202     UNUSED(val);
 2203     UNUSED(prev);
 2204     UNUSED(err);
 2205 
 2206     /* If TLS is enabled, try to configure OpenSSL. */
 2207     if ((server.tls_port || server.tls_replication || server.tls_cluster)
 2208             && tlsConfigure(&server.tls_ctx_config) == C_ERR) {
 2209         *err = "Unable to update TLS configuration. Check server logs.";
 2210         return 0;
 2211     }
 2212     return 1;
 2213 }
 2214 static int updateTlsCfgBool(int val, int prev, char **err) {
 2215     UNUSED(val);
 2216     UNUSED(prev);
 2217     return updateTlsCfg(NULL, NULL, err);
 2218 }
 2219 
 2220 static int updateTlsCfgInt(long long val, long long prev, char **err) {
 2221     UNUSED(val);
 2222     UNUSED(prev);
 2223     return updateTlsCfg(NULL, NULL, err);
 2224 }
 2225 #endif  /* USE_OPENSSL */
 2226 
 2227 standardConfig configs[] = {
 2228     /* Bool configs */
 2229     createBoolConfig("rdbchecksum", NULL, IMMUTABLE_CONFIG, server.rdb_checksum, 1, NULL, NULL),
 2230     createBoolConfig("daemonize", NULL, IMMUTABLE_CONFIG, server.daemonize, 0, NULL, NULL),
 2231     createBoolConfig("io-threads-do-reads", NULL, IMMUTABLE_CONFIG, server.io_threads_do_reads, 0,NULL, NULL), /* Read + parse from threads? */
 2232     createBoolConfig("lua-replicate-commands", NULL, MODIFIABLE_CONFIG, server.lua_always_replicate_commands, 1, NULL, NULL),
 2233     createBoolConfig("always-show-logo", NULL, IMMUTABLE_CONFIG, server.always_show_logo, 0, NULL, NULL),
 2234     createBoolConfig("protected-mode", NULL, MODIFIABLE_CONFIG, server.protected_mode, 1, NULL, NULL),
 2235     createBoolConfig("rdbcompression", NULL, MODIFIABLE_CONFIG, server.rdb_compression, 1, NULL, NULL),
 2236     createBoolConfig("rdb-del-sync-files", NULL, MODIFIABLE_CONFIG, server.rdb_del_sync_files, 0, NULL, NULL),
 2237     createBoolConfig("activerehashing", NULL, MODIFIABLE_CONFIG, server.activerehashing, 1, NULL, NULL),
 2238     createBoolConfig("stop-writes-on-bgsave-error", NULL, MODIFIABLE_CONFIG, server.stop_writes_on_bgsave_err, 1, NULL, NULL),
 2239     createBoolConfig("dynamic-hz", NULL, MODIFIABLE_CONFIG, server.dynamic_hz, 1, NULL, NULL), /* Adapt hz to # of clients.*/
 2240     createBoolConfig("lazyfree-lazy-eviction", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_eviction, 0, NULL, NULL),
 2241     createBoolConfig("lazyfree-lazy-expire", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_expire, 0, NULL, NULL),
 2242     createBoolConfig("lazyfree-lazy-server-del", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_server_del, 0, NULL, NULL),
 2243     createBoolConfig("lazyfree-lazy-user-del", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_user_del , 0, NULL, NULL),
 2244     createBoolConfig("repl-disable-tcp-nodelay", NULL, MODIFIABLE_CONFIG, server.repl_disable_tcp_nodelay, 0, NULL, NULL),
 2245     createBoolConfig("repl-diskless-sync", NULL, MODIFIABLE_CONFIG, server.repl_diskless_sync, 0, NULL, NULL),
 2246     createBoolConfig("gopher-enabled", NULL, MODIFIABLE_CONFIG, server.gopher_enabled, 0, NULL, NULL),
 2247     createBoolConfig("aof-rewrite-incremental-fsync", NULL, MODIFIABLE_CONFIG, server.aof_rewrite_incremental_fsync, 1, NULL, NULL),
 2248     createBoolConfig("no-appendfsync-on-rewrite", NULL, MODIFIABLE_CONFIG, server.aof_no_fsync_on_rewrite, 0, NULL, NULL),
 2249     createBoolConfig("cluster-require-full-coverage", NULL, MODIFIABLE_CONFIG, server.cluster_require_full_coverage, 1, NULL, NULL),
 2250     createBoolConfig("rdb-save-incremental-fsync", NULL, MODIFIABLE_CONFIG, server.rdb_save_incremental_fsync, 1, NULL, NULL),
 2251     createBoolConfig("aof-load-truncated", NULL, MODIFIABLE_CONFIG, server.aof_load_truncated, 1, NULL, NULL),
 2252     createBoolConfig("aof-use-rdb-preamble", NULL, MODIFIABLE_CONFIG, server.aof_use_rdb_preamble, 1, NULL, NULL),
 2253     createBoolConfig("cluster-replica-no-failover", "cluster-slave-no-failover", MODIFIABLE_CONFIG, server.cluster_slave_no_failover, 0, NULL, NULL), /* Failover by default. */
 2254     createBoolConfig("replica-lazy-flush", "slave-lazy-flush", MODIFIABLE_CONFIG, server.repl_slave_lazy_flush, 0, NULL, NULL),
 2255     createBoolConfig("replica-serve-stale-data", "slave-serve-stale-data", MODIFIABLE_CONFIG, server.repl_serve_stale_data, 1, NULL, NULL),
 2256     createBoolConfig("replica-read-only", "slave-read-only", MODIFIABLE_CONFIG, server.repl_slave_ro, 1, NULL, NULL),
 2257     createBoolConfig("replica-ignore-maxmemory", "slave-ignore-maxmemory", MODIFIABLE_CONFIG, server.repl_slave_ignore_maxmemory, 1, NULL, NULL),
 2258     createBoolConfig("jemalloc-bg-thread", NULL, MODIFIABLE_CONFIG, server.jemalloc_bg_thread, 1, NULL, updateJemallocBgThread),
 2259     createBoolConfig("activedefrag", NULL, MODIFIABLE_CONFIG, server.active_defrag_enabled, 0, isValidActiveDefrag, NULL),
 2260     createBoolConfig("syslog-enabled", NULL, IMMUTABLE_CONFIG, server.syslog_enabled, 0, NULL, NULL),
 2261     createBoolConfig("cluster-enabled", NULL, IMMUTABLE_CONFIG, server.cluster_enabled, 0, NULL, NULL),
 2262     createBoolConfig("appendonly", NULL, MODIFIABLE_CONFIG, server.aof_enabled, 0, NULL, updateAppendonly),
 2263     createBoolConfig("cluster-allow-reads-when-down", NULL, MODIFIABLE_CONFIG, server.cluster_allow_reads_when_down, 0, NULL, NULL),
 2264     createBoolConfig("oom-score-adj", NULL, MODIFIABLE_CONFIG, server.oom_score_adj, 0, NULL, updateOOMScoreAdj),
 2265 
 2266     /* String Configs */
 2267     createStringConfig("aclfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.acl_filename, "", NULL, NULL),
 2268     createStringConfig("unixsocket", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.unixsocket, NULL, NULL, NULL),
 2269     createStringConfig("pidfile", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.pidfile, NULL, NULL, NULL),
 2270     createStringConfig("replica-announce-ip", "slave-announce-ip", MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.slave_announce_ip, NULL, NULL, NULL),
 2271     createStringConfig("masteruser", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.masteruser, NULL, NULL, NULL),
 2272     createStringConfig("masterauth", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.masterauth, NULL, NULL, NULL),
 2273     createStringConfig("cluster-announce-ip", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.cluster_announce_ip, NULL, NULL, NULL),
 2274     createStringConfig("syslog-ident", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.syslog_ident, "redis", NULL, NULL),
 2275     createStringConfig("dbfilename", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.rdb_filename, "dump.rdb", isValidDBfilename, NULL),
 2276     createStringConfig("appendfilename", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.aof_filename, "appendonly.aof", isValidAOFfilename, NULL),
 2277     createStringConfig("server_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.server_cpulist, NULL, NULL, NULL),
 2278     createStringConfig("bio_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bio_cpulist, NULL, NULL, NULL),
 2279     createStringConfig("aof_rewrite_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.aof_rewrite_cpulist, NULL, NULL, NULL),
 2280     createStringConfig("bgsave_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bgsave_cpulist, NULL, NULL, NULL),
 2281 
 2282     /* Enum Configs */
 2283     createEnumConfig("supervised", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, server.supervised_mode, SUPERVISED_NONE, NULL, NULL),
 2284     createEnumConfig("syslog-facility", NULL, IMMUTABLE_CONFIG, syslog_facility_enum, server.syslog_facility, LOG_LOCAL0, NULL, NULL),
 2285     createEnumConfig("repl-diskless-load", NULL, MODIFIABLE_CONFIG, repl_diskless_load_enum, server.repl_diskless_load, REPL_DISKLESS_LOAD_DISABLED, NULL, NULL),
 2286     createEnumConfig("loglevel", NULL, MODIFIABLE_CONFIG, loglevel_enum, server.verbosity, LL_NOTICE, NULL, NULL),
 2287     createEnumConfig("maxmemory-policy", NULL, MODIFIABLE_CONFIG, maxmemory_policy_enum, server.maxmemory_policy, MAXMEMORY_NO_EVICTION, NULL, NULL),
 2288     createEnumConfig("appendfsync", NULL, MODIFIABLE_CONFIG, aof_fsync_enum, server.aof_fsync, AOF_FSYNC_EVERYSEC, NULL, NULL),
 2289 
 2290     /* Integer configs */
 2291     createIntConfig("databases", NULL, IMMUTABLE_CONFIG, 1, INT_MAX, server.dbnum, 16, INTEGER_CONFIG, NULL, NULL),
 2292     createIntConfig("port", NULL, IMMUTABLE_CONFIG, 0, 65535, server.port, 6379, INTEGER_CONFIG, NULL, NULL), /* TCP port. */
 2293     createIntConfig("io-threads", NULL, IMMUTABLE_CONFIG, 1, 128, server.io_threads_num, 1, INTEGER_CONFIG, NULL, NULL), /* Single threaded by default */
 2294     createIntConfig("auto-aof-rewrite-percentage", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.aof_rewrite_perc, 100, INTEGER_CONFIG, NULL, NULL),
 2295     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. */
 2296     createIntConfig("list-max-ziplist-size", NULL, MODIFIABLE_CONFIG, INT_MIN, INT_MAX, server.list_max_ziplist_size, -2, INTEGER_CONFIG, NULL, NULL),
 2297     createIntConfig("tcp-keepalive", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tcpkeepalive, 300, INTEGER_CONFIG, NULL, NULL),
 2298     createIntConfig("cluster-migration-barrier", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.cluster_migration_barrier, 1, INTEGER_CONFIG, NULL, NULL),
 2299     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) */
 2300     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) */
 2301     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% */
 2302     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 */
 2303     createIntConfig("lfu-log-factor", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.lfu_log_factor, 10, INTEGER_CONFIG, NULL, NULL),
 2304     createIntConfig("lfu-decay-time", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.lfu_decay_time, 1, INTEGER_CONFIG, NULL, NULL),
 2305     createIntConfig("replica-priority", "slave-priority", MODIFIABLE_CONFIG, 0, INT_MAX, server.slave_priority, 100, INTEGER_CONFIG, NULL, NULL),
 2306     createIntConfig("repl-diskless-sync-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_diskless_sync_delay, 5, INTEGER_CONFIG, NULL, NULL),
 2307     createIntConfig("maxmemory-samples", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.maxmemory_samples, 5, INTEGER_CONFIG, NULL, NULL),
 2308     createIntConfig("timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.maxidletime, 0, INTEGER_CONFIG, NULL, NULL), /* Default client timeout: infinite */
 2309     createIntConfig("replica-announce-port", "slave-announce-port", MODIFIABLE_CONFIG, 0, 65535, server.slave_announce_port, 0, INTEGER_CONFIG, NULL, NULL),
 2310     createIntConfig("tcp-backlog", NULL, IMMUTABLE_CONFIG, 0, INT_MAX, server.tcp_backlog, 511, INTEGER_CONFIG, NULL, NULL), /* TCP listen backlog. */
 2311     createIntConfig("cluster-announce-bus-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_bus_port, 0, INTEGER_CONFIG, NULL, NULL), /* Default: Use +10000 offset. */
 2312     createIntConfig("cluster-announce-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.cluster_announce_port, 0, INTEGER_CONFIG, NULL, NULL), /* Use server.port */
 2313     createIntConfig("repl-timeout", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_timeout, 60, INTEGER_CONFIG, NULL, NULL),
 2314     createIntConfig("repl-ping-replica-period", "repl-ping-slave-period", MODIFIABLE_CONFIG, 1, INT_MAX, server.repl_ping_slave_period, 10, INTEGER_CONFIG, NULL, NULL),
 2315     createIntConfig("list-compress-depth", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.list_compress_depth, 0, INTEGER_CONFIG, NULL, NULL),
 2316     createIntConfig("rdb-key-save-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.rdb_key_save_delay, 0, INTEGER_CONFIG, NULL, NULL),
 2317     createIntConfig("key-load-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.key_load_delay, 0, INTEGER_CONFIG, NULL, NULL),
 2318     createIntConfig("active-expire-effort", NULL, MODIFIABLE_CONFIG, 1, 10, server.active_expire_effort, 1, INTEGER_CONFIG, NULL, NULL), /* From 1 to 10. */
 2319     createIntConfig("hz", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ),
 2320     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),
 2321     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),
 2322 
 2323     /* Unsigned int configs */
 2324     createUIntConfig("maxclients", NULL, MODIFIABLE_CONFIG, 1, UINT_MAX, server.maxclients, 10000, INTEGER_CONFIG, NULL, updateMaxclients),
 2325 
 2326     /* Unsigned Long configs */
 2327     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 */
 2328     createULongConfig("slowlog-max-len", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.slowlog_max_len, 128, INTEGER_CONFIG, NULL, NULL),
 2329     createULongConfig("acllog-max-len", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.acllog_max_len, 128, INTEGER_CONFIG, NULL, NULL),
 2330 
 2331     /* Long Long configs */
 2332     createLongLongConfig("lua-time-limit", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.lua_time_limit, 5000, INTEGER_CONFIG, NULL, NULL),/* milliseconds */
 2333     createLongLongConfig("cluster-node-timeout", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.cluster_node_timeout, 15000, INTEGER_CONFIG, NULL, NULL),
 2334     createLongLongConfig("slowlog-log-slower-than", NULL, MODIFIABLE_CONFIG, -1, LLONG_MAX, server.slowlog_log_slower_than, 10000, INTEGER_CONFIG, NULL, NULL),
 2335     createLongLongConfig("latency-monitor-threshold", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.latency_monitor_threshold, 0, INTEGER_CONFIG, NULL, NULL),
 2336     createLongLongConfig("proto-max-bulk-len", NULL, MODIFIABLE_CONFIG, 1024*1024, LLONG_MAX, server.proto_max_bulk_len, 512ll*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */
 2337     createLongLongConfig("stream-node-max-entries", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.stream_node_max_entries, 100, INTEGER_CONFIG, NULL, NULL),
 2338     createLongLongConfig("repl-backlog-size", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, server.repl_backlog_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */
 2339 
 2340     /* Unsigned Long Long configs */
 2341     createULongLongConfig("maxmemory", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),
 2342 
 2343     /* Size_t configs */
 2344     createSizeTConfig("hash-max-ziplist-entries", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hash_max_ziplist_entries, 512, INTEGER_CONFIG, NULL, NULL),
 2345     createSizeTConfig("set-max-intset-entries", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.set_max_intset_entries, 512, INTEGER_CONFIG, NULL, NULL),
 2346     createSizeTConfig("zset-max-ziplist-entries", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_ziplist_entries, 128, INTEGER_CONFIG, NULL, NULL),
 2347     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 */
 2348     createSizeTConfig("hash-max-ziplist-value", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hash_max_ziplist_value, 64, MEMORY_CONFIG, NULL, NULL),
 2349     createSizeTConfig("stream-node-max-bytes", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.stream_node_max_bytes, 4096, MEMORY_CONFIG, NULL, NULL),
 2350     createSizeTConfig("zset-max-ziplist-value", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_ziplist_value, 64, MEMORY_CONFIG, NULL, NULL),
 2351     createSizeTConfig("hll-sparse-max-bytes", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hll_sparse_max_bytes, 3000, MEMORY_CONFIG, NULL, NULL),
 2352     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. */
 2353 
 2354     /* Other configs */
 2355     createTimeTConfig("repl-backlog-ttl", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.repl_backlog_time_limit, 60*60, INTEGER_CONFIG, NULL, NULL), /* Default: 1 hour */
 2356     createOffTConfig("auto-aof-rewrite-min-size", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.aof_rewrite_min_size, 64*1024*1024, MEMORY_CONFIG, NULL, NULL),
 2357 
 2358 #ifdef USE_OPENSSL
 2359     createIntConfig("tls-port", NULL, IMMUTABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, updateTlsCfgInt), /* TCP port. */
 2360     createIntConfig("tls-session-cache-size", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_size, 20*1024, INTEGER_CONFIG, NULL, updateTlsCfgInt),
 2361     createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, updateTlsCfgInt),
 2362     createBoolConfig("tls-cluster", NULL, MODIFIABLE_CONFIG, server.tls_cluster, 0, NULL, updateTlsCfgBool),
 2363     createBoolConfig("tls-replication", NULL, MODIFIABLE_CONFIG, server.tls_replication, 0, NULL, updateTlsCfgBool),
 2364     createEnumConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, tls_auth_clients_enum, server.tls_auth_clients, TLS_CLIENT_AUTH_YES, NULL, NULL),
 2365     createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.prefer_server_ciphers, 0, NULL, updateTlsCfgBool),
 2366     createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.session_caching, 1, NULL, updateTlsCfgBool),
 2367     createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg),
 2368     createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, updateTlsCfg),
 2369     createStringConfig("tls-dh-params-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, updateTlsCfg),
 2370     createStringConfig("tls-ca-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, updateTlsCfg),
 2371     createStringConfig("tls-ca-cert-dir", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, updateTlsCfg),
 2372     createStringConfig("tls-protocols", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.protocols, NULL, NULL, updateTlsCfg),
 2373     createStringConfig("tls-ciphers", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphers, NULL, NULL, updateTlsCfg),
 2374     createStringConfig("tls-ciphersuites", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphersuites, NULL, NULL, updateTlsCfg),
 2375 #endif
 2376 
 2377     /* NULL Terminator */
 2378     {NULL}
 2379 };
 2380 
 2381 /*-----------------------------------------------------------------------------
 2382  * CONFIG command entry point
 2383  *----------------------------------------------------------------------------*/
 2384 
 2385 void configCommand(client *c) {
 2386     /* Only allow CONFIG GET while loading. */
 2387     if (server.loading && strcasecmp(c->argv[1]->ptr,"get")) {
 2388         addReplyError(c,"Only CONFIG GET is allowed during loading");
 2389         return;
 2390     }
 2391 
 2392     if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
 2393         const char *help[] = {
 2394 "GET <pattern> -- Return parameters matching the glob-like <pattern> and their values.",
 2395 "SET <parameter> <value> -- Set parameter to value.",
 2396 "RESETSTAT -- Reset statistics reported by INFO.",
 2397 "REWRITE -- Rewrite the configuration file.",
 2398 NULL
 2399         };
 2400         addReplyHelp(c, help);
 2401     } else if (!strcasecmp(c->argv[1]->ptr,"set") && c->argc == 4) {
 2402         configSetCommand(c);
 2403     } else if (!strcasecmp(c->argv[1]->ptr,"get") && c->argc == 3) {
 2404         configGetCommand(c);
 2405     } else if (!strcasecmp(c->argv[1]->ptr,"resetstat") && c->argc == 2) {
 2406         resetServerStats();
 2407         resetCommandTableStats();
 2408         addReply(c,shared.ok);
 2409     } else if (!strcasecmp(c->argv[1]->ptr,"rewrite") && c->argc == 2) {
 2410         if (server.configfile == NULL) {
 2411             addReplyError(c,"The server is running without a config file");
 2412             return;
 2413         }
 2414         if (rewriteConfig(server.configfile, 0) == -1) {
 2415             serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(errno));
 2416             addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno));
 2417         } else {
 2418             serverLog(LL_WARNING,"CONFIG REWRITE executed with success.");
 2419             addReply(c,shared.ok);
 2420         }
 2421     } else {
 2422         addReplySubcommandSyntaxError(c);
 2423         return;
 2424     }
 2425 }