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