xref: /redis-3.2.3/src/config.c (revision 40f822da)
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             if ((server.notify_keyspace_events = yesnotoi(argv[1])) == -1) {
396                 err = "argument must be 'yes' or 'no'"; goto loaderr;
397             }
398         } else if (!strcasecmp(argv[0],"sentinel")) {
399             /* argc == 1 is handled by main() as we need to enter the sentinel
400              * mode ASAP. */
401             if (argc != 1) {
402                 if (!server.sentinel_mode) {
403                     err = "sentinel directive while not in sentinel mode";
404                     goto loaderr;
405                 }
406                 err = sentinelHandleConfiguration(argv+1,argc-1);
407                 if (err) goto loaderr;
408             }
409         } else {
410             err = "Bad directive or wrong number of arguments"; goto loaderr;
411         }
412         sdsfreesplitres(argv,argc);
413     }
414     sdsfreesplitres(lines,totlines);
415     return;
416 
417 loaderr:
418     fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n");
419     fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
420     fprintf(stderr, ">>> '%s'\n", lines[i]);
421     fprintf(stderr, "%s\n", err);
422     exit(1);
423 }
424 
425 /* Load the server configuration from the specified filename.
426  * The function appends the additional configuration directives stored
427  * in the 'options' string to the config file before loading.
428  *
429  * Both filename and options can be NULL, in such a case are considered
430  * empty. This way loadServerConfig can be used to just load a file or
431  * just load a string. */
432 void loadServerConfig(char *filename, char *options) {
433     sds config = sdsempty();
434     char buf[REDIS_CONFIGLINE_MAX+1];
435 
436     /* Load the file content */
437     if (filename) {
438         FILE *fp;
439 
440         if (filename[0] == '-' && filename[1] == '\0') {
441             fp = stdin;
442         } else {
443             if ((fp = fopen(filename,"r")) == NULL) {
444                 redisLog(REDIS_WARNING,
445                     "Fatal error, can't open config file '%s'", filename);
446                 exit(1);
447             }
448         }
449         while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL)
450             config = sdscat(config,buf);
451         if (fp != stdin) fclose(fp);
452     }
453     /* Append the additional options */
454     if (options) {
455         config = sdscat(config,"\n");
456         config = sdscat(config,options);
457     }
458     loadServerConfigFromString(config);
459     sdsfree(config);
460 }
461 
462 /*-----------------------------------------------------------------------------
463  * CONFIG command for remote configuration
464  *----------------------------------------------------------------------------*/
465 
466 void configSetCommand(redisClient *c) {
467     robj *o;
468     long long ll;
469     redisAssertWithInfo(c,c->argv[2],c->argv[2]->encoding == REDIS_ENCODING_RAW);
470     redisAssertWithInfo(c,c->argv[2],c->argv[3]->encoding == REDIS_ENCODING_RAW);
471     o = c->argv[3];
472 
473     if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) {
474         zfree(server.rdb_filename);
475         server.rdb_filename = zstrdup(o->ptr);
476     } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) {
477         if (sdslen(o->ptr) > REDIS_AUTHPASS_MAX_LEN) goto badfmt;
478         zfree(server.requirepass);
479         server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
480     } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) {
481         zfree(server.masterauth);
482         server.masterauth = zstrdup(o->ptr);
483     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) {
484         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
485             ll < 0) goto badfmt;
486         server.maxmemory = ll;
487         if (server.maxmemory) {
488             if (server.maxmemory < zmalloc_used_memory()) {
489                 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.");
490             }
491             freeMemoryIfNeeded();
492         }
493     } else if (!strcasecmp(c->argv[2]->ptr,"hz")) {
494         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
495             ll < 0) goto badfmt;
496         server.hz = (int) ll;
497         if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
498         if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
499     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) {
500         if (!strcasecmp(o->ptr,"volatile-lru")) {
501             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
502         } else if (!strcasecmp(o->ptr,"volatile-random")) {
503             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
504         } else if (!strcasecmp(o->ptr,"volatile-ttl")) {
505             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
506         } else if (!strcasecmp(o->ptr,"allkeys-lru")) {
507             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
508         } else if (!strcasecmp(o->ptr,"allkeys-random")) {
509             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
510         } else if (!strcasecmp(o->ptr,"noeviction")) {
511             server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
512         } else {
513             goto badfmt;
514         }
515     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) {
516         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
517             ll <= 0) goto badfmt;
518         server.maxmemory_samples = ll;
519     } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) {
520         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
521             ll < 0 || ll > LONG_MAX) goto badfmt;
522         server.maxidletime = ll;
523     } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) {
524         if (!strcasecmp(o->ptr,"no")) {
525             server.aof_fsync = AOF_FSYNC_NO;
526         } else if (!strcasecmp(o->ptr,"everysec")) {
527             server.aof_fsync = AOF_FSYNC_EVERYSEC;
528         } else if (!strcasecmp(o->ptr,"always")) {
529             server.aof_fsync = AOF_FSYNC_ALWAYS;
530         } else {
531             goto badfmt;
532         }
533     } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) {
534         int yn = yesnotoi(o->ptr);
535 
536         if (yn == -1) goto badfmt;
537         server.aof_no_fsync_on_rewrite = yn;
538     } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) {
539         int enable = yesnotoi(o->ptr);
540 
541         if (enable == -1) goto badfmt;
542         if (enable == 0 && server.aof_state != REDIS_AOF_OFF) {
543             stopAppendOnly();
544         } else if (enable && server.aof_state == REDIS_AOF_OFF) {
545             if (startAppendOnly() == REDIS_ERR) {
546                 addReplyError(c,
547                     "Unable to turn on AOF. Check server logs.");
548                 return;
549             }
550         }
551     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) {
552         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
553         server.aof_rewrite_perc = ll;
554     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) {
555         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
556         server.aof_rewrite_min_size = ll;
557     } else if (!strcasecmp(c->argv[2]->ptr,"save")) {
558         int vlen, j;
559         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
560 
561         /* Perform sanity check before setting the new config:
562          * - Even number of args
563          * - Seconds >= 1, changes >= 0 */
564         if (vlen & 1) {
565             sdsfreesplitres(v,vlen);
566             goto badfmt;
567         }
568         for (j = 0; j < vlen; j++) {
569             char *eptr;
570             long val;
571 
572             val = strtoll(v[j], &eptr, 10);
573             if (eptr[0] != '\0' ||
574                 ((j & 1) == 0 && val < 1) ||
575                 ((j & 1) == 1 && val < 0)) {
576                 sdsfreesplitres(v,vlen);
577                 goto badfmt;
578             }
579         }
580         /* Finally set the new config */
581         resetServerSaveParams();
582         for (j = 0; j < vlen; j += 2) {
583             time_t seconds;
584             int changes;
585 
586             seconds = strtoll(v[j],NULL,10);
587             changes = strtoll(v[j+1],NULL,10);
588             appendServerSaveParams(seconds, changes);
589         }
590         sdsfreesplitres(v,vlen);
591     } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) {
592         int yn = yesnotoi(o->ptr);
593 
594         if (yn == -1) goto badfmt;
595         server.repl_serve_stale_data = yn;
596     } else if (!strcasecmp(c->argv[2]->ptr,"slave-read-only")) {
597         int yn = yesnotoi(o->ptr);
598 
599         if (yn == -1) goto badfmt;
600         server.repl_slave_ro = yn;
601     } else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
602         if (chdir((char*)o->ptr) == -1) {
603             addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
604             return;
605         }
606     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-entries")) {
607         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
608         server.hash_max_ziplist_entries = ll;
609     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) {
610         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
611         server.hash_max_ziplist_value = ll;
612     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) {
613         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
614         server.list_max_ziplist_entries = ll;
615     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) {
616         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
617         server.list_max_ziplist_value = ll;
618     } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
619         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
620         server.set_max_intset_entries = ll;
621     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) {
622         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
623         server.zset_max_ziplist_entries = ll;
624     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) {
625         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
626         server.zset_max_ziplist_value = ll;
627     } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) {
628         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
629         server.lua_time_limit = ll;
630     } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) {
631         if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt;
632         server.slowlog_log_slower_than = ll;
633     } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) {
634         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
635         server.slowlog_max_len = (unsigned)ll;
636     } else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) {
637         if (!strcasecmp(o->ptr,"warning")) {
638             server.verbosity = REDIS_WARNING;
639         } else if (!strcasecmp(o->ptr,"notice")) {
640             server.verbosity = REDIS_NOTICE;
641         } else if (!strcasecmp(o->ptr,"verbose")) {
642             server.verbosity = REDIS_VERBOSE;
643         } else if (!strcasecmp(o->ptr,"debug")) {
644             server.verbosity = REDIS_DEBUG;
645         } else {
646             goto badfmt;
647         }
648     } else if (!strcasecmp(c->argv[2]->ptr,"client-output-buffer-limit")) {
649         int vlen, j;
650         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
651 
652         /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */
653         if (vlen % 4) {
654             sdsfreesplitres(v,vlen);
655             goto badfmt;
656         }
657 
658         /* Sanity check of single arguments, so that we either refuse the
659          * whole configuration string or accept it all, even if a single
660          * error in a single client class is present. */
661         for (j = 0; j < vlen; j++) {
662             char *eptr;
663             long val;
664 
665             if ((j % 4) == 0) {
666                 if (getClientLimitClassByName(v[j]) == -1) {
667                     sdsfreesplitres(v,vlen);
668                     goto badfmt;
669                 }
670             } else {
671                 val = strtoll(v[j], &eptr, 10);
672                 if (eptr[0] != '\0' || val < 0) {
673                     sdsfreesplitres(v,vlen);
674                     goto badfmt;
675                 }
676             }
677         }
678         /* Finally set the new config */
679         for (j = 0; j < vlen; j += 4) {
680             int class;
681             unsigned long long hard, soft;
682             int soft_seconds;
683 
684             class = getClientLimitClassByName(v[j]);
685             hard = strtoll(v[j+1],NULL,10);
686             soft = strtoll(v[j+2],NULL,10);
687             soft_seconds = strtoll(v[j+3],NULL,10);
688 
689             server.client_obuf_limits[class].hard_limit_bytes = hard;
690             server.client_obuf_limits[class].soft_limit_bytes = soft;
691             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
692         }
693         sdsfreesplitres(v,vlen);
694     } else if (!strcasecmp(c->argv[2]->ptr,"stop-writes-on-bgsave-error")) {
695         int yn = yesnotoi(o->ptr);
696 
697         if (yn == -1) goto badfmt;
698         server.stop_writes_on_bgsave_err = yn;
699     } else if (!strcasecmp(c->argv[2]->ptr,"repl-ping-slave-period")) {
700         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
701         server.repl_ping_slave_period = ll;
702     } else if (!strcasecmp(c->argv[2]->ptr,"repl-timeout")) {
703         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
704         server.repl_timeout = ll;
705     } else if (!strcasecmp(c->argv[2]->ptr,"watchdog-period")) {
706         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
707         if (ll)
708             enableWatchdog(ll);
709         else
710             disableWatchdog();
711     } else if (!strcasecmp(c->argv[2]->ptr,"rdbcompression")) {
712         int yn = yesnotoi(o->ptr);
713 
714         if (yn == -1) goto badfmt;
715         server.rdb_compression = yn;
716     } else if (!strcasecmp(c->argv[2]->ptr,"notify-keyspace-events")) {
717         int yn = yesnotoi(o->ptr);
718 
719         if (yn == -1) goto badfmt;
720         server.notify_keyspace_events = yn;
721     } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) {
722         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
723             ll <= 0) goto badfmt;
724         server.slave_priority = ll;
725     } else {
726         addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
727             (char*)c->argv[2]->ptr);
728         return;
729     }
730     addReply(c,shared.ok);
731     return;
732 
733 badfmt: /* Bad format errors */
734     addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
735             (char*)o->ptr,
736             (char*)c->argv[2]->ptr);
737 }
738 
739 #define config_get_string_field(_name,_var) do { \
740     if (stringmatch(pattern,_name,0)) { \
741         addReplyBulkCString(c,_name); \
742         addReplyBulkCString(c,_var ? _var : ""); \
743         matches++; \
744     } \
745 } while(0);
746 
747 #define config_get_bool_field(_name,_var) do { \
748     if (stringmatch(pattern,_name,0)) { \
749         addReplyBulkCString(c,_name); \
750         addReplyBulkCString(c,_var ? "yes" : "no"); \
751         matches++; \
752     } \
753 } while(0);
754 
755 #define config_get_numerical_field(_name,_var) do { \
756     if (stringmatch(pattern,_name,0)) { \
757         ll2string(buf,sizeof(buf),_var); \
758         addReplyBulkCString(c,_name); \
759         addReplyBulkCString(c,buf); \
760         matches++; \
761     } \
762 } while(0);
763 
764 void configGetCommand(redisClient *c) {
765     robj *o = c->argv[2];
766     void *replylen = addDeferredMultiBulkLength(c);
767     char *pattern = o->ptr;
768     char buf[128];
769     int matches = 0;
770     redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW);
771 
772     /* String values */
773     config_get_string_field("dbfilename",server.rdb_filename);
774     config_get_string_field("requirepass",server.requirepass);
775     config_get_string_field("masterauth",server.requirepass);
776     config_get_string_field("bind",server.bindaddr);
777     config_get_string_field("unixsocket",server.unixsocket);
778     config_get_string_field("logfile",server.logfile);
779     config_get_string_field("pidfile",server.pidfile);
780 
781     /* Numerical values */
782     config_get_numerical_field("maxmemory",server.maxmemory);
783     config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
784     config_get_numerical_field("timeout",server.maxidletime);
785     config_get_numerical_field("auto-aof-rewrite-percentage",
786             server.aof_rewrite_perc);
787     config_get_numerical_field("auto-aof-rewrite-min-size",
788             server.aof_rewrite_min_size);
789     config_get_numerical_field("hash-max-ziplist-entries",
790             server.hash_max_ziplist_entries);
791     config_get_numerical_field("hash-max-ziplist-value",
792             server.hash_max_ziplist_value);
793     config_get_numerical_field("list-max-ziplist-entries",
794             server.list_max_ziplist_entries);
795     config_get_numerical_field("list-max-ziplist-value",
796             server.list_max_ziplist_value);
797     config_get_numerical_field("set-max-intset-entries",
798             server.set_max_intset_entries);
799     config_get_numerical_field("zset-max-ziplist-entries",
800             server.zset_max_ziplist_entries);
801     config_get_numerical_field("zset-max-ziplist-value",
802             server.zset_max_ziplist_value);
803     config_get_numerical_field("lua-time-limit",server.lua_time_limit);
804     config_get_numerical_field("slowlog-log-slower-than",
805             server.slowlog_log_slower_than);
806     config_get_numerical_field("slowlog-max-len",
807             server.slowlog_max_len);
808     config_get_numerical_field("port",server.port);
809     config_get_numerical_field("databases",server.dbnum);
810     config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
811     config_get_numerical_field("repl-timeout",server.repl_timeout);
812     config_get_numerical_field("maxclients",server.maxclients);
813     config_get_numerical_field("watchdog-period",server.watchdog_period);
814     config_get_numerical_field("slave-priority",server.slave_priority);
815     config_get_numerical_field("hz",server.hz);
816 
817     /* Bool (yes/no) values */
818     config_get_bool_field("no-appendfsync-on-rewrite",
819             server.aof_no_fsync_on_rewrite);
820     config_get_bool_field("slave-serve-stale-data",
821             server.repl_serve_stale_data);
822     config_get_bool_field("slave-read-only",
823             server.repl_slave_ro);
824     config_get_bool_field("stop-writes-on-bgsave-error",
825             server.stop_writes_on_bgsave_err);
826     config_get_bool_field("daemonize", server.daemonize);
827     config_get_bool_field("rdbcompression", server.rdb_compression);
828     config_get_bool_field("rdbchecksum", server.rdb_checksum);
829     config_get_bool_field("activerehashing", server.activerehashing);
830     config_get_bool_field("notify-keyspace-events", server.notify_keyspace_events);
831 
832     /* Everything we can't handle with macros follows. */
833 
834     if (stringmatch(pattern,"appendonly",0)) {
835         addReplyBulkCString(c,"appendonly");
836         addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes");
837         matches++;
838     }
839     if (stringmatch(pattern,"dir",0)) {
840         char buf[1024];
841 
842         if (getcwd(buf,sizeof(buf)) == NULL)
843             buf[0] = '\0';
844 
845         addReplyBulkCString(c,"dir");
846         addReplyBulkCString(c,buf);
847         matches++;
848     }
849     if (stringmatch(pattern,"maxmemory-policy",0)) {
850         char *s;
851 
852         switch(server.maxmemory_policy) {
853         case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break;
854         case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break;
855         case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break;
856         case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break;
857         case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break;
858         case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break;
859         default: s = "unknown"; break; /* too harmless to panic */
860         }
861         addReplyBulkCString(c,"maxmemory-policy");
862         addReplyBulkCString(c,s);
863         matches++;
864     }
865     if (stringmatch(pattern,"appendfsync",0)) {
866         char *policy;
867 
868         switch(server.aof_fsync) {
869         case AOF_FSYNC_NO: policy = "no"; break;
870         case AOF_FSYNC_EVERYSEC: policy = "everysec"; break;
871         case AOF_FSYNC_ALWAYS: policy = "always"; break;
872         default: policy = "unknown"; break; /* too harmless to panic */
873         }
874         addReplyBulkCString(c,"appendfsync");
875         addReplyBulkCString(c,policy);
876         matches++;
877     }
878     if (stringmatch(pattern,"save",0)) {
879         sds buf = sdsempty();
880         int j;
881 
882         for (j = 0; j < server.saveparamslen; j++) {
883             buf = sdscatprintf(buf,"%ld %d",
884                     server.saveparams[j].seconds,
885                     server.saveparams[j].changes);
886             if (j != server.saveparamslen-1)
887                 buf = sdscatlen(buf," ",1);
888         }
889         addReplyBulkCString(c,"save");
890         addReplyBulkCString(c,buf);
891         sdsfree(buf);
892         matches++;
893     }
894     if (stringmatch(pattern,"loglevel",0)) {
895         char *s;
896 
897         switch(server.verbosity) {
898         case REDIS_WARNING: s = "warning"; break;
899         case REDIS_VERBOSE: s = "verbose"; break;
900         case REDIS_NOTICE: s = "notice"; break;
901         case REDIS_DEBUG: s = "debug"; break;
902         default: s = "unknown"; break; /* too harmless to panic */
903         }
904         addReplyBulkCString(c,"loglevel");
905         addReplyBulkCString(c,s);
906         matches++;
907     }
908     if (stringmatch(pattern,"client-output-buffer-limit",0)) {
909         sds buf = sdsempty();
910         int j;
911 
912         for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {
913             buf = sdscatprintf(buf,"%s %llu %llu %ld",
914                     getClientLimitClassName(j),
915                     server.client_obuf_limits[j].hard_limit_bytes,
916                     server.client_obuf_limits[j].soft_limit_bytes,
917                     (long) server.client_obuf_limits[j].soft_limit_seconds);
918             if (j != REDIS_CLIENT_LIMIT_NUM_CLASSES-1)
919                 buf = sdscatlen(buf," ",1);
920         }
921         addReplyBulkCString(c,"client-output-buffer-limit");
922         addReplyBulkCString(c,buf);
923         sdsfree(buf);
924         matches++;
925     }
926     if (stringmatch(pattern,"unixsocketperm",0)) {
927         char buf[32];
928         snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
929         addReplyBulkCString(c,"unixsocketperm");
930         addReplyBulkCString(c,buf);
931         matches++;
932     }
933     if (stringmatch(pattern,"slaveof",0)) {
934         char buf[256];
935 
936         addReplyBulkCString(c,"slaveof");
937         if (server.masterhost)
938             snprintf(buf,sizeof(buf),"%s %d",
939                 server.masterhost, server.masterport);
940         else
941             buf[0] = '\0';
942         addReplyBulkCString(c,buf);
943         matches++;
944     }
945     setDeferredMultiBulkLength(c,replylen,matches*2);
946 }
947 
948 void configCommand(redisClient *c) {
949     if (!strcasecmp(c->argv[1]->ptr,"set")) {
950         if (c->argc != 4) goto badarity;
951         configSetCommand(c);
952     } else if (!strcasecmp(c->argv[1]->ptr,"get")) {
953         if (c->argc != 3) goto badarity;
954         configGetCommand(c);
955     } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) {
956         if (c->argc != 2) goto badarity;
957         server.stat_keyspace_hits = 0;
958         server.stat_keyspace_misses = 0;
959         server.stat_numcommands = 0;
960         server.stat_numconnections = 0;
961         server.stat_expiredkeys = 0;
962         server.stat_rejected_conn = 0;
963         server.stat_fork_time = 0;
964         server.aof_delayed_fsync = 0;
965         resetCommandTableStats();
966         addReply(c,shared.ok);
967     } else {
968         addReplyError(c,
969             "CONFIG subcommand must be one of GET, SET, RESETSTAT");
970     }
971     return;
972 
973 badarity:
974     addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s",
975         (char*) c->argv[1]->ptr);
976 }
977