xref: /redis-3.2.3/src/config.c (revision edd7eb9b)
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             continue;
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 = memtoll(argv[1],NULL);
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],"min-slaves-to-write") && argc == 2) {
444             server.repl_min_slaves_to_write = atoi(argv[1]);
445             if (server.repl_min_slaves_to_write < 0) {
446                 err = "Invalid value for min-slaves-to-write."; goto loaderr;
447             }
448         } else if (!strcasecmp(argv[0],"min-slaves-max-lag") && argc == 2) {
449             server.repl_min_slaves_max_lag = atoi(argv[1]);
450             if (server.repl_min_slaves_max_lag < 0) {
451                 err = "Invalid value for min-slaves-max-lag."; goto loaderr;
452             }
453         } else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) {
454             int flags = keyspaceEventsStringToFlags(argv[1]);
455 
456             if (flags == -1) {
457                 err = "Invalid event class character. Use 'g$lshzxeA'.";
458                 goto loaderr;
459             }
460             server.notify_keyspace_events = flags;
461         } else if (!strcasecmp(argv[0],"sentinel")) {
462             /* argc == 1 is handled by main() as we need to enter the sentinel
463              * mode ASAP. */
464             if (argc != 1) {
465                 if (!server.sentinel_mode) {
466                     err = "sentinel directive while not in sentinel mode";
467                     goto loaderr;
468                 }
469                 err = sentinelHandleConfiguration(argv+1,argc-1);
470                 if (err) goto loaderr;
471             }
472         } else {
473             err = "Bad directive or wrong number of arguments"; goto loaderr;
474         }
475         sdsfreesplitres(argv,argc);
476     }
477     sdsfreesplitres(lines,totlines);
478 
479     /* Sanity checks. */
480     if (server.cluster_enabled && server.masterhost) {
481         err = "slaveof directive not allowed in cluster mode";
482         goto loaderr;
483     }
484     return;
485 
486 loaderr:
487     fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n");
488     fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
489     fprintf(stderr, ">>> '%s'\n", lines[i]);
490     fprintf(stderr, "%s\n", err);
491     exit(1);
492 }
493 
494 /* Load the server configuration from the specified filename.
495  * The function appends the additional configuration directives stored
496  * in the 'options' string to the config file before loading.
497  *
498  * Both filename and options can be NULL, in such a case are considered
499  * empty. This way loadServerConfig can be used to just load a file or
500  * just load a string. */
501 void loadServerConfig(char *filename, char *options) {
502     sds config = sdsempty();
503     char buf[REDIS_CONFIGLINE_MAX+1];
504 
505     /* Load the file content */
506     if (filename) {
507         FILE *fp;
508 
509         if (filename[0] == '-' && filename[1] == '\0') {
510             fp = stdin;
511         } else {
512             if ((fp = fopen(filename,"r")) == NULL) {
513                 redisLog(REDIS_WARNING,
514                     "Fatal error, can't open config file '%s'", filename);
515                 exit(1);
516             }
517         }
518         while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL)
519             config = sdscat(config,buf);
520         if (fp != stdin) fclose(fp);
521     }
522     /* Append the additional options */
523     if (options) {
524         config = sdscat(config,"\n");
525         config = sdscat(config,options);
526     }
527     loadServerConfigFromString(config);
528     sdsfree(config);
529 }
530 
531 /*-----------------------------------------------------------------------------
532  * CONFIG SET implementation
533  *----------------------------------------------------------------------------*/
534 
535 void configSetCommand(redisClient *c) {
536     robj *o;
537     long long ll;
538     redisAssertWithInfo(c,c->argv[2],c->argv[2]->encoding == REDIS_ENCODING_RAW);
539     redisAssertWithInfo(c,c->argv[2],c->argv[3]->encoding == REDIS_ENCODING_RAW);
540     o = c->argv[3];
541 
542     if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) {
543         zfree(server.rdb_filename);
544         server.rdb_filename = zstrdup(o->ptr);
545     } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) {
546         if (sdslen(o->ptr) > REDIS_AUTHPASS_MAX_LEN) goto badfmt;
547         zfree(server.requirepass);
548         server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
549     } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) {
550         zfree(server.masterauth);
551         server.masterauth = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
552     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) {
553         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
554             ll < 0) goto badfmt;
555         server.maxmemory = ll;
556         if (server.maxmemory) {
557             if (server.maxmemory < zmalloc_used_memory()) {
558                 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.");
559             }
560             freeMemoryIfNeeded();
561         }
562     } else if (!strcasecmp(c->argv[2]->ptr,"hz")) {
563         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
564             ll < 0) goto badfmt;
565         server.hz = (int) ll;
566         if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
567         if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
568     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) {
569         if (!strcasecmp(o->ptr,"volatile-lru")) {
570             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
571         } else if (!strcasecmp(o->ptr,"volatile-random")) {
572             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
573         } else if (!strcasecmp(o->ptr,"volatile-ttl")) {
574             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
575         } else if (!strcasecmp(o->ptr,"allkeys-lru")) {
576             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
577         } else if (!strcasecmp(o->ptr,"allkeys-random")) {
578             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
579         } else if (!strcasecmp(o->ptr,"noeviction")) {
580             server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
581         } else {
582             goto badfmt;
583         }
584     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) {
585         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
586             ll <= 0) goto badfmt;
587         server.maxmemory_samples = ll;
588     } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) {
589         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
590             ll < 0 || ll > LONG_MAX) goto badfmt;
591         server.maxidletime = ll;
592     } else if (!strcasecmp(c->argv[2]->ptr,"tcp-keepalive")) {
593         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
594             ll < 0 || ll > INT_MAX) goto badfmt;
595         server.tcpkeepalive = ll;
596     } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) {
597         if (!strcasecmp(o->ptr,"no")) {
598             server.aof_fsync = AOF_FSYNC_NO;
599         } else if (!strcasecmp(o->ptr,"everysec")) {
600             server.aof_fsync = AOF_FSYNC_EVERYSEC;
601         } else if (!strcasecmp(o->ptr,"always")) {
602             server.aof_fsync = AOF_FSYNC_ALWAYS;
603         } else {
604             goto badfmt;
605         }
606     } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) {
607         int yn = yesnotoi(o->ptr);
608 
609         if (yn == -1) goto badfmt;
610         server.aof_no_fsync_on_rewrite = yn;
611     } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) {
612         int enable = yesnotoi(o->ptr);
613 
614         if (enable == -1) goto badfmt;
615         if (enable == 0 && server.aof_state != REDIS_AOF_OFF) {
616             stopAppendOnly();
617         } else if (enable && server.aof_state == REDIS_AOF_OFF) {
618             if (startAppendOnly() == REDIS_ERR) {
619                 addReplyError(c,
620                     "Unable to turn on AOF. Check server logs.");
621                 return;
622             }
623         }
624     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) {
625         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
626         server.aof_rewrite_perc = ll;
627     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) {
628         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
629         server.aof_rewrite_min_size = ll;
630     } else if (!strcasecmp(c->argv[2]->ptr,"aof-rewrite-incremental-fsync")) {
631         int yn = yesnotoi(o->ptr);
632 
633         if (yn == -1) goto badfmt;
634         server.aof_rewrite_incremental_fsync = yn;
635     } else if (!strcasecmp(c->argv[2]->ptr,"save")) {
636         int vlen, j;
637         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
638 
639         /* Perform sanity check before setting the new config:
640          * - Even number of args
641          * - Seconds >= 1, changes >= 0 */
642         if (vlen & 1) {
643             sdsfreesplitres(v,vlen);
644             goto badfmt;
645         }
646         for (j = 0; j < vlen; j++) {
647             char *eptr;
648             long val;
649 
650             val = strtoll(v[j], &eptr, 10);
651             if (eptr[0] != '\0' ||
652                 ((j & 1) == 0 && val < 1) ||
653                 ((j & 1) == 1 && val < 0)) {
654                 sdsfreesplitres(v,vlen);
655                 goto badfmt;
656             }
657         }
658         /* Finally set the new config */
659         resetServerSaveParams();
660         for (j = 0; j < vlen; j += 2) {
661             time_t seconds;
662             int changes;
663 
664             seconds = strtoll(v[j],NULL,10);
665             changes = strtoll(v[j+1],NULL,10);
666             appendServerSaveParams(seconds, changes);
667         }
668         sdsfreesplitres(v,vlen);
669     } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) {
670         int yn = yesnotoi(o->ptr);
671 
672         if (yn == -1) goto badfmt;
673         server.repl_serve_stale_data = yn;
674     } else if (!strcasecmp(c->argv[2]->ptr,"slave-read-only")) {
675         int yn = yesnotoi(o->ptr);
676 
677         if (yn == -1) goto badfmt;
678         server.repl_slave_ro = yn;
679     } else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
680         if (chdir((char*)o->ptr) == -1) {
681             addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
682             return;
683         }
684     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-entries")) {
685         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
686         server.hash_max_ziplist_entries = ll;
687     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) {
688         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
689         server.hash_max_ziplist_value = ll;
690     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) {
691         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
692         server.list_max_ziplist_entries = ll;
693     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) {
694         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
695         server.list_max_ziplist_value = ll;
696     } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
697         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
698         server.set_max_intset_entries = ll;
699     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) {
700         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
701         server.zset_max_ziplist_entries = ll;
702     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) {
703         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
704         server.zset_max_ziplist_value = ll;
705     } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) {
706         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
707         server.lua_time_limit = ll;
708     } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) {
709         if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt;
710         server.slowlog_log_slower_than = ll;
711     } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) {
712         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
713         server.slowlog_max_len = (unsigned)ll;
714     } else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) {
715         if (!strcasecmp(o->ptr,"warning")) {
716             server.verbosity = REDIS_WARNING;
717         } else if (!strcasecmp(o->ptr,"notice")) {
718             server.verbosity = REDIS_NOTICE;
719         } else if (!strcasecmp(o->ptr,"verbose")) {
720             server.verbosity = REDIS_VERBOSE;
721         } else if (!strcasecmp(o->ptr,"debug")) {
722             server.verbosity = REDIS_DEBUG;
723         } else {
724             goto badfmt;
725         }
726     } else if (!strcasecmp(c->argv[2]->ptr,"client-output-buffer-limit")) {
727         int vlen, j;
728         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
729 
730         /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */
731         if (vlen % 4) {
732             sdsfreesplitres(v,vlen);
733             goto badfmt;
734         }
735 
736         /* Sanity check of single arguments, so that we either refuse the
737          * whole configuration string or accept it all, even if a single
738          * error in a single client class is present. */
739         for (j = 0; j < vlen; j++) {
740             char *eptr;
741             long val;
742 
743             if ((j % 4) == 0) {
744                 if (getClientLimitClassByName(v[j]) == -1) {
745                     sdsfreesplitres(v,vlen);
746                     goto badfmt;
747                 }
748             } else {
749                 val = strtoll(v[j], &eptr, 10);
750                 if (eptr[0] != '\0' || val < 0) {
751                     sdsfreesplitres(v,vlen);
752                     goto badfmt;
753                 }
754             }
755         }
756         /* Finally set the new config */
757         for (j = 0; j < vlen; j += 4) {
758             int class;
759             unsigned long long hard, soft;
760             int soft_seconds;
761 
762             class = getClientLimitClassByName(v[j]);
763             hard = strtoll(v[j+1],NULL,10);
764             soft = strtoll(v[j+2],NULL,10);
765             soft_seconds = strtoll(v[j+3],NULL,10);
766 
767             server.client_obuf_limits[class].hard_limit_bytes = hard;
768             server.client_obuf_limits[class].soft_limit_bytes = soft;
769             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
770         }
771         sdsfreesplitres(v,vlen);
772     } else if (!strcasecmp(c->argv[2]->ptr,"stop-writes-on-bgsave-error")) {
773         int yn = yesnotoi(o->ptr);
774 
775         if (yn == -1) goto badfmt;
776         server.stop_writes_on_bgsave_err = yn;
777     } else if (!strcasecmp(c->argv[2]->ptr,"repl-ping-slave-period")) {
778         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
779         server.repl_ping_slave_period = ll;
780     } else if (!strcasecmp(c->argv[2]->ptr,"repl-timeout")) {
781         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
782         server.repl_timeout = ll;
783     } else if (!strcasecmp(c->argv[2]->ptr,"repl-backlog-size")) {
784         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
785         resizeReplicationBacklog(ll);
786     } else if (!strcasecmp(c->argv[2]->ptr,"repl-backlog-ttl")) {
787         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
788         server.repl_backlog_time_limit = ll;
789     } else if (!strcasecmp(c->argv[2]->ptr,"watchdog-period")) {
790         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
791         if (ll)
792             enableWatchdog(ll);
793         else
794             disableWatchdog();
795     } else if (!strcasecmp(c->argv[2]->ptr,"rdbcompression")) {
796         int yn = yesnotoi(o->ptr);
797 
798         if (yn == -1) goto badfmt;
799         server.rdb_compression = yn;
800     } else if (!strcasecmp(c->argv[2]->ptr,"notify-keyspace-events")) {
801         int flags = keyspaceEventsStringToFlags(o->ptr);
802 
803         if (flags == -1) goto badfmt;
804         server.notify_keyspace_events = flags;
805     } else if (!strcasecmp(c->argv[2]->ptr,"repl-disable-tcp-nodelay")) {
806         int yn = yesnotoi(o->ptr);
807 
808         if (yn == -1) goto badfmt;
809         server.repl_disable_tcp_nodelay = yn;
810     } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) {
811         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
812             ll < 0) goto badfmt;
813         server.slave_priority = ll;
814     } else if (!strcasecmp(c->argv[2]->ptr,"min-slaves-to-write")) {
815         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
816             ll < 0) goto badfmt;
817         server.repl_min_slaves_to_write = ll;
818         refreshGoodSlavesCount();
819     } else if (!strcasecmp(c->argv[2]->ptr,"min-slaves-max-lag")) {
820         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
821             ll < 0) goto badfmt;
822         server.repl_min_slaves_max_lag = ll;
823         refreshGoodSlavesCount();
824     } else if (!strcasecmp(c->argv[2]->ptr,"cluster-node-timeout")) {
825         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
826             ll <= 0) goto badfmt;
827         server.cluster_node_timeout = ll;
828     } else {
829         addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
830             (char*)c->argv[2]->ptr);
831         return;
832     }
833     addReply(c,shared.ok);
834     return;
835 
836 badfmt: /* Bad format errors */
837     addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
838             (char*)o->ptr,
839             (char*)c->argv[2]->ptr);
840 }
841 
842 /*-----------------------------------------------------------------------------
843  * CONFIG GET implementation
844  *----------------------------------------------------------------------------*/
845 
846 #define config_get_string_field(_name,_var) do { \
847     if (stringmatch(pattern,_name,0)) { \
848         addReplyBulkCString(c,_name); \
849         addReplyBulkCString(c,_var ? _var : ""); \
850         matches++; \
851     } \
852 } while(0);
853 
854 #define config_get_bool_field(_name,_var) do { \
855     if (stringmatch(pattern,_name,0)) { \
856         addReplyBulkCString(c,_name); \
857         addReplyBulkCString(c,_var ? "yes" : "no"); \
858         matches++; \
859     } \
860 } while(0);
861 
862 #define config_get_numerical_field(_name,_var) do { \
863     if (stringmatch(pattern,_name,0)) { \
864         ll2string(buf,sizeof(buf),_var); \
865         addReplyBulkCString(c,_name); \
866         addReplyBulkCString(c,buf); \
867         matches++; \
868     } \
869 } while(0);
870 
871 void configGetCommand(redisClient *c) {
872     robj *o = c->argv[2];
873     void *replylen = addDeferredMultiBulkLength(c);
874     char *pattern = o->ptr;
875     char buf[128];
876     int matches = 0;
877     redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW);
878 
879     /* String values */
880     config_get_string_field("dbfilename",server.rdb_filename);
881     config_get_string_field("requirepass",server.requirepass);
882     config_get_string_field("masterauth",server.masterauth);
883     config_get_string_field("bind",server.bindaddr);
884     config_get_string_field("unixsocket",server.unixsocket);
885     config_get_string_field("logfile",server.logfile);
886     config_get_string_field("pidfile",server.pidfile);
887 
888     /* Numerical values */
889     config_get_numerical_field("maxmemory",server.maxmemory);
890     config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
891     config_get_numerical_field("timeout",server.maxidletime);
892     config_get_numerical_field("tcp-keepalive",server.tcpkeepalive);
893     config_get_numerical_field("auto-aof-rewrite-percentage",
894             server.aof_rewrite_perc);
895     config_get_numerical_field("auto-aof-rewrite-min-size",
896             server.aof_rewrite_min_size);
897     config_get_numerical_field("hash-max-ziplist-entries",
898             server.hash_max_ziplist_entries);
899     config_get_numerical_field("hash-max-ziplist-value",
900             server.hash_max_ziplist_value);
901     config_get_numerical_field("list-max-ziplist-entries",
902             server.list_max_ziplist_entries);
903     config_get_numerical_field("list-max-ziplist-value",
904             server.list_max_ziplist_value);
905     config_get_numerical_field("set-max-intset-entries",
906             server.set_max_intset_entries);
907     config_get_numerical_field("zset-max-ziplist-entries",
908             server.zset_max_ziplist_entries);
909     config_get_numerical_field("zset-max-ziplist-value",
910             server.zset_max_ziplist_value);
911     config_get_numerical_field("lua-time-limit",server.lua_time_limit);
912     config_get_numerical_field("slowlog-log-slower-than",
913             server.slowlog_log_slower_than);
914     config_get_numerical_field("slowlog-max-len",
915             server.slowlog_max_len);
916     config_get_numerical_field("port",server.port);
917     config_get_numerical_field("databases",server.dbnum);
918     config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
919     config_get_numerical_field("repl-timeout",server.repl_timeout);
920     config_get_numerical_field("repl-backlog-size",server.repl_backlog_size);
921     config_get_numerical_field("repl-backlog-ttl",server.repl_backlog_time_limit);
922     config_get_numerical_field("maxclients",server.maxclients);
923     config_get_numerical_field("watchdog-period",server.watchdog_period);
924     config_get_numerical_field("slave-priority",server.slave_priority);
925     config_get_numerical_field("min-slaves-to-write",server.repl_min_slaves_to_write);
926     config_get_numerical_field("min-slaves-max-lag",server.repl_min_slaves_max_lag);
927     config_get_numerical_field("hz",server.hz);
928     config_get_numerical_field("cluster-node-timeout",server.cluster_node_timeout);
929 
930     /* Bool (yes/no) values */
931     config_get_bool_field("no-appendfsync-on-rewrite",
932             server.aof_no_fsync_on_rewrite);
933     config_get_bool_field("slave-serve-stale-data",
934             server.repl_serve_stale_data);
935     config_get_bool_field("slave-read-only",
936             server.repl_slave_ro);
937     config_get_bool_field("stop-writes-on-bgsave-error",
938             server.stop_writes_on_bgsave_err);
939     config_get_bool_field("daemonize", server.daemonize);
940     config_get_bool_field("rdbcompression", server.rdb_compression);
941     config_get_bool_field("rdbchecksum", server.rdb_checksum);
942     config_get_bool_field("activerehashing", server.activerehashing);
943     config_get_bool_field("repl-disable-tcp-nodelay",
944             server.repl_disable_tcp_nodelay);
945     config_get_bool_field("aof-rewrite-incremental-fsync",
946             server.aof_rewrite_incremental_fsync);
947 
948     /* Everything we can't handle with macros follows. */
949 
950     if (stringmatch(pattern,"appendonly",0)) {
951         addReplyBulkCString(c,"appendonly");
952         addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes");
953         matches++;
954     }
955     if (stringmatch(pattern,"dir",0)) {
956         char buf[1024];
957 
958         if (getcwd(buf,sizeof(buf)) == NULL)
959             buf[0] = '\0';
960 
961         addReplyBulkCString(c,"dir");
962         addReplyBulkCString(c,buf);
963         matches++;
964     }
965     if (stringmatch(pattern,"maxmemory-policy",0)) {
966         char *s;
967 
968         switch(server.maxmemory_policy) {
969         case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break;
970         case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break;
971         case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break;
972         case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break;
973         case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break;
974         case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break;
975         default: s = "unknown"; break; /* too harmless to panic */
976         }
977         addReplyBulkCString(c,"maxmemory-policy");
978         addReplyBulkCString(c,s);
979         matches++;
980     }
981     if (stringmatch(pattern,"appendfsync",0)) {
982         char *policy;
983 
984         switch(server.aof_fsync) {
985         case AOF_FSYNC_NO: policy = "no"; break;
986         case AOF_FSYNC_EVERYSEC: policy = "everysec"; break;
987         case AOF_FSYNC_ALWAYS: policy = "always"; break;
988         default: policy = "unknown"; break; /* too harmless to panic */
989         }
990         addReplyBulkCString(c,"appendfsync");
991         addReplyBulkCString(c,policy);
992         matches++;
993     }
994     if (stringmatch(pattern,"save",0)) {
995         sds buf = sdsempty();
996         int j;
997 
998         for (j = 0; j < server.saveparamslen; j++) {
999             buf = sdscatprintf(buf,"%ld %d",
1000                     server.saveparams[j].seconds,
1001                     server.saveparams[j].changes);
1002             if (j != server.saveparamslen-1)
1003                 buf = sdscatlen(buf," ",1);
1004         }
1005         addReplyBulkCString(c,"save");
1006         addReplyBulkCString(c,buf);
1007         sdsfree(buf);
1008         matches++;
1009     }
1010     if (stringmatch(pattern,"loglevel",0)) {
1011         char *s;
1012 
1013         switch(server.verbosity) {
1014         case REDIS_WARNING: s = "warning"; break;
1015         case REDIS_VERBOSE: s = "verbose"; break;
1016         case REDIS_NOTICE: s = "notice"; break;
1017         case REDIS_DEBUG: s = "debug"; break;
1018         default: s = "unknown"; break; /* too harmless to panic */
1019         }
1020         addReplyBulkCString(c,"loglevel");
1021         addReplyBulkCString(c,s);
1022         matches++;
1023     }
1024     if (stringmatch(pattern,"client-output-buffer-limit",0)) {
1025         sds buf = sdsempty();
1026         int j;
1027 
1028         for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {
1029             buf = sdscatprintf(buf,"%s %llu %llu %ld",
1030                     getClientLimitClassName(j),
1031                     server.client_obuf_limits[j].hard_limit_bytes,
1032                     server.client_obuf_limits[j].soft_limit_bytes,
1033                     (long) server.client_obuf_limits[j].soft_limit_seconds);
1034             if (j != REDIS_CLIENT_LIMIT_NUM_CLASSES-1)
1035                 buf = sdscatlen(buf," ",1);
1036         }
1037         addReplyBulkCString(c,"client-output-buffer-limit");
1038         addReplyBulkCString(c,buf);
1039         sdsfree(buf);
1040         matches++;
1041     }
1042     if (stringmatch(pattern,"unixsocketperm",0)) {
1043         char buf[32];
1044         snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
1045         addReplyBulkCString(c,"unixsocketperm");
1046         addReplyBulkCString(c,buf);
1047         matches++;
1048     }
1049     if (stringmatch(pattern,"slaveof",0)) {
1050         char buf[256];
1051 
1052         addReplyBulkCString(c,"slaveof");
1053         if (server.masterhost)
1054             snprintf(buf,sizeof(buf),"%s %d",
1055                 server.masterhost, server.masterport);
1056         else
1057             buf[0] = '\0';
1058         addReplyBulkCString(c,buf);
1059         matches++;
1060     }
1061     if (stringmatch(pattern,"notify-keyspace-events",0)) {
1062         robj *flagsobj = createObject(REDIS_STRING,
1063             keyspaceEventsFlagsToString(server.notify_keyspace_events));
1064 
1065         addReplyBulkCString(c,"notify-keyspace-events");
1066         addReplyBulk(c,flagsobj);
1067         decrRefCount(flagsobj);
1068         matches++;
1069     }
1070     setDeferredMultiBulkLength(c,replylen,matches*2);
1071 }
1072 
1073 /*-----------------------------------------------------------------------------
1074  * CONFIG REWRITE implementation
1075  *----------------------------------------------------------------------------*/
1076 
1077 /* IGNORE:
1078  *
1079  * rename-command
1080  * include
1081  *
1082  * Special handling:
1083  *
1084  * notify-keyspace-events
1085  * client-output-buffer-limit
1086  * save
1087  * appendonly
1088  * appendfsync
1089  * dir
1090  * maxmemory-policy
1091  * loglevel
1092  * unixsocketperm
1093  * slaveof
1094  *
1095  * Type of config directives:
1096  *
1097  * CUSTOM
1098  * VERBATIM
1099  * YESNO
1100  * L
1101  * LL
1102  *
1103  */
1104 
1105 /* We use the following dictionary type to store where a configuration
1106  * option is mentioned in the old configuration file, so it's
1107  * like "maxmemory" -> list of line numbers (first line is zero). */
1108 unsigned int dictSdsHash(const void *key);
1109 int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);
1110 void dictSdsDestructor(void *privdata, void *val);
1111 void dictListDestructor(void *privdata, void *val);
1112 
1113 dictType optionToLineDictType = {
1114     dictSdsHash,                /* hash function */
1115     NULL,                       /* key dup */
1116     NULL,                       /* val dup */
1117     dictSdsKeyCompare,          /* key compare */
1118     dictSdsDestructor,          /* key destructor */
1119     dictListDestructor          /* val destructor */
1120 };
1121 
1122 /* The config rewrite state. */
1123 struct rewriteConfigState {
1124     dict *option_to_line; /* Option -> list of config file lines map */
1125     int numlines;         /* Number of lines in current config */
1126     sds *lines;           /* Current lines as an array of sds strings */
1127     int has_tail;         /* True if we already added directives that were
1128                              not present in the original config file. */
1129 };
1130 
1131 /* Append the new line to the current configuration state. */
1132 void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) {
1133     state->lines = zrealloc(state->lines, sizeof(char*) * (state->numlines+1));
1134     state->lines[state->numlines++] = line;
1135 }
1136 
1137 /* Populate the option -> list of line numbers map. */
1138 void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) {
1139     list *l = dictFetchValue(state->option_to_line,option);
1140 
1141     if (l == NULL) {
1142         l = listCreate();
1143         dictAdd(state->option_to_line,sdsdup(option),l);
1144     }
1145     listAddNodeTail(l,(void*)(long)linenum);
1146 }
1147 
1148 /* Read the old file, split it into lines to populate a newly created
1149  * config rewrite state, and return it to the caller.
1150  *
1151  * If it is impossible to read the old file, NULL is returned.
1152  * If the old file does not exist at all, an empty state is returned. */
1153 struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
1154     FILE *fp = fopen(path,"r");
1155     struct rewriteConfigState *state = zmalloc(sizeof(*state));
1156     char buf[REDIS_CONFIGLINE_MAX+1];
1157     int linenum = -1;
1158 
1159     if (fp == NULL && errno != ENOENT) return NULL;
1160 
1161     state->option_to_line = dictCreate(&optionToLineDictType,NULL);
1162     state->numlines = 0;
1163     state->lines = NULL;
1164     state->has_tail = 0;
1165     if (fp == NULL) return state;
1166 
1167     /* Read the old file line by line, populate the state. */
1168     while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) {
1169         int argc;
1170         sds *argv;
1171         sds line = sdstrim(sdsnew(buf),"\r\n\t ");
1172 
1173         linenum++; /* Zero based, so we init at -1 */
1174 
1175         /* Handle comments and empty lines. */
1176         if (line[0] == '#' || line[0] == '\0') {
1177             rewriteConfigAppendLine(state,line);
1178             continue;
1179         }
1180 
1181         /* Not a comment, split into arguments. */
1182         argv = sdssplitargs(line,&argc);
1183         if (argv == NULL) {
1184             /* Apparently the line is unparsable for some reason, for
1185              * instance it may have unbalanced quotes. Load it as a
1186              * comment. */
1187             sds aux = sdsnew("# ??? ");
1188             aux = sdscatsds(aux,line);
1189             sdsfree(line);
1190             rewriteConfigAppendLine(state,aux);
1191             continue;
1192         }
1193 
1194         sdstolower(argv[0]); /* We only want lowercase config directives. */
1195 
1196         /* Now we populate the state according to the content of this line.
1197          * Append the line and populate the option -> line numbers map. */
1198         rewriteConfigAppendLine(state,line);
1199         rewriteConfigAddLineNumberToOption(state,argv[0],linenum);
1200 
1201         sdsfreesplitres(argv,argc);
1202     }
1203     fclose(fp);
1204     return state;
1205 }
1206 
1207 /* Rewrite the specified configuration option with the new "line".
1208  * It progressively uses lines of the file that were already used for the same
1209  * configuraiton option in the old version of the file, removing that line from
1210  * the map of options -> line numbers.
1211  *
1212  * If there are lines associated with a given configuration option and
1213  * "force" is non-zero, the line is appended to the configuration file.
1214  * Usually "force" is true when an option has not its default value, so it
1215  * must be rewritten even if not present previously.
1216  *
1217  * The first time a line is appended into a configuration file, a comment
1218  * is added to show that starting from that point the config file was generated
1219  * by CONFIG REWRITE.
1220  *
1221  * "line" is either used, or freed, so the caller does not need to free it
1222  * in any way. */
1223 void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force) {
1224     sds o = sdsnew(option);
1225     list *l = dictFetchValue(state->option_to_line,o);
1226 
1227     if (!l && !force) {
1228         /* Option not used previously, and we are not forced to use it. */
1229         sdsfree(line);
1230         sdsfree(o);
1231         return;
1232     }
1233 
1234     if (l) {
1235         listNode *ln = listFirst(l);
1236         int linenum = (long) ln->value;
1237 
1238         /* There are still lines in the old configuration file we can reuse
1239          * for this option. Replace the line with the new one. */
1240         listDelNode(l,ln);
1241         if (listLength(l) == 0) dictDelete(state->option_to_line,o);
1242         sdsfree(state->lines[linenum]);
1243         state->lines[linenum] = line;
1244     } else {
1245         /* Append a new line. */
1246         if (!state->has_tail) {
1247             rewriteConfigAppendLine(state,
1248                 sdsnew("# Generated by CONFIG REWRITE"));
1249             state->has_tail = 1;
1250         }
1251         rewriteConfigAppendLine(state,line);
1252     }
1253     sdsfree(o);
1254 }
1255 
1256 /* Write the long long 'bytes' value as a string in a way that is parsable
1257  * inside redis.conf. If possible uses the GB, MB, KB notation. */
1258 int rewriteConfigFormatMemory(char *buf, size_t len, long long bytes) {
1259     int gb = 1024*1024*1024;
1260     int mb = 1024*1024;
1261     int kb = 1024;
1262 
1263     if (bytes && (bytes % gb) == 0) {
1264         return snprintf(buf,len,"%lldgb",bytes/gb);
1265     } else if (bytes && (bytes % mb) == 0) {
1266         return snprintf(buf,len,"%lldmb",bytes/mb);
1267     } else if (bytes && (bytes % kb) == 0) {
1268         return snprintf(buf,len,"%lldkb",bytes/kb);
1269     } else {
1270         return snprintf(buf,len,"%lld",bytes);
1271     }
1272 }
1273 
1274 /* Rewrite a simple "option-name <bytes>" configuration option. */
1275 void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
1276     char buf[64];
1277     int force = value != defvalue;
1278     sds line;
1279 
1280     rewriteConfigFormatMemory(buf,sizeof(buf),value);
1281     line = sdscatprintf(sdsempty(),"%s %s",option,buf);
1282     rewriteConfigRewriteLine(state,option,line,force);
1283 
1284 }
1285 
1286 /* Rewrite a yes/no option. */
1287 void rewriteConfigYesNoOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
1288     int force = value != defvalue;
1289     sds line = sdscatprintf(sdsempty(),"%s %s",option,
1290         value ? "yes" : "no");
1291 
1292     rewriteConfigRewriteLine(state,option,line,force);
1293 }
1294 
1295 /* Rewrite a string option. */
1296 void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, char *value, char *defvalue) {
1297     int force = 1;
1298     sds line;
1299 
1300     /* String options set to NULL need to be not present at all in the
1301      * configuration file to be set to NULL again at the next reboot. */
1302     if (value == NULL) return;
1303 
1304     /* Compare the strings as sds strings to have a binary safe comparison. */
1305     if (defvalue && strcmp(value,defvalue) == 0) force = 0;
1306 
1307     line = sdsnew(option);
1308     line = sdscatlen(line, " ", 1);
1309     line = sdscatrepr(line, value, strlen(value));
1310 
1311     rewriteConfigRewriteLine(state,option,line,force);
1312 }
1313 
1314 /* Rewrite a numerical (long long range) option. */
1315 void rewriteConfigNumericalOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
1316     int force = value != defvalue;
1317     sds line = sdscatprintf(sdsempty(),"%s %lld",option,value);
1318 
1319     rewriteConfigRewriteLine(state,option,line,force);
1320 }
1321 
1322 /* Rewrite a octal option. */
1323 void rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
1324     int force = value != defvalue;
1325     sds line = sdscatprintf(sdsempty(),"%s %o",option,value);
1326 
1327     rewriteConfigRewriteLine(state,option,line,force);
1328 }
1329 
1330 /* Rewrite an enumeration option, after the "value" every enum/value pair
1331  * is specified, terminated by NULL. After NULL the default value is
1332  * specified. See how the function is used for more information. */
1333 void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int value, ...) {
1334     va_list ap;
1335     char *enum_name, *matching_name;
1336     int enum_val, def_val, force;
1337     sds line;
1338 
1339     va_start(ap, value);
1340     while(1) {
1341         enum_name = va_arg(ap,char*);
1342         enum_val = va_arg(ap,int);
1343         if (enum_name == NULL) {
1344             def_val = enum_val;
1345             break;
1346         }
1347         if (value == enum_val) matching_name = enum_name;
1348     }
1349     va_end(ap);
1350 
1351     force = value != def_val;
1352     line = sdscatprintf(sdsempty(),"%s %s",option,matching_name);
1353     rewriteConfigRewriteLine(state,option,line,force);
1354 }
1355 
1356 /* Rewrite the syslog-fability option. */
1357 void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) {
1358     int value = server.syslog_facility, j;
1359     int force = value != LOG_LOCAL0;
1360     char *name, *option = "syslog-facility";
1361     sds line;
1362 
1363     for (j = 0; validSyslogFacilities[j].name; j++) {
1364         if (validSyslogFacilities[j].value == value) {
1365             name = (char*) validSyslogFacilities[j].name;
1366             break;
1367         }
1368     }
1369     line = sdscatprintf(sdsempty(),"%s %s",option,name);
1370     rewriteConfigRewriteLine(state,option,line,force);
1371 }
1372 
1373 /* Rewrite the save option. */
1374 void rewriteConfigSaveOption(struct rewriteConfigState *state) {
1375     int j;
1376     sds line;
1377 
1378     /* Note that if there are no save parameters at all, all the current
1379      * config line with "save" will be detected as orphaned and deleted,
1380      * resulting into no RDB persistence as expected. */
1381     for (j = 0; j < server.saveparamslen; j++) {
1382         line = sdscatprintf(sdsempty(),"save %ld %d",
1383             server.saveparams[j].seconds, server.saveparams[j].changes);
1384         rewriteConfigRewriteLine(state,"save",line,1);
1385     }
1386 }
1387 
1388 /* Rewrite the dir option, always using absolute paths.*/
1389 void rewriteConfigDirOption(struct rewriteConfigState *state) {
1390     char cwd[1024];
1391 
1392     if (getcwd(cwd,sizeof(cwd)) == NULL) return; /* no rewrite on error. */
1393     rewriteConfigStringOption(state,"dir",cwd,NULL);
1394 }
1395 
1396 /* Rewrite the slaveof option. */
1397 void rewriteConfigSlaveofOption(struct rewriteConfigState *state) {
1398     char *option = "slaveof";
1399     sds line;
1400 
1401     /* If this is a master, we want all the slaveof config options
1402      * in the file to be removed. */
1403     if (server.masterhost == NULL) return;
1404     line = sdscatprintf(sdsempty(),"%s %s %d", option,
1405         server.masterhost, server.masterport);
1406     rewriteConfigRewriteLine(state,option,line,1);
1407 }
1408 
1409 /* Rewrite the appendonly option. */
1410 void rewriteConfigAppendonlyOption(struct rewriteConfigState *state) {
1411     int force = server.aof_state != REDIS_AOF_OFF;
1412     char *option = "appendonly";
1413     sds line;
1414 
1415     line = sdscatprintf(sdsempty(),"%s %s", option,
1416         (server.aof_state == REDIS_AOF_OFF) ? "no" : "yes");
1417     rewriteConfigRewriteLine(state,option,line,force);
1418 }
1419 
1420 /* Rewrite the notify-keyspace-events option. */
1421 void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) {
1422     int force = server.notify_keyspace_events != 0;
1423     char *option = "notify-keyspace-events";
1424     sds line, flags;
1425 
1426     flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);
1427     line = sdsnew(option);
1428     line = sdscatlen(line, " ", 1);
1429     line = sdscatrepr(line, flags, sdslen(flags));
1430     sdsfree(flags);
1431     rewriteConfigRewriteLine(state,option,line,force);
1432 }
1433 
1434 /* Rewrite the client-output-buffer-limit option. */
1435 void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) {
1436     int j;
1437     char *option = "client-output-buffer-limit";
1438 
1439     for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {
1440         int force = (server.client_obuf_limits[j].hard_limit_bytes !=
1441                     clientBufferLimitsDefaults[j].hard_limit_bytes) ||
1442                     (server.client_obuf_limits[j].soft_limit_bytes !=
1443                     clientBufferLimitsDefaults[j].soft_limit_bytes) ||
1444                     (server.client_obuf_limits[j].soft_limit_seconds !=
1445                     clientBufferLimitsDefaults[j].soft_limit_seconds);
1446         sds line;
1447         char hard[64], soft[64];
1448 
1449         rewriteConfigFormatMemory(hard,sizeof(hard),
1450                 server.client_obuf_limits[j].hard_limit_bytes);
1451         rewriteConfigFormatMemory(soft,sizeof(soft),
1452                 server.client_obuf_limits[j].soft_limit_bytes);
1453 
1454         line = sdscatprintf(sdsempty(),"%s %s %s %s %ld",
1455                 option, getClientLimitClassName(j), hard, soft,
1456                 (long) server.client_obuf_limits[j].soft_limit_seconds);
1457         rewriteConfigRewriteLine(state,option,line,force);
1458     }
1459 }
1460 
1461 /* Glue together the configuration lines in the current configuration
1462  * rewrite state into a single string, stripping multiple empty lines. */
1463 sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {
1464     sds content = sdsempty();
1465     int j, was_empty = 0;
1466 
1467     for (j = 0; j < state->numlines; j++) {
1468         /* Every cluster of empty lines is turned into a single empty line. */
1469         if (sdslen(state->lines[j]) == 0) {
1470             if (was_empty) continue;
1471             was_empty = 1;
1472         } else {
1473             was_empty = 0;
1474         }
1475         content = sdscatsds(content,state->lines[j]);
1476         content = sdscatlen(content,"\n",1);
1477     }
1478     return content;
1479 }
1480 
1481 /* Free the configuration rewrite state. */
1482 void rewriteConfigReleaseState(struct rewriteConfigState *state) {
1483     sdsfreesplitres(state->lines,state->numlines);
1484     dictRelease(state->option_to_line);
1485     zfree(state);
1486 }
1487 
1488 /* At the end of the rewrite process the state contains the remaining
1489  * map between "option name" => "lines in the original config file".
1490  * Lines used by the rewrite process were removed by the function
1491  * rewriteConfigRewriteLine(), all the other lines are "orphaned" and
1492  * should be replaced by empty lines.
1493  *
1494  * This function does just this, iterating all the option names and
1495  * blanking all the lines still associated. */
1496 void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
1497     dictIterator *di = dictGetIterator(state->option_to_line);
1498     dictEntry *de;
1499 
1500     while((de = dictNext(di)) != NULL) {
1501         list *l = dictGetVal(de);
1502 
1503         while(listLength(l)) {
1504             listNode *ln = listFirst(l);
1505             int linenum = (long) ln->value;
1506 
1507             sdsfree(state->lines[linenum]);
1508             state->lines[linenum] = sdsempty();
1509             listDelNode(l,ln);
1510         }
1511     }
1512     dictReleaseIterator(di);
1513 }
1514 
1515 /* This function overwrites the old configuration file with the new content.
1516  *
1517  * 1) The old file length is obtained.
1518  * 2) If the new content is smaller, padding is added.
1519  * 3) A single write(2) call is used to replace the content of the file.
1520  * 4) Later the file is truncated to the length of the new content.
1521  *
1522  * This way we are sure the file is left in a consistent state even if the
1523  * process is stopped between any of the four operations.
1524  *
1525  * The function returns 0 on success, otherwise -1 is returned and errno
1526  * set accordingly. */
1527 int rewriteConfigOverwriteFile(char *configfile, sds content) {
1528     int retval = 0;
1529     int fd = open(configfile,O_RDWR|O_CREAT,0644);
1530     int content_size = sdslen(content), padding = 0;
1531     struct stat sb;
1532     sds content_padded;
1533 
1534     /* 1) Open the old file (or create a new one if it does not
1535      *    exist), get the size. */
1536     if (fd == -1) return -1; /* errno set by open(). */
1537     if (fstat(fd,&sb) == -1) {
1538         close(fd);
1539         return -1; /* errno set by fstat(). */
1540     }
1541 
1542     /* 2) Pad the content at least match the old file size. */
1543     content_padded = sdsdup(content);
1544     if (content_size < sb.st_size) {
1545         /* If the old file was bigger, pad the content with
1546          * a newline plus as many "#" chars as required. */
1547         padding = sb.st_size - content_size;
1548         content_padded = sdsgrowzero(content_padded,sb.st_size);
1549         content_padded[content_size] = '\n';
1550         memset(content_padded+content_size+1,'#',padding-1);
1551     }
1552 
1553     /* 3) Write the new content using a single write(2). */
1554     if (write(fd,content_padded,strlen(content_padded)) == -1) {
1555         retval = -1;
1556         goto cleanup;
1557     }
1558 
1559     /* 4) Truncate the file to the right length if we used padding. */
1560     if (padding) {
1561         if (ftruncate(fd,content_size) == -1) {
1562             /* Non critical error... */
1563         }
1564     }
1565 
1566 cleanup:
1567     sdsfree(content_padded);
1568     close(fd);
1569     return retval;
1570 }
1571 
1572 /* Rewrite the configuration file at "path".
1573  * If the configuration file already exists, we try at best to retain comments
1574  * and overall structure.
1575  *
1576  * Configuration parameters that are at their default value, unless already
1577  * explicitly included in the old configuration file, are not rewritten.
1578  *
1579  * On error -1 is returned and errno is set accordingly, otherwise 0. */
1580 int rewriteConfig(char *path) {
1581     struct rewriteConfigState *state;
1582     sds newcontent;
1583     int retval;
1584 
1585     /* Step 1: read the old config into our rewrite state. */
1586     if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;
1587 
1588     /* Step 2: rewrite every single option, replacing or appending it inside
1589      * the rewrite state. */
1590 
1591     /* TODO: Turn every default into a define, use it also in
1592      * initServerConfig(). */
1593     rewriteConfigYesNoOption(state,"daemonize",server.daemonize,0);
1594     rewriteConfigStringOption(state,"pidfile",server.pidfile,REDIS_DEFAULT_PID_FILE);
1595     rewriteConfigNumericalOption(state,"port",server.port,REDIS_SERVERPORT);
1596     rewriteConfigStringOption(state,"bind",server.bindaddr,NULL);
1597     rewriteConfigStringOption(state,"unixsocket",server.unixsocket,NULL);
1598     rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,REDIS_DEFAULT_UNIX_SOCKET_PERM);
1599     rewriteConfigNumericalOption(state,"timeout",server.maxidletime,REDIS_MAXIDLETIME);
1600     rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,REDIS_DEFAULT_TCP_KEEPALIVE);
1601     rewriteConfigEnumOption(state,"loglevel",server.verbosity,
1602         "debug", REDIS_DEBUG,
1603         "verbose", REDIS_VERBOSE,
1604         "notice", REDIS_NOTICE,
1605         "warning", REDIS_WARNING,
1606         NULL, REDIS_DEFAULT_VERBOSITY);
1607     rewriteConfigStringOption(state,"logfile",server.logfile,REDIS_DEFAULT_LOGFILE);
1608     rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,REDIS_DEFAULT_SYSLOG_ENABLED);
1609     rewriteConfigStringOption(state,"syslog-ident",server.syslog_ident,REDIS_DEFAULT_SYSLOG_IDENT);
1610     rewriteConfigSyslogfacilityOption(state);
1611     rewriteConfigSaveOption(state);
1612     rewriteConfigNumericalOption(state,"databases",server.dbnum,REDIS_DEFAULT_DBNUM);
1613     rewriteConfigYesNoOption(state,"stop-writes-on-bgsave-error",server.stop_writes_on_bgsave_err,REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR);
1614     rewriteConfigYesNoOption(state,"rdbcompression",server.rdb_compression,REDIS_DEFAULT_RDB_COMPRESSION);
1615     rewriteConfigYesNoOption(state,"rdbchecksum",server.rdb_checksum,REDIS_DEFAULT_RDB_CHECKSUM);
1616     rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,REDIS_DEFAULT_RDB_FILENAME);
1617     rewriteConfigDirOption(state);
1618     rewriteConfigSlaveofOption(state);
1619     rewriteConfigStringOption(state,"masterauth",server.masterauth,NULL);
1620     rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA);
1621     rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,REDIS_DEFAULT_SLAVE_READ_ONLY);
1622     rewriteConfigNumericalOption(state,"repl-ping-slave-period",server.repl_ping_slave_period,REDIS_REPL_PING_SLAVE_PERIOD);
1623     rewriteConfigNumericalOption(state,"repl-timeout",server.repl_timeout,REDIS_REPL_TIMEOUT);
1624     rewriteConfigBytesOption(state,"repl-backlog-size",server.repl_backlog_size,REDIS_DEFAULT_REPL_BACKLOG_SIZE);
1625     rewriteConfigBytesOption(state,"repl-backlog-ttl",server.repl_backlog_time_limit,REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT);
1626     rewriteConfigYesNoOption(state,"repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay,REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY);
1627     rewriteConfigNumericalOption(state,"slave-priority",server.slave_priority,REDIS_DEFAULT_SLAVE_PRIORITY);
1628     rewriteConfigStringOption(state,"requirepass",server.requirepass,NULL);
1629     rewriteConfigNumericalOption(state,"maxclients",server.maxclients,REDIS_MAX_CLIENTS);
1630     rewriteConfigBytesOption(state,"maxmemory",server.maxmemory,REDIS_DEFAULT_MAXMEMORY);
1631     rewriteConfigEnumOption(state,"maxmemory-policy",server.maxmemory_policy,
1632         "volatile-lru", REDIS_MAXMEMORY_VOLATILE_LRU,
1633         "allkeys-lru", REDIS_MAXMEMORY_ALLKEYS_LRU,
1634         "volatile-random", REDIS_MAXMEMORY_VOLATILE_RANDOM,
1635         "allkeys-random", REDIS_MAXMEMORY_ALLKEYS_RANDOM,
1636         "volatile-ttl", REDIS_MAXMEMORY_VOLATILE_TTL,
1637         "noeviction", REDIS_MAXMEMORY_NO_EVICTION,
1638         NULL, REDIS_DEFAULT_MAXMEMORY_POLICY);
1639     rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,REDIS_DEFAULT_MAXMEMORY_SAMPLES);
1640     rewriteConfigAppendonlyOption(state);
1641     rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync,
1642         "everysec", AOF_FSYNC_EVERYSEC,
1643         "always", AOF_FSYNC_ALWAYS,
1644         "no", AOF_FSYNC_NO,
1645         NULL, REDIS_DEFAULT_AOF_FSYNC);
1646     rewriteConfigYesNoOption(state,"no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite,REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE);
1647     rewriteConfigNumericalOption(state,"auto-aof-rewrite-percentage",server.aof_rewrite_perc,REDIS_AOF_REWRITE_PERC);
1648     rewriteConfigBytesOption(state,"auto-aof-rewrite-min-size",server.aof_rewrite_min_size,REDIS_AOF_REWRITE_MIN_SIZE);
1649     rewriteConfigNumericalOption(state,"lua-time-limit",server.lua_time_limit,REDIS_LUA_TIME_LIMIT);
1650     rewriteConfigYesNoOption(state,"cluster-enabled",server.cluster_enabled,0);
1651     rewriteConfigStringOption(state,"cluster-config-file",server.cluster_configfile,REDIS_DEFAULT_CLUSTER_CONFIG_FILE);
1652     rewriteConfigNumericalOption(state,"cluster-node-timeout",server.cluster_node_timeout,REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT);
1653     rewriteConfigNumericalOption(state,"slowlog-log-slower-than",server.slowlog_log_slower_than,REDIS_SLOWLOG_LOG_SLOWER_THAN);
1654     rewriteConfigNumericalOption(state,"slowlog-max-len",server.slowlog_max_len,REDIS_SLOWLOG_MAX_LEN);
1655     rewriteConfigNotifykeyspaceeventsOption(state);
1656     rewriteConfigNumericalOption(state,"hash-max-ziplist-entries",server.hash_max_ziplist_entries,REDIS_HASH_MAX_ZIPLIST_ENTRIES);
1657     rewriteConfigNumericalOption(state,"hash-max-ziplist-value",server.hash_max_ziplist_value,REDIS_HASH_MAX_ZIPLIST_VALUE);
1658     rewriteConfigNumericalOption(state,"list-max-ziplist-entries",server.list_max_ziplist_entries,REDIS_LIST_MAX_ZIPLIST_ENTRIES);
1659     rewriteConfigNumericalOption(state,"list-max-ziplist-value",server.list_max_ziplist_value,REDIS_LIST_MAX_ZIPLIST_VALUE);
1660     rewriteConfigNumericalOption(state,"set-max-intset-entries",server.set_max_intset_entries,REDIS_SET_MAX_INTSET_ENTRIES);
1661     rewriteConfigNumericalOption(state,"zset-max-ziplist-entries",server.zset_max_ziplist_entries,REDIS_ZSET_MAX_ZIPLIST_ENTRIES);
1662     rewriteConfigNumericalOption(state,"zset-max-ziplist-value",server.zset_max_ziplist_value,REDIS_ZSET_MAX_ZIPLIST_VALUE);
1663     rewriteConfigYesNoOption(state,"activerehashing",server.activerehashing,REDIS_DEFAULT_ACTIVE_REHASHING);
1664     rewriteConfigClientoutputbufferlimitOption(state);
1665     rewriteConfigNumericalOption(state,"hz",server.hz,REDIS_DEFAULT_HZ);
1666     rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC);
1667 
1668     /* Step 3: remove all the orphaned lines in the old file, that is, lines
1669      * that were used by a config option and are no longer used, like in case
1670      * of multiple "save" options or duplicated options. */
1671     rewriteConfigRemoveOrphaned(state);
1672 
1673     /* Step 4: generate a new configuration file from the modified state
1674      * and write it into the original file. */
1675     newcontent = rewriteConfigGetContentFromState(state);
1676     retval = rewriteConfigOverwriteFile(server.configfile,newcontent);
1677 
1678     sdsfree(newcontent);
1679     rewriteConfigReleaseState(state);
1680     return retval;
1681 }
1682 
1683 /*-----------------------------------------------------------------------------
1684  * CONFIG command entry point
1685  *----------------------------------------------------------------------------*/
1686 
1687 void configCommand(redisClient *c) {
1688     if (!strcasecmp(c->argv[1]->ptr,"set")) {
1689         if (c->argc != 4) goto badarity;
1690         configSetCommand(c);
1691     } else if (!strcasecmp(c->argv[1]->ptr,"get")) {
1692         if (c->argc != 3) goto badarity;
1693         configGetCommand(c);
1694     } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) {
1695         if (c->argc != 2) goto badarity;
1696         server.stat_keyspace_hits = 0;
1697         server.stat_keyspace_misses = 0;
1698         server.stat_numcommands = 0;
1699         server.stat_numconnections = 0;
1700         server.stat_expiredkeys = 0;
1701         server.stat_rejected_conn = 0;
1702         server.stat_fork_time = 0;
1703         server.aof_delayed_fsync = 0;
1704         resetCommandTableStats();
1705         addReply(c,shared.ok);
1706     } else if (!strcasecmp(c->argv[1]->ptr,"rewrite")) {
1707         if (c->argc != 2) goto badarity;
1708         if (server.configfile == NULL) {
1709             addReplyError(c,"The server is running without a config file");
1710             return;
1711         }
1712         if (rewriteConfig(server.configfile) == -1) {
1713             addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno));
1714         } else {
1715             addReply(c,shared.ok);
1716         }
1717     } else {
1718         addReplyError(c,
1719             "CONFIG subcommand must be one of GET, SET, RESETSTAT");
1720     }
1721     return;
1722 
1723 badarity:
1724     addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s",
1725         (char*) c->argv[1]->ptr);
1726 }
1727