xref: /redis-3.2.3/src/config.c (revision fce016d3)
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 
32 #include "redis.h"
33 
34 /*-----------------------------------------------------------------------------
35  * Config file parsing
36  *----------------------------------------------------------------------------*/
37 
38 int yesnotoi(char *s) {
39     if (!strcasecmp(s,"yes")) return 1;
40     else if (!strcasecmp(s,"no")) return 0;
41     else return -1;
42 }
43 
44 void appendServerSaveParams(time_t seconds, int changes) {
45     server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));
46     server.saveparams[server.saveparamslen].seconds = seconds;
47     server.saveparams[server.saveparamslen].changes = changes;
48     server.saveparamslen++;
49 }
50 
51 void resetServerSaveParams() {
52     zfree(server.saveparams);
53     server.saveparams = NULL;
54     server.saveparamslen = 0;
55 }
56 
57 void loadServerConfigFromString(char *config) {
58     char *err = NULL;
59     int linenum = 0, totlines, i;
60     sds *lines;
61 
62     lines = sdssplitlen(config,strlen(config),"\n",1,&totlines);
63 
64     for (i = 0; i < totlines; i++) {
65         sds *argv;
66         int argc;
67 
68         linenum = i+1;
69         lines[i] = sdstrim(lines[i]," \t\r\n");
70 
71         /* Skip comments and blank lines*/
72         if (lines[i][0] == '#' || lines[i][0] == '\0') continue;
73 
74         /* Split into arguments */
75         argv = sdssplitargs(lines[i],&argc);
76         sdstolower(argv[0]);
77 
78         /* Execute config directives */
79         if (!strcasecmp(argv[0],"timeout") && argc == 2) {
80             server.maxidletime = atoi(argv[1]);
81             if (server.maxidletime < 0) {
82                 err = "Invalid timeout value"; goto loaderr;
83             }
84         } else if (!strcasecmp(argv[0],"port") && argc == 2) {
85             server.port = atoi(argv[1]);
86             if (server.port < 0 || server.port > 65535) {
87                 err = "Invalid port"; goto loaderr;
88             }
89         } else if (!strcasecmp(argv[0],"bind") && argc == 2) {
90             server.bindaddr = zstrdup(argv[1]);
91         } else if (!strcasecmp(argv[0],"unixsocket") && argc == 2) {
92             server.unixsocket = zstrdup(argv[1]);
93         } else if (!strcasecmp(argv[0],"unixsocketperm") && argc == 2) {
94             errno = 0;
95             server.unixsocketperm = (mode_t)strtol(argv[1], NULL, 8);
96             if (errno || server.unixsocketperm > 0777) {
97                 err = "Invalid socket file permissions"; goto loaderr;
98             }
99         } else if (!strcasecmp(argv[0],"save")) {
100             if (argc == 3) {
101                 int seconds = atoi(argv[1]);
102                 int changes = atoi(argv[2]);
103                 if (seconds < 1 || changes < 0) {
104                     err = "Invalid save parameters"; goto loaderr;
105                 }
106                 appendServerSaveParams(seconds,changes);
107             } else if (argc == 2 && !strcasecmp(argv[1],"")) {
108                 resetServerSaveParams();
109             }
110         } else if (!strcasecmp(argv[0],"dir") && argc == 2) {
111             if (chdir(argv[1]) == -1) {
112                 redisLog(REDIS_WARNING,"Can't chdir to '%s': %s",
113                     argv[1], strerror(errno));
114                 exit(1);
115             }
116         } else if (!strcasecmp(argv[0],"loglevel") && argc == 2) {
117             if (!strcasecmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG;
118             else if (!strcasecmp(argv[1],"verbose")) server.verbosity = REDIS_VERBOSE;
119             else if (!strcasecmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE;
120             else if (!strcasecmp(argv[1],"warning")) server.verbosity = REDIS_WARNING;
121             else {
122                 err = "Invalid log level. Must be one of debug, notice, warning";
123                 goto loaderr;
124             }
125         } else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
126             FILE *logfp;
127 
128             server.logfile = zstrdup(argv[1]);
129             if (!strcasecmp(server.logfile,"stdout")) {
130                 zfree(server.logfile);
131                 server.logfile = NULL;
132             }
133             if (server.logfile) {
134                 /* Test if we are able to open the file. The server will not
135                  * be able to abort just for this problem later... */
136                 logfp = fopen(server.logfile,"a");
137                 if (logfp == NULL) {
138                     err = sdscatprintf(sdsempty(),
139                         "Can't open the log file: %s", strerror(errno));
140                     goto loaderr;
141                 }
142                 fclose(logfp);
143             }
144         } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) {
145             if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) {
146                 err = "argument must be 'yes' or 'no'"; goto loaderr;
147             }
148         } else if (!strcasecmp(argv[0],"syslog-ident") && argc == 2) {
149             if (server.syslog_ident) zfree(server.syslog_ident);
150             server.syslog_ident = zstrdup(argv[1]);
151         } else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) {
152             struct {
153                 const char     *name;
154                 const int       value;
155             } validSyslogFacilities[] = {
156                 {"user",    LOG_USER},
157                 {"local0",  LOG_LOCAL0},
158                 {"local1",  LOG_LOCAL1},
159                 {"local2",  LOG_LOCAL2},
160                 {"local3",  LOG_LOCAL3},
161                 {"local4",  LOG_LOCAL4},
162                 {"local5",  LOG_LOCAL5},
163                 {"local6",  LOG_LOCAL6},
164                 {"local7",  LOG_LOCAL7},
165                 {NULL, 0}
166             };
167             int i;
168 
169             for (i = 0; validSyslogFacilities[i].name; i++) {
170                 if (!strcasecmp(validSyslogFacilities[i].name, argv[1])) {
171                     server.syslog_facility = validSyslogFacilities[i].value;
172                     break;
173                 }
174             }
175 
176             if (!validSyslogFacilities[i].name) {
177                 err = "Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7";
178                 goto loaderr;
179             }
180         } else if (!strcasecmp(argv[0],"databases") && argc == 2) {
181             server.dbnum = atoi(argv[1]);
182             if (server.dbnum < 1) {
183                 err = "Invalid number of databases"; goto loaderr;
184             }
185         } else if (!strcasecmp(argv[0],"include") && argc == 2) {
186             loadServerConfig(argv[1],NULL);
187         } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
188             server.maxclients = atoi(argv[1]);
189             if (server.maxclients < 1) {
190                 err = "Invalid max clients limit"; goto loaderr;
191             }
192         } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
193             server.maxmemory = memtoll(argv[1],NULL);
194         } else if (!strcasecmp(argv[0],"maxmemory-policy") && argc == 2) {
195             if (!strcasecmp(argv[1],"volatile-lru")) {
196                 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
197             } else if (!strcasecmp(argv[1],"volatile-random")) {
198                 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
199             } else if (!strcasecmp(argv[1],"volatile-ttl")) {
200                 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
201             } else if (!strcasecmp(argv[1],"allkeys-lru")) {
202                 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
203             } else if (!strcasecmp(argv[1],"allkeys-random")) {
204                 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
205             } else if (!strcasecmp(argv[1],"noeviction")) {
206                 server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
207             } else {
208                 err = "Invalid maxmemory policy";
209                 goto loaderr;
210             }
211         } else if (!strcasecmp(argv[0],"maxmemory-samples") && argc == 2) {
212             server.maxmemory_samples = atoi(argv[1]);
213             if (server.maxmemory_samples <= 0) {
214                 err = "maxmemory-samples must be 1 or greater";
215                 goto loaderr;
216             }
217         } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
218             server.masterhost = sdsnew(argv[1]);
219             server.masterport = atoi(argv[2]);
220             server.repl_state = REDIS_REPL_CONNECT;
221         } else if (!strcasecmp(argv[0],"repl-ping-slave-period") && argc == 2) {
222             server.repl_ping_slave_period = atoi(argv[1]);
223             if (server.repl_ping_slave_period <= 0) {
224                 err = "repl-ping-slave-period must be 1 or greater";
225                 goto loaderr;
226             }
227         } else if (!strcasecmp(argv[0],"repl-timeout") && argc == 2) {
228             server.repl_timeout = atoi(argv[1]);
229             if (server.repl_timeout <= 0) {
230                 err = "repl-timeout must be 1 or greater";
231                 goto loaderr;
232             }
233         } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) {
234         	server.masterauth = zstrdup(argv[1]);
235         } else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) {
236             if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) {
237                 err = "argument must be 'yes' or 'no'"; goto loaderr;
238             }
239         } else if (!strcasecmp(argv[0],"slave-read-only") && argc == 2) {
240             if ((server.repl_slave_ro = yesnotoi(argv[1])) == -1) {
241                 err = "argument must be 'yes' or 'no'"; goto loaderr;
242             }
243         } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
244             if ((server.rdb_compression = yesnotoi(argv[1])) == -1) {
245                 err = "argument must be 'yes' or 'no'"; goto loaderr;
246             }
247         } else if (!strcasecmp(argv[0],"rdbchecksum") && argc == 2) {
248             if ((server.rdb_checksum = yesnotoi(argv[1])) == -1) {
249                 err = "argument must be 'yes' or 'no'"; goto loaderr;
250             }
251         } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) {
252             if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
253                 err = "argument must be 'yes' or 'no'"; goto loaderr;
254             }
255         } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
256             if ((server.daemonize = yesnotoi(argv[1])) == -1) {
257                 err = "argument must be 'yes' or 'no'"; goto loaderr;
258             }
259         } else if (!strcasecmp(argv[0],"hz") && argc == 2) {
260             server.hz = atoi(argv[1]);
261             if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
262             if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
263         } else if (!strcasecmp(argv[0],"appendonly") && argc == 2) {
264             int yes;
265 
266             if ((yes = yesnotoi(argv[1])) == -1) {
267                 err = "argument must be 'yes' or 'no'"; goto loaderr;
268             }
269             server.aof_state = yes ? REDIS_AOF_ON : REDIS_AOF_OFF;
270         } else if (!strcasecmp(argv[0],"appendfilename") && argc == 2) {
271             zfree(server.aof_filename);
272             server.aof_filename = zstrdup(argv[1]);
273         } else if (!strcasecmp(argv[0],"no-appendfsync-on-rewrite")
274                    && argc == 2) {
275             if ((server.aof_no_fsync_on_rewrite= yesnotoi(argv[1])) == -1) {
276                 err = "argument must be 'yes' or 'no'"; goto loaderr;
277             }
278         } else if (!strcasecmp(argv[0],"appendfsync") && argc == 2) {
279             if (!strcasecmp(argv[1],"no")) {
280                 server.aof_fsync = AOF_FSYNC_NO;
281             } else if (!strcasecmp(argv[1],"always")) {
282                 server.aof_fsync = AOF_FSYNC_ALWAYS;
283             } else if (!strcasecmp(argv[1],"everysec")) {
284                 server.aof_fsync = AOF_FSYNC_EVERYSEC;
285             } else {
286                 err = "argument must be 'no', 'always' or 'everysec'";
287                 goto loaderr;
288             }
289         } else if (!strcasecmp(argv[0],"auto-aof-rewrite-percentage") &&
290                    argc == 2)
291         {
292             server.aof_rewrite_perc = atoi(argv[1]);
293             if (server.aof_rewrite_perc < 0) {
294                 err = "Invalid negative percentage for AOF auto rewrite";
295                 goto loaderr;
296             }
297         } else if (!strcasecmp(argv[0],"auto-aof-rewrite-min-size") &&
298                    argc == 2)
299         {
300             server.aof_rewrite_min_size = memtoll(argv[1],NULL);
301         } else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
302             if (strlen(argv[1]) > REDIS_AUTHPASS_MAX_LEN) {
303                 err = "Password is longer than REDIS_AUTHPASS_MAX_LEN";
304                 goto loaderr;
305             }
306             server.requirepass = zstrdup(argv[1]);
307         } else if (!strcasecmp(argv[0],"pidfile") && argc == 2) {
308             zfree(server.pidfile);
309             server.pidfile = zstrdup(argv[1]);
310         } else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) {
311             zfree(server.rdb_filename);
312             server.rdb_filename = zstrdup(argv[1]);
313         } else if (!strcasecmp(argv[0],"hash-max-ziplist-entries") && argc == 2) {
314             server.hash_max_ziplist_entries = memtoll(argv[1], NULL);
315         } else if (!strcasecmp(argv[0],"hash-max-ziplist-value") && argc == 2) {
316             server.hash_max_ziplist_value = memtoll(argv[1], NULL);
317         } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
318             server.list_max_ziplist_entries = memtoll(argv[1], NULL);
319         } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
320             server.list_max_ziplist_value = memtoll(argv[1], NULL);
321         } else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) {
322             server.set_max_intset_entries = memtoll(argv[1], NULL);
323         } else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) {
324             server.zset_max_ziplist_entries = memtoll(argv[1], NULL);
325         } else if (!strcasecmp(argv[0],"zset-max-ziplist-value") && argc == 2) {
326             server.zset_max_ziplist_value = memtoll(argv[1], NULL);
327         } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) {
328             struct redisCommand *cmd = lookupCommand(argv[1]);
329             int retval;
330 
331             if (!cmd) {
332                 err = "No such command in rename-command";
333                 goto loaderr;
334             }
335 
336             /* If the target command name is the empty string we just
337              * remove it from the command table. */
338             retval = dictDelete(server.commands, argv[1]);
339             redisAssert(retval == DICT_OK);
340 
341             /* Otherwise we re-add the command under a different name. */
342             if (sdslen(argv[2]) != 0) {
343                 sds copy = sdsdup(argv[2]);
344 
345                 retval = dictAdd(server.commands, copy, cmd);
346                 if (retval != DICT_OK) {
347                     sdsfree(copy);
348                     err = "Target command name already exists"; goto loaderr;
349                 }
350             }
351         } else if (!strcasecmp(argv[0],"cluster-enabled") && argc == 2) {
352             if ((server.cluster_enabled = yesnotoi(argv[1])) == -1) {
353                 err = "argument must be 'yes' or 'no'"; goto loaderr;
354             }
355         } else if (!strcasecmp(argv[0],"cluster-config-file") && argc == 2) {
356             zfree(server.cluster.configfile);
357             server.cluster.configfile = zstrdup(argv[1]);
358         } else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) {
359             server.lua_time_limit = strtoll(argv[1],NULL,10);
360         } else if (!strcasecmp(argv[0],"slowlog-log-slower-than") &&
361                    argc == 2)
362         {
363             server.slowlog_log_slower_than = strtoll(argv[1],NULL,10);
364         } else if (!strcasecmp(argv[0],"slowlog-max-len") && argc == 2) {
365             server.slowlog_max_len = strtoll(argv[1],NULL,10);
366         } else if (!strcasecmp(argv[0],"client-output-buffer-limit") &&
367                    argc == 5)
368         {
369             int class = getClientLimitClassByName(argv[1]);
370             unsigned long long hard, soft;
371             int soft_seconds;
372 
373             if (class == -1) {
374                 err = "Unrecognized client limit class";
375                 goto loaderr;
376             }
377             hard = memtoll(argv[2],NULL);
378             soft = memtoll(argv[3],NULL);
379             soft_seconds = atoi(argv[4]);
380             if (soft_seconds < 0) {
381                 err = "Negative number of seconds in soft limit is invalid";
382                 goto loaderr;
383             }
384             server.client_obuf_limits[class].hard_limit_bytes = hard;
385             server.client_obuf_limits[class].soft_limit_bytes = soft;
386             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
387         } else if (!strcasecmp(argv[0],"stop-writes-on-bgsave-error") &&
388                    argc == 2) {
389             if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) {
390                 err = "argument must be 'yes' or 'no'"; goto loaderr;
391             }
392         } else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) {
393             server.slave_priority = atoi(argv[1]);
394         } else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) {
395             int flags = keyspaceEventsStringToFlags(argv[1]);
396 
397             if (flags == -1) {
398                 err = "Invalid event class character. Use 'g$lshzxeA'.";
399                 goto loaderr;
400             }
401             server.notify_keyspace_events = flags;
402         } else if (!strcasecmp(argv[0],"sentinel")) {
403             /* argc == 1 is handled by main() as we need to enter the sentinel
404              * mode ASAP. */
405             if (argc != 1) {
406                 if (!server.sentinel_mode) {
407                     err = "sentinel directive while not in sentinel mode";
408                     goto loaderr;
409                 }
410                 err = sentinelHandleConfiguration(argv+1,argc-1);
411                 if (err) goto loaderr;
412             }
413         } else {
414             err = "Bad directive or wrong number of arguments"; goto loaderr;
415         }
416         sdsfreesplitres(argv,argc);
417     }
418     sdsfreesplitres(lines,totlines);
419     return;
420 
421 loaderr:
422     fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n");
423     fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
424     fprintf(stderr, ">>> '%s'\n", lines[i]);
425     fprintf(stderr, "%s\n", err);
426     exit(1);
427 }
428 
429 /* Load the server configuration from the specified filename.
430  * The function appends the additional configuration directives stored
431  * in the 'options' string to the config file before loading.
432  *
433  * Both filename and options can be NULL, in such a case are considered
434  * empty. This way loadServerConfig can be used to just load a file or
435  * just load a string. */
436 void loadServerConfig(char *filename, char *options) {
437     sds config = sdsempty();
438     char buf[REDIS_CONFIGLINE_MAX+1];
439 
440     /* Load the file content */
441     if (filename) {
442         FILE *fp;
443 
444         if (filename[0] == '-' && filename[1] == '\0') {
445             fp = stdin;
446         } else {
447             if ((fp = fopen(filename,"r")) == NULL) {
448                 redisLog(REDIS_WARNING,
449                     "Fatal error, can't open config file '%s'", filename);
450                 exit(1);
451             }
452         }
453         while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL)
454             config = sdscat(config,buf);
455         if (fp != stdin) fclose(fp);
456     }
457     /* Append the additional options */
458     if (options) {
459         config = sdscat(config,"\n");
460         config = sdscat(config,options);
461     }
462     loadServerConfigFromString(config);
463     sdsfree(config);
464 }
465 
466 /*-----------------------------------------------------------------------------
467  * CONFIG command for remote configuration
468  *----------------------------------------------------------------------------*/
469 
470 void configSetCommand(redisClient *c) {
471     robj *o;
472     long long ll;
473     redisAssertWithInfo(c,c->argv[2],c->argv[2]->encoding == REDIS_ENCODING_RAW);
474     redisAssertWithInfo(c,c->argv[2],c->argv[3]->encoding == REDIS_ENCODING_RAW);
475     o = c->argv[3];
476 
477     if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) {
478         zfree(server.rdb_filename);
479         server.rdb_filename = zstrdup(o->ptr);
480     } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) {
481         if (sdslen(o->ptr) > REDIS_AUTHPASS_MAX_LEN) goto badfmt;
482         zfree(server.requirepass);
483         server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
484     } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) {
485         zfree(server.masterauth);
486         server.masterauth = zstrdup(o->ptr);
487     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) {
488         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
489             ll < 0) goto badfmt;
490         server.maxmemory = ll;
491         if (server.maxmemory) {
492             if (server.maxmemory < zmalloc_used_memory()) {
493                 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.");
494             }
495             freeMemoryIfNeeded();
496         }
497     } else if (!strcasecmp(c->argv[2]->ptr,"hz")) {
498         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
499             ll < 0) goto badfmt;
500         server.hz = (int) ll;
501         if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
502         if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
503     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) {
504         if (!strcasecmp(o->ptr,"volatile-lru")) {
505             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
506         } else if (!strcasecmp(o->ptr,"volatile-random")) {
507             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
508         } else if (!strcasecmp(o->ptr,"volatile-ttl")) {
509             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
510         } else if (!strcasecmp(o->ptr,"allkeys-lru")) {
511             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
512         } else if (!strcasecmp(o->ptr,"allkeys-random")) {
513             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
514         } else if (!strcasecmp(o->ptr,"noeviction")) {
515             server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
516         } else {
517             goto badfmt;
518         }
519     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) {
520         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
521             ll <= 0) goto badfmt;
522         server.maxmemory_samples = ll;
523     } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) {
524         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
525             ll < 0 || ll > LONG_MAX) goto badfmt;
526         server.maxidletime = ll;
527     } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) {
528         if (!strcasecmp(o->ptr,"no")) {
529             server.aof_fsync = AOF_FSYNC_NO;
530         } else if (!strcasecmp(o->ptr,"everysec")) {
531             server.aof_fsync = AOF_FSYNC_EVERYSEC;
532         } else if (!strcasecmp(o->ptr,"always")) {
533             server.aof_fsync = AOF_FSYNC_ALWAYS;
534         } else {
535             goto badfmt;
536         }
537     } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) {
538         int yn = yesnotoi(o->ptr);
539 
540         if (yn == -1) goto badfmt;
541         server.aof_no_fsync_on_rewrite = yn;
542     } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) {
543         int enable = yesnotoi(o->ptr);
544 
545         if (enable == -1) goto badfmt;
546         if (enable == 0 && server.aof_state != REDIS_AOF_OFF) {
547             stopAppendOnly();
548         } else if (enable && server.aof_state == REDIS_AOF_OFF) {
549             if (startAppendOnly() == REDIS_ERR) {
550                 addReplyError(c,
551                     "Unable to turn on AOF. Check server logs.");
552                 return;
553             }
554         }
555     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) {
556         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
557         server.aof_rewrite_perc = ll;
558     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) {
559         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
560         server.aof_rewrite_min_size = ll;
561     } else if (!strcasecmp(c->argv[2]->ptr,"save")) {
562         int vlen, j;
563         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
564 
565         /* Perform sanity check before setting the new config:
566          * - Even number of args
567          * - Seconds >= 1, changes >= 0 */
568         if (vlen & 1) {
569             sdsfreesplitres(v,vlen);
570             goto badfmt;
571         }
572         for (j = 0; j < vlen; j++) {
573             char *eptr;
574             long val;
575 
576             val = strtoll(v[j], &eptr, 10);
577             if (eptr[0] != '\0' ||
578                 ((j & 1) == 0 && val < 1) ||
579                 ((j & 1) == 1 && val < 0)) {
580                 sdsfreesplitres(v,vlen);
581                 goto badfmt;
582             }
583         }
584         /* Finally set the new config */
585         resetServerSaveParams();
586         for (j = 0; j < vlen; j += 2) {
587             time_t seconds;
588             int changes;
589 
590             seconds = strtoll(v[j],NULL,10);
591             changes = strtoll(v[j+1],NULL,10);
592             appendServerSaveParams(seconds, changes);
593         }
594         sdsfreesplitres(v,vlen);
595     } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) {
596         int yn = yesnotoi(o->ptr);
597 
598         if (yn == -1) goto badfmt;
599         server.repl_serve_stale_data = yn;
600     } else if (!strcasecmp(c->argv[2]->ptr,"slave-read-only")) {
601         int yn = yesnotoi(o->ptr);
602 
603         if (yn == -1) goto badfmt;
604         server.repl_slave_ro = yn;
605     } else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
606         if (chdir((char*)o->ptr) == -1) {
607             addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
608             return;
609         }
610     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-entries")) {
611         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
612         server.hash_max_ziplist_entries = ll;
613     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) {
614         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
615         server.hash_max_ziplist_value = ll;
616     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) {
617         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
618         server.list_max_ziplist_entries = ll;
619     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) {
620         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
621         server.list_max_ziplist_value = ll;
622     } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
623         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
624         server.set_max_intset_entries = ll;
625     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) {
626         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
627         server.zset_max_ziplist_entries = ll;
628     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) {
629         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
630         server.zset_max_ziplist_value = ll;
631     } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) {
632         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
633         server.lua_time_limit = ll;
634     } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) {
635         if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt;
636         server.slowlog_log_slower_than = ll;
637     } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) {
638         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
639         server.slowlog_max_len = (unsigned)ll;
640     } else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) {
641         if (!strcasecmp(o->ptr,"warning")) {
642             server.verbosity = REDIS_WARNING;
643         } else if (!strcasecmp(o->ptr,"notice")) {
644             server.verbosity = REDIS_NOTICE;
645         } else if (!strcasecmp(o->ptr,"verbose")) {
646             server.verbosity = REDIS_VERBOSE;
647         } else if (!strcasecmp(o->ptr,"debug")) {
648             server.verbosity = REDIS_DEBUG;
649         } else {
650             goto badfmt;
651         }
652     } else if (!strcasecmp(c->argv[2]->ptr,"client-output-buffer-limit")) {
653         int vlen, j;
654         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
655 
656         /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */
657         if (vlen % 4) {
658             sdsfreesplitres(v,vlen);
659             goto badfmt;
660         }
661 
662         /* Sanity check of single arguments, so that we either refuse the
663          * whole configuration string or accept it all, even if a single
664          * error in a single client class is present. */
665         for (j = 0; j < vlen; j++) {
666             char *eptr;
667             long val;
668 
669             if ((j % 4) == 0) {
670                 if (getClientLimitClassByName(v[j]) == -1) {
671                     sdsfreesplitres(v,vlen);
672                     goto badfmt;
673                 }
674             } else {
675                 val = strtoll(v[j], &eptr, 10);
676                 if (eptr[0] != '\0' || val < 0) {
677                     sdsfreesplitres(v,vlen);
678                     goto badfmt;
679                 }
680             }
681         }
682         /* Finally set the new config */
683         for (j = 0; j < vlen; j += 4) {
684             int class;
685             unsigned long long hard, soft;
686             int soft_seconds;
687 
688             class = getClientLimitClassByName(v[j]);
689             hard = strtoll(v[j+1],NULL,10);
690             soft = strtoll(v[j+2],NULL,10);
691             soft_seconds = strtoll(v[j+3],NULL,10);
692 
693             server.client_obuf_limits[class].hard_limit_bytes = hard;
694             server.client_obuf_limits[class].soft_limit_bytes = soft;
695             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
696         }
697         sdsfreesplitres(v,vlen);
698     } else if (!strcasecmp(c->argv[2]->ptr,"stop-writes-on-bgsave-error")) {
699         int yn = yesnotoi(o->ptr);
700 
701         if (yn == -1) goto badfmt;
702         server.stop_writes_on_bgsave_err = yn;
703     } else if (!strcasecmp(c->argv[2]->ptr,"repl-ping-slave-period")) {
704         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
705         server.repl_ping_slave_period = ll;
706     } else if (!strcasecmp(c->argv[2]->ptr,"repl-timeout")) {
707         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
708         server.repl_timeout = ll;
709     } else if (!strcasecmp(c->argv[2]->ptr,"watchdog-period")) {
710         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
711         if (ll)
712             enableWatchdog(ll);
713         else
714             disableWatchdog();
715     } else if (!strcasecmp(c->argv[2]->ptr,"rdbcompression")) {
716         int yn = yesnotoi(o->ptr);
717 
718         if (yn == -1) goto badfmt;
719         server.rdb_compression = yn;
720     } else if (!strcasecmp(c->argv[2]->ptr,"notify-keyspace-events")) {
721         int flags = keyspaceEventsStringToFlags(o->ptr);
722 
723         if (flags == -1) goto badfmt;
724         server.notify_keyspace_events = flags;
725     } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) {
726         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
727             ll <= 0) goto badfmt;
728         server.slave_priority = ll;
729     } else {
730         addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
731             (char*)c->argv[2]->ptr);
732         return;
733     }
734     addReply(c,shared.ok);
735     return;
736 
737 badfmt: /* Bad format errors */
738     addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
739             (char*)o->ptr,
740             (char*)c->argv[2]->ptr);
741 }
742 
743 #define config_get_string_field(_name,_var) do { \
744     if (stringmatch(pattern,_name,0)) { \
745         addReplyBulkCString(c,_name); \
746         addReplyBulkCString(c,_var ? _var : ""); \
747         matches++; \
748     } \
749 } while(0);
750 
751 #define config_get_bool_field(_name,_var) do { \
752     if (stringmatch(pattern,_name,0)) { \
753         addReplyBulkCString(c,_name); \
754         addReplyBulkCString(c,_var ? "yes" : "no"); \
755         matches++; \
756     } \
757 } while(0);
758 
759 #define config_get_numerical_field(_name,_var) do { \
760     if (stringmatch(pattern,_name,0)) { \
761         ll2string(buf,sizeof(buf),_var); \
762         addReplyBulkCString(c,_name); \
763         addReplyBulkCString(c,buf); \
764         matches++; \
765     } \
766 } while(0);
767 
768 void configGetCommand(redisClient *c) {
769     robj *o = c->argv[2];
770     void *replylen = addDeferredMultiBulkLength(c);
771     char *pattern = o->ptr;
772     char buf[128];
773     int matches = 0;
774     redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW);
775 
776     /* String values */
777     config_get_string_field("dbfilename",server.rdb_filename);
778     config_get_string_field("requirepass",server.requirepass);
779     config_get_string_field("masterauth",server.requirepass);
780     config_get_string_field("bind",server.bindaddr);
781     config_get_string_field("unixsocket",server.unixsocket);
782     config_get_string_field("logfile",server.logfile);
783     config_get_string_field("pidfile",server.pidfile);
784 
785     /* Numerical values */
786     config_get_numerical_field("maxmemory",server.maxmemory);
787     config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
788     config_get_numerical_field("timeout",server.maxidletime);
789     config_get_numerical_field("auto-aof-rewrite-percentage",
790             server.aof_rewrite_perc);
791     config_get_numerical_field("auto-aof-rewrite-min-size",
792             server.aof_rewrite_min_size);
793     config_get_numerical_field("hash-max-ziplist-entries",
794             server.hash_max_ziplist_entries);
795     config_get_numerical_field("hash-max-ziplist-value",
796             server.hash_max_ziplist_value);
797     config_get_numerical_field("list-max-ziplist-entries",
798             server.list_max_ziplist_entries);
799     config_get_numerical_field("list-max-ziplist-value",
800             server.list_max_ziplist_value);
801     config_get_numerical_field("set-max-intset-entries",
802             server.set_max_intset_entries);
803     config_get_numerical_field("zset-max-ziplist-entries",
804             server.zset_max_ziplist_entries);
805     config_get_numerical_field("zset-max-ziplist-value",
806             server.zset_max_ziplist_value);
807     config_get_numerical_field("lua-time-limit",server.lua_time_limit);
808     config_get_numerical_field("slowlog-log-slower-than",
809             server.slowlog_log_slower_than);
810     config_get_numerical_field("slowlog-max-len",
811             server.slowlog_max_len);
812     config_get_numerical_field("port",server.port);
813     config_get_numerical_field("databases",server.dbnum);
814     config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
815     config_get_numerical_field("repl-timeout",server.repl_timeout);
816     config_get_numerical_field("maxclients",server.maxclients);
817     config_get_numerical_field("watchdog-period",server.watchdog_period);
818     config_get_numerical_field("slave-priority",server.slave_priority);
819     config_get_numerical_field("hz",server.hz);
820 
821     /* Bool (yes/no) values */
822     config_get_bool_field("no-appendfsync-on-rewrite",
823             server.aof_no_fsync_on_rewrite);
824     config_get_bool_field("slave-serve-stale-data",
825             server.repl_serve_stale_data);
826     config_get_bool_field("slave-read-only",
827             server.repl_slave_ro);
828     config_get_bool_field("stop-writes-on-bgsave-error",
829             server.stop_writes_on_bgsave_err);
830     config_get_bool_field("daemonize", server.daemonize);
831     config_get_bool_field("rdbcompression", server.rdb_compression);
832     config_get_bool_field("rdbchecksum", server.rdb_checksum);
833     config_get_bool_field("activerehashing", server.activerehashing);
834 
835     /* Everything we can't handle with macros follows. */
836 
837     if (stringmatch(pattern,"appendonly",0)) {
838         addReplyBulkCString(c,"appendonly");
839         addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes");
840         matches++;
841     }
842     if (stringmatch(pattern,"dir",0)) {
843         char buf[1024];
844 
845         if (getcwd(buf,sizeof(buf)) == NULL)
846             buf[0] = '\0';
847 
848         addReplyBulkCString(c,"dir");
849         addReplyBulkCString(c,buf);
850         matches++;
851     }
852     if (stringmatch(pattern,"maxmemory-policy",0)) {
853         char *s;
854 
855         switch(server.maxmemory_policy) {
856         case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break;
857         case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break;
858         case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break;
859         case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break;
860         case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break;
861         case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break;
862         default: s = "unknown"; break; /* too harmless to panic */
863         }
864         addReplyBulkCString(c,"maxmemory-policy");
865         addReplyBulkCString(c,s);
866         matches++;
867     }
868     if (stringmatch(pattern,"appendfsync",0)) {
869         char *policy;
870 
871         switch(server.aof_fsync) {
872         case AOF_FSYNC_NO: policy = "no"; break;
873         case AOF_FSYNC_EVERYSEC: policy = "everysec"; break;
874         case AOF_FSYNC_ALWAYS: policy = "always"; break;
875         default: policy = "unknown"; break; /* too harmless to panic */
876         }
877         addReplyBulkCString(c,"appendfsync");
878         addReplyBulkCString(c,policy);
879         matches++;
880     }
881     if (stringmatch(pattern,"save",0)) {
882         sds buf = sdsempty();
883         int j;
884 
885         for (j = 0; j < server.saveparamslen; j++) {
886             buf = sdscatprintf(buf,"%ld %d",
887                     server.saveparams[j].seconds,
888                     server.saveparams[j].changes);
889             if (j != server.saveparamslen-1)
890                 buf = sdscatlen(buf," ",1);
891         }
892         addReplyBulkCString(c,"save");
893         addReplyBulkCString(c,buf);
894         sdsfree(buf);
895         matches++;
896     }
897     if (stringmatch(pattern,"loglevel",0)) {
898         char *s;
899 
900         switch(server.verbosity) {
901         case REDIS_WARNING: s = "warning"; break;
902         case REDIS_VERBOSE: s = "verbose"; break;
903         case REDIS_NOTICE: s = "notice"; break;
904         case REDIS_DEBUG: s = "debug"; break;
905         default: s = "unknown"; break; /* too harmless to panic */
906         }
907         addReplyBulkCString(c,"loglevel");
908         addReplyBulkCString(c,s);
909         matches++;
910     }
911     if (stringmatch(pattern,"client-output-buffer-limit",0)) {
912         sds buf = sdsempty();
913         int j;
914 
915         for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {
916             buf = sdscatprintf(buf,"%s %llu %llu %ld",
917                     getClientLimitClassName(j),
918                     server.client_obuf_limits[j].hard_limit_bytes,
919                     server.client_obuf_limits[j].soft_limit_bytes,
920                     (long) server.client_obuf_limits[j].soft_limit_seconds);
921             if (j != REDIS_CLIENT_LIMIT_NUM_CLASSES-1)
922                 buf = sdscatlen(buf," ",1);
923         }
924         addReplyBulkCString(c,"client-output-buffer-limit");
925         addReplyBulkCString(c,buf);
926         sdsfree(buf);
927         matches++;
928     }
929     if (stringmatch(pattern,"unixsocketperm",0)) {
930         char buf[32];
931         snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
932         addReplyBulkCString(c,"unixsocketperm");
933         addReplyBulkCString(c,buf);
934         matches++;
935     }
936     if (stringmatch(pattern,"slaveof",0)) {
937         char buf[256];
938 
939         addReplyBulkCString(c,"slaveof");
940         if (server.masterhost)
941             snprintf(buf,sizeof(buf),"%s %d",
942                 server.masterhost, server.masterport);
943         else
944             buf[0] = '\0';
945         addReplyBulkCString(c,buf);
946         matches++;
947     }
948     if (stringmatch(pattern,"notify-keyspace-events",0)) {
949         robj *flagsobj = createObject(REDIS_STRING,
950             keyspaceEventsFlagsToString(server.notify_keyspace_events));
951 
952         addReplyBulkCString(c,"notify-keyspace-events");
953         addReplyBulk(c,flagsobj);
954         decrRefCount(flagsobj);
955         matches++;
956     }
957     setDeferredMultiBulkLength(c,replylen,matches*2);
958 }
959 
960 void configCommand(redisClient *c) {
961     if (!strcasecmp(c->argv[1]->ptr,"set")) {
962         if (c->argc != 4) goto badarity;
963         configSetCommand(c);
964     } else if (!strcasecmp(c->argv[1]->ptr,"get")) {
965         if (c->argc != 3) goto badarity;
966         configGetCommand(c);
967     } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) {
968         if (c->argc != 2) goto badarity;
969         server.stat_keyspace_hits = 0;
970         server.stat_keyspace_misses = 0;
971         server.stat_numcommands = 0;
972         server.stat_numconnections = 0;
973         server.stat_expiredkeys = 0;
974         server.stat_rejected_conn = 0;
975         server.stat_fork_time = 0;
976         server.aof_delayed_fsync = 0;
977         resetCommandTableStats();
978         addReply(c,shared.ok);
979     } else {
980         addReplyError(c,
981             "CONFIG subcommand must be one of GET, SET, RESETSTAT");
982     }
983     return;
984 
985 badarity:
986     addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s",
987         (char*) c->argv[1]->ptr);
988 }
989