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