xref: /redis-3.2.3/src/config.c (revision 34882878)
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-random",MAXMEMORY_VOLATILE_RANDOM},
49     {"volatile-ttl",MAXMEMORY_VOLATILE_TTL},
50     {"allkeys-lru",MAXMEMORY_ALLKEYS_LRU},
51     {"allkeys-random",MAXMEMORY_ALLKEYS_RANDOM},
52     {"noeviction",MAXMEMORY_NO_EVICTION},
53     {NULL, 0}
54 };
55 
56 configEnum syslog_facility_enum[] = {
57     {"user",    LOG_USER},
58     {"local0",  LOG_LOCAL0},
59     {"local1",  LOG_LOCAL1},
60     {"local2",  LOG_LOCAL2},
61     {"local3",  LOG_LOCAL3},
62     {"local4",  LOG_LOCAL4},
63     {"local5",  LOG_LOCAL5},
64     {"local6",  LOG_LOCAL6},
65     {"local7",  LOG_LOCAL7},
66     {NULL, 0}
67 };
68 
69 configEnum loglevel_enum[] = {
70     {"debug", LL_DEBUG},
71     {"verbose", LL_VERBOSE},
72     {"notice", LL_NOTICE},
73     {"warning", LL_WARNING},
74     {NULL,0}
75 };
76 
77 configEnum supervised_mode_enum[] = {
78     {"upstart", SUPERVISED_UPSTART},
79     {"systemd", SUPERVISED_SYSTEMD},
80     {"auto", SUPERVISED_AUTODETECT},
81     {"no", SUPERVISED_NONE},
82     {NULL, 0}
83 };
84 
85 configEnum aof_fsync_enum[] = {
86     {"everysec", AOF_FSYNC_EVERYSEC},
87     {"always", AOF_FSYNC_ALWAYS},
88     {"no", AOF_FSYNC_NO},
89     {NULL, 0}
90 };
91 
92 /* Output buffer limits presets. */
93 clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
94     {0, 0, 0}, /* normal */
95     {1024*1024*256, 1024*1024*64, 60}, /* slave */
96     {1024*1024*32, 1024*1024*8, 60}  /* pubsub */
97 };
98 
99 /*-----------------------------------------------------------------------------
100  * Enum access functions
101  *----------------------------------------------------------------------------*/
102 
103 /* Get enum value from name. If there is no match INT_MIN is returned. */
104 int configEnumGetValue(configEnum *ce, char *name) {
105     while(ce->name != NULL) {
106         if (!strcasecmp(ce->name,name)) return ce->val;
107         ce++;
108     }
109     return INT_MIN;
110 }
111 
112 /* Get enum name from value. If no match is found NULL is returned. */
113 const char *configEnumGetName(configEnum *ce, int val) {
114     while(ce->name != NULL) {
115         if (ce->val == val) return ce->name;
116         ce++;
117     }
118     return NULL;
119 }
120 
121 /* Wrapper for configEnumGetName() returning "unknown" insetad of NULL if
122  * there is no match. */
123 const char *configEnumGetNameOrUnknown(configEnum *ce, int val) {
124     const char *name = configEnumGetName(ce,val);
125     return name ? name : "unknown";
126 }
127 
128 /* Used for INFO generation. */
129 const char *maxmemoryToString(void) {
130     return configEnumGetNameOrUnknown(maxmemory_policy_enum,server.maxmemory);
131 }
132 
133 /*-----------------------------------------------------------------------------
134  * Config file parsing
135  *----------------------------------------------------------------------------*/
136 
137 int yesnotoi(char *s) {
138     if (!strcasecmp(s,"yes")) return 1;
139     else if (!strcasecmp(s,"no")) return 0;
140     else return -1;
141 }
142 
143 void appendServerSaveParams(time_t seconds, int changes) {
144     server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));
145     server.saveparams[server.saveparamslen].seconds = seconds;
146     server.saveparams[server.saveparamslen].changes = changes;
147     server.saveparamslen++;
148 }
149 
150 void resetServerSaveParams(void) {
151     zfree(server.saveparams);
152     server.saveparams = NULL;
153     server.saveparamslen = 0;
154 }
155 
156 void loadServerConfigFromString(char *config) {
157     char *err = NULL;
158     int linenum = 0, totlines, i;
159     int slaveof_linenum = 0;
160     sds *lines;
161 
162     lines = sdssplitlen(config,strlen(config),"\n",1,&totlines);
163 
164     for (i = 0; i < totlines; i++) {
165         sds *argv;
166         int argc;
167 
168         linenum = i+1;
169         lines[i] = sdstrim(lines[i]," \t\r\n");
170 
171         /* Skip comments and blank lines */
172         if (lines[i][0] == '#' || lines[i][0] == '\0') continue;
173 
174         /* Split into arguments */
175         argv = sdssplitargs(lines[i],&argc);
176         if (argv == NULL) {
177             err = "Unbalanced quotes in configuration line";
178             goto loaderr;
179         }
180 
181         /* Skip this line if the resulting command vector is empty. */
182         if (argc == 0) {
183             sdsfreesplitres(argv,argc);
184             continue;
185         }
186         sdstolower(argv[0]);
187 
188         /* Execute config directives */
189         if (!strcasecmp(argv[0],"timeout") && argc == 2) {
190             server.maxidletime = atoi(argv[1]);
191             if (server.maxidletime < 0) {
192                 err = "Invalid timeout value"; goto loaderr;
193             }
194         } else if (!strcasecmp(argv[0],"tcp-keepalive") && argc == 2) {
195             server.tcpkeepalive = atoi(argv[1]);
196             if (server.tcpkeepalive < 0) {
197                 err = "Invalid tcp-keepalive value"; goto loaderr;
198             }
199         } else if (!strcasecmp(argv[0],"port") && argc == 2) {
200             server.port = atoi(argv[1]);
201             if (server.port < 0 || server.port > 65535) {
202                 err = "Invalid port"; goto loaderr;
203             }
204         } else if (!strcasecmp(argv[0],"tcp-backlog") && argc == 2) {
205             server.tcp_backlog = atoi(argv[1]);
206             if (server.tcp_backlog < 0) {
207                 err = "Invalid backlog value"; goto loaderr;
208             }
209         } else if (!strcasecmp(argv[0],"bind") && argc >= 2) {
210             int j, addresses = argc-1;
211 
212             if (addresses > CONFIG_BINDADDR_MAX) {
213                 err = "Too many bind addresses specified"; goto loaderr;
214             }
215             for (j = 0; j < addresses; j++)
216                 server.bindaddr[j] = zstrdup(argv[j+1]);
217             server.bindaddr_count = addresses;
218         } else if (!strcasecmp(argv[0],"unixsocket") && argc == 2) {
219             server.unixsocket = zstrdup(argv[1]);
220         } else if (!strcasecmp(argv[0],"unixsocketperm") && argc == 2) {
221             errno = 0;
222             server.unixsocketperm = (mode_t)strtol(argv[1], NULL, 8);
223             if (errno || server.unixsocketperm > 0777) {
224                 err = "Invalid socket file permissions"; goto loaderr;
225             }
226         } else if (!strcasecmp(argv[0],"save")) {
227             if (argc == 3) {
228                 int seconds = atoi(argv[1]);
229                 int changes = atoi(argv[2]);
230                 if (seconds < 1 || changes < 0) {
231                     err = "Invalid save parameters"; goto loaderr;
232                 }
233                 appendServerSaveParams(seconds,changes);
234             } else if (argc == 2 && !strcasecmp(argv[1],"")) {
235                 resetServerSaveParams();
236             }
237         } else if (!strcasecmp(argv[0],"dir") && argc == 2) {
238             if (chdir(argv[1]) == -1) {
239                 serverLog(LL_WARNING,"Can't chdir to '%s': %s",
240                     argv[1], strerror(errno));
241                 exit(1);
242             }
243         } else if (!strcasecmp(argv[0],"loglevel") && argc == 2) {
244             server.verbosity = configEnumGetValue(loglevel_enum,argv[1]);
245             if (server.verbosity == INT_MIN) {
246                 err = "Invalid log level. "
247                       "Must be one of debug, verbose, notice, warning";
248                 goto loaderr;
249             }
250         } else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
251             FILE *logfp;
252 
253             zfree(server.logfile);
254             server.logfile = zstrdup(argv[1]);
255             if (server.logfile[0] != '\0') {
256                 /* Test if we are able to open the file. The server will not
257                  * be able to abort just for this problem later... */
258                 logfp = fopen(server.logfile,"a");
259                 if (logfp == NULL) {
260                     err = sdscatprintf(sdsempty(),
261                         "Can't open the log file: %s", strerror(errno));
262                     goto loaderr;
263                 }
264                 fclose(logfp);
265             }
266         } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) {
267             if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) {
268                 err = "argument must be 'yes' or 'no'"; goto loaderr;
269             }
270         } else if (!strcasecmp(argv[0],"syslog-ident") && argc == 2) {
271             if (server.syslog_ident) zfree(server.syslog_ident);
272             server.syslog_ident = zstrdup(argv[1]);
273         } else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) {
274             server.syslog_facility =
275                 configEnumGetValue(syslog_facility_enum,argv[1]);
276             if (server.syslog_facility == INT_MIN) {
277                 err = "Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7";
278                 goto loaderr;
279             }
280         } else if (!strcasecmp(argv[0],"databases") && argc == 2) {
281             server.dbnum = atoi(argv[1]);
282             if (server.dbnum < 1) {
283                 err = "Invalid number of databases"; goto loaderr;
284             }
285         } else if (!strcasecmp(argv[0],"include") && argc == 2) {
286             loadServerConfig(argv[1],NULL);
287         } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
288             server.maxclients = atoi(argv[1]);
289             if (server.maxclients < 1) {
290                 err = "Invalid max clients limit"; goto loaderr;
291             }
292         } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
293             server.maxmemory = memtoll(argv[1],NULL);
294         } else if (!strcasecmp(argv[0],"maxmemory-policy") && argc == 2) {
295             server.maxmemory_policy =
296                 configEnumGetValue(maxmemory_policy_enum,argv[1]);
297             if (server.maxmemory_policy == INT_MIN) {
298                 err = "Invalid maxmemory policy";
299                 goto loaderr;
300             }
301         } else if (!strcasecmp(argv[0],"maxmemory-samples") && argc == 2) {
302             server.maxmemory_samples = atoi(argv[1]);
303             if (server.maxmemory_samples <= 0) {
304                 err = "maxmemory-samples must be 1 or greater";
305                 goto loaderr;
306             }
307         } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
308             slaveof_linenum = linenum;
309             server.masterhost = sdsnew(argv[1]);
310             server.masterport = atoi(argv[2]);
311             server.repl_state = REPL_STATE_CONNECT;
312         } else if (!strcasecmp(argv[0],"repl-ping-slave-period") && argc == 2) {
313             server.repl_ping_slave_period = atoi(argv[1]);
314             if (server.repl_ping_slave_period <= 0) {
315                 err = "repl-ping-slave-period must be 1 or greater";
316                 goto loaderr;
317             }
318         } else if (!strcasecmp(argv[0],"repl-timeout") && argc == 2) {
319             server.repl_timeout = atoi(argv[1]);
320             if (server.repl_timeout <= 0) {
321                 err = "repl-timeout must be 1 or greater";
322                 goto loaderr;
323             }
324         } else if (!strcasecmp(argv[0],"repl-disable-tcp-nodelay") && argc==2) {
325             if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) {
326                 err = "argument must be 'yes' or 'no'"; goto loaderr;
327             }
328         } else if (!strcasecmp(argv[0],"repl-diskless-sync") && argc==2) {
329             if ((server.repl_diskless_sync = yesnotoi(argv[1])) == -1) {
330                 err = "argument must be 'yes' or 'no'"; goto loaderr;
331             }
332         } else if (!strcasecmp(argv[0],"repl-diskless-sync-delay") && argc==2) {
333             server.repl_diskless_sync_delay = atoi(argv[1]);
334             if (server.repl_diskless_sync_delay < 0) {
335                 err = "repl-diskless-sync-delay can't be negative";
336                 goto loaderr;
337             }
338         } else if (!strcasecmp(argv[0],"repl-backlog-size") && argc == 2) {
339             long long size = memtoll(argv[1],NULL);
340             if (size <= 0) {
341                 err = "repl-backlog-size must be 1 or greater.";
342                 goto loaderr;
343             }
344             resizeReplicationBacklog(size);
345         } else if (!strcasecmp(argv[0],"repl-backlog-ttl") && argc == 2) {
346             server.repl_backlog_time_limit = atoi(argv[1]);
347             if (server.repl_backlog_time_limit < 0) {
348                 err = "repl-backlog-ttl can't be negative ";
349                 goto loaderr;
350             }
351         } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) {
352             server.masterauth = zstrdup(argv[1]);
353         } else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) {
354             if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) {
355                 err = "argument must be 'yes' or 'no'"; goto loaderr;
356             }
357         } else if (!strcasecmp(argv[0],"slave-read-only") && argc == 2) {
358             if ((server.repl_slave_ro = yesnotoi(argv[1])) == -1) {
359                 err = "argument must be 'yes' or 'no'"; goto loaderr;
360             }
361         } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
362             if ((server.rdb_compression = yesnotoi(argv[1])) == -1) {
363                 err = "argument must be 'yes' or 'no'"; goto loaderr;
364             }
365         } else if (!strcasecmp(argv[0],"rdbchecksum") && argc == 2) {
366             if ((server.rdb_checksum = yesnotoi(argv[1])) == -1) {
367                 err = "argument must be 'yes' or 'no'"; goto loaderr;
368             }
369         } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) {
370             if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
371                 err = "argument must be 'yes' or 'no'"; goto loaderr;
372             }
373         } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
374             if ((server.daemonize = yesnotoi(argv[1])) == -1) {
375                 err = "argument must be 'yes' or 'no'"; goto loaderr;
376             }
377         } else if (!strcasecmp(argv[0],"hz") && argc == 2) {
378             server.hz = atoi(argv[1]);
379             if (server.hz < CONFIG_MIN_HZ) server.hz = CONFIG_MIN_HZ;
380             if (server.hz > CONFIG_MAX_HZ) server.hz = CONFIG_MAX_HZ;
381         } else if (!strcasecmp(argv[0],"appendonly") && argc == 2) {
382             int yes;
383 
384             if ((yes = yesnotoi(argv[1])) == -1) {
385                 err = "argument must be 'yes' or 'no'"; goto loaderr;
386             }
387             server.aof_state = yes ? AOF_ON : AOF_OFF;
388         } else if (!strcasecmp(argv[0],"appendfilename") && argc == 2) {
389             if (!pathIsBaseName(argv[1])) {
390                 err = "appendfilename can't be a path, just a filename";
391                 goto loaderr;
392             }
393             zfree(server.aof_filename);
394             server.aof_filename = zstrdup(argv[1]);
395         } else if (!strcasecmp(argv[0],"no-appendfsync-on-rewrite")
396                    && argc == 2) {
397             if ((server.aof_no_fsync_on_rewrite= yesnotoi(argv[1])) == -1) {
398                 err = "argument must be 'yes' or 'no'"; goto loaderr;
399             }
400         } else if (!strcasecmp(argv[0],"appendfsync") && argc == 2) {
401             server.aof_fsync = configEnumGetValue(aof_fsync_enum,argv[1]);
402             if (server.aof_fsync == INT_MIN) {
403                 err = "argument must be 'no', 'always' or 'everysec'";
404                 goto loaderr;
405             }
406         } else if (!strcasecmp(argv[0],"auto-aof-rewrite-percentage") &&
407                    argc == 2)
408         {
409             server.aof_rewrite_perc = atoi(argv[1]);
410             if (server.aof_rewrite_perc < 0) {
411                 err = "Invalid negative percentage for AOF auto rewrite";
412                 goto loaderr;
413             }
414         } else if (!strcasecmp(argv[0],"auto-aof-rewrite-min-size") &&
415                    argc == 2)
416         {
417             server.aof_rewrite_min_size = memtoll(argv[1],NULL);
418         } else if (!strcasecmp(argv[0],"aof-rewrite-incremental-fsync") &&
419                    argc == 2)
420         {
421             if ((server.aof_rewrite_incremental_fsync =
422                  yesnotoi(argv[1])) == -1) {
423                 err = "argument must be 'yes' or 'no'"; goto loaderr;
424             }
425         } else if (!strcasecmp(argv[0],"aof-load-truncated") && argc == 2) {
426             if ((server.aof_load_truncated = yesnotoi(argv[1])) == -1) {
427                 err = "argument must be 'yes' or 'no'"; goto loaderr;
428             }
429         } else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
430             if (strlen(argv[1]) > CONFIG_AUTHPASS_MAX_LEN) {
431                 err = "Password is longer than CONFIG_AUTHPASS_MAX_LEN";
432                 goto loaderr;
433             }
434             server.requirepass = zstrdup(argv[1]);
435         } else if (!strcasecmp(argv[0],"pidfile") && argc == 2) {
436             zfree(server.pidfile);
437             server.pidfile = zstrdup(argv[1]);
438         } else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) {
439             if (!pathIsBaseName(argv[1])) {
440                 err = "dbfilename can't be a path, just a filename";
441                 goto loaderr;
442             }
443             zfree(server.rdb_filename);
444             server.rdb_filename = zstrdup(argv[1]);
445         } else if (!strcasecmp(argv[0],"hash-max-ziplist-entries") && argc == 2) {
446             server.hash_max_ziplist_entries = memtoll(argv[1], NULL);
447         } else if (!strcasecmp(argv[0],"hash-max-ziplist-value") && argc == 2) {
448             server.hash_max_ziplist_value = memtoll(argv[1], NULL);
449         } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
450             /* DEAD OPTION */
451         } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
452             /* DEAD OPTION */
453         } else if (!strcasecmp(argv[0],"list-max-ziplist-size") && argc == 2) {
454             server.list_max_ziplist_size = atoi(argv[1]);
455         } else if (!strcasecmp(argv[0],"list-compress-depth") && argc == 2) {
456             server.list_compress_depth = atoi(argv[1]);
457         } else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) {
458             server.set_max_intset_entries = memtoll(argv[1], NULL);
459         } else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) {
460             server.zset_max_ziplist_entries = memtoll(argv[1], NULL);
461         } else if (!strcasecmp(argv[0],"zset-max-ziplist-value") && argc == 2) {
462             server.zset_max_ziplist_value = memtoll(argv[1], NULL);
463         } else if (!strcasecmp(argv[0],"hll-sparse-max-bytes") && argc == 2) {
464             server.hll_sparse_max_bytes = memtoll(argv[1], NULL);
465         } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) {
466             struct redisCommand *cmd = lookupCommand(argv[1]);
467             int retval;
468 
469             if (!cmd) {
470                 err = "No such command in rename-command";
471                 goto loaderr;
472             }
473 
474             /* If the target command name is the empty string we just
475              * remove it from the command table. */
476             retval = dictDelete(server.commands, argv[1]);
477             serverAssert(retval == DICT_OK);
478 
479             /* Otherwise we re-add the command under a different name. */
480             if (sdslen(argv[2]) != 0) {
481                 sds copy = sdsdup(argv[2]);
482 
483                 retval = dictAdd(server.commands, copy, cmd);
484                 if (retval != DICT_OK) {
485                     sdsfree(copy);
486                     err = "Target command name already exists"; goto loaderr;
487                 }
488             }
489         } else if (!strcasecmp(argv[0],"cluster-enabled") && argc == 2) {
490             if ((server.cluster_enabled = yesnotoi(argv[1])) == -1) {
491                 err = "argument must be 'yes' or 'no'"; goto loaderr;
492             }
493         } else if (!strcasecmp(argv[0],"cluster-config-file") && argc == 2) {
494             zfree(server.cluster_configfile);
495             server.cluster_configfile = zstrdup(argv[1]);
496         } else if (!strcasecmp(argv[0],"cluster-require-full-coverage") &&
497                     argc == 2)
498         {
499             if ((server.cluster_require_full_coverage = yesnotoi(argv[1])) == -1)
500             {
501                 err = "argument must be 'yes' or 'no'"; goto loaderr;
502             }
503         } else if (!strcasecmp(argv[0],"cluster-node-timeout") && argc == 2) {
504             server.cluster_node_timeout = strtoll(argv[1],NULL,10);
505             if (server.cluster_node_timeout <= 0) {
506                 err = "cluster node timeout must be 1 or greater"; goto loaderr;
507             }
508         } else if (!strcasecmp(argv[0],"cluster-migration-barrier")
509                    && argc == 2)
510         {
511             server.cluster_migration_barrier = atoi(argv[1]);
512             if (server.cluster_migration_barrier < 0) {
513                 err = "cluster migration barrier must zero or positive";
514                 goto loaderr;
515             }
516         } else if (!strcasecmp(argv[0],"cluster-slave-validity-factor")
517                    && argc == 2)
518         {
519             server.cluster_slave_validity_factor = atoi(argv[1]);
520             if (server.cluster_slave_validity_factor < 0) {
521                 err = "cluster slave validity factor must be zero or positive";
522                 goto loaderr;
523             }
524         } else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) {
525             server.lua_time_limit = strtoll(argv[1],NULL,10);
526         } else if (!strcasecmp(argv[0],"slowlog-log-slower-than") &&
527                    argc == 2)
528         {
529             server.slowlog_log_slower_than = strtoll(argv[1],NULL,10);
530         } else if (!strcasecmp(argv[0],"latency-monitor-threshold") &&
531                    argc == 2)
532         {
533             server.latency_monitor_threshold = strtoll(argv[1],NULL,10);
534             if (server.latency_monitor_threshold < 0) {
535                 err = "The latency threshold can't be negative";
536                 goto loaderr;
537             }
538         } else if (!strcasecmp(argv[0],"slowlog-max-len") && argc == 2) {
539             server.slowlog_max_len = strtoll(argv[1],NULL,10);
540         } else if (!strcasecmp(argv[0],"client-output-buffer-limit") &&
541                    argc == 5)
542         {
543             int class = getClientTypeByName(argv[1]);
544             unsigned long long hard, soft;
545             int soft_seconds;
546 
547             if (class == -1) {
548                 err = "Unrecognized client limit class";
549                 goto loaderr;
550             }
551             hard = memtoll(argv[2],NULL);
552             soft = memtoll(argv[3],NULL);
553             soft_seconds = atoi(argv[4]);
554             if (soft_seconds < 0) {
555                 err = "Negative number of seconds in soft limit is invalid";
556                 goto loaderr;
557             }
558             server.client_obuf_limits[class].hard_limit_bytes = hard;
559             server.client_obuf_limits[class].soft_limit_bytes = soft;
560             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
561         } else if (!strcasecmp(argv[0],"stop-writes-on-bgsave-error") &&
562                    argc == 2) {
563             if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) {
564                 err = "argument must be 'yes' or 'no'"; goto loaderr;
565             }
566         } else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) {
567             server.slave_priority = atoi(argv[1]);
568         } else if (!strcasecmp(argv[0],"min-slaves-to-write") && argc == 2) {
569             server.repl_min_slaves_to_write = atoi(argv[1]);
570             if (server.repl_min_slaves_to_write < 0) {
571                 err = "Invalid value for min-slaves-to-write."; goto loaderr;
572             }
573         } else if (!strcasecmp(argv[0],"min-slaves-max-lag") && argc == 2) {
574             server.repl_min_slaves_max_lag = atoi(argv[1]);
575             if (server.repl_min_slaves_max_lag < 0) {
576                 err = "Invalid value for min-slaves-max-lag."; goto loaderr;
577             }
578         } else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) {
579             int flags = keyspaceEventsStringToFlags(argv[1]);
580 
581             if (flags == -1) {
582                 err = "Invalid event class character. Use 'g$lshzxeA'.";
583                 goto loaderr;
584             }
585             server.notify_keyspace_events = flags;
586         } else if (!strcasecmp(argv[0],"supervised") && argc == 2) {
587             server.supervised_mode =
588                 configEnumGetValue(supervised_mode_enum,argv[1]);
589 
590             if (server.supervised_mode == INT_MIN) {
591                 err = "Invalid option for 'supervised'. "
592                     "Allowed values: 'upstart', 'systemd', 'auto', or 'no'";
593                 goto loaderr;
594             }
595         } else if (!strcasecmp(argv[0],"sentinel")) {
596             /* argc == 1 is handled by main() as we need to enter the sentinel
597              * mode ASAP. */
598             if (argc != 1) {
599                 if (!server.sentinel_mode) {
600                     err = "sentinel directive while not in sentinel mode";
601                     goto loaderr;
602                 }
603                 err = sentinelHandleConfiguration(argv+1,argc-1);
604                 if (err) goto loaderr;
605             }
606         } else {
607             err = "Bad directive or wrong number of arguments"; goto loaderr;
608         }
609         sdsfreesplitres(argv,argc);
610     }
611 
612     /* Sanity checks. */
613     if (server.cluster_enabled && server.masterhost) {
614         linenum = slaveof_linenum;
615         i = linenum-1;
616         err = "slaveof directive not allowed in cluster mode";
617         goto loaderr;
618     }
619 
620     sdsfreesplitres(lines,totlines);
621     return;
622 
623 loaderr:
624     fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n");
625     fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
626     fprintf(stderr, ">>> '%s'\n", lines[i]);
627     fprintf(stderr, "%s\n", err);
628     exit(1);
629 }
630 
631 /* Load the server configuration from the specified filename.
632  * The function appends the additional configuration directives stored
633  * in the 'options' string to the config file before loading.
634  *
635  * Both filename and options can be NULL, in such a case are considered
636  * empty. This way loadServerConfig can be used to just load a file or
637  * just load a string. */
638 void loadServerConfig(char *filename, char *options) {
639     sds config = sdsempty();
640     char buf[CONFIG_MAX_LINE+1];
641 
642     /* Load the file content */
643     if (filename) {
644         FILE *fp;
645 
646         if (filename[0] == '-' && filename[1] == '\0') {
647             fp = stdin;
648         } else {
649             if ((fp = fopen(filename,"r")) == NULL) {
650                 serverLog(LL_WARNING,
651                     "Fatal error, can't open config file '%s'", filename);
652                 exit(1);
653             }
654         }
655         while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL)
656             config = sdscat(config,buf);
657         if (fp != stdin) fclose(fp);
658     }
659     /* Append the additional options */
660     if (options) {
661         config = sdscat(config,"\n");
662         config = sdscat(config,options);
663     }
664     loadServerConfigFromString(config);
665     sdsfree(config);
666 }
667 
668 /*-----------------------------------------------------------------------------
669  * CONFIG SET implementation
670  *----------------------------------------------------------------------------*/
671 
672 #define config_set_bool_field(_name,_var) \
673     } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \
674         int yn = yesnotoi(o->ptr); \
675         if (yn == -1) goto badfmt; \
676         _var = yn;
677 
678 #define config_set_numerical_field(_name,_var,min,max) \
679     } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \
680         if (getLongLongFromObject(o,&ll) == C_ERR || ll < 0) goto badfmt; \
681         if (min != LLONG_MIN && ll < min) goto badfmt; \
682         if (max != LLONG_MAX && ll > max) goto badfmt; \
683         _var = ll;
684 
685 #define config_set_memory_field(_name,_var) \
686     } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \
687         ll = memtoll(o->ptr,&err); \
688         if (err || ll < 0) goto badfmt; \
689         _var = ll;
690 
691 #define config_set_enum_field(_name,_var,_enumvar) \
692     } else if (!strcasecmp(c->argv[2]->ptr,_name)) { \
693         int enumval = configEnumGetValue(_enumvar,o->ptr); \
694         if (enumval == INT_MIN) goto badfmt; \
695         _var = enumval;
696 
697 #define config_set_special_field(_name) \
698     } else if (!strcasecmp(c->argv[2]->ptr,_name)) {
699 
700 #define config_set_else } else
701 
702 void configSetCommand(client *c) {
703     robj *o;
704     long long ll;
705     int err;
706     serverAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));
707     serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));
708     o = c->argv[3];
709 
710     if (0) { /* this starts the config_set macros else-if chain. */
711 
712     /* Special fields that can't be handled with general macros. */
713     config_set_special_field("dbfilename") {
714         if (!pathIsBaseName(o->ptr)) {
715             addReplyError(c, "dbfilename can't be a path, just a filename");
716             return;
717         }
718         zfree(server.rdb_filename);
719         server.rdb_filename = zstrdup(o->ptr);
720     } config_set_special_field("requirepass") {
721         if (sdslen(o->ptr) > CONFIG_AUTHPASS_MAX_LEN) goto badfmt;
722         zfree(server.requirepass);
723         server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
724     } config_set_special_field("masterauth") {
725         zfree(server.masterauth);
726         server.masterauth = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
727     } config_set_special_field("maxclients") {
728         int orig_value = server.maxclients;
729 
730         if (getLongLongFromObject(o,&ll) == C_ERR || ll < 1) goto badfmt;
731 
732         /* Try to check if the OS is capable of supporting so many FDs. */
733         server.maxclients = ll;
734         if (ll > orig_value) {
735             adjustOpenFilesLimit();
736             if (server.maxclients != ll) {
737                 addReplyErrorFormat(c,"The operating system is not able to handle the specified number of clients, try with %d", server.maxclients);
738                 server.maxclients = orig_value;
739                 return;
740             }
741             if ((unsigned int) aeGetSetSize(server.el) <
742                 server.maxclients + CONFIG_FDSET_INCR)
743             {
744                 if (aeResizeSetSize(server.el,
745                     server.maxclients + CONFIG_FDSET_INCR) == AE_ERR)
746                 {
747                     addReplyError(c,"The event loop API used by Redis is not able to handle the specified number of clients");
748                     server.maxclients = orig_value;
749                     return;
750                 }
751             }
752         }
753     } config_set_special_field("appendonly") {
754         int enable = yesnotoi(o->ptr);
755 
756         if (enable == -1) goto badfmt;
757         if (enable == 0 && server.aof_state != AOF_OFF) {
758             stopAppendOnly();
759         } else if (enable && server.aof_state == AOF_OFF) {
760             if (startAppendOnly() == C_ERR) {
761                 addReplyError(c,
762                     "Unable to turn on AOF. Check server logs.");
763                 return;
764             }
765         }
766     } config_set_special_field("save") {
767         int vlen, j;
768         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
769 
770         /* Perform sanity check before setting the new config:
771          * - Even number of args
772          * - Seconds >= 1, changes >= 0 */
773         if (vlen & 1) {
774             sdsfreesplitres(v,vlen);
775             goto badfmt;
776         }
777         for (j = 0; j < vlen; j++) {
778             char *eptr;
779             long val;
780 
781             val = strtoll(v[j], &eptr, 10);
782             if (eptr[0] != '\0' ||
783                 ((j & 1) == 0 && val < 1) ||
784                 ((j & 1) == 1 && val < 0)) {
785                 sdsfreesplitres(v,vlen);
786                 goto badfmt;
787             }
788         }
789         /* Finally set the new config */
790         resetServerSaveParams();
791         for (j = 0; j < vlen; j += 2) {
792             time_t seconds;
793             int changes;
794 
795             seconds = strtoll(v[j],NULL,10);
796             changes = strtoll(v[j+1],NULL,10);
797             appendServerSaveParams(seconds, changes);
798         }
799         sdsfreesplitres(v,vlen);
800     } config_set_special_field("dir") {
801         if (chdir((char*)o->ptr) == -1) {
802             addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
803             return;
804         }
805     } config_set_special_field("client-output-buffer-limit") {
806         int vlen, j;
807         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
808 
809         /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */
810         if (vlen % 4) {
811             sdsfreesplitres(v,vlen);
812             goto badfmt;
813         }
814 
815         /* Sanity check of single arguments, so that we either refuse the
816          * whole configuration string or accept it all, even if a single
817          * error in a single client class is present. */
818         for (j = 0; j < vlen; j++) {
819             long val;
820 
821             if ((j % 4) == 0) {
822                 if (getClientTypeByName(v[j]) == -1) {
823                     sdsfreesplitres(v,vlen);
824                     goto badfmt;
825                 }
826             } else {
827                 val = memtoll(v[j], &err);
828                 if (err || val < 0) {
829                     sdsfreesplitres(v,vlen);
830                     goto badfmt;
831                 }
832             }
833         }
834         /* Finally set the new config */
835         for (j = 0; j < vlen; j += 4) {
836             int class;
837             unsigned long long hard, soft;
838             int soft_seconds;
839 
840             class = getClientTypeByName(v[j]);
841             hard = strtoll(v[j+1],NULL,10);
842             soft = strtoll(v[j+2],NULL,10);
843             soft_seconds = strtoll(v[j+3],NULL,10);
844 
845             server.client_obuf_limits[class].hard_limit_bytes = hard;
846             server.client_obuf_limits[class].soft_limit_bytes = soft;
847             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
848         }
849         sdsfreesplitres(v,vlen);
850     } config_set_special_field("notify-keyspace-events") {
851         int flags = keyspaceEventsStringToFlags(o->ptr);
852 
853         if (flags == -1) goto badfmt;
854         server.notify_keyspace_events = flags;
855 
856     /* Boolean fields.
857      * config_set_bool_field(name,var). */
858     } config_set_bool_field(
859       "rdbcompression", server.rdb_compression) {
860     } config_set_bool_field(
861       "repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay) {
862     } config_set_bool_field(
863       "repl-diskless-sync",server.repl_diskless_sync) {
864     } config_set_bool_field(
865       "cluster-require-full-coverage",server.cluster_require_full_coverage) {
866     } config_set_bool_field(
867       "aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync) {
868     } config_set_bool_field(
869       "aof-load-truncated",server.aof_load_truncated) {
870     } config_set_bool_field(
871       "slave-serve-stale-data",server.repl_serve_stale_data) {
872     } config_set_bool_field(
873       "slave-read-only",server.repl_slave_ro) {
874     } config_set_bool_field(
875       "activerehashing",server.activerehashing) {
876     } config_set_bool_field(
877       "stop-writes-on-bgsave-error",server.stop_writes_on_bgsave_err) {
878 
879     /* Numerical fields.
880      * config_set_numerical_field(name,var,min,max) */
881     } config_set_numerical_field(
882       "tcp-keepalive",server.tcpkeepalive,0,LLONG_MAX) {
883     } config_set_numerical_field(
884       "maxmemory-samples",server.maxmemory_samples,1,LLONG_MAX) {
885     } config_set_numerical_field(
886       "timeout",server.maxidletime,0,LONG_MAX) {
887     } config_set_numerical_field(
888       "auto-aof-rewrite-percentage",server.aof_rewrite_perc,0,LLONG_MAX){
889     } config_set_numerical_field(
890       "auto-aof-rewrite-min-size",server.aof_rewrite_min_size,0,LLONG_MAX) {
891     } config_set_numerical_field(
892       "hash-max-ziplist-entries",server.hash_max_ziplist_entries,0,LLONG_MAX) {
893     } config_set_numerical_field(
894       "hash-max-ziplist-value",server.hash_max_ziplist_value,0,LLONG_MAX) {
895     } config_set_numerical_field(
896       "list-max-ziplist-size",server.list_max_ziplist_size,0,LLONG_MAX) {
897     } config_set_numerical_field(
898       "list-compress-depth",server.list_compress_depth,0,LLONG_MAX) {
899     } config_set_numerical_field(
900       "set-max-intset-entries",server.set_max_intset_entries,0,LLONG_MAX) {
901     } config_set_numerical_field(
902       "zset-max-ziplist-entries",server.zset_max_ziplist_entries,0,LLONG_MAX) {
903     } config_set_numerical_field(
904       "zset-max-ziplist-value",server.zset_max_ziplist_value,0,LLONG_MAX) {
905     } config_set_numerical_field(
906       "hll-sparse-max-bytes",server.hll_sparse_max_bytes,0,LLONG_MAX) {
907     } config_set_numerical_field(
908       "lua-time-limit",server.lua_time_limit,0,LLONG_MAX) {
909     } config_set_numerical_field(
910       "slowlog-log-slower-than",server.slowlog_log_slower_than,0,LLONG_MAX) {
911     } config_set_numerical_field(
912       "slowlog-max-len",ll,0,LLONG_MAX) {
913       /* Cast to unsigned. */
914         server.slowlog_max_len = (unsigned)ll;
915     } config_set_numerical_field(
916       "latency-monitor-threshold",server.latency_monitor_threshold,0,LLONG_MAX){
917     } config_set_numerical_field(
918       "repl-ping-slave-period",server.repl_ping_slave_period,1,LLONG_MAX) {
919     } config_set_numerical_field(
920       "repl-timeout",server.repl_timeout,1,LLONG_MAX) {
921     } config_set_numerical_field(
922       "repl-backlog-ttl",server.repl_backlog_time_limit,0,LLONG_MAX) {
923     } config_set_numerical_field(
924       "repl-diskless-sync-delay",server.repl_diskless_sync_delay,0,LLONG_MAX) {
925     } config_set_numerical_field(
926       "slave-priority",server.slave_priority,0,LLONG_MAX) {
927     } config_set_numerical_field(
928       "min-slaves-to-write",server.repl_min_slaves_to_write,0,LLONG_MAX) {
929         refreshGoodSlavesCount();
930     } config_set_numerical_field(
931       "min-slaves-max-lag",server.repl_min_slaves_max_lag,0,LLONG_MAX) {
932         refreshGoodSlavesCount();
933     } config_set_numerical_field(
934       "cluster-node-timeout",server.cluster_node_timeout,0,LLONG_MAX) {
935     } config_set_numerical_field(
936       "cluster-migration-barrier",server.cluster_migration_barrier,0,LLONG_MAX){
937     } config_set_numerical_field(
938       "cluster-slave-validity-factor",server.cluster_slave_validity_factor,0,LLONG_MAX) {
939     } config_set_numerical_field(
940       "hz",server.hz,0,LLONG_MAX) {
941         /* Hz is more an hint from the user, so we accept values out of range
942          * but cap them to reasonable values. */
943         if (server.hz < CONFIG_MIN_HZ) server.hz = CONFIG_MIN_HZ;
944         if (server.hz > CONFIG_MAX_HZ) server.hz = CONFIG_MAX_HZ;
945     } config_set_numerical_field(
946       "watchdog-period",ll,0,LLONG_MAX) {
947         if (ll)
948             enableWatchdog(ll);
949         else
950             disableWatchdog();
951 
952     /* Memory fields.
953      * config_set_memory_field(name,var) */
954     } config_set_memory_field("maxmemory",server.maxmemory) {
955         if (server.maxmemory) {
956             if (server.maxmemory < zmalloc_used_memory()) {
957                 serverLog(LL_WARNING,"WARNING: the new maxmemory value set via CONFIG SET is smaller than the current memory usage. This will result in keys eviction and/or inability to accept new write commands depending on the maxmemory-policy.");
958             }
959             freeMemoryIfNeeded();
960         }
961     } config_set_memory_field("repl-backlog-size",ll) {
962         resizeReplicationBacklog(ll);
963 
964     /* Enumeration fields.
965      * config_set_enum_field(name,var,enum_var) */
966     } config_set_enum_field(
967       "loglevel",server.verbosity,loglevel_enum) {
968     } config_set_enum_field(
969       "maxmemory-policy",server.maxmemory_policy,maxmemory_policy_enum) {
970     } config_set_enum_field(
971       "appendfsync",server.aof_fsync,aof_fsync_enum) {
972 
973     /* Everyhing else is an error... */
974     } config_set_else {
975         addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
976             (char*)c->argv[2]->ptr);
977         return;
978     }
979 
980     /* On success we just return a generic OK for all the options. */
981     addReply(c,shared.ok);
982     return;
983 
984 badfmt: /* Bad format errors */
985     addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
986             (char*)o->ptr,
987             (char*)c->argv[2]->ptr);
988 }
989 
990 /*-----------------------------------------------------------------------------
991  * CONFIG GET implementation
992  *----------------------------------------------------------------------------*/
993 
994 #define config_get_string_field(_name,_var) do { \
995     if (stringmatch(pattern,_name,0)) { \
996         addReplyBulkCString(c,_name); \
997         addReplyBulkCString(c,_var ? _var : ""); \
998         matches++; \
999     } \
1000 } while(0);
1001 
1002 #define config_get_bool_field(_name,_var) do { \
1003     if (stringmatch(pattern,_name,0)) { \
1004         addReplyBulkCString(c,_name); \
1005         addReplyBulkCString(c,_var ? "yes" : "no"); \
1006         matches++; \
1007     } \
1008 } while(0);
1009 
1010 #define config_get_numerical_field(_name,_var) do { \
1011     if (stringmatch(pattern,_name,0)) { \
1012         ll2string(buf,sizeof(buf),_var); \
1013         addReplyBulkCString(c,_name); \
1014         addReplyBulkCString(c,buf); \
1015         matches++; \
1016     } \
1017 } while(0);
1018 
1019 #define config_get_enum_field(_name,_var,_enumvar) do { \
1020     if (stringmatch(pattern,_name,0)) { \
1021         addReplyBulkCString(c,_name); \
1022         addReplyBulkCString(c,configEnumGetNameOrUnknown(_enumvar,_var)); \
1023         matches++; \
1024     } \
1025 } while(0);
1026 
1027 void configGetCommand(client *c) {
1028     robj *o = c->argv[2];
1029     void *replylen = addDeferredMultiBulkLength(c);
1030     char *pattern = o->ptr;
1031     char buf[128];
1032     int matches = 0;
1033     serverAssertWithInfo(c,o,sdsEncodedObject(o));
1034 
1035     /* String values */
1036     config_get_string_field("dbfilename",server.rdb_filename);
1037     config_get_string_field("requirepass",server.requirepass);
1038     config_get_string_field("masterauth",server.masterauth);
1039     config_get_string_field("unixsocket",server.unixsocket);
1040     config_get_string_field("logfile",server.logfile);
1041     config_get_string_field("pidfile",server.pidfile);
1042 
1043     /* Numerical values */
1044     config_get_numerical_field("maxmemory",server.maxmemory);
1045     config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
1046     config_get_numerical_field("timeout",server.maxidletime);
1047     config_get_numerical_field("auto-aof-rewrite-percentage",
1048             server.aof_rewrite_perc);
1049     config_get_numerical_field("auto-aof-rewrite-min-size",
1050             server.aof_rewrite_min_size);
1051     config_get_numerical_field("hash-max-ziplist-entries",
1052             server.hash_max_ziplist_entries);
1053     config_get_numerical_field("hash-max-ziplist-value",
1054             server.hash_max_ziplist_value);
1055     config_get_numerical_field("list-max-ziplist-size",
1056             server.list_max_ziplist_size);
1057     config_get_numerical_field("list-compress-depth",
1058             server.list_compress_depth);
1059     config_get_numerical_field("set-max-intset-entries",
1060             server.set_max_intset_entries);
1061     config_get_numerical_field("zset-max-ziplist-entries",
1062             server.zset_max_ziplist_entries);
1063     config_get_numerical_field("zset-max-ziplist-value",
1064             server.zset_max_ziplist_value);
1065     config_get_numerical_field("hll-sparse-max-bytes",
1066             server.hll_sparse_max_bytes);
1067     config_get_numerical_field("lua-time-limit",server.lua_time_limit);
1068     config_get_numerical_field("slowlog-log-slower-than",
1069             server.slowlog_log_slower_than);
1070     config_get_numerical_field("latency-monitor-threshold",
1071             server.latency_monitor_threshold);
1072     config_get_numerical_field("slowlog-max-len",
1073             server.slowlog_max_len);
1074     config_get_numerical_field("port",server.port);
1075     config_get_numerical_field("tcp-backlog",server.tcp_backlog);
1076     config_get_numerical_field("databases",server.dbnum);
1077     config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
1078     config_get_numerical_field("repl-timeout",server.repl_timeout);
1079     config_get_numerical_field("repl-backlog-size",server.repl_backlog_size);
1080     config_get_numerical_field("repl-backlog-ttl",server.repl_backlog_time_limit);
1081     config_get_numerical_field("maxclients",server.maxclients);
1082     config_get_numerical_field("watchdog-period",server.watchdog_period);
1083     config_get_numerical_field("slave-priority",server.slave_priority);
1084     config_get_numerical_field("min-slaves-to-write",server.repl_min_slaves_to_write);
1085     config_get_numerical_field("min-slaves-max-lag",server.repl_min_slaves_max_lag);
1086     config_get_numerical_field("hz",server.hz);
1087     config_get_numerical_field("cluster-node-timeout",server.cluster_node_timeout);
1088     config_get_numerical_field("cluster-migration-barrier",server.cluster_migration_barrier);
1089     config_get_numerical_field("cluster-slave-validity-factor",server.cluster_slave_validity_factor);
1090     config_get_numerical_field("repl-diskless-sync-delay",server.repl_diskless_sync_delay);
1091     config_get_numerical_field("tcp-keepalive",server.tcpkeepalive);
1092 
1093     /* Bool (yes/no) values */
1094     config_get_bool_field("cluster-require-full-coverage",
1095             server.cluster_require_full_coverage);
1096     config_get_bool_field("no-appendfsync-on-rewrite",
1097             server.aof_no_fsync_on_rewrite);
1098     config_get_bool_field("slave-serve-stale-data",
1099             server.repl_serve_stale_data);
1100     config_get_bool_field("slave-read-only",
1101             server.repl_slave_ro);
1102     config_get_bool_field("stop-writes-on-bgsave-error",
1103             server.stop_writes_on_bgsave_err);
1104     config_get_bool_field("daemonize", server.daemonize);
1105     config_get_bool_field("rdbcompression", server.rdb_compression);
1106     config_get_bool_field("rdbchecksum", server.rdb_checksum);
1107     config_get_bool_field("activerehashing", server.activerehashing);
1108     config_get_bool_field("repl-disable-tcp-nodelay",
1109             server.repl_disable_tcp_nodelay);
1110     config_get_bool_field("repl-diskless-sync",
1111             server.repl_diskless_sync);
1112     config_get_bool_field("aof-rewrite-incremental-fsync",
1113             server.aof_rewrite_incremental_fsync);
1114     config_get_bool_field("aof-load-truncated",
1115             server.aof_load_truncated);
1116 
1117     /* Enum values */
1118     config_get_enum_field("maxmemory-policy",
1119             server.maxmemory_policy,maxmemory_policy_enum);
1120     config_get_enum_field("loglevel",
1121             server.verbosity,loglevel_enum);
1122     config_get_enum_field("supervised",
1123             server.supervised_mode,supervised_mode_enum);
1124     config_get_enum_field("appendfsync",
1125             server.aof_fsync,aof_fsync_enum);
1126     config_get_enum_field("syslog-facility",
1127             server.syslog_facility,syslog_facility_enum);
1128 
1129     /* Everything we can't handle with macros follows. */
1130 
1131     if (stringmatch(pattern,"appendonly",0)) {
1132         addReplyBulkCString(c,"appendonly");
1133         addReplyBulkCString(c,server.aof_state == AOF_OFF ? "no" : "yes");
1134         matches++;
1135     }
1136     if (stringmatch(pattern,"dir",0)) {
1137         char buf[1024];
1138 
1139         if (getcwd(buf,sizeof(buf)) == NULL)
1140             buf[0] = '\0';
1141 
1142         addReplyBulkCString(c,"dir");
1143         addReplyBulkCString(c,buf);
1144         matches++;
1145     }
1146     if (stringmatch(pattern,"save",0)) {
1147         sds buf = sdsempty();
1148         int j;
1149 
1150         for (j = 0; j < server.saveparamslen; j++) {
1151             buf = sdscatprintf(buf,"%jd %d",
1152                     (intmax_t)server.saveparams[j].seconds,
1153                     server.saveparams[j].changes);
1154             if (j != server.saveparamslen-1)
1155                 buf = sdscatlen(buf," ",1);
1156         }
1157         addReplyBulkCString(c,"save");
1158         addReplyBulkCString(c,buf);
1159         sdsfree(buf);
1160         matches++;
1161     }
1162     if (stringmatch(pattern,"client-output-buffer-limit",0)) {
1163         sds buf = sdsempty();
1164         int j;
1165 
1166         for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
1167             buf = sdscatprintf(buf,"%s %llu %llu %ld",
1168                     getClientTypeName(j),
1169                     server.client_obuf_limits[j].hard_limit_bytes,
1170                     server.client_obuf_limits[j].soft_limit_bytes,
1171                     (long) server.client_obuf_limits[j].soft_limit_seconds);
1172             if (j != CLIENT_TYPE_OBUF_COUNT-1)
1173                 buf = sdscatlen(buf," ",1);
1174         }
1175         addReplyBulkCString(c,"client-output-buffer-limit");
1176         addReplyBulkCString(c,buf);
1177         sdsfree(buf);
1178         matches++;
1179     }
1180     if (stringmatch(pattern,"unixsocketperm",0)) {
1181         char buf[32];
1182         snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
1183         addReplyBulkCString(c,"unixsocketperm");
1184         addReplyBulkCString(c,buf);
1185         matches++;
1186     }
1187     if (stringmatch(pattern,"slaveof",0)) {
1188         char buf[256];
1189 
1190         addReplyBulkCString(c,"slaveof");
1191         if (server.masterhost)
1192             snprintf(buf,sizeof(buf),"%s %d",
1193                 server.masterhost, server.masterport);
1194         else
1195             buf[0] = '\0';
1196         addReplyBulkCString(c,buf);
1197         matches++;
1198     }
1199     if (stringmatch(pattern,"notify-keyspace-events",0)) {
1200         robj *flagsobj = createObject(OBJ_STRING,
1201             keyspaceEventsFlagsToString(server.notify_keyspace_events));
1202 
1203         addReplyBulkCString(c,"notify-keyspace-events");
1204         addReplyBulk(c,flagsobj);
1205         decrRefCount(flagsobj);
1206         matches++;
1207     }
1208     if (stringmatch(pattern,"bind",0)) {
1209         sds aux = sdsjoin(server.bindaddr,server.bindaddr_count," ");
1210 
1211         addReplyBulkCString(c,"bind");
1212         addReplyBulkCString(c,aux);
1213         sdsfree(aux);
1214         matches++;
1215     }
1216     setDeferredMultiBulkLength(c,replylen,matches*2);
1217 }
1218 
1219 /*-----------------------------------------------------------------------------
1220  * CONFIG REWRITE implementation
1221  *----------------------------------------------------------------------------*/
1222 
1223 #define REDIS_CONFIG_REWRITE_SIGNATURE "# Generated by CONFIG REWRITE"
1224 
1225 /* We use the following dictionary type to store where a configuration
1226  * option is mentioned in the old configuration file, so it's
1227  * like "maxmemory" -> list of line numbers (first line is zero). */
1228 unsigned int dictSdsCaseHash(const void *key);
1229 int dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2);
1230 void dictSdsDestructor(void *privdata, void *val);
1231 void dictListDestructor(void *privdata, void *val);
1232 
1233 /* Sentinel config rewriting is implemented inside sentinel.c by
1234  * rewriteConfigSentinelOption(). */
1235 void rewriteConfigSentinelOption(struct rewriteConfigState *state);
1236 
1237 dictType optionToLineDictType = {
1238     dictSdsCaseHash,            /* hash function */
1239     NULL,                       /* key dup */
1240     NULL,                       /* val dup */
1241     dictSdsKeyCaseCompare,      /* key compare */
1242     dictSdsDestructor,          /* key destructor */
1243     dictListDestructor          /* val destructor */
1244 };
1245 
1246 dictType optionSetDictType = {
1247     dictSdsCaseHash,            /* hash function */
1248     NULL,                       /* key dup */
1249     NULL,                       /* val dup */
1250     dictSdsKeyCaseCompare,      /* key compare */
1251     dictSdsDestructor,          /* key destructor */
1252     NULL                        /* val destructor */
1253 };
1254 
1255 /* The config rewrite state. */
1256 struct rewriteConfigState {
1257     dict *option_to_line; /* Option -> list of config file lines map */
1258     dict *rewritten;      /* Dictionary of already processed options */
1259     int numlines;         /* Number of lines in current config */
1260     sds *lines;           /* Current lines as an array of sds strings */
1261     int has_tail;         /* True if we already added directives that were
1262                              not present in the original config file. */
1263 };
1264 
1265 /* Append the new line to the current configuration state. */
1266 void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) {
1267     state->lines = zrealloc(state->lines, sizeof(char*) * (state->numlines+1));
1268     state->lines[state->numlines++] = line;
1269 }
1270 
1271 /* Populate the option -> list of line numbers map. */
1272 void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) {
1273     list *l = dictFetchValue(state->option_to_line,option);
1274 
1275     if (l == NULL) {
1276         l = listCreate();
1277         dictAdd(state->option_to_line,sdsdup(option),l);
1278     }
1279     listAddNodeTail(l,(void*)(long)linenum);
1280 }
1281 
1282 /* Add the specified option to the set of processed options.
1283  * This is useful as only unused lines of processed options will be blanked
1284  * in the config file, while options the rewrite process does not understand
1285  * remain untouched. */
1286 void rewriteConfigMarkAsProcessed(struct rewriteConfigState *state, const char *option) {
1287     sds opt = sdsnew(option);
1288 
1289     if (dictAdd(state->rewritten,opt,NULL) != DICT_OK) sdsfree(opt);
1290 }
1291 
1292 /* Read the old file, split it into lines to populate a newly created
1293  * config rewrite state, and return it to the caller.
1294  *
1295  * If it is impossible to read the old file, NULL is returned.
1296  * If the old file does not exist at all, an empty state is returned. */
1297 struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
1298     FILE *fp = fopen(path,"r");
1299     struct rewriteConfigState *state = zmalloc(sizeof(*state));
1300     char buf[CONFIG_MAX_LINE+1];
1301     int linenum = -1;
1302 
1303     if (fp == NULL && errno != ENOENT) return NULL;
1304 
1305     state->option_to_line = dictCreate(&optionToLineDictType,NULL);
1306     state->rewritten = dictCreate(&optionSetDictType,NULL);
1307     state->numlines = 0;
1308     state->lines = NULL;
1309     state->has_tail = 0;
1310     if (fp == NULL) return state;
1311 
1312     /* Read the old file line by line, populate the state. */
1313     while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) {
1314         int argc;
1315         sds *argv;
1316         sds line = sdstrim(sdsnew(buf),"\r\n\t ");
1317 
1318         linenum++; /* Zero based, so we init at -1 */
1319 
1320         /* Handle comments and empty lines. */
1321         if (line[0] == '#' || line[0] == '\0') {
1322             if (!state->has_tail && !strcmp(line,REDIS_CONFIG_REWRITE_SIGNATURE))
1323                 state->has_tail = 1;
1324             rewriteConfigAppendLine(state,line);
1325             continue;
1326         }
1327 
1328         /* Not a comment, split into arguments. */
1329         argv = sdssplitargs(line,&argc);
1330         if (argv == NULL) {
1331             /* Apparently the line is unparsable for some reason, for
1332              * instance it may have unbalanced quotes. Load it as a
1333              * comment. */
1334             sds aux = sdsnew("# ??? ");
1335             aux = sdscatsds(aux,line);
1336             sdsfree(line);
1337             rewriteConfigAppendLine(state,aux);
1338             continue;
1339         }
1340 
1341         sdstolower(argv[0]); /* We only want lowercase config directives. */
1342 
1343         /* Now we populate the state according to the content of this line.
1344          * Append the line and populate the option -> line numbers map. */
1345         rewriteConfigAppendLine(state,line);
1346         rewriteConfigAddLineNumberToOption(state,argv[0],linenum);
1347 
1348         sdsfreesplitres(argv,argc);
1349     }
1350     fclose(fp);
1351     return state;
1352 }
1353 
1354 /* Rewrite the specified configuration option with the new "line".
1355  * It progressively uses lines of the file that were already used for the same
1356  * configuration option in the old version of the file, removing that line from
1357  * the map of options -> line numbers.
1358  *
1359  * If there are lines associated with a given configuration option and
1360  * "force" is non-zero, the line is appended to the configuration file.
1361  * Usually "force" is true when an option has not its default value, so it
1362  * must be rewritten even if not present previously.
1363  *
1364  * The first time a line is appended into a configuration file, a comment
1365  * is added to show that starting from that point the config file was generated
1366  * by CONFIG REWRITE.
1367  *
1368  * "line" is either used, or freed, so the caller does not need to free it
1369  * in any way. */
1370 void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force) {
1371     sds o = sdsnew(option);
1372     list *l = dictFetchValue(state->option_to_line,o);
1373 
1374     rewriteConfigMarkAsProcessed(state,option);
1375 
1376     if (!l && !force) {
1377         /* Option not used previously, and we are not forced to use it. */
1378         sdsfree(line);
1379         sdsfree(o);
1380         return;
1381     }
1382 
1383     if (l) {
1384         listNode *ln = listFirst(l);
1385         int linenum = (long) ln->value;
1386 
1387         /* There are still lines in the old configuration file we can reuse
1388          * for this option. Replace the line with the new one. */
1389         listDelNode(l,ln);
1390         if (listLength(l) == 0) dictDelete(state->option_to_line,o);
1391         sdsfree(state->lines[linenum]);
1392         state->lines[linenum] = line;
1393     } else {
1394         /* Append a new line. */
1395         if (!state->has_tail) {
1396             rewriteConfigAppendLine(state,
1397                 sdsnew(REDIS_CONFIG_REWRITE_SIGNATURE));
1398             state->has_tail = 1;
1399         }
1400         rewriteConfigAppendLine(state,line);
1401     }
1402     sdsfree(o);
1403 }
1404 
1405 /* Write the long long 'bytes' value as a string in a way that is parsable
1406  * inside redis.conf. If possible uses the GB, MB, KB notation. */
1407 int rewriteConfigFormatMemory(char *buf, size_t len, long long bytes) {
1408     int gb = 1024*1024*1024;
1409     int mb = 1024*1024;
1410     int kb = 1024;
1411 
1412     if (bytes && (bytes % gb) == 0) {
1413         return snprintf(buf,len,"%lldgb",bytes/gb);
1414     } else if (bytes && (bytes % mb) == 0) {
1415         return snprintf(buf,len,"%lldmb",bytes/mb);
1416     } else if (bytes && (bytes % kb) == 0) {
1417         return snprintf(buf,len,"%lldkb",bytes/kb);
1418     } else {
1419         return snprintf(buf,len,"%lld",bytes);
1420     }
1421 }
1422 
1423 /* Rewrite a simple "option-name <bytes>" configuration option. */
1424 void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
1425     char buf[64];
1426     int force = value != defvalue;
1427     sds line;
1428 
1429     rewriteConfigFormatMemory(buf,sizeof(buf),value);
1430     line = sdscatprintf(sdsempty(),"%s %s",option,buf);
1431     rewriteConfigRewriteLine(state,option,line,force);
1432 }
1433 
1434 /* Rewrite a yes/no option. */
1435 void rewriteConfigYesNoOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
1436     int force = value != defvalue;
1437     sds line = sdscatprintf(sdsempty(),"%s %s",option,
1438         value ? "yes" : "no");
1439 
1440     rewriteConfigRewriteLine(state,option,line,force);
1441 }
1442 
1443 /* Rewrite a string option. */
1444 void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, char *value, char *defvalue) {
1445     int force = 1;
1446     sds line;
1447 
1448     /* String options set to NULL need to be not present at all in the
1449      * configuration file to be set to NULL again at the next reboot. */
1450     if (value == NULL) {
1451         rewriteConfigMarkAsProcessed(state,option);
1452         return;
1453     }
1454 
1455     /* Set force to zero if the value is set to its default. */
1456     if (defvalue && strcmp(value,defvalue) == 0) force = 0;
1457 
1458     line = sdsnew(option);
1459     line = sdscatlen(line, " ", 1);
1460     line = sdscatrepr(line, value, strlen(value));
1461 
1462     rewriteConfigRewriteLine(state,option,line,force);
1463 }
1464 
1465 /* Rewrite a numerical (long long range) option. */
1466 void rewriteConfigNumericalOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
1467     int force = value != defvalue;
1468     sds line = sdscatprintf(sdsempty(),"%s %lld",option,value);
1469 
1470     rewriteConfigRewriteLine(state,option,line,force);
1471 }
1472 
1473 /* Rewrite a octal option. */
1474 void rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
1475     int force = value != defvalue;
1476     sds line = sdscatprintf(sdsempty(),"%s %o",option,value);
1477 
1478     rewriteConfigRewriteLine(state,option,line,force);
1479 }
1480 
1481 /* Rewrite an enumeration option. It takes as usually state and option name,
1482  * and in addition the enumeration array and the default value for the
1483  * option. */
1484 void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int value, configEnum *ce, int defval) {
1485     sds line;
1486     const char *name = configEnumGetNameOrUnknown(ce,value);
1487     int force = value != defval;
1488 
1489     line = sdscatprintf(sdsempty(),"%s %s",option,name);
1490     rewriteConfigRewriteLine(state,option,line,force);
1491 }
1492 
1493 /* Rewrite the syslog-facility option. */
1494 void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) {
1495     int value = server.syslog_facility;
1496     int force = value != LOG_LOCAL0;
1497     const char *name = NULL, *option = "syslog-facility";
1498     sds line;
1499 
1500     name = configEnumGetNameOrUnknown(syslog_facility_enum,value);
1501     line = sdscatprintf(sdsempty(),"%s %s",option,name);
1502     rewriteConfigRewriteLine(state,option,line,force);
1503 }
1504 
1505 /* Rewrite the save option. */
1506 void rewriteConfigSaveOption(struct rewriteConfigState *state) {
1507     int j;
1508     sds line;
1509 
1510     /* Note that if there are no save parameters at all, all the current
1511      * config line with "save" will be detected as orphaned and deleted,
1512      * resulting into no RDB persistence as expected. */
1513     for (j = 0; j < server.saveparamslen; j++) {
1514         line = sdscatprintf(sdsempty(),"save %ld %d",
1515             (long) server.saveparams[j].seconds, server.saveparams[j].changes);
1516         rewriteConfigRewriteLine(state,"save",line,1);
1517     }
1518     /* Mark "save" as processed in case server.saveparamslen is zero. */
1519     rewriteConfigMarkAsProcessed(state,"save");
1520 }
1521 
1522 /* Rewrite the dir option, always using absolute paths.*/
1523 void rewriteConfigDirOption(struct rewriteConfigState *state) {
1524     char cwd[1024];
1525 
1526     if (getcwd(cwd,sizeof(cwd)) == NULL) {
1527         rewriteConfigMarkAsProcessed(state,"dir");
1528         return; /* no rewrite on error. */
1529     }
1530     rewriteConfigStringOption(state,"dir",cwd,NULL);
1531 }
1532 
1533 /* Rewrite the slaveof option. */
1534 void rewriteConfigSlaveofOption(struct rewriteConfigState *state) {
1535     char *option = "slaveof";
1536     sds line;
1537 
1538     /* If this is a master, we want all the slaveof config options
1539      * in the file to be removed. Note that if this is a cluster instance
1540      * we don't want a slaveof directive inside redis.conf. */
1541     if (server.cluster_enabled || server.masterhost == NULL) {
1542         rewriteConfigMarkAsProcessed(state,"slaveof");
1543         return;
1544     }
1545     line = sdscatprintf(sdsempty(),"%s %s %d", option,
1546         server.masterhost, server.masterport);
1547     rewriteConfigRewriteLine(state,option,line,1);
1548 }
1549 
1550 /* Rewrite the notify-keyspace-events option. */
1551 void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) {
1552     int force = server.notify_keyspace_events != 0;
1553     char *option = "notify-keyspace-events";
1554     sds line, flags;
1555 
1556     flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);
1557     line = sdsnew(option);
1558     line = sdscatlen(line, " ", 1);
1559     line = sdscatrepr(line, flags, sdslen(flags));
1560     sdsfree(flags);
1561     rewriteConfigRewriteLine(state,option,line,force);
1562 }
1563 
1564 /* Rewrite the client-output-buffer-limit option. */
1565 void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) {
1566     int j;
1567     char *option = "client-output-buffer-limit";
1568 
1569     for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
1570         int force = (server.client_obuf_limits[j].hard_limit_bytes !=
1571                     clientBufferLimitsDefaults[j].hard_limit_bytes) ||
1572                     (server.client_obuf_limits[j].soft_limit_bytes !=
1573                     clientBufferLimitsDefaults[j].soft_limit_bytes) ||
1574                     (server.client_obuf_limits[j].soft_limit_seconds !=
1575                     clientBufferLimitsDefaults[j].soft_limit_seconds);
1576         sds line;
1577         char hard[64], soft[64];
1578 
1579         rewriteConfigFormatMemory(hard,sizeof(hard),
1580                 server.client_obuf_limits[j].hard_limit_bytes);
1581         rewriteConfigFormatMemory(soft,sizeof(soft),
1582                 server.client_obuf_limits[j].soft_limit_bytes);
1583 
1584         line = sdscatprintf(sdsempty(),"%s %s %s %s %ld",
1585                 option, getClientTypeName(j), hard, soft,
1586                 (long) server.client_obuf_limits[j].soft_limit_seconds);
1587         rewriteConfigRewriteLine(state,option,line,force);
1588     }
1589 }
1590 
1591 /* Rewrite the bind option. */
1592 void rewriteConfigBindOption(struct rewriteConfigState *state) {
1593     int force = 1;
1594     sds line, addresses;
1595     char *option = "bind";
1596 
1597     /* Nothing to rewrite if we don't have bind addresses. */
1598     if (server.bindaddr_count == 0) {
1599         rewriteConfigMarkAsProcessed(state,option);
1600         return;
1601     }
1602 
1603     /* Rewrite as bind <addr1> <addr2> ... <addrN> */
1604     addresses = sdsjoin(server.bindaddr,server.bindaddr_count," ");
1605     line = sdsnew(option);
1606     line = sdscatlen(line, " ", 1);
1607     line = sdscatsds(line, addresses);
1608     sdsfree(addresses);
1609 
1610     rewriteConfigRewriteLine(state,option,line,force);
1611 }
1612 
1613 /* Glue together the configuration lines in the current configuration
1614  * rewrite state into a single string, stripping multiple empty lines. */
1615 sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {
1616     sds content = sdsempty();
1617     int j, was_empty = 0;
1618 
1619     for (j = 0; j < state->numlines; j++) {
1620         /* Every cluster of empty lines is turned into a single empty line. */
1621         if (sdslen(state->lines[j]) == 0) {
1622             if (was_empty) continue;
1623             was_empty = 1;
1624         } else {
1625             was_empty = 0;
1626         }
1627         content = sdscatsds(content,state->lines[j]);
1628         content = sdscatlen(content,"\n",1);
1629     }
1630     return content;
1631 }
1632 
1633 /* Free the configuration rewrite state. */
1634 void rewriteConfigReleaseState(struct rewriteConfigState *state) {
1635     sdsfreesplitres(state->lines,state->numlines);
1636     dictRelease(state->option_to_line);
1637     dictRelease(state->rewritten);
1638     zfree(state);
1639 }
1640 
1641 /* At the end of the rewrite process the state contains the remaining
1642  * map between "option name" => "lines in the original config file".
1643  * Lines used by the rewrite process were removed by the function
1644  * rewriteConfigRewriteLine(), all the other lines are "orphaned" and
1645  * should be replaced by empty lines.
1646  *
1647  * This function does just this, iterating all the option names and
1648  * blanking all the lines still associated. */
1649 void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
1650     dictIterator *di = dictGetIterator(state->option_to_line);
1651     dictEntry *de;
1652 
1653     while((de = dictNext(di)) != NULL) {
1654         list *l = dictGetVal(de);
1655         sds option = dictGetKey(de);
1656 
1657         /* Don't blank lines about options the rewrite process
1658          * don't understand. */
1659         if (dictFind(state->rewritten,option) == NULL) {
1660             serverLog(LL_DEBUG,"Not rewritten option: %s", option);
1661             continue;
1662         }
1663 
1664         while(listLength(l)) {
1665             listNode *ln = listFirst(l);
1666             int linenum = (long) ln->value;
1667 
1668             sdsfree(state->lines[linenum]);
1669             state->lines[linenum] = sdsempty();
1670             listDelNode(l,ln);
1671         }
1672     }
1673     dictReleaseIterator(di);
1674 }
1675 
1676 /* This function overwrites the old configuration file with the new content.
1677  *
1678  * 1) The old file length is obtained.
1679  * 2) If the new content is smaller, padding is added.
1680  * 3) A single write(2) call is used to replace the content of the file.
1681  * 4) Later the file is truncated to the length of the new content.
1682  *
1683  * This way we are sure the file is left in a consistent state even if the
1684  * process is stopped between any of the four operations.
1685  *
1686  * The function returns 0 on success, otherwise -1 is returned and errno
1687  * set accordingly. */
1688 int rewriteConfigOverwriteFile(char *configfile, sds content) {
1689     int retval = 0;
1690     int fd = open(configfile,O_RDWR|O_CREAT,0644);
1691     int content_size = sdslen(content), padding = 0;
1692     struct stat sb;
1693     sds content_padded;
1694 
1695     /* 1) Open the old file (or create a new one if it does not
1696      *    exist), get the size. */
1697     if (fd == -1) return -1; /* errno set by open(). */
1698     if (fstat(fd,&sb) == -1) {
1699         close(fd);
1700         return -1; /* errno set by fstat(). */
1701     }
1702 
1703     /* 2) Pad the content at least match the old file size. */
1704     content_padded = sdsdup(content);
1705     if (content_size < sb.st_size) {
1706         /* If the old file was bigger, pad the content with
1707          * a newline plus as many "#" chars as required. */
1708         padding = sb.st_size - content_size;
1709         content_padded = sdsgrowzero(content_padded,sb.st_size);
1710         content_padded[content_size] = '\n';
1711         memset(content_padded+content_size+1,'#',padding-1);
1712     }
1713 
1714     /* 3) Write the new content using a single write(2). */
1715     if (write(fd,content_padded,strlen(content_padded)) == -1) {
1716         retval = -1;
1717         goto cleanup;
1718     }
1719 
1720     /* 4) Truncate the file to the right length if we used padding. */
1721     if (padding) {
1722         if (ftruncate(fd,content_size) == -1) {
1723             /* Non critical error... */
1724         }
1725     }
1726 
1727 cleanup:
1728     sdsfree(content_padded);
1729     close(fd);
1730     return retval;
1731 }
1732 
1733 /* Rewrite the configuration file at "path".
1734  * If the configuration file already exists, we try at best to retain comments
1735  * and overall structure.
1736  *
1737  * Configuration parameters that are at their default value, unless already
1738  * explicitly included in the old configuration file, are not rewritten.
1739  *
1740  * On error -1 is returned and errno is set accordingly, otherwise 0. */
1741 int rewriteConfig(char *path) {
1742     struct rewriteConfigState *state;
1743     sds newcontent;
1744     int retval;
1745 
1746     /* Step 1: read the old config into our rewrite state. */
1747     if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;
1748 
1749     /* Step 2: rewrite every single option, replacing or appending it inside
1750      * the rewrite state. */
1751 
1752     rewriteConfigYesNoOption(state,"daemonize",server.daemonize,0);
1753     rewriteConfigStringOption(state,"pidfile",server.pidfile,CONFIG_DEFAULT_PID_FILE);
1754     rewriteConfigNumericalOption(state,"port",server.port,CONFIG_DEFAULT_SERVER_PORT);
1755     rewriteConfigNumericalOption(state,"tcp-backlog",server.tcp_backlog,CONFIG_DEFAULT_TCP_BACKLOG);
1756     rewriteConfigBindOption(state);
1757     rewriteConfigStringOption(state,"unixsocket",server.unixsocket,NULL);
1758     rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,CONFIG_DEFAULT_UNIX_SOCKET_PERM);
1759     rewriteConfigNumericalOption(state,"timeout",server.maxidletime,CONFIG_DEFAULT_CLIENT_TIMEOUT);
1760     rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,CONFIG_DEFAULT_TCP_KEEPALIVE);
1761     rewriteConfigEnumOption(state,"loglevel",server.verbosity,loglevel_enum,CONFIG_DEFAULT_VERBOSITY);
1762     rewriteConfigStringOption(state,"logfile",server.logfile,CONFIG_DEFAULT_LOGFILE);
1763     rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,CONFIG_DEFAULT_SYSLOG_ENABLED);
1764     rewriteConfigStringOption(state,"syslog-ident",server.syslog_ident,CONFIG_DEFAULT_SYSLOG_IDENT);
1765     rewriteConfigSyslogfacilityOption(state);
1766     rewriteConfigSaveOption(state);
1767     rewriteConfigNumericalOption(state,"databases",server.dbnum,CONFIG_DEFAULT_DBNUM);
1768     rewriteConfigYesNoOption(state,"stop-writes-on-bgsave-error",server.stop_writes_on_bgsave_err,CONFIG_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR);
1769     rewriteConfigYesNoOption(state,"rdbcompression",server.rdb_compression,CONFIG_DEFAULT_RDB_COMPRESSION);
1770     rewriteConfigYesNoOption(state,"rdbchecksum",server.rdb_checksum,CONFIG_DEFAULT_RDB_CHECKSUM);
1771     rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,CONFIG_DEFAULT_RDB_FILENAME);
1772     rewriteConfigDirOption(state);
1773     rewriteConfigSlaveofOption(state);
1774     rewriteConfigStringOption(state,"masterauth",server.masterauth,NULL);
1775     rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA);
1776     rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,CONFIG_DEFAULT_SLAVE_READ_ONLY);
1777     rewriteConfigNumericalOption(state,"repl-ping-slave-period",server.repl_ping_slave_period,CONFIG_DEFAULT_REPL_PING_SLAVE_PERIOD);
1778     rewriteConfigNumericalOption(state,"repl-timeout",server.repl_timeout,CONFIG_DEFAULT_REPL_TIMEOUT);
1779     rewriteConfigBytesOption(state,"repl-backlog-size",server.repl_backlog_size,CONFIG_DEFAULT_REPL_BACKLOG_SIZE);
1780     rewriteConfigBytesOption(state,"repl-backlog-ttl",server.repl_backlog_time_limit,CONFIG_DEFAULT_REPL_BACKLOG_TIME_LIMIT);
1781     rewriteConfigYesNoOption(state,"repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay,CONFIG_DEFAULT_REPL_DISABLE_TCP_NODELAY);
1782     rewriteConfigYesNoOption(state,"repl-diskless-sync",server.repl_diskless_sync,CONFIG_DEFAULT_REPL_DISKLESS_SYNC);
1783     rewriteConfigNumericalOption(state,"repl-diskless-sync-delay",server.repl_diskless_sync_delay,CONFIG_DEFAULT_REPL_DISKLESS_SYNC_DELAY);
1784     rewriteConfigNumericalOption(state,"slave-priority",server.slave_priority,CONFIG_DEFAULT_SLAVE_PRIORITY);
1785     rewriteConfigNumericalOption(state,"min-slaves-to-write",server.repl_min_slaves_to_write,CONFIG_DEFAULT_MIN_SLAVES_TO_WRITE);
1786     rewriteConfigNumericalOption(state,"min-slaves-max-lag",server.repl_min_slaves_max_lag,CONFIG_DEFAULT_MIN_SLAVES_MAX_LAG);
1787     rewriteConfigStringOption(state,"requirepass",server.requirepass,NULL);
1788     rewriteConfigNumericalOption(state,"maxclients",server.maxclients,CONFIG_DEFAULT_MAX_CLIENTS);
1789     rewriteConfigBytesOption(state,"maxmemory",server.maxmemory,CONFIG_DEFAULT_MAXMEMORY);
1790     rewriteConfigEnumOption(state,"maxmemory-policy",server.maxmemory_policy,maxmemory_policy_enum,CONFIG_DEFAULT_MAXMEMORY_POLICY);
1791     rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,CONFIG_DEFAULT_MAXMEMORY_SAMPLES);
1792     rewriteConfigYesNoOption(state,"appendonly",server.aof_state != AOF_OFF,0);
1793     rewriteConfigStringOption(state,"appendfilename",server.aof_filename,CONFIG_DEFAULT_AOF_FILENAME);
1794     rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync,aof_fsync_enum,CONFIG_DEFAULT_AOF_FSYNC);
1795     rewriteConfigYesNoOption(state,"no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite,CONFIG_DEFAULT_AOF_NO_FSYNC_ON_REWRITE);
1796     rewriteConfigNumericalOption(state,"auto-aof-rewrite-percentage",server.aof_rewrite_perc,AOF_REWRITE_PERC);
1797     rewriteConfigBytesOption(state,"auto-aof-rewrite-min-size",server.aof_rewrite_min_size,AOF_REWRITE_MIN_SIZE);
1798     rewriteConfigNumericalOption(state,"lua-time-limit",server.lua_time_limit,LUA_SCRIPT_TIME_LIMIT);
1799     rewriteConfigYesNoOption(state,"cluster-enabled",server.cluster_enabled,0);
1800     rewriteConfigStringOption(state,"cluster-config-file",server.cluster_configfile,CONFIG_DEFAULT_CLUSTER_CONFIG_FILE);
1801     rewriteConfigYesNoOption(state,"cluster-require-full-coverage",server.cluster_require_full_coverage,CLUSTER_DEFAULT_REQUIRE_FULL_COVERAGE);
1802     rewriteConfigNumericalOption(state,"cluster-node-timeout",server.cluster_node_timeout,CLUSTER_DEFAULT_NODE_TIMEOUT);
1803     rewriteConfigNumericalOption(state,"cluster-migration-barrier",server.cluster_migration_barrier,CLUSTER_DEFAULT_MIGRATION_BARRIER);
1804     rewriteConfigNumericalOption(state,"cluster-slave-validity-factor",server.cluster_slave_validity_factor,CLUSTER_DEFAULT_SLAVE_VALIDITY);
1805     rewriteConfigNumericalOption(state,"slowlog-log-slower-than",server.slowlog_log_slower_than,CONFIG_DEFAULT_SLOWLOG_LOG_SLOWER_THAN);
1806     rewriteConfigNumericalOption(state,"latency-monitor-threshold",server.latency_monitor_threshold,CONFIG_DEFAULT_LATENCY_MONITOR_THRESHOLD);
1807     rewriteConfigNumericalOption(state,"slowlog-max-len",server.slowlog_max_len,CONFIG_DEFAULT_SLOWLOG_MAX_LEN);
1808     rewriteConfigNotifykeyspaceeventsOption(state);
1809     rewriteConfigNumericalOption(state,"hash-max-ziplist-entries",server.hash_max_ziplist_entries,OBJ_HASH_MAX_ZIPLIST_ENTRIES);
1810     rewriteConfigNumericalOption(state,"hash-max-ziplist-value",server.hash_max_ziplist_value,OBJ_HASH_MAX_ZIPLIST_VALUE);
1811     rewriteConfigNumericalOption(state,"list-max-ziplist-size",server.list_max_ziplist_size,OBJ_LIST_MAX_ZIPLIST_SIZE);
1812     rewriteConfigNumericalOption(state,"list-compress-depth",server.list_compress_depth,OBJ_LIST_COMPRESS_DEPTH);
1813     rewriteConfigNumericalOption(state,"set-max-intset-entries",server.set_max_intset_entries,OBJ_SET_MAX_INTSET_ENTRIES);
1814     rewriteConfigNumericalOption(state,"zset-max-ziplist-entries",server.zset_max_ziplist_entries,OBJ_ZSET_MAX_ZIPLIST_ENTRIES);
1815     rewriteConfigNumericalOption(state,"zset-max-ziplist-value",server.zset_max_ziplist_value,OBJ_ZSET_MAX_ZIPLIST_VALUE);
1816     rewriteConfigNumericalOption(state,"hll-sparse-max-bytes",server.hll_sparse_max_bytes,CONFIG_DEFAULT_HLL_SPARSE_MAX_BYTES);
1817     rewriteConfigYesNoOption(state,"activerehashing",server.activerehashing,CONFIG_DEFAULT_ACTIVE_REHASHING);
1818     rewriteConfigClientoutputbufferlimitOption(state);
1819     rewriteConfigNumericalOption(state,"hz",server.hz,CONFIG_DEFAULT_HZ);
1820     rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,CONFIG_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC);
1821     rewriteConfigYesNoOption(state,"aof-load-truncated",server.aof_load_truncated,CONFIG_DEFAULT_AOF_LOAD_TRUNCATED);
1822     rewriteConfigEnumOption(state,"supervised",server.supervised_mode,supervised_mode_enum,SUPERVISED_NONE);
1823 
1824     /* Rewrite Sentinel config if in Sentinel mode. */
1825     if (server.sentinel_mode) rewriteConfigSentinelOption(state);
1826 
1827     /* Step 3: remove all the orphaned lines in the old file, that is, lines
1828      * that were used by a config option and are no longer used, like in case
1829      * of multiple "save" options or duplicated options. */
1830     rewriteConfigRemoveOrphaned(state);
1831 
1832     /* Step 4: generate a new configuration file from the modified state
1833      * and write it into the original file. */
1834     newcontent = rewriteConfigGetContentFromState(state);
1835     retval = rewriteConfigOverwriteFile(server.configfile,newcontent);
1836 
1837     sdsfree(newcontent);
1838     rewriteConfigReleaseState(state);
1839     return retval;
1840 }
1841 
1842 /*-----------------------------------------------------------------------------
1843  * CONFIG command entry point
1844  *----------------------------------------------------------------------------*/
1845 
1846 void configCommand(client *c) {
1847     if (!strcasecmp(c->argv[1]->ptr,"set")) {
1848         if (c->argc != 4) goto badarity;
1849         configSetCommand(c);
1850     } else if (!strcasecmp(c->argv[1]->ptr,"get")) {
1851         if (c->argc != 3) goto badarity;
1852         configGetCommand(c);
1853     } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) {
1854         if (c->argc != 2) goto badarity;
1855         resetServerStats();
1856         resetCommandTableStats();
1857         addReply(c,shared.ok);
1858     } else if (!strcasecmp(c->argv[1]->ptr,"rewrite")) {
1859         if (c->argc != 2) goto badarity;
1860         if (server.configfile == NULL) {
1861             addReplyError(c,"The server is running without a config file");
1862             return;
1863         }
1864         if (rewriteConfig(server.configfile) == -1) {
1865             serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(errno));
1866             addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno));
1867         } else {
1868             serverLog(LL_WARNING,"CONFIG REWRITE executed with success.");
1869             addReply(c,shared.ok);
1870         }
1871     } else {
1872         addReplyError(c,
1873             "CONFIG subcommand must be one of GET, SET, RESETSTAT, REWRITE");
1874     }
1875     return;
1876 
1877 badarity:
1878     addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s",
1879         (char*) c->argv[1]->ptr);
1880 }
1881