xref: /redis-3.2.3/src/config.c (revision c9d0c362)
1 #include "redis.h"
2 
3 /*-----------------------------------------------------------------------------
4  * Config file parsing
5  *----------------------------------------------------------------------------*/
6 
7 int yesnotoi(char *s) {
8     if (!strcasecmp(s,"yes")) return 1;
9     else if (!strcasecmp(s,"no")) return 0;
10     else return -1;
11 }
12 
13 void appendServerSaveParams(time_t seconds, int changes) {
14     server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));
15     server.saveparams[server.saveparamslen].seconds = seconds;
16     server.saveparams[server.saveparamslen].changes = changes;
17     server.saveparamslen++;
18 }
19 
20 void resetServerSaveParams() {
21     zfree(server.saveparams);
22     server.saveparams = NULL;
23     server.saveparamslen = 0;
24 }
25 
26 /* I agree, this is a very rudimental way to load a configuration...
27    will improve later if the config gets more complex */
28 void loadServerConfig(char *filename) {
29     FILE *fp;
30     char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL;
31     int linenum = 0;
32     sds line = NULL;
33 
34     if (filename[0] == '-' && filename[1] == '\0')
35         fp = stdin;
36     else {
37         if ((fp = fopen(filename,"r")) == NULL) {
38             redisLog(REDIS_WARNING, "Fatal error, can't open config file '%s'", filename);
39             exit(1);
40         }
41     }
42 
43     while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) {
44         sds *argv;
45         int argc, j;
46 
47         linenum++;
48         line = sdsnew(buf);
49         line = sdstrim(line," \t\r\n");
50 
51         /* Skip comments and blank lines*/
52         if (line[0] == '#' || line[0] == '\0') {
53             sdsfree(line);
54             continue;
55         }
56 
57         /* Split into arguments */
58         argv = sdssplitargs(line,&argc);
59         sdstolower(argv[0]);
60 
61         /* Execute config directives */
62         if (!strcasecmp(argv[0],"timeout") && argc == 2) {
63             server.maxidletime = atoi(argv[1]);
64             if (server.maxidletime < 0) {
65                 err = "Invalid timeout value"; goto loaderr;
66             }
67         } else if (!strcasecmp(argv[0],"port") && argc == 2) {
68             server.port = atoi(argv[1]);
69             if (server.port < 0 || server.port > 65535) {
70                 err = "Invalid port"; goto loaderr;
71             }
72         } else if (!strcasecmp(argv[0],"bind") && argc == 2) {
73             server.bindaddr = zstrdup(argv[1]);
74         } else if (!strcasecmp(argv[0],"unixsocket") && argc == 2) {
75             server.unixsocket = zstrdup(argv[1]);
76         } else if (!strcasecmp(argv[0],"save") && argc == 3) {
77             int seconds = atoi(argv[1]);
78             int changes = atoi(argv[2]);
79             if (seconds < 1 || changes < 0) {
80                 err = "Invalid save parameters"; goto loaderr;
81             }
82             appendServerSaveParams(seconds,changes);
83         } else if (!strcasecmp(argv[0],"dir") && argc == 2) {
84             if (chdir(argv[1]) == -1) {
85                 redisLog(REDIS_WARNING,"Can't chdir to '%s': %s",
86                     argv[1], strerror(errno));
87                 exit(1);
88             }
89         } else if (!strcasecmp(argv[0],"loglevel") && argc == 2) {
90             if (!strcasecmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG;
91             else if (!strcasecmp(argv[1],"verbose")) server.verbosity = REDIS_VERBOSE;
92             else if (!strcasecmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE;
93             else if (!strcasecmp(argv[1],"warning")) server.verbosity = REDIS_WARNING;
94             else {
95                 err = "Invalid log level. Must be one of debug, notice, warning";
96                 goto loaderr;
97             }
98         } else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
99             FILE *logfp;
100 
101             server.logfile = zstrdup(argv[1]);
102             if (!strcasecmp(server.logfile,"stdout")) {
103                 zfree(server.logfile);
104                 server.logfile = NULL;
105             }
106             if (server.logfile) {
107                 /* Test if we are able to open the file. The server will not
108                  * be able to abort just for this problem later... */
109                 logfp = fopen(server.logfile,"a");
110                 if (logfp == NULL) {
111                     err = sdscatprintf(sdsempty(),
112                         "Can't open the log file: %s", strerror(errno));
113                     goto loaderr;
114                 }
115                 fclose(logfp);
116             }
117         } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) {
118             if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) {
119                 err = "argument must be 'yes' or 'no'"; goto loaderr;
120             }
121         } else if (!strcasecmp(argv[0],"syslog-ident") && argc == 2) {
122             if (server.syslog_ident) zfree(server.syslog_ident);
123             server.syslog_ident = zstrdup(argv[1]);
124         } else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) {
125             struct {
126                 const char     *name;
127                 const int       value;
128             } validSyslogFacilities[] = {
129                 {"user",    LOG_USER},
130                 {"local0",  LOG_LOCAL0},
131                 {"local1",  LOG_LOCAL1},
132                 {"local2",  LOG_LOCAL2},
133                 {"local3",  LOG_LOCAL3},
134                 {"local4",  LOG_LOCAL4},
135                 {"local5",  LOG_LOCAL5},
136                 {"local6",  LOG_LOCAL6},
137                 {"local7",  LOG_LOCAL7},
138                 {NULL, 0}
139             };
140             int i;
141 
142             for (i = 0; validSyslogFacilities[i].name; i++) {
143                 if (!strcasecmp(validSyslogFacilities[i].name, argv[1])) {
144                     server.syslog_facility = validSyslogFacilities[i].value;
145                     break;
146                 }
147             }
148 
149             if (!validSyslogFacilities[i].name) {
150                 err = "Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7";
151                 goto loaderr;
152             }
153         } else if (!strcasecmp(argv[0],"databases") && argc == 2) {
154             server.dbnum = atoi(argv[1]);
155             if (server.dbnum < 1) {
156                 err = "Invalid number of databases"; goto loaderr;
157             }
158         } else if (!strcasecmp(argv[0],"include") && argc == 2) {
159             loadServerConfig(argv[1]);
160         } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
161             server.maxclients = atoi(argv[1]);
162         } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
163             server.maxmemory = memtoll(argv[1],NULL);
164         } else if (!strcasecmp(argv[0],"maxmemory-policy") && argc == 2) {
165             if (!strcasecmp(argv[1],"volatile-lru")) {
166                 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
167             } else if (!strcasecmp(argv[1],"volatile-random")) {
168                 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
169             } else if (!strcasecmp(argv[1],"volatile-ttl")) {
170                 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
171             } else if (!strcasecmp(argv[1],"allkeys-lru")) {
172                 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
173             } else if (!strcasecmp(argv[1],"allkeys-random")) {
174                 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
175             } else if (!strcasecmp(argv[1],"noeviction")) {
176                 server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
177             } else {
178                 err = "Invalid maxmemory policy";
179                 goto loaderr;
180             }
181         } else if (!strcasecmp(argv[0],"maxmemory-samples") && argc == 2) {
182             server.maxmemory_samples = atoi(argv[1]);
183             if (server.maxmemory_samples <= 0) {
184                 err = "maxmemory-samples must be 1 or greater";
185                 goto loaderr;
186             }
187         } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
188             server.masterhost = sdsnew(argv[1]);
189             server.masterport = atoi(argv[2]);
190             server.replstate = REDIS_REPL_CONNECT;
191         } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) {
192         	server.masterauth = zstrdup(argv[1]);
193         } else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) {
194             if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) {
195                 err = "argument must be 'yes' or 'no'"; goto loaderr;
196             }
197         } else if (!strcasecmp(argv[0],"glueoutputbuf")) {
198             redisLog(REDIS_WARNING, "Deprecated configuration directive: \"%s\"", argv[0]);
199         } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
200             if ((server.rdbcompression = yesnotoi(argv[1])) == -1) {
201                 err = "argument must be 'yes' or 'no'"; goto loaderr;
202             }
203         } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) {
204             if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
205                 err = "argument must be 'yes' or 'no'"; goto loaderr;
206             }
207         } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
208             if ((server.daemonize = yesnotoi(argv[1])) == -1) {
209                 err = "argument must be 'yes' or 'no'"; goto loaderr;
210             }
211         } else if (!strcasecmp(argv[0],"appendonly") && argc == 2) {
212             if ((server.appendonly = yesnotoi(argv[1])) == -1) {
213                 err = "argument must be 'yes' or 'no'"; goto loaderr;
214             }
215         } else if (!strcasecmp(argv[0],"appendfilename") && argc == 2) {
216             zfree(server.appendfilename);
217             server.appendfilename = zstrdup(argv[1]);
218         } else if (!strcasecmp(argv[0],"no-appendfsync-on-rewrite")
219                    && argc == 2) {
220             if ((server.no_appendfsync_on_rewrite= yesnotoi(argv[1])) == -1) {
221                 err = "argument must be 'yes' or 'no'"; goto loaderr;
222             }
223         } else if (!strcasecmp(argv[0],"appendfsync") && argc == 2) {
224             if (!strcasecmp(argv[1],"no")) {
225                 server.appendfsync = APPENDFSYNC_NO;
226             } else if (!strcasecmp(argv[1],"always")) {
227                 server.appendfsync = APPENDFSYNC_ALWAYS;
228             } else if (!strcasecmp(argv[1],"everysec")) {
229                 server.appendfsync = APPENDFSYNC_EVERYSEC;
230             } else {
231                 err = "argument must be 'no', 'always' or 'everysec'";
232                 goto loaderr;
233             }
234         } else if (!strcasecmp(argv[0],"auto-aof-rewrite-percentage") &&
235                    argc == 2)
236         {
237             server.auto_aofrewrite_perc = atoi(argv[1]);
238             if (server.auto_aofrewrite_perc < 0) {
239                 err = "Invalid negative percentage for AOF auto rewrite";
240                 goto loaderr;
241             }
242         } else if (!strcasecmp(argv[0],"auto-aof-rewrite-min-size") &&
243                    argc == 2)
244         {
245             server.auto_aofrewrite_min_size = memtoll(argv[1],NULL);
246         } else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
247             server.requirepass = zstrdup(argv[1]);
248         } else if (!strcasecmp(argv[0],"pidfile") && argc == 2) {
249             zfree(server.pidfile);
250             server.pidfile = zstrdup(argv[1]);
251         } else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) {
252             zfree(server.dbfilename);
253             server.dbfilename = zstrdup(argv[1]);
254         } else if (!strcasecmp(argv[0],"hash-max-zipmap-entries") && argc == 2) {
255             server.hash_max_zipmap_entries = memtoll(argv[1], NULL);
256         } else if (!strcasecmp(argv[0],"hash-max-zipmap-value") && argc == 2) {
257             server.hash_max_zipmap_value = memtoll(argv[1], NULL);
258         } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
259             server.list_max_ziplist_entries = memtoll(argv[1], NULL);
260         } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
261             server.list_max_ziplist_value = memtoll(argv[1], NULL);
262         } else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) {
263             server.set_max_intset_entries = memtoll(argv[1], NULL);
264         } else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) {
265             server.zset_max_ziplist_entries = memtoll(argv[1], NULL);
266         } else if (!strcasecmp(argv[0],"zset-max-ziplist-value") && argc == 2) {
267             server.zset_max_ziplist_value = memtoll(argv[1], NULL);
268         } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) {
269             struct redisCommand *cmd = lookupCommand(argv[1]);
270             int retval;
271 
272             if (!cmd) {
273                 err = "No such command in rename-command";
274                 goto loaderr;
275             }
276 
277             /* If the target command name is the emtpy string we just
278              * remove it from the command table. */
279             retval = dictDelete(server.commands, argv[1]);
280             redisAssert(retval == DICT_OK);
281 
282             /* Otherwise we re-add the command under a different name. */
283             if (sdslen(argv[2]) != 0) {
284                 sds copy = sdsdup(argv[2]);
285 
286                 retval = dictAdd(server.commands, copy, cmd);
287                 if (retval != DICT_OK) {
288                     sdsfree(copy);
289                     err = "Target command name already exists"; goto loaderr;
290                 }
291             }
292         } else if (!strcasecmp(argv[0],"cluster-enabled") && argc == 2) {
293             if ((server.cluster_enabled = yesnotoi(argv[1])) == -1) {
294                 err = "argument must be 'yes' or 'no'"; goto loaderr;
295             }
296         } else if (!strcasecmp(argv[0],"cluster-config-file") && argc == 2) {
297             zfree(server.cluster.configfile);
298             server.cluster.configfile = zstrdup(argv[1]);
299         } else {
300             err = "Bad directive or wrong number of arguments"; goto loaderr;
301         }
302         for (j = 0; j < argc; j++)
303             sdsfree(argv[j]);
304         zfree(argv);
305         sdsfree(line);
306     }
307     if (fp != stdin) fclose(fp);
308     return;
309 
310 loaderr:
311     fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n");
312     fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
313     fprintf(stderr, ">>> '%s'\n", line);
314     fprintf(stderr, "%s\n", err);
315     exit(1);
316 }
317 
318 /*-----------------------------------------------------------------------------
319  * CONFIG command for remote configuration
320  *----------------------------------------------------------------------------*/
321 
322 void configSetCommand(redisClient *c) {
323     robj *o;
324     long long ll;
325     redisAssert(c->argv[2]->encoding == REDIS_ENCODING_RAW);
326     redisAssert(c->argv[3]->encoding == REDIS_ENCODING_RAW);
327     o = c->argv[3];
328 
329     if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) {
330         zfree(server.dbfilename);
331         server.dbfilename = zstrdup(o->ptr);
332     } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) {
333         zfree(server.requirepass);
334         server.requirepass = zstrdup(o->ptr);
335     } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) {
336         zfree(server.masterauth);
337         server.masterauth = zstrdup(o->ptr);
338     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) {
339         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
340             ll < 0) goto badfmt;
341         server.maxmemory = ll;
342         if (server.maxmemory) freeMemoryIfNeeded();
343     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) {
344         if (!strcasecmp(o->ptr,"volatile-lru")) {
345             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
346         } else if (!strcasecmp(o->ptr,"volatile-random")) {
347             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
348         } else if (!strcasecmp(o->ptr,"volatile-ttl")) {
349             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
350         } else if (!strcasecmp(o->ptr,"allkeys-lru")) {
351             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
352         } else if (!strcasecmp(o->ptr,"allkeys-random")) {
353             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
354         } else if (!strcasecmp(o->ptr,"noeviction")) {
355             server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
356         } else {
357             goto badfmt;
358         }
359     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) {
360         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
361             ll <= 0) goto badfmt;
362         server.maxmemory_samples = ll;
363     } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) {
364         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
365             ll < 0 || ll > LONG_MAX) goto badfmt;
366         server.maxidletime = ll;
367     } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) {
368         if (!strcasecmp(o->ptr,"no")) {
369             server.appendfsync = APPENDFSYNC_NO;
370         } else if (!strcasecmp(o->ptr,"everysec")) {
371             server.appendfsync = APPENDFSYNC_EVERYSEC;
372         } else if (!strcasecmp(o->ptr,"always")) {
373             server.appendfsync = APPENDFSYNC_ALWAYS;
374         } else {
375             goto badfmt;
376         }
377     } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) {
378         int yn = yesnotoi(o->ptr);
379 
380         if (yn == -1) goto badfmt;
381         server.no_appendfsync_on_rewrite = yn;
382     } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) {
383         int old = server.appendonly;
384         int new = yesnotoi(o->ptr);
385 
386         if (new == -1) goto badfmt;
387         if (old != new) {
388             if (new == 0) {
389                 stopAppendOnly();
390             } else {
391                 if (startAppendOnly() == REDIS_ERR) {
392                     addReplyError(c,
393                         "Unable to turn on AOF. Check server logs.");
394                     return;
395                 }
396             }
397         }
398     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) {
399         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
400         server.auto_aofrewrite_perc = ll;
401     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) {
402         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
403         server.auto_aofrewrite_min_size = ll;
404     } else if (!strcasecmp(c->argv[2]->ptr,"save")) {
405         int vlen, j;
406         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
407 
408         /* Perform sanity check before setting the new config:
409          * - Even number of args
410          * - Seconds >= 1, changes >= 0 */
411         if (vlen & 1) {
412             sdsfreesplitres(v,vlen);
413             goto badfmt;
414         }
415         for (j = 0; j < vlen; j++) {
416             char *eptr;
417             long val;
418 
419             val = strtoll(v[j], &eptr, 10);
420             if (eptr[0] != '\0' ||
421                 ((j & 1) == 0 && val < 1) ||
422                 ((j & 1) == 1 && val < 0)) {
423                 sdsfreesplitres(v,vlen);
424                 goto badfmt;
425             }
426         }
427         /* Finally set the new config */
428         resetServerSaveParams();
429         for (j = 0; j < vlen; j += 2) {
430             time_t seconds;
431             int changes;
432 
433             seconds = strtoll(v[j],NULL,10);
434             changes = strtoll(v[j+1],NULL,10);
435             appendServerSaveParams(seconds, changes);
436         }
437         sdsfreesplitres(v,vlen);
438     } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) {
439         int yn = yesnotoi(o->ptr);
440 
441         if (yn == -1) goto badfmt;
442         server.repl_serve_stale_data = yn;
443     } else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
444         if (chdir((char*)o->ptr) == -1) {
445             addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
446             return;
447         }
448     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-zipmap-entries")) {
449         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
450         server.hash_max_zipmap_entries = ll;
451     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-zipmap-value")) {
452         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
453         server.hash_max_zipmap_value = ll;
454     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) {
455         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
456         server.list_max_ziplist_entries = ll;
457     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) {
458         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
459         server.list_max_ziplist_value = ll;
460     } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
461         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
462         server.set_max_intset_entries = ll;
463     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) {
464         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
465         server.zset_max_ziplist_entries = ll;
466     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) {
467         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
468         server.zset_max_ziplist_value = ll;
469     } else {
470         addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
471             (char*)c->argv[2]->ptr);
472         return;
473     }
474     addReply(c,shared.ok);
475     return;
476 
477 badfmt: /* Bad format errors */
478     addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
479             (char*)o->ptr,
480             (char*)c->argv[2]->ptr);
481 }
482 
483 void configGetCommand(redisClient *c) {
484     robj *o = c->argv[2];
485     void *replylen = addDeferredMultiBulkLength(c);
486     char *pattern = o->ptr;
487     char buf[128];
488     int matches = 0;
489     redisAssert(o->encoding == REDIS_ENCODING_RAW);
490 
491     if (stringmatch(pattern,"dir",0)) {
492         char buf[1024];
493 
494         addReplyBulkCString(c,"dir");
495         if (getcwd(buf,sizeof(buf)) == NULL) {
496             buf[0] = '\0';
497         } else {
498             addReplyBulkCString(c,buf);
499         }
500         matches++;
501     }
502     if (stringmatch(pattern,"dbfilename",0)) {
503         addReplyBulkCString(c,"dbfilename");
504         addReplyBulkCString(c,server.dbfilename);
505         matches++;
506     }
507     if (stringmatch(pattern,"requirepass",0)) {
508         addReplyBulkCString(c,"requirepass");
509         addReplyBulkCString(c,server.requirepass);
510         matches++;
511     }
512     if (stringmatch(pattern,"masterauth",0)) {
513         addReplyBulkCString(c,"masterauth");
514         addReplyBulkCString(c,server.masterauth);
515         matches++;
516     }
517     if (stringmatch(pattern,"maxmemory",0)) {
518         ll2string(buf,sizeof(buf),server.maxmemory);
519         addReplyBulkCString(c,"maxmemory");
520         addReplyBulkCString(c,buf);
521         matches++;
522     }
523     if (stringmatch(pattern,"maxmemory-policy",0)) {
524         char *s;
525 
526         switch(server.maxmemory_policy) {
527         case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break;
528         case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break;
529         case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break;
530         case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break;
531         case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break;
532         case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break;
533         default: s = "unknown"; break; /* too harmless to panic */
534         }
535         addReplyBulkCString(c,"maxmemory-policy");
536         addReplyBulkCString(c,s);
537         matches++;
538     }
539     if (stringmatch(pattern,"maxmemory-samples",0)) {
540         ll2string(buf,sizeof(buf),server.maxmemory_samples);
541         addReplyBulkCString(c,"maxmemory-samples");
542         addReplyBulkCString(c,buf);
543         matches++;
544     }
545     if (stringmatch(pattern,"timeout",0)) {
546         ll2string(buf,sizeof(buf),server.maxidletime);
547         addReplyBulkCString(c,"timeout");
548         addReplyBulkCString(c,buf);
549         matches++;
550     }
551     if (stringmatch(pattern,"appendonly",0)) {
552         addReplyBulkCString(c,"appendonly");
553         addReplyBulkCString(c,server.appendonly ? "yes" : "no");
554         matches++;
555     }
556     if (stringmatch(pattern,"no-appendfsync-on-rewrite",0)) {
557         addReplyBulkCString(c,"no-appendfsync-on-rewrite");
558         addReplyBulkCString(c,server.no_appendfsync_on_rewrite ? "yes" : "no");
559         matches++;
560     }
561     if (stringmatch(pattern,"appendfsync",0)) {
562         char *policy;
563 
564         switch(server.appendfsync) {
565         case APPENDFSYNC_NO: policy = "no"; break;
566         case APPENDFSYNC_EVERYSEC: policy = "everysec"; break;
567         case APPENDFSYNC_ALWAYS: policy = "always"; break;
568         default: policy = "unknown"; break; /* too harmless to panic */
569         }
570         addReplyBulkCString(c,"appendfsync");
571         addReplyBulkCString(c,policy);
572         matches++;
573     }
574     if (stringmatch(pattern,"save",0)) {
575         sds buf = sdsempty();
576         int j;
577 
578         for (j = 0; j < server.saveparamslen; j++) {
579             buf = sdscatprintf(buf,"%ld %d",
580                     server.saveparams[j].seconds,
581                     server.saveparams[j].changes);
582             if (j != server.saveparamslen-1)
583                 buf = sdscatlen(buf," ",1);
584         }
585         addReplyBulkCString(c,"save");
586         addReplyBulkCString(c,buf);
587         sdsfree(buf);
588         matches++;
589     }
590     if (stringmatch(pattern,"auto-aof-rewrite-percentage",0)) {
591         addReplyBulkCString(c,"auto-aof-rewrite-percentage");
592         addReplyBulkLongLong(c,server.auto_aofrewrite_perc);
593         matches++;
594     }
595     if (stringmatch(pattern,"auto-aof-rewrite-min-size",0)) {
596         addReplyBulkCString(c,"auto-aof-rewrite-min-size");
597         addReplyBulkLongLong(c,server.auto_aofrewrite_min_size);
598         matches++;
599     }
600     if (stringmatch(pattern,"slave-serve-stale-data",0)) {
601         addReplyBulkCString(c,"slave-serve-stale-data");
602         addReplyBulkCString(c,server.repl_serve_stale_data ? "yes" : "no");
603         matches++;
604     }
605     if (stringmatch(pattern,"hash-max-zipmap-entries",0)) {
606         addReplyBulkCString(c,"hash-max-zipmap-entries");
607         addReplyBulkLongLong(c,server.hash_max_zipmap_entries);
608         matches++;
609     }
610     if (stringmatch(pattern,"hash-max-zipmap-value",0)) {
611         addReplyBulkCString(c,"hash-max-zipmap-value");
612         addReplyBulkLongLong(c,server.hash_max_zipmap_value);
613         matches++;
614     }
615     if (stringmatch(pattern,"list-max-ziplist-entries",0)) {
616         addReplyBulkCString(c,"list-max-ziplist-entries");
617         addReplyBulkLongLong(c,server.list_max_ziplist_entries);
618         matches++;
619     }
620     if (stringmatch(pattern,"list-max-ziplist-value",0)) {
621         addReplyBulkCString(c,"list-max-ziplist-value");
622         addReplyBulkLongLong(c,server.list_max_ziplist_value);
623         matches++;
624     }
625     if (stringmatch(pattern,"set-max-intset-entries",0)) {
626         addReplyBulkCString(c,"set-max-intset-entries");
627         addReplyBulkLongLong(c,server.set_max_intset_entries);
628         matches++;
629     }
630     if (stringmatch(pattern,"zset-max-ziplist-entries",0)) {
631         addReplyBulkCString(c,"zset-max-ziplist-entries");
632         addReplyBulkLongLong(c,server.zset_max_ziplist_entries);
633         matches++;
634     }
635     if (stringmatch(pattern,"zset-max-ziplist-value",0)) {
636         addReplyBulkCString(c,"zset-max-ziplist-value");
637         addReplyBulkLongLong(c,server.zset_max_ziplist_value);
638         matches++;
639     }
640     setDeferredMultiBulkLength(c,replylen,matches*2);
641 }
642 
643 void configCommand(redisClient *c) {
644     if (!strcasecmp(c->argv[1]->ptr,"set")) {
645         if (c->argc != 4) goto badarity;
646         configSetCommand(c);
647     } else if (!strcasecmp(c->argv[1]->ptr,"get")) {
648         if (c->argc != 3) goto badarity;
649         configGetCommand(c);
650     } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) {
651         if (c->argc != 2) goto badarity;
652         server.stat_keyspace_hits = 0;
653         server.stat_keyspace_misses = 0;
654         server.stat_numcommands = 0;
655         server.stat_numconnections = 0;
656         server.stat_expiredkeys = 0;
657         resetCommandTableStats();
658         addReply(c,shared.ok);
659     } else {
660         addReplyError(c,
661             "CONFIG subcommand must be one of GET, SET, RESETSTAT");
662     }
663     return;
664 
665 badarity:
666     addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s",
667         (char*) c->argv[1]->ptr);
668 }
669