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