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