xref: /redis-3.2.3/src/config.c (revision 5b5e6520)
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 *evictPolicyToString(void) {
130     return configEnumGetNameOrUnknown(maxmemory_policy_enum,server.maxmemory_policy);
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) 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     } config_set_bool_field(
886       "no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite) {
887 
888     /* Numerical fields.
889      * config_set_numerical_field(name,var,min,max) */
890     } config_set_numerical_field(
891       "tcp-keepalive",server.tcpkeepalive,0,LLONG_MAX) {
892     } config_set_numerical_field(
893       "maxmemory-samples",server.maxmemory_samples,1,LLONG_MAX) {
894     } config_set_numerical_field(
895       "timeout",server.maxidletime,0,LONG_MAX) {
896     } config_set_numerical_field(
897       "auto-aof-rewrite-percentage",server.aof_rewrite_perc,0,LLONG_MAX){
898     } config_set_numerical_field(
899       "auto-aof-rewrite-min-size",server.aof_rewrite_min_size,0,LLONG_MAX) {
900     } config_set_numerical_field(
901       "hash-max-ziplist-entries",server.hash_max_ziplist_entries,0,LLONG_MAX) {
902     } config_set_numerical_field(
903       "hash-max-ziplist-value",server.hash_max_ziplist_value,0,LLONG_MAX) {
904     } config_set_numerical_field(
905       "list-max-ziplist-size",server.list_max_ziplist_size,INT_MIN,INT_MAX) {
906     } config_set_numerical_field(
907       "list-compress-depth",server.list_compress_depth,0,INT_MAX) {
908     } config_set_numerical_field(
909       "set-max-intset-entries",server.set_max_intset_entries,0,LLONG_MAX) {
910     } config_set_numerical_field(
911       "zset-max-ziplist-entries",server.zset_max_ziplist_entries,0,LLONG_MAX) {
912     } config_set_numerical_field(
913       "zset-max-ziplist-value",server.zset_max_ziplist_value,0,LLONG_MAX) {
914     } config_set_numerical_field(
915       "hll-sparse-max-bytes",server.hll_sparse_max_bytes,0,LLONG_MAX) {
916     } config_set_numerical_field(
917       "lua-time-limit",server.lua_time_limit,0,LLONG_MAX) {
918     } config_set_numerical_field(
919       "slowlog-log-slower-than",server.slowlog_log_slower_than,0,LLONG_MAX) {
920     } config_set_numerical_field(
921       "slowlog-max-len",ll,0,LLONG_MAX) {
922       /* Cast to unsigned. */
923         server.slowlog_max_len = (unsigned)ll;
924     } config_set_numerical_field(
925       "latency-monitor-threshold",server.latency_monitor_threshold,0,LLONG_MAX){
926     } config_set_numerical_field(
927       "repl-ping-slave-period",server.repl_ping_slave_period,1,LLONG_MAX) {
928     } config_set_numerical_field(
929       "repl-timeout",server.repl_timeout,1,LLONG_MAX) {
930     } config_set_numerical_field(
931       "repl-backlog-ttl",server.repl_backlog_time_limit,0,LLONG_MAX) {
932     } config_set_numerical_field(
933       "repl-diskless-sync-delay",server.repl_diskless_sync_delay,0,LLONG_MAX) {
934     } config_set_numerical_field(
935       "slave-priority",server.slave_priority,0,LLONG_MAX) {
936     } config_set_numerical_field(
937       "min-slaves-to-write",server.repl_min_slaves_to_write,0,LLONG_MAX) {
938         refreshGoodSlavesCount();
939     } config_set_numerical_field(
940       "min-slaves-max-lag",server.repl_min_slaves_max_lag,0,LLONG_MAX) {
941         refreshGoodSlavesCount();
942     } config_set_numerical_field(
943       "cluster-node-timeout",server.cluster_node_timeout,0,LLONG_MAX) {
944     } config_set_numerical_field(
945       "cluster-migration-barrier",server.cluster_migration_barrier,0,LLONG_MAX){
946     } config_set_numerical_field(
947       "cluster-slave-validity-factor",server.cluster_slave_validity_factor,0,LLONG_MAX) {
948     } config_set_numerical_field(
949       "hz",server.hz,0,LLONG_MAX) {
950         /* Hz is more an hint from the user, so we accept values out of range
951          * but cap them to reasonable values. */
952         if (server.hz < CONFIG_MIN_HZ) server.hz = CONFIG_MIN_HZ;
953         if (server.hz > CONFIG_MAX_HZ) server.hz = CONFIG_MAX_HZ;
954     } config_set_numerical_field(
955       "watchdog-period",ll,0,LLONG_MAX) {
956         if (ll)
957             enableWatchdog(ll);
958         else
959             disableWatchdog();
960 
961     /* Memory fields.
962      * config_set_memory_field(name,var) */
963     } config_set_memory_field("maxmemory",server.maxmemory) {
964         if (server.maxmemory) {
965             if (server.maxmemory < zmalloc_used_memory()) {
966                 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.");
967             }
968             freeMemoryIfNeeded();
969         }
970     } config_set_memory_field("repl-backlog-size",ll) {
971         resizeReplicationBacklog(ll);
972 
973     /* Enumeration fields.
974      * config_set_enum_field(name,var,enum_var) */
975     } config_set_enum_field(
976       "loglevel",server.verbosity,loglevel_enum) {
977     } config_set_enum_field(
978       "maxmemory-policy",server.maxmemory_policy,maxmemory_policy_enum) {
979     } config_set_enum_field(
980       "appendfsync",server.aof_fsync,aof_fsync_enum) {
981 
982     /* Everyhing else is an error... */
983     } config_set_else {
984         addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
985             (char*)c->argv[2]->ptr);
986         return;
987     }
988 
989     /* On success we just return a generic OK for all the options. */
990     addReply(c,shared.ok);
991     return;
992 
993 badfmt: /* Bad format errors */
994     addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
995             (char*)o->ptr,
996             (char*)c->argv[2]->ptr);
997 }
998 
999 /*-----------------------------------------------------------------------------
1000  * CONFIG GET implementation
1001  *----------------------------------------------------------------------------*/
1002 
1003 #define config_get_string_field(_name,_var) do { \
1004     if (stringmatch(pattern,_name,1)) { \
1005         addReplyBulkCString(c,_name); \
1006         addReplyBulkCString(c,_var ? _var : ""); \
1007         matches++; \
1008     } \
1009 } while(0);
1010 
1011 #define config_get_bool_field(_name,_var) do { \
1012     if (stringmatch(pattern,_name,1)) { \
1013         addReplyBulkCString(c,_name); \
1014         addReplyBulkCString(c,_var ? "yes" : "no"); \
1015         matches++; \
1016     } \
1017 } while(0);
1018 
1019 #define config_get_numerical_field(_name,_var) do { \
1020     if (stringmatch(pattern,_name,1)) { \
1021         ll2string(buf,sizeof(buf),_var); \
1022         addReplyBulkCString(c,_name); \
1023         addReplyBulkCString(c,buf); \
1024         matches++; \
1025     } \
1026 } while(0);
1027 
1028 #define config_get_enum_field(_name,_var,_enumvar) do { \
1029     if (stringmatch(pattern,_name,1)) { \
1030         addReplyBulkCString(c,_name); \
1031         addReplyBulkCString(c,configEnumGetNameOrUnknown(_enumvar,_var)); \
1032         matches++; \
1033     } \
1034 } while(0);
1035 
1036 void configGetCommand(client *c) {
1037     robj *o = c->argv[2];
1038     void *replylen = addDeferredMultiBulkLength(c);
1039     char *pattern = o->ptr;
1040     char buf[128];
1041     int matches = 0;
1042     serverAssertWithInfo(c,o,sdsEncodedObject(o));
1043 
1044     /* String values */
1045     config_get_string_field("dbfilename",server.rdb_filename);
1046     config_get_string_field("requirepass",server.requirepass);
1047     config_get_string_field("masterauth",server.masterauth);
1048     config_get_string_field("unixsocket",server.unixsocket);
1049     config_get_string_field("logfile",server.logfile);
1050     config_get_string_field("pidfile",server.pidfile);
1051 
1052     /* Numerical values */
1053     config_get_numerical_field("maxmemory",server.maxmemory);
1054     config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
1055     config_get_numerical_field("timeout",server.maxidletime);
1056     config_get_numerical_field("auto-aof-rewrite-percentage",
1057             server.aof_rewrite_perc);
1058     config_get_numerical_field("auto-aof-rewrite-min-size",
1059             server.aof_rewrite_min_size);
1060     config_get_numerical_field("hash-max-ziplist-entries",
1061             server.hash_max_ziplist_entries);
1062     config_get_numerical_field("hash-max-ziplist-value",
1063             server.hash_max_ziplist_value);
1064     config_get_numerical_field("list-max-ziplist-size",
1065             server.list_max_ziplist_size);
1066     config_get_numerical_field("list-compress-depth",
1067             server.list_compress_depth);
1068     config_get_numerical_field("set-max-intset-entries",
1069             server.set_max_intset_entries);
1070     config_get_numerical_field("zset-max-ziplist-entries",
1071             server.zset_max_ziplist_entries);
1072     config_get_numerical_field("zset-max-ziplist-value",
1073             server.zset_max_ziplist_value);
1074     config_get_numerical_field("hll-sparse-max-bytes",
1075             server.hll_sparse_max_bytes);
1076     config_get_numerical_field("lua-time-limit",server.lua_time_limit);
1077     config_get_numerical_field("slowlog-log-slower-than",
1078             server.slowlog_log_slower_than);
1079     config_get_numerical_field("latency-monitor-threshold",
1080             server.latency_monitor_threshold);
1081     config_get_numerical_field("slowlog-max-len",
1082             server.slowlog_max_len);
1083     config_get_numerical_field("port",server.port);
1084     config_get_numerical_field("tcp-backlog",server.tcp_backlog);
1085     config_get_numerical_field("databases",server.dbnum);
1086     config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
1087     config_get_numerical_field("repl-timeout",server.repl_timeout);
1088     config_get_numerical_field("repl-backlog-size",server.repl_backlog_size);
1089     config_get_numerical_field("repl-backlog-ttl",server.repl_backlog_time_limit);
1090     config_get_numerical_field("maxclients",server.maxclients);
1091     config_get_numerical_field("watchdog-period",server.watchdog_period);
1092     config_get_numerical_field("slave-priority",server.slave_priority);
1093     config_get_numerical_field("min-slaves-to-write",server.repl_min_slaves_to_write);
1094     config_get_numerical_field("min-slaves-max-lag",server.repl_min_slaves_max_lag);
1095     config_get_numerical_field("hz",server.hz);
1096     config_get_numerical_field("cluster-node-timeout",server.cluster_node_timeout);
1097     config_get_numerical_field("cluster-migration-barrier",server.cluster_migration_barrier);
1098     config_get_numerical_field("cluster-slave-validity-factor",server.cluster_slave_validity_factor);
1099     config_get_numerical_field("repl-diskless-sync-delay",server.repl_diskless_sync_delay);
1100     config_get_numerical_field("tcp-keepalive",server.tcpkeepalive);
1101 
1102     /* Bool (yes/no) values */
1103     config_get_bool_field("cluster-require-full-coverage",
1104             server.cluster_require_full_coverage);
1105     config_get_bool_field("no-appendfsync-on-rewrite",
1106             server.aof_no_fsync_on_rewrite);
1107     config_get_bool_field("slave-serve-stale-data",
1108             server.repl_serve_stale_data);
1109     config_get_bool_field("slave-read-only",
1110             server.repl_slave_ro);
1111     config_get_bool_field("stop-writes-on-bgsave-error",
1112             server.stop_writes_on_bgsave_err);
1113     config_get_bool_field("daemonize", server.daemonize);
1114     config_get_bool_field("rdbcompression", server.rdb_compression);
1115     config_get_bool_field("rdbchecksum", server.rdb_checksum);
1116     config_get_bool_field("activerehashing", server.activerehashing);
1117     config_get_bool_field("protected-mode", server.protected_mode);
1118     config_get_bool_field("repl-disable-tcp-nodelay",
1119             server.repl_disable_tcp_nodelay);
1120     config_get_bool_field("repl-diskless-sync",
1121             server.repl_diskless_sync);
1122     config_get_bool_field("aof-rewrite-incremental-fsync",
1123             server.aof_rewrite_incremental_fsync);
1124     config_get_bool_field("aof-load-truncated",
1125             server.aof_load_truncated);
1126 
1127     /* Enum values */
1128     config_get_enum_field("maxmemory-policy",
1129             server.maxmemory_policy,maxmemory_policy_enum);
1130     config_get_enum_field("loglevel",
1131             server.verbosity,loglevel_enum);
1132     config_get_enum_field("supervised",
1133             server.supervised_mode,supervised_mode_enum);
1134     config_get_enum_field("appendfsync",
1135             server.aof_fsync,aof_fsync_enum);
1136     config_get_enum_field("syslog-facility",
1137             server.syslog_facility,syslog_facility_enum);
1138 
1139     /* Everything we can't handle with macros follows. */
1140 
1141     if (stringmatch(pattern,"appendonly",1)) {
1142         addReplyBulkCString(c,"appendonly");
1143         addReplyBulkCString(c,server.aof_state == AOF_OFF ? "no" : "yes");
1144         matches++;
1145     }
1146     if (stringmatch(pattern,"dir",1)) {
1147         char buf[1024];
1148 
1149         if (getcwd(buf,sizeof(buf)) == NULL)
1150             buf[0] = '\0';
1151 
1152         addReplyBulkCString(c,"dir");
1153         addReplyBulkCString(c,buf);
1154         matches++;
1155     }
1156     if (stringmatch(pattern,"save",1)) {
1157         sds buf = sdsempty();
1158         int j;
1159 
1160         for (j = 0; j < server.saveparamslen; j++) {
1161             buf = sdscatprintf(buf,"%jd %d",
1162                     (intmax_t)server.saveparams[j].seconds,
1163                     server.saveparams[j].changes);
1164             if (j != server.saveparamslen-1)
1165                 buf = sdscatlen(buf," ",1);
1166         }
1167         addReplyBulkCString(c,"save");
1168         addReplyBulkCString(c,buf);
1169         sdsfree(buf);
1170         matches++;
1171     }
1172     if (stringmatch(pattern,"client-output-buffer-limit",1)) {
1173         sds buf = sdsempty();
1174         int j;
1175 
1176         for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
1177             buf = sdscatprintf(buf,"%s %llu %llu %ld",
1178                     getClientTypeName(j),
1179                     server.client_obuf_limits[j].hard_limit_bytes,
1180                     server.client_obuf_limits[j].soft_limit_bytes,
1181                     (long) server.client_obuf_limits[j].soft_limit_seconds);
1182             if (j != CLIENT_TYPE_OBUF_COUNT-1)
1183                 buf = sdscatlen(buf," ",1);
1184         }
1185         addReplyBulkCString(c,"client-output-buffer-limit");
1186         addReplyBulkCString(c,buf);
1187         sdsfree(buf);
1188         matches++;
1189     }
1190     if (stringmatch(pattern,"unixsocketperm",1)) {
1191         char buf[32];
1192         snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
1193         addReplyBulkCString(c,"unixsocketperm");
1194         addReplyBulkCString(c,buf);
1195         matches++;
1196     }
1197     if (stringmatch(pattern,"slaveof",1)) {
1198         char buf[256];
1199 
1200         addReplyBulkCString(c,"slaveof");
1201         if (server.masterhost)
1202             snprintf(buf,sizeof(buf),"%s %d",
1203                 server.masterhost, server.masterport);
1204         else
1205             buf[0] = '\0';
1206         addReplyBulkCString(c,buf);
1207         matches++;
1208     }
1209     if (stringmatch(pattern,"notify-keyspace-events",1)) {
1210         robj *flagsobj = createObject(OBJ_STRING,
1211             keyspaceEventsFlagsToString(server.notify_keyspace_events));
1212 
1213         addReplyBulkCString(c,"notify-keyspace-events");
1214         addReplyBulk(c,flagsobj);
1215         decrRefCount(flagsobj);
1216         matches++;
1217     }
1218     if (stringmatch(pattern,"bind",1)) {
1219         sds aux = sdsjoin(server.bindaddr,server.bindaddr_count," ");
1220 
1221         addReplyBulkCString(c,"bind");
1222         addReplyBulkCString(c,aux);
1223         sdsfree(aux);
1224         matches++;
1225     }
1226     setDeferredMultiBulkLength(c,replylen,matches*2);
1227 }
1228 
1229 /*-----------------------------------------------------------------------------
1230  * CONFIG REWRITE implementation
1231  *----------------------------------------------------------------------------*/
1232 
1233 #define REDIS_CONFIG_REWRITE_SIGNATURE "# Generated by CONFIG REWRITE"
1234 
1235 /* We use the following dictionary type to store where a configuration
1236  * option is mentioned in the old configuration file, so it's
1237  * like "maxmemory" -> list of line numbers (first line is zero). */
1238 unsigned int dictSdsCaseHash(const void *key);
1239 int dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2);
1240 void dictSdsDestructor(void *privdata, void *val);
1241 void dictListDestructor(void *privdata, void *val);
1242 
1243 /* Sentinel config rewriting is implemented inside sentinel.c by
1244  * rewriteConfigSentinelOption(). */
1245 void rewriteConfigSentinelOption(struct rewriteConfigState *state);
1246 
1247 dictType optionToLineDictType = {
1248     dictSdsCaseHash,            /* hash function */
1249     NULL,                       /* key dup */
1250     NULL,                       /* val dup */
1251     dictSdsKeyCaseCompare,      /* key compare */
1252     dictSdsDestructor,          /* key destructor */
1253     dictListDestructor          /* val destructor */
1254 };
1255 
1256 dictType optionSetDictType = {
1257     dictSdsCaseHash,            /* hash function */
1258     NULL,                       /* key dup */
1259     NULL,                       /* val dup */
1260     dictSdsKeyCaseCompare,      /* key compare */
1261     dictSdsDestructor,          /* key destructor */
1262     NULL                        /* val destructor */
1263 };
1264 
1265 /* The config rewrite state. */
1266 struct rewriteConfigState {
1267     dict *option_to_line; /* Option -> list of config file lines map */
1268     dict *rewritten;      /* Dictionary of already processed options */
1269     int numlines;         /* Number of lines in current config */
1270     sds *lines;           /* Current lines as an array of sds strings */
1271     int has_tail;         /* True if we already added directives that were
1272                              not present in the original config file. */
1273 };
1274 
1275 /* Append the new line to the current configuration state. */
1276 void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) {
1277     state->lines = zrealloc(state->lines, sizeof(char*) * (state->numlines+1));
1278     state->lines[state->numlines++] = line;
1279 }
1280 
1281 /* Populate the option -> list of line numbers map. */
1282 void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) {
1283     list *l = dictFetchValue(state->option_to_line,option);
1284 
1285     if (l == NULL) {
1286         l = listCreate();
1287         dictAdd(state->option_to_line,sdsdup(option),l);
1288     }
1289     listAddNodeTail(l,(void*)(long)linenum);
1290 }
1291 
1292 /* Add the specified option to the set of processed options.
1293  * This is useful as only unused lines of processed options will be blanked
1294  * in the config file, while options the rewrite process does not understand
1295  * remain untouched. */
1296 void rewriteConfigMarkAsProcessed(struct rewriteConfigState *state, const char *option) {
1297     sds opt = sdsnew(option);
1298 
1299     if (dictAdd(state->rewritten,opt,NULL) != DICT_OK) sdsfree(opt);
1300 }
1301 
1302 /* Read the old file, split it into lines to populate a newly created
1303  * config rewrite state, and return it to the caller.
1304  *
1305  * If it is impossible to read the old file, NULL is returned.
1306  * If the old file does not exist at all, an empty state is returned. */
1307 struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
1308     FILE *fp = fopen(path,"r");
1309     struct rewriteConfigState *state = zmalloc(sizeof(*state));
1310     char buf[CONFIG_MAX_LINE+1];
1311     int linenum = -1;
1312 
1313     if (fp == NULL && errno != ENOENT) return NULL;
1314 
1315     state->option_to_line = dictCreate(&optionToLineDictType,NULL);
1316     state->rewritten = dictCreate(&optionSetDictType,NULL);
1317     state->numlines = 0;
1318     state->lines = NULL;
1319     state->has_tail = 0;
1320     if (fp == NULL) return state;
1321 
1322     /* Read the old file line by line, populate the state. */
1323     while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) {
1324         int argc;
1325         sds *argv;
1326         sds line = sdstrim(sdsnew(buf),"\r\n\t ");
1327 
1328         linenum++; /* Zero based, so we init at -1 */
1329 
1330         /* Handle comments and empty lines. */
1331         if (line[0] == '#' || line[0] == '\0') {
1332             if (!state->has_tail && !strcmp(line,REDIS_CONFIG_REWRITE_SIGNATURE))
1333                 state->has_tail = 1;
1334             rewriteConfigAppendLine(state,line);
1335             continue;
1336         }
1337 
1338         /* Not a comment, split into arguments. */
1339         argv = sdssplitargs(line,&argc);
1340         if (argv == NULL) {
1341             /* Apparently the line is unparsable for some reason, for
1342              * instance it may have unbalanced quotes. Load it as a
1343              * comment. */
1344             sds aux = sdsnew("# ??? ");
1345             aux = sdscatsds(aux,line);
1346             sdsfree(line);
1347             rewriteConfigAppendLine(state,aux);
1348             continue;
1349         }
1350 
1351         sdstolower(argv[0]); /* We only want lowercase config directives. */
1352 
1353         /* Now we populate the state according to the content of this line.
1354          * Append the line and populate the option -> line numbers map. */
1355         rewriteConfigAppendLine(state,line);
1356         rewriteConfigAddLineNumberToOption(state,argv[0],linenum);
1357 
1358         sdsfreesplitres(argv,argc);
1359     }
1360     fclose(fp);
1361     return state;
1362 }
1363 
1364 /* Rewrite the specified configuration option with the new "line".
1365  * It progressively uses lines of the file that were already used for the same
1366  * configuration option in the old version of the file, removing that line from
1367  * the map of options -> line numbers.
1368  *
1369  * If there are lines associated with a given configuration option and
1370  * "force" is non-zero, the line is appended to the configuration file.
1371  * Usually "force" is true when an option has not its default value, so it
1372  * must be rewritten even if not present previously.
1373  *
1374  * The first time a line is appended into a configuration file, a comment
1375  * is added to show that starting from that point the config file was generated
1376  * by CONFIG REWRITE.
1377  *
1378  * "line" is either used, or freed, so the caller does not need to free it
1379  * in any way. */
1380 void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force) {
1381     sds o = sdsnew(option);
1382     list *l = dictFetchValue(state->option_to_line,o);
1383 
1384     rewriteConfigMarkAsProcessed(state,option);
1385 
1386     if (!l && !force) {
1387         /* Option not used previously, and we are not forced to use it. */
1388         sdsfree(line);
1389         sdsfree(o);
1390         return;
1391     }
1392 
1393     if (l) {
1394         listNode *ln = listFirst(l);
1395         int linenum = (long) ln->value;
1396 
1397         /* There are still lines in the old configuration file we can reuse
1398          * for this option. Replace the line with the new one. */
1399         listDelNode(l,ln);
1400         if (listLength(l) == 0) dictDelete(state->option_to_line,o);
1401         sdsfree(state->lines[linenum]);
1402         state->lines[linenum] = line;
1403     } else {
1404         /* Append a new line. */
1405         if (!state->has_tail) {
1406             rewriteConfigAppendLine(state,
1407                 sdsnew(REDIS_CONFIG_REWRITE_SIGNATURE));
1408             state->has_tail = 1;
1409         }
1410         rewriteConfigAppendLine(state,line);
1411     }
1412     sdsfree(o);
1413 }
1414 
1415 /* Write the long long 'bytes' value as a string in a way that is parsable
1416  * inside redis.conf. If possible uses the GB, MB, KB notation. */
1417 int rewriteConfigFormatMemory(char *buf, size_t len, long long bytes) {
1418     int gb = 1024*1024*1024;
1419     int mb = 1024*1024;
1420     int kb = 1024;
1421 
1422     if (bytes && (bytes % gb) == 0) {
1423         return snprintf(buf,len,"%lldgb",bytes/gb);
1424     } else if (bytes && (bytes % mb) == 0) {
1425         return snprintf(buf,len,"%lldmb",bytes/mb);
1426     } else if (bytes && (bytes % kb) == 0) {
1427         return snprintf(buf,len,"%lldkb",bytes/kb);
1428     } else {
1429         return snprintf(buf,len,"%lld",bytes);
1430     }
1431 }
1432 
1433 /* Rewrite a simple "option-name <bytes>" configuration option. */
1434 void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
1435     char buf[64];
1436     int force = value != defvalue;
1437     sds line;
1438 
1439     rewriteConfigFormatMemory(buf,sizeof(buf),value);
1440     line = sdscatprintf(sdsempty(),"%s %s",option,buf);
1441     rewriteConfigRewriteLine(state,option,line,force);
1442 }
1443 
1444 /* Rewrite a yes/no option. */
1445 void rewriteConfigYesNoOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
1446     int force = value != defvalue;
1447     sds line = sdscatprintf(sdsempty(),"%s %s",option,
1448         value ? "yes" : "no");
1449 
1450     rewriteConfigRewriteLine(state,option,line,force);
1451 }
1452 
1453 /* Rewrite a string option. */
1454 void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, char *value, char *defvalue) {
1455     int force = 1;
1456     sds line;
1457 
1458     /* String options set to NULL need to be not present at all in the
1459      * configuration file to be set to NULL again at the next reboot. */
1460     if (value == NULL) {
1461         rewriteConfigMarkAsProcessed(state,option);
1462         return;
1463     }
1464 
1465     /* Set force to zero if the value is set to its default. */
1466     if (defvalue && strcmp(value,defvalue) == 0) force = 0;
1467 
1468     line = sdsnew(option);
1469     line = sdscatlen(line, " ", 1);
1470     line = sdscatrepr(line, value, strlen(value));
1471 
1472     rewriteConfigRewriteLine(state,option,line,force);
1473 }
1474 
1475 /* Rewrite a numerical (long long range) option. */
1476 void rewriteConfigNumericalOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
1477     int force = value != defvalue;
1478     sds line = sdscatprintf(sdsempty(),"%s %lld",option,value);
1479 
1480     rewriteConfigRewriteLine(state,option,line,force);
1481 }
1482 
1483 /* Rewrite a octal option. */
1484 void rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
1485     int force = value != defvalue;
1486     sds line = sdscatprintf(sdsempty(),"%s %o",option,value);
1487 
1488     rewriteConfigRewriteLine(state,option,line,force);
1489 }
1490 
1491 /* Rewrite an enumeration option. It takes as usually state and option name,
1492  * and in addition the enumeration array and the default value for the
1493  * option. */
1494 void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int value, configEnum *ce, int defval) {
1495     sds line;
1496     const char *name = configEnumGetNameOrUnknown(ce,value);
1497     int force = value != defval;
1498 
1499     line = sdscatprintf(sdsempty(),"%s %s",option,name);
1500     rewriteConfigRewriteLine(state,option,line,force);
1501 }
1502 
1503 /* Rewrite the syslog-facility option. */
1504 void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) {
1505     int value = server.syslog_facility;
1506     int force = value != LOG_LOCAL0;
1507     const char *name = NULL, *option = "syslog-facility";
1508     sds line;
1509 
1510     name = configEnumGetNameOrUnknown(syslog_facility_enum,value);
1511     line = sdscatprintf(sdsempty(),"%s %s",option,name);
1512     rewriteConfigRewriteLine(state,option,line,force);
1513 }
1514 
1515 /* Rewrite the save option. */
1516 void rewriteConfigSaveOption(struct rewriteConfigState *state) {
1517     int j;
1518     sds line;
1519 
1520     /* Note that if there are no save parameters at all, all the current
1521      * config line with "save" will be detected as orphaned and deleted,
1522      * resulting into no RDB persistence as expected. */
1523     for (j = 0; j < server.saveparamslen; j++) {
1524         line = sdscatprintf(sdsempty(),"save %ld %d",
1525             (long) server.saveparams[j].seconds, server.saveparams[j].changes);
1526         rewriteConfigRewriteLine(state,"save",line,1);
1527     }
1528     /* Mark "save" as processed in case server.saveparamslen is zero. */
1529     rewriteConfigMarkAsProcessed(state,"save");
1530 }
1531 
1532 /* Rewrite the dir option, always using absolute paths.*/
1533 void rewriteConfigDirOption(struct rewriteConfigState *state) {
1534     char cwd[1024];
1535 
1536     if (getcwd(cwd,sizeof(cwd)) == NULL) {
1537         rewriteConfigMarkAsProcessed(state,"dir");
1538         return; /* no rewrite on error. */
1539     }
1540     rewriteConfigStringOption(state,"dir",cwd,NULL);
1541 }
1542 
1543 /* Rewrite the slaveof option. */
1544 void rewriteConfigSlaveofOption(struct rewriteConfigState *state) {
1545     char *option = "slaveof";
1546     sds line;
1547 
1548     /* If this is a master, we want all the slaveof config options
1549      * in the file to be removed. Note that if this is a cluster instance
1550      * we don't want a slaveof directive inside redis.conf. */
1551     if (server.cluster_enabled || server.masterhost == NULL) {
1552         rewriteConfigMarkAsProcessed(state,"slaveof");
1553         return;
1554     }
1555     line = sdscatprintf(sdsempty(),"%s %s %d", option,
1556         server.masterhost, server.masterport);
1557     rewriteConfigRewriteLine(state,option,line,1);
1558 }
1559 
1560 /* Rewrite the notify-keyspace-events option. */
1561 void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) {
1562     int force = server.notify_keyspace_events != 0;
1563     char *option = "notify-keyspace-events";
1564     sds line, flags;
1565 
1566     flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);
1567     line = sdsnew(option);
1568     line = sdscatlen(line, " ", 1);
1569     line = sdscatrepr(line, flags, sdslen(flags));
1570     sdsfree(flags);
1571     rewriteConfigRewriteLine(state,option,line,force);
1572 }
1573 
1574 /* Rewrite the client-output-buffer-limit option. */
1575 void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) {
1576     int j;
1577     char *option = "client-output-buffer-limit";
1578 
1579     for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++) {
1580         int force = (server.client_obuf_limits[j].hard_limit_bytes !=
1581                     clientBufferLimitsDefaults[j].hard_limit_bytes) ||
1582                     (server.client_obuf_limits[j].soft_limit_bytes !=
1583                     clientBufferLimitsDefaults[j].soft_limit_bytes) ||
1584                     (server.client_obuf_limits[j].soft_limit_seconds !=
1585                     clientBufferLimitsDefaults[j].soft_limit_seconds);
1586         sds line;
1587         char hard[64], soft[64];
1588 
1589         rewriteConfigFormatMemory(hard,sizeof(hard),
1590                 server.client_obuf_limits[j].hard_limit_bytes);
1591         rewriteConfigFormatMemory(soft,sizeof(soft),
1592                 server.client_obuf_limits[j].soft_limit_bytes);
1593 
1594         line = sdscatprintf(sdsempty(),"%s %s %s %s %ld",
1595                 option, getClientTypeName(j), hard, soft,
1596                 (long) server.client_obuf_limits[j].soft_limit_seconds);
1597         rewriteConfigRewriteLine(state,option,line,force);
1598     }
1599 }
1600 
1601 /* Rewrite the bind option. */
1602 void rewriteConfigBindOption(struct rewriteConfigState *state) {
1603     int force = 1;
1604     sds line, addresses;
1605     char *option = "bind";
1606 
1607     /* Nothing to rewrite if we don't have bind addresses. */
1608     if (server.bindaddr_count == 0) {
1609         rewriteConfigMarkAsProcessed(state,option);
1610         return;
1611     }
1612 
1613     /* Rewrite as bind <addr1> <addr2> ... <addrN> */
1614     addresses = sdsjoin(server.bindaddr,server.bindaddr_count," ");
1615     line = sdsnew(option);
1616     line = sdscatlen(line, " ", 1);
1617     line = sdscatsds(line, addresses);
1618     sdsfree(addresses);
1619 
1620     rewriteConfigRewriteLine(state,option,line,force);
1621 }
1622 
1623 /* Glue together the configuration lines in the current configuration
1624  * rewrite state into a single string, stripping multiple empty lines. */
1625 sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {
1626     sds content = sdsempty();
1627     int j, was_empty = 0;
1628 
1629     for (j = 0; j < state->numlines; j++) {
1630         /* Every cluster of empty lines is turned into a single empty line. */
1631         if (sdslen(state->lines[j]) == 0) {
1632             if (was_empty) continue;
1633             was_empty = 1;
1634         } else {
1635             was_empty = 0;
1636         }
1637         content = sdscatsds(content,state->lines[j]);
1638         content = sdscatlen(content,"\n",1);
1639     }
1640     return content;
1641 }
1642 
1643 /* Free the configuration rewrite state. */
1644 void rewriteConfigReleaseState(struct rewriteConfigState *state) {
1645     sdsfreesplitres(state->lines,state->numlines);
1646     dictRelease(state->option_to_line);
1647     dictRelease(state->rewritten);
1648     zfree(state);
1649 }
1650 
1651 /* At the end of the rewrite process the state contains the remaining
1652  * map between "option name" => "lines in the original config file".
1653  * Lines used by the rewrite process were removed by the function
1654  * rewriteConfigRewriteLine(), all the other lines are "orphaned" and
1655  * should be replaced by empty lines.
1656  *
1657  * This function does just this, iterating all the option names and
1658  * blanking all the lines still associated. */
1659 void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
1660     dictIterator *di = dictGetIterator(state->option_to_line);
1661     dictEntry *de;
1662 
1663     while((de = dictNext(di)) != NULL) {
1664         list *l = dictGetVal(de);
1665         sds option = dictGetKey(de);
1666 
1667         /* Don't blank lines about options the rewrite process
1668          * don't understand. */
1669         if (dictFind(state->rewritten,option) == NULL) {
1670             serverLog(LL_DEBUG,"Not rewritten option: %s", option);
1671             continue;
1672         }
1673 
1674         while(listLength(l)) {
1675             listNode *ln = listFirst(l);
1676             int linenum = (long) ln->value;
1677 
1678             sdsfree(state->lines[linenum]);
1679             state->lines[linenum] = sdsempty();
1680             listDelNode(l,ln);
1681         }
1682     }
1683     dictReleaseIterator(di);
1684 }
1685 
1686 /* This function overwrites the old configuration file with the new content.
1687  *
1688  * 1) The old file length is obtained.
1689  * 2) If the new content is smaller, padding is added.
1690  * 3) A single write(2) call is used to replace the content of the file.
1691  * 4) Later the file is truncated to the length of the new content.
1692  *
1693  * This way we are sure the file is left in a consistent state even if the
1694  * process is stopped between any of the four operations.
1695  *
1696  * The function returns 0 on success, otherwise -1 is returned and errno
1697  * set accordingly. */
1698 int rewriteConfigOverwriteFile(char *configfile, sds content) {
1699     int retval = 0;
1700     int fd = open(configfile,O_RDWR|O_CREAT,0644);
1701     int content_size = sdslen(content), padding = 0;
1702     struct stat sb;
1703     sds content_padded;
1704 
1705     /* 1) Open the old file (or create a new one if it does not
1706      *    exist), get the size. */
1707     if (fd == -1) return -1; /* errno set by open(). */
1708     if (fstat(fd,&sb) == -1) {
1709         close(fd);
1710         return -1; /* errno set by fstat(). */
1711     }
1712 
1713     /* 2) Pad the content at least match the old file size. */
1714     content_padded = sdsdup(content);
1715     if (content_size < sb.st_size) {
1716         /* If the old file was bigger, pad the content with
1717          * a newline plus as many "#" chars as required. */
1718         padding = sb.st_size - content_size;
1719         content_padded = sdsgrowzero(content_padded,sb.st_size);
1720         content_padded[content_size] = '\n';
1721         memset(content_padded+content_size+1,'#',padding-1);
1722     }
1723 
1724     /* 3) Write the new content using a single write(2). */
1725     if (write(fd,content_padded,strlen(content_padded)) == -1) {
1726         retval = -1;
1727         goto cleanup;
1728     }
1729 
1730     /* 4) Truncate the file to the right length if we used padding. */
1731     if (padding) {
1732         if (ftruncate(fd,content_size) == -1) {
1733             /* Non critical error... */
1734         }
1735     }
1736 
1737 cleanup:
1738     sdsfree(content_padded);
1739     close(fd);
1740     return retval;
1741 }
1742 
1743 /* Rewrite the configuration file at "path".
1744  * If the configuration file already exists, we try at best to retain comments
1745  * and overall structure.
1746  *
1747  * Configuration parameters that are at their default value, unless already
1748  * explicitly included in the old configuration file, are not rewritten.
1749  *
1750  * On error -1 is returned and errno is set accordingly, otherwise 0. */
1751 int rewriteConfig(char *path) {
1752     struct rewriteConfigState *state;
1753     sds newcontent;
1754     int retval;
1755 
1756     /* Step 1: read the old config into our rewrite state. */
1757     if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;
1758 
1759     /* Step 2: rewrite every single option, replacing or appending it inside
1760      * the rewrite state. */
1761 
1762     rewriteConfigYesNoOption(state,"daemonize",server.daemonize,0);
1763     rewriteConfigStringOption(state,"pidfile",server.pidfile,CONFIG_DEFAULT_PID_FILE);
1764     rewriteConfigNumericalOption(state,"port",server.port,CONFIG_DEFAULT_SERVER_PORT);
1765     rewriteConfigNumericalOption(state,"tcp-backlog",server.tcp_backlog,CONFIG_DEFAULT_TCP_BACKLOG);
1766     rewriteConfigBindOption(state);
1767     rewriteConfigStringOption(state,"unixsocket",server.unixsocket,NULL);
1768     rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,CONFIG_DEFAULT_UNIX_SOCKET_PERM);
1769     rewriteConfigNumericalOption(state,"timeout",server.maxidletime,CONFIG_DEFAULT_CLIENT_TIMEOUT);
1770     rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,CONFIG_DEFAULT_TCP_KEEPALIVE);
1771     rewriteConfigEnumOption(state,"loglevel",server.verbosity,loglevel_enum,CONFIG_DEFAULT_VERBOSITY);
1772     rewriteConfigStringOption(state,"logfile",server.logfile,CONFIG_DEFAULT_LOGFILE);
1773     rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,CONFIG_DEFAULT_SYSLOG_ENABLED);
1774     rewriteConfigStringOption(state,"syslog-ident",server.syslog_ident,CONFIG_DEFAULT_SYSLOG_IDENT);
1775     rewriteConfigSyslogfacilityOption(state);
1776     rewriteConfigSaveOption(state);
1777     rewriteConfigNumericalOption(state,"databases",server.dbnum,CONFIG_DEFAULT_DBNUM);
1778     rewriteConfigYesNoOption(state,"stop-writes-on-bgsave-error",server.stop_writes_on_bgsave_err,CONFIG_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR);
1779     rewriteConfigYesNoOption(state,"rdbcompression",server.rdb_compression,CONFIG_DEFAULT_RDB_COMPRESSION);
1780     rewriteConfigYesNoOption(state,"rdbchecksum",server.rdb_checksum,CONFIG_DEFAULT_RDB_CHECKSUM);
1781     rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,CONFIG_DEFAULT_RDB_FILENAME);
1782     rewriteConfigDirOption(state);
1783     rewriteConfigSlaveofOption(state);
1784     rewriteConfigStringOption(state,"masterauth",server.masterauth,NULL);
1785     rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA);
1786     rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,CONFIG_DEFAULT_SLAVE_READ_ONLY);
1787     rewriteConfigNumericalOption(state,"repl-ping-slave-period",server.repl_ping_slave_period,CONFIG_DEFAULT_REPL_PING_SLAVE_PERIOD);
1788     rewriteConfigNumericalOption(state,"repl-timeout",server.repl_timeout,CONFIG_DEFAULT_REPL_TIMEOUT);
1789     rewriteConfigBytesOption(state,"repl-backlog-size",server.repl_backlog_size,CONFIG_DEFAULT_REPL_BACKLOG_SIZE);
1790     rewriteConfigBytesOption(state,"repl-backlog-ttl",server.repl_backlog_time_limit,CONFIG_DEFAULT_REPL_BACKLOG_TIME_LIMIT);
1791     rewriteConfigYesNoOption(state,"repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay,CONFIG_DEFAULT_REPL_DISABLE_TCP_NODELAY);
1792     rewriteConfigYesNoOption(state,"repl-diskless-sync",server.repl_diskless_sync,CONFIG_DEFAULT_REPL_DISKLESS_SYNC);
1793     rewriteConfigNumericalOption(state,"repl-diskless-sync-delay",server.repl_diskless_sync_delay,CONFIG_DEFAULT_REPL_DISKLESS_SYNC_DELAY);
1794     rewriteConfigNumericalOption(state,"slave-priority",server.slave_priority,CONFIG_DEFAULT_SLAVE_PRIORITY);
1795     rewriteConfigNumericalOption(state,"min-slaves-to-write",server.repl_min_slaves_to_write,CONFIG_DEFAULT_MIN_SLAVES_TO_WRITE);
1796     rewriteConfigNumericalOption(state,"min-slaves-max-lag",server.repl_min_slaves_max_lag,CONFIG_DEFAULT_MIN_SLAVES_MAX_LAG);
1797     rewriteConfigStringOption(state,"requirepass",server.requirepass,NULL);
1798     rewriteConfigNumericalOption(state,"maxclients",server.maxclients,CONFIG_DEFAULT_MAX_CLIENTS);
1799     rewriteConfigBytesOption(state,"maxmemory",server.maxmemory,CONFIG_DEFAULT_MAXMEMORY);
1800     rewriteConfigEnumOption(state,"maxmemory-policy",server.maxmemory_policy,maxmemory_policy_enum,CONFIG_DEFAULT_MAXMEMORY_POLICY);
1801     rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,CONFIG_DEFAULT_MAXMEMORY_SAMPLES);
1802     rewriteConfigYesNoOption(state,"appendonly",server.aof_state != AOF_OFF,0);
1803     rewriteConfigStringOption(state,"appendfilename",server.aof_filename,CONFIG_DEFAULT_AOF_FILENAME);
1804     rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync,aof_fsync_enum,CONFIG_DEFAULT_AOF_FSYNC);
1805     rewriteConfigYesNoOption(state,"no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite,CONFIG_DEFAULT_AOF_NO_FSYNC_ON_REWRITE);
1806     rewriteConfigNumericalOption(state,"auto-aof-rewrite-percentage",server.aof_rewrite_perc,AOF_REWRITE_PERC);
1807     rewriteConfigBytesOption(state,"auto-aof-rewrite-min-size",server.aof_rewrite_min_size,AOF_REWRITE_MIN_SIZE);
1808     rewriteConfigNumericalOption(state,"lua-time-limit",server.lua_time_limit,LUA_SCRIPT_TIME_LIMIT);
1809     rewriteConfigYesNoOption(state,"cluster-enabled",server.cluster_enabled,0);
1810     rewriteConfigStringOption(state,"cluster-config-file",server.cluster_configfile,CONFIG_DEFAULT_CLUSTER_CONFIG_FILE);
1811     rewriteConfigYesNoOption(state,"cluster-require-full-coverage",server.cluster_require_full_coverage,CLUSTER_DEFAULT_REQUIRE_FULL_COVERAGE);
1812     rewriteConfigNumericalOption(state,"cluster-node-timeout",server.cluster_node_timeout,CLUSTER_DEFAULT_NODE_TIMEOUT);
1813     rewriteConfigNumericalOption(state,"cluster-migration-barrier",server.cluster_migration_barrier,CLUSTER_DEFAULT_MIGRATION_BARRIER);
1814     rewriteConfigNumericalOption(state,"cluster-slave-validity-factor",server.cluster_slave_validity_factor,CLUSTER_DEFAULT_SLAVE_VALIDITY);
1815     rewriteConfigNumericalOption(state,"slowlog-log-slower-than",server.slowlog_log_slower_than,CONFIG_DEFAULT_SLOWLOG_LOG_SLOWER_THAN);
1816     rewriteConfigNumericalOption(state,"latency-monitor-threshold",server.latency_monitor_threshold,CONFIG_DEFAULT_LATENCY_MONITOR_THRESHOLD);
1817     rewriteConfigNumericalOption(state,"slowlog-max-len",server.slowlog_max_len,CONFIG_DEFAULT_SLOWLOG_MAX_LEN);
1818     rewriteConfigNotifykeyspaceeventsOption(state);
1819     rewriteConfigNumericalOption(state,"hash-max-ziplist-entries",server.hash_max_ziplist_entries,OBJ_HASH_MAX_ZIPLIST_ENTRIES);
1820     rewriteConfigNumericalOption(state,"hash-max-ziplist-value",server.hash_max_ziplist_value,OBJ_HASH_MAX_ZIPLIST_VALUE);
1821     rewriteConfigNumericalOption(state,"list-max-ziplist-size",server.list_max_ziplist_size,OBJ_LIST_MAX_ZIPLIST_SIZE);
1822     rewriteConfigNumericalOption(state,"list-compress-depth",server.list_compress_depth,OBJ_LIST_COMPRESS_DEPTH);
1823     rewriteConfigNumericalOption(state,"set-max-intset-entries",server.set_max_intset_entries,OBJ_SET_MAX_INTSET_ENTRIES);
1824     rewriteConfigNumericalOption(state,"zset-max-ziplist-entries",server.zset_max_ziplist_entries,OBJ_ZSET_MAX_ZIPLIST_ENTRIES);
1825     rewriteConfigNumericalOption(state,"zset-max-ziplist-value",server.zset_max_ziplist_value,OBJ_ZSET_MAX_ZIPLIST_VALUE);
1826     rewriteConfigNumericalOption(state,"hll-sparse-max-bytes",server.hll_sparse_max_bytes,CONFIG_DEFAULT_HLL_SPARSE_MAX_BYTES);
1827     rewriteConfigYesNoOption(state,"activerehashing",server.activerehashing,CONFIG_DEFAULT_ACTIVE_REHASHING);
1828     rewriteConfigYesNoOption(state,"protected-mode",server.protected_mode,CONFIG_DEFAULT_PROTECTED_MODE);
1829     rewriteConfigClientoutputbufferlimitOption(state);
1830     rewriteConfigNumericalOption(state,"hz",server.hz,CONFIG_DEFAULT_HZ);
1831     rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,CONFIG_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC);
1832     rewriteConfigYesNoOption(state,"aof-load-truncated",server.aof_load_truncated,CONFIG_DEFAULT_AOF_LOAD_TRUNCATED);
1833     rewriteConfigEnumOption(state,"supervised",server.supervised_mode,supervised_mode_enum,SUPERVISED_NONE);
1834 
1835     /* Rewrite Sentinel config if in Sentinel mode. */
1836     if (server.sentinel_mode) rewriteConfigSentinelOption(state);
1837 
1838     /* Step 3: remove all the orphaned lines in the old file, that is, lines
1839      * that were used by a config option and are no longer used, like in case
1840      * of multiple "save" options or duplicated options. */
1841     rewriteConfigRemoveOrphaned(state);
1842 
1843     /* Step 4: generate a new configuration file from the modified state
1844      * and write it into the original file. */
1845     newcontent = rewriteConfigGetContentFromState(state);
1846     retval = rewriteConfigOverwriteFile(server.configfile,newcontent);
1847 
1848     sdsfree(newcontent);
1849     rewriteConfigReleaseState(state);
1850     return retval;
1851 }
1852 
1853 /*-----------------------------------------------------------------------------
1854  * CONFIG command entry point
1855  *----------------------------------------------------------------------------*/
1856 
1857 void configCommand(client *c) {
1858     /* Only allow CONFIG GET while loading. */
1859     if (server.loading && strcasecmp(c->argv[1]->ptr,"get")) {
1860         addReplyError(c,"Only CONFIG GET is allowed during loading");
1861         return;
1862     }
1863 
1864     if (!strcasecmp(c->argv[1]->ptr,"set")) {
1865         if (c->argc != 4) goto badarity;
1866         configSetCommand(c);
1867     } else if (!strcasecmp(c->argv[1]->ptr,"get")) {
1868         if (c->argc != 3) goto badarity;
1869         configGetCommand(c);
1870     } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) {
1871         if (c->argc != 2) goto badarity;
1872         resetServerStats();
1873         resetCommandTableStats();
1874         addReply(c,shared.ok);
1875     } else if (!strcasecmp(c->argv[1]->ptr,"rewrite")) {
1876         if (c->argc != 2) goto badarity;
1877         if (server.configfile == NULL) {
1878             addReplyError(c,"The server is running without a config file");
1879             return;
1880         }
1881         if (rewriteConfig(server.configfile) == -1) {
1882             serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(errno));
1883             addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno));
1884         } else {
1885             serverLog(LL_WARNING,"CONFIG REWRITE executed with success.");
1886             addReply(c,shared.ok);
1887         }
1888     } else {
1889         addReplyError(c,
1890             "CONFIG subcommand must be one of GET, SET, RESETSTAT, REWRITE");
1891     }
1892     return;
1893 
1894 badarity:
1895     addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s",
1896         (char*) c->argv[1]->ptr);
1897 }
1898