Back to home page

Redis cross reference

 
 

    


0001 /* Configuration file parsing and CONFIG GET/SET commands implementation.
0002  *
0003  * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
0004  * All rights reserved.
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions are met:
0008  *
0009  *   * Redistributions of source code must retain the above copyright notice,
0010  *     this list of conditions and the following disclaimer.
0011  *   * Redistributions in binary form must reproduce the above copyright
0012  *     notice, this list of conditions and the following disclaimer in the
0013  *     documentation and/or other materials provided with the distribution.
0014  *   * Neither the name of Redis nor the names of its contributors may be used
0015  *     to endorse or promote products derived from this software without
0016  *     specific prior written permission.
0017  *
0018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0022  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0024  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0025  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0026  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0027  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0028  * POSSIBILITY OF SUCH DAMAGE.
0029  */
0030 
0031 
0032 #include "redis.h"
0033 
0034 /*-----------------------------------------------------------------------------
0035  * Config file parsing
0036  *----------------------------------------------------------------------------*/
0037 
0038 int yesnotoi(char *s) {
0039     if (!strcasecmp(s,"yes")) return 1;
0040     else if (!strcasecmp(s,"no")) return 0;
0041     else return -1;
0042 }
0043 
0044 void appendServerSaveParams(time_t seconds, int changes) {
0045     server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));
0046     server.saveparams[server.saveparamslen].seconds = seconds;
0047     server.saveparams[server.saveparamslen].changes = changes;
0048     server.saveparamslen++;
0049 }
0050 
0051 void resetServerSaveParams() {
0052     zfree(server.saveparams);
0053     server.saveparams = NULL;
0054     server.saveparamslen = 0;
0055 }
0056 
0057 void loadServerConfigFromString(char *config) {
0058     char *err = NULL;
0059     int linenum = 0, totlines, i;
0060     sds *lines;
0061 
0062     lines = sdssplitlen(config,strlen(config),"\n",1,&totlines);
0063 
0064     for (i = 0; i < totlines; i++) {
0065         sds *argv;
0066         int argc;
0067 
0068         linenum = i+1;
0069         lines[i] = sdstrim(lines[i]," \t\r\n");
0070 
0071         /* Skip comments and blank lines */
0072         if (lines[i][0] == '#' || lines[i][0] == '\0') continue;
0073 
0074         /* Split into arguments */
0075         argv = sdssplitargs(lines[i],&argc);
0076         if (argv == NULL) {
0077             err = "Unbalanced quotes in configuration line";
0078             goto loaderr;
0079         }
0080 
0081         /* Skip this line if the resulting command vector is empty. */
0082         if (argc == 0) {
0083             sdsfreesplitres(argv,argc);
0084             return;
0085         }
0086         sdstolower(argv[0]);
0087 
0088         /* Execute config directives */
0089         if (!strcasecmp(argv[0],"timeout") && argc == 2) {
0090             server.maxidletime = atoi(argv[1]);
0091             if (server.maxidletime < 0) {
0092                 err = "Invalid timeout value"; goto loaderr;
0093             }
0094         } else if (!strcasecmp(argv[0],"tcp-keepalive") && argc == 2) {
0095             server.tcpkeepalive = atoi(argv[1]);
0096             if (server.tcpkeepalive < 0) {
0097                 err = "Invalid tcp-keepalive value"; goto loaderr;
0098             }
0099         } else if (!strcasecmp(argv[0],"port") && argc == 2) {
0100             server.port = atoi(argv[1]);
0101             if (server.port < 0 || server.port > 65535) {
0102                 err = "Invalid port"; goto loaderr;
0103             }
0104         } else if (!strcasecmp(argv[0],"bind") && argc == 2) {
0105             server.bindaddr = zstrdup(argv[1]);
0106         } else if (!strcasecmp(argv[0],"unixsocket") && argc == 2) {
0107             server.unixsocket = zstrdup(argv[1]);
0108         } else if (!strcasecmp(argv[0],"unixsocketperm") && argc == 2) {
0109             errno = 0;
0110             server.unixsocketperm = (mode_t)strtol(argv[1], NULL, 8);
0111             if (errno || server.unixsocketperm > 0777) {
0112                 err = "Invalid socket file permissions"; goto loaderr;
0113             }
0114         } else if (!strcasecmp(argv[0],"save")) {
0115             if (argc == 3) {
0116                 int seconds = atoi(argv[1]);
0117                 int changes = atoi(argv[2]);
0118                 if (seconds < 1 || changes < 0) {
0119                     err = "Invalid save parameters"; goto loaderr;
0120                 }
0121                 appendServerSaveParams(seconds,changes);
0122             } else if (argc == 2 && !strcasecmp(argv[1],"")) {
0123                 resetServerSaveParams();
0124             }
0125         } else if (!strcasecmp(argv[0],"dir") && argc == 2) {
0126             if (chdir(argv[1]) == -1) {
0127                 redisLog(REDIS_WARNING,"Can't chdir to '%s': %s",
0128                     argv[1], strerror(errno));
0129                 exit(1);
0130             }
0131         } else if (!strcasecmp(argv[0],"loglevel") && argc == 2) {
0132             if (!strcasecmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG;
0133             else if (!strcasecmp(argv[1],"verbose")) server.verbosity = REDIS_VERBOSE;
0134             else if (!strcasecmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE;
0135             else if (!strcasecmp(argv[1],"warning")) server.verbosity = REDIS_WARNING;
0136             else {
0137                 err = "Invalid log level. Must be one of debug, notice, warning";
0138                 goto loaderr;
0139             }
0140         } else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
0141             FILE *logfp;
0142 
0143             server.logfile = zstrdup(argv[1]);
0144             if (!strcasecmp(server.logfile,"stdout")) {
0145                 zfree(server.logfile);
0146                 server.logfile = NULL;
0147             }
0148             if (server.logfile) {
0149                 /* Test if we are able to open the file. The server will not
0150                  * be able to abort just for this problem later... */
0151                 logfp = fopen(server.logfile,"a");
0152                 if (logfp == NULL) {
0153                     err = sdscatprintf(sdsempty(),
0154                         "Can't open the log file: %s", strerror(errno));
0155                     goto loaderr;
0156                 }
0157                 fclose(logfp);
0158             }
0159         } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) {
0160             if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) {
0161                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0162             }
0163         } else if (!strcasecmp(argv[0],"syslog-ident") && argc == 2) {
0164             if (server.syslog_ident) zfree(server.syslog_ident);
0165             server.syslog_ident = zstrdup(argv[1]);
0166         } else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) {
0167             struct {
0168                 const char     *name;
0169                 const int       value;
0170             } validSyslogFacilities[] = {
0171                 {"user",    LOG_USER},
0172                 {"local0",  LOG_LOCAL0},
0173                 {"local1",  LOG_LOCAL1},
0174                 {"local2",  LOG_LOCAL2},
0175                 {"local3",  LOG_LOCAL3},
0176                 {"local4",  LOG_LOCAL4},
0177                 {"local5",  LOG_LOCAL5},
0178                 {"local6",  LOG_LOCAL6},
0179                 {"local7",  LOG_LOCAL7},
0180                 {NULL, 0}
0181             };
0182             int i;
0183 
0184             for (i = 0; validSyslogFacilities[i].name; i++) {
0185                 if (!strcasecmp(validSyslogFacilities[i].name, argv[1])) {
0186                     server.syslog_facility = validSyslogFacilities[i].value;
0187                     break;
0188                 }
0189             }
0190 
0191             if (!validSyslogFacilities[i].name) {
0192                 err = "Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7";
0193                 goto loaderr;
0194             }
0195         } else if (!strcasecmp(argv[0],"databases") && argc == 2) {
0196             server.dbnum = atoi(argv[1]);
0197             if (server.dbnum < 1) {
0198                 err = "Invalid number of databases"; goto loaderr;
0199             }
0200         } else if (!strcasecmp(argv[0],"include") && argc == 2) {
0201             loadServerConfig(argv[1],NULL);
0202         } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
0203             server.maxclients = atoi(argv[1]);
0204             if (server.maxclients < 1) {
0205                 err = "Invalid max clients limit"; goto loaderr;
0206             }
0207         } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
0208             server.maxmemory = memtoll(argv[1],NULL);
0209         } else if (!strcasecmp(argv[0],"maxmemory-policy") && argc == 2) {
0210             if (!strcasecmp(argv[1],"volatile-lru")) {
0211                 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
0212             } else if (!strcasecmp(argv[1],"volatile-random")) {
0213                 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
0214             } else if (!strcasecmp(argv[1],"volatile-ttl")) {
0215                 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
0216             } else if (!strcasecmp(argv[1],"allkeys-lru")) {
0217                 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
0218             } else if (!strcasecmp(argv[1],"allkeys-random")) {
0219                 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
0220             } else if (!strcasecmp(argv[1],"noeviction")) {
0221                 server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
0222             } else {
0223                 err = "Invalid maxmemory policy";
0224                 goto loaderr;
0225             }
0226         } else if (!strcasecmp(argv[0],"maxmemory-samples") && argc == 2) {
0227             server.maxmemory_samples = atoi(argv[1]);
0228             if (server.maxmemory_samples <= 0) {
0229                 err = "maxmemory-samples must be 1 or greater";
0230                 goto loaderr;
0231             }
0232         } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
0233             server.masterhost = sdsnew(argv[1]);
0234             server.masterport = atoi(argv[2]);
0235             server.repl_state = REDIS_REPL_CONNECT;
0236         } else if (!strcasecmp(argv[0],"repl-ping-slave-period") && argc == 2) {
0237             server.repl_ping_slave_period = atoi(argv[1]);
0238             if (server.repl_ping_slave_period <= 0) {
0239                 err = "repl-ping-slave-period must be 1 or greater";
0240                 goto loaderr;
0241             }
0242         } else if (!strcasecmp(argv[0],"repl-timeout") && argc == 2) {
0243             server.repl_timeout = atoi(argv[1]);
0244             if (server.repl_timeout <= 0) {
0245                 err = "repl-timeout must be 1 or greater";
0246                 goto loaderr;
0247             }
0248         } else if (!strcasecmp(argv[0],"repl-disable-tcp-nodelay") && argc==2) {
0249             if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) {
0250                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0251             }
0252         } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) {
0253             server.masterauth = zstrdup(argv[1]);
0254         } else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) {
0255             if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) {
0256                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0257             }
0258         } else if (!strcasecmp(argv[0],"slave-read-only") && argc == 2) {
0259             if ((server.repl_slave_ro = yesnotoi(argv[1])) == -1) {
0260                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0261             }
0262         } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
0263             if ((server.rdb_compression = yesnotoi(argv[1])) == -1) {
0264                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0265             }
0266         } else if (!strcasecmp(argv[0],"rdbchecksum") && argc == 2) {
0267             if ((server.rdb_checksum = yesnotoi(argv[1])) == -1) {
0268                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0269             }
0270         } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) {
0271             if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
0272                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0273             }
0274         } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
0275             if ((server.daemonize = yesnotoi(argv[1])) == -1) {
0276                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0277             }
0278         } else if (!strcasecmp(argv[0],"hz") && argc == 2) {
0279             server.hz = atoi(argv[1]);
0280             if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
0281             if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
0282         } else if (!strcasecmp(argv[0],"appendonly") && argc == 2) {
0283             int yes;
0284 
0285             if ((yes = yesnotoi(argv[1])) == -1) {
0286                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0287             }
0288             server.aof_state = yes ? REDIS_AOF_ON : REDIS_AOF_OFF;
0289         } else if (!strcasecmp(argv[0],"appendfilename") && argc == 2) {
0290             zfree(server.aof_filename);
0291             server.aof_filename = zstrdup(argv[1]);
0292         } else if (!strcasecmp(argv[0],"no-appendfsync-on-rewrite")
0293                    && argc == 2) {
0294             if ((server.aof_no_fsync_on_rewrite= yesnotoi(argv[1])) == -1) {
0295                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0296             }
0297         } else if (!strcasecmp(argv[0],"appendfsync") && argc == 2) {
0298             if (!strcasecmp(argv[1],"no")) {
0299                 server.aof_fsync = AOF_FSYNC_NO;
0300             } else if (!strcasecmp(argv[1],"always")) {
0301                 server.aof_fsync = AOF_FSYNC_ALWAYS;
0302             } else if (!strcasecmp(argv[1],"everysec")) {
0303                 server.aof_fsync = AOF_FSYNC_EVERYSEC;
0304             } else {
0305                 err = "argument must be 'no', 'always' or 'everysec'";
0306                 goto loaderr;
0307             }
0308         } else if (!strcasecmp(argv[0],"auto-aof-rewrite-percentage") &&
0309                    argc == 2)
0310         {
0311             server.aof_rewrite_perc = atoi(argv[1]);
0312             if (server.aof_rewrite_perc < 0) {
0313                 err = "Invalid negative percentage for AOF auto rewrite";
0314                 goto loaderr;
0315             }
0316         } else if (!strcasecmp(argv[0],"auto-aof-rewrite-min-size") &&
0317                    argc == 2)
0318         {
0319             server.aof_rewrite_min_size = memtoll(argv[1],NULL);
0320         } else if (!strcasecmp(argv[0],"aof-rewrite-incremental-fsync") &&
0321                    argc == 2)
0322         {
0323             if ((server.aof_rewrite_incremental_fsync = yesnotoi(argv[1])) == -1) {
0324                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0325             }
0326         } else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
0327             if (strlen(argv[1]) > REDIS_AUTHPASS_MAX_LEN) {
0328                 err = "Password is longer than REDIS_AUTHPASS_MAX_LEN";
0329                 goto loaderr;
0330             }
0331             server.requirepass = zstrdup(argv[1]);
0332         } else if (!strcasecmp(argv[0],"pidfile") && argc == 2) {
0333             zfree(server.pidfile);
0334             server.pidfile = zstrdup(argv[1]);
0335         } else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) {
0336             zfree(server.rdb_filename);
0337             server.rdb_filename = zstrdup(argv[1]);
0338         } else if (!strcasecmp(argv[0],"hash-max-ziplist-entries") && argc == 2) {
0339             server.hash_max_ziplist_entries = memtoll(argv[1], NULL);
0340         } else if (!strcasecmp(argv[0],"hash-max-ziplist-value") && argc == 2) {
0341             server.hash_max_ziplist_value = memtoll(argv[1], NULL);
0342         } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
0343             server.list_max_ziplist_entries = memtoll(argv[1], NULL);
0344         } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
0345             server.list_max_ziplist_value = memtoll(argv[1], NULL);
0346         } else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) {
0347             server.set_max_intset_entries = memtoll(argv[1], NULL);
0348         } else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) {
0349             server.zset_max_ziplist_entries = memtoll(argv[1], NULL);
0350         } else if (!strcasecmp(argv[0],"zset-max-ziplist-value") && argc == 2) {
0351             server.zset_max_ziplist_value = memtoll(argv[1], NULL);
0352         } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) {
0353             struct redisCommand *cmd = lookupCommand(argv[1]);
0354             int retval;
0355 
0356             if (!cmd) {
0357                 err = "No such command in rename-command";
0358                 goto loaderr;
0359             }
0360 
0361             /* If the target command name is the empty string we just
0362              * remove it from the command table. */
0363             retval = dictDelete(server.commands, argv[1]);
0364             redisAssert(retval == DICT_OK);
0365 
0366             /* Otherwise we re-add the command under a different name. */
0367             if (sdslen(argv[2]) != 0) {
0368                 sds copy = sdsdup(argv[2]);
0369 
0370                 retval = dictAdd(server.commands, copy, cmd);
0371                 if (retval != DICT_OK) {
0372                     sdsfree(copy);
0373                     err = "Target command name already exists"; goto loaderr;
0374                 }
0375             }
0376         } else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) {
0377             server.lua_time_limit = strtoll(argv[1],NULL,10);
0378         } else if (!strcasecmp(argv[0],"slowlog-log-slower-than") &&
0379                    argc == 2)
0380         {
0381             server.slowlog_log_slower_than = strtoll(argv[1],NULL,10);
0382         } else if (!strcasecmp(argv[0],"slowlog-max-len") && argc == 2) {
0383             server.slowlog_max_len = strtoll(argv[1],NULL,10);
0384         } else if (!strcasecmp(argv[0],"client-output-buffer-limit") &&
0385                    argc == 5)
0386         {
0387             int class = getClientLimitClassByName(argv[1]);
0388             unsigned long long hard, soft;
0389             int soft_seconds;
0390 
0391             if (class == -1) {
0392                 err = "Unrecognized client limit class";
0393                 goto loaderr;
0394             }
0395             hard = memtoll(argv[2],NULL);
0396             soft = memtoll(argv[3],NULL);
0397             soft_seconds = atoi(argv[4]);
0398             if (soft_seconds < 0) {
0399                 err = "Negative number of seconds in soft limit is invalid";
0400                 goto loaderr;
0401             }
0402             server.client_obuf_limits[class].hard_limit_bytes = hard;
0403             server.client_obuf_limits[class].soft_limit_bytes = soft;
0404             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
0405         } else if (!strcasecmp(argv[0],"stop-writes-on-bgsave-error") &&
0406                    argc == 2) {
0407             if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) {
0408                 err = "argument must be 'yes' or 'no'"; goto loaderr;
0409             }
0410         } else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) {
0411             server.slave_priority = atoi(argv[1]);
0412         } else if (!strcasecmp(argv[0],"sentinel")) {
0413             /* argc == 1 is handled by main() as we need to enter the sentinel
0414              * mode ASAP. */
0415             if (argc != 1) {
0416                 if (!server.sentinel_mode) {
0417                     err = "sentinel directive while not in sentinel mode";
0418                     goto loaderr;
0419                 }
0420                 err = sentinelHandleConfiguration(argv+1,argc-1);
0421                 if (err) goto loaderr;
0422             }
0423         } else {
0424             err = "Bad directive or wrong number of arguments"; goto loaderr;
0425         }
0426         sdsfreesplitres(argv,argc);
0427     }
0428     sdsfreesplitres(lines,totlines);
0429     return;
0430 
0431 loaderr:
0432     fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n");
0433     fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
0434     fprintf(stderr, ">>> '%s'\n", lines[i]);
0435     fprintf(stderr, "%s\n", err);
0436     exit(1);
0437 }
0438 
0439 /* Load the server configuration from the specified filename.
0440  * The function appends the additional configuration directives stored
0441  * in the 'options' string to the config file before loading.
0442  *
0443  * Both filename and options can be NULL, in such a case are considered
0444  * empty. This way loadServerConfig can be used to just load a file or
0445  * just load a string. */
0446 void loadServerConfig(char *filename, char *options) {
0447     sds config = sdsempty();
0448     char buf[REDIS_CONFIGLINE_MAX+1];
0449 
0450     /* Load the file content */
0451     if (filename) {
0452         FILE *fp;
0453 
0454         if (filename[0] == '-' && filename[1] == '\0') {
0455             fp = stdin;
0456         } else {
0457             if ((fp = fopen(filename,"r")) == NULL) {
0458                 redisLog(REDIS_WARNING,
0459                     "Fatal error, can't open config file '%s'", filename);
0460                 exit(1);
0461             }
0462         }
0463         while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL)
0464             config = sdscat(config,buf);
0465         if (fp != stdin) fclose(fp);
0466     }
0467     /* Append the additional options */
0468     if (options) {
0469         config = sdscat(config,"\n");
0470         config = sdscat(config,options);
0471     }
0472     loadServerConfigFromString(config);
0473     sdsfree(config);
0474 }
0475 
0476 /*-----------------------------------------------------------------------------
0477  * CONFIG command for remote configuration
0478  *----------------------------------------------------------------------------*/
0479 
0480 void configSetCommand(redisClient *c) {
0481     robj *o;
0482     long long ll;
0483     redisAssertWithInfo(c,c->argv[2],c->argv[2]->encoding == REDIS_ENCODING_RAW);
0484     redisAssertWithInfo(c,c->argv[2],c->argv[3]->encoding == REDIS_ENCODING_RAW);
0485     o = c->argv[3];
0486 
0487     if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) {
0488         zfree(server.rdb_filename);
0489         server.rdb_filename = zstrdup(o->ptr);
0490     } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) {
0491         if (sdslen(o->ptr) > REDIS_AUTHPASS_MAX_LEN) goto badfmt;
0492         zfree(server.requirepass);
0493         server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
0494     } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) {
0495         zfree(server.masterauth);
0496         server.masterauth = zstrdup(o->ptr);
0497     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) {
0498         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
0499             ll < 0) goto badfmt;
0500         server.maxmemory = ll;
0501         if (server.maxmemory) {
0502             if (server.maxmemory < zmalloc_used_memory()) {
0503                 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.");
0504             }
0505             freeMemoryIfNeeded();
0506         }
0507     } else if (!strcasecmp(c->argv[2]->ptr,"hz")) {
0508         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
0509             ll < 0) goto badfmt;
0510         server.hz = (int) ll;
0511         if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
0512         if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
0513     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) {
0514         if (!strcasecmp(o->ptr,"volatile-lru")) {
0515             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
0516         } else if (!strcasecmp(o->ptr,"volatile-random")) {
0517             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
0518         } else if (!strcasecmp(o->ptr,"volatile-ttl")) {
0519             server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
0520         } else if (!strcasecmp(o->ptr,"allkeys-lru")) {
0521             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
0522         } else if (!strcasecmp(o->ptr,"allkeys-random")) {
0523             server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
0524         } else if (!strcasecmp(o->ptr,"noeviction")) {
0525             server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
0526         } else {
0527             goto badfmt;
0528         }
0529     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) {
0530         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
0531             ll <= 0) goto badfmt;
0532         server.maxmemory_samples = ll;
0533     } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) {
0534         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
0535             ll < 0 || ll > LONG_MAX) goto badfmt;
0536         server.maxidletime = ll;
0537     } else if (!strcasecmp(c->argv[2]->ptr,"tcp-keepalive")) {
0538         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
0539             ll < 0 || ll > INT_MAX) goto badfmt;
0540         server.tcpkeepalive = ll;
0541     } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) {
0542         if (!strcasecmp(o->ptr,"no")) {
0543             server.aof_fsync = AOF_FSYNC_NO;
0544         } else if (!strcasecmp(o->ptr,"everysec")) {
0545             server.aof_fsync = AOF_FSYNC_EVERYSEC;
0546         } else if (!strcasecmp(o->ptr,"always")) {
0547             server.aof_fsync = AOF_FSYNC_ALWAYS;
0548         } else {
0549             goto badfmt;
0550         }
0551     } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) {
0552         int yn = yesnotoi(o->ptr);
0553 
0554         if (yn == -1) goto badfmt;
0555         server.aof_no_fsync_on_rewrite = yn;
0556     } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) {
0557         int enable = yesnotoi(o->ptr);
0558 
0559         if (enable == -1) goto badfmt;
0560         if (enable == 0 && server.aof_state != REDIS_AOF_OFF) {
0561             stopAppendOnly();
0562         } else if (enable && server.aof_state == REDIS_AOF_OFF) {
0563             if (startAppendOnly() == REDIS_ERR) {
0564                 addReplyError(c,
0565                     "Unable to turn on AOF. Check server logs.");
0566                 return;
0567             }
0568         }
0569     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) {
0570         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0571         server.aof_rewrite_perc = ll;
0572     } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) {
0573         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0574         server.aof_rewrite_min_size = ll;
0575     } else if (!strcasecmp(c->argv[2]->ptr,"aof-rewrite-incremental-fsync")) {
0576         int yn = yesnotoi(o->ptr);
0577 
0578         if (yn == -1) goto badfmt;
0579         server.aof_rewrite_incremental_fsync = yn;
0580     } else if (!strcasecmp(c->argv[2]->ptr,"save")) {
0581         int vlen, j;
0582         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
0583 
0584         /* Perform sanity check before setting the new config:
0585          * - Even number of args
0586          * - Seconds >= 1, changes >= 0 */
0587         if (vlen & 1) {
0588             sdsfreesplitres(v,vlen);
0589             goto badfmt;
0590         }
0591         for (j = 0; j < vlen; j++) {
0592             char *eptr;
0593             long val;
0594 
0595             val = strtoll(v[j], &eptr, 10);
0596             if (eptr[0] != '\0' ||
0597                 ((j & 1) == 0 && val < 1) ||
0598                 ((j & 1) == 1 && val < 0)) {
0599                 sdsfreesplitres(v,vlen);
0600                 goto badfmt;
0601             }
0602         }
0603         /* Finally set the new config */
0604         resetServerSaveParams();
0605         for (j = 0; j < vlen; j += 2) {
0606             time_t seconds;
0607             int changes;
0608 
0609             seconds = strtoll(v[j],NULL,10);
0610             changes = strtoll(v[j+1],NULL,10);
0611             appendServerSaveParams(seconds, changes);
0612         }
0613         sdsfreesplitres(v,vlen);
0614     } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) {
0615         int yn = yesnotoi(o->ptr);
0616 
0617         if (yn == -1) goto badfmt;
0618         server.repl_serve_stale_data = yn;
0619     } else if (!strcasecmp(c->argv[2]->ptr,"slave-read-only")) {
0620         int yn = yesnotoi(o->ptr);
0621 
0622         if (yn == -1) goto badfmt;
0623         server.repl_slave_ro = yn;
0624     } else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
0625         if (chdir((char*)o->ptr) == -1) {
0626             addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
0627             return;
0628         }
0629     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-entries")) {
0630         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0631         server.hash_max_ziplist_entries = ll;
0632     } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) {
0633         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0634         server.hash_max_ziplist_value = ll;
0635     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) {
0636         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0637         server.list_max_ziplist_entries = ll;
0638     } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) {
0639         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0640         server.list_max_ziplist_value = ll;
0641     } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
0642         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0643         server.set_max_intset_entries = ll;
0644     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) {
0645         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0646         server.zset_max_ziplist_entries = ll;
0647     } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) {
0648         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0649         server.zset_max_ziplist_value = ll;
0650     } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) {
0651         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0652         server.lua_time_limit = ll;
0653     } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) {
0654         if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt;
0655         server.slowlog_log_slower_than = ll;
0656     } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) {
0657         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0658         server.slowlog_max_len = (unsigned)ll;
0659     } else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) {
0660         if (!strcasecmp(o->ptr,"warning")) {
0661             server.verbosity = REDIS_WARNING;
0662         } else if (!strcasecmp(o->ptr,"notice")) {
0663             server.verbosity = REDIS_NOTICE;
0664         } else if (!strcasecmp(o->ptr,"verbose")) {
0665             server.verbosity = REDIS_VERBOSE;
0666         } else if (!strcasecmp(o->ptr,"debug")) {
0667             server.verbosity = REDIS_DEBUG;
0668         } else {
0669             goto badfmt;
0670         }
0671     } else if (!strcasecmp(c->argv[2]->ptr,"client-output-buffer-limit")) {
0672         int vlen, j;
0673         sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
0674 
0675         /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */
0676         if (vlen % 4) {
0677             sdsfreesplitres(v,vlen);
0678             goto badfmt;
0679         }
0680 
0681         /* Sanity check of single arguments, so that we either refuse the
0682          * whole configuration string or accept it all, even if a single
0683          * error in a single client class is present. */
0684         for (j = 0; j < vlen; j++) {
0685             char *eptr;
0686             long val;
0687 
0688             if ((j % 4) == 0) {
0689                 if (getClientLimitClassByName(v[j]) == -1) {
0690                     sdsfreesplitres(v,vlen);
0691                     goto badfmt;
0692                 }
0693             } else {
0694                 val = strtoll(v[j], &eptr, 10);
0695                 if (eptr[0] != '\0' || val < 0) {
0696                     sdsfreesplitres(v,vlen);
0697                     goto badfmt;
0698                 }
0699             }
0700         }
0701         /* Finally set the new config */
0702         for (j = 0; j < vlen; j += 4) {
0703             int class;
0704             unsigned long long hard, soft;
0705             int soft_seconds;
0706 
0707             class = getClientLimitClassByName(v[j]);
0708             hard = strtoll(v[j+1],NULL,10);
0709             soft = strtoll(v[j+2],NULL,10);
0710             soft_seconds = strtoll(v[j+3],NULL,10);
0711 
0712             server.client_obuf_limits[class].hard_limit_bytes = hard;
0713             server.client_obuf_limits[class].soft_limit_bytes = soft;
0714             server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
0715         }
0716         sdsfreesplitres(v,vlen);
0717     } else if (!strcasecmp(c->argv[2]->ptr,"stop-writes-on-bgsave-error")) {
0718         int yn = yesnotoi(o->ptr);
0719 
0720         if (yn == -1) goto badfmt;
0721         server.stop_writes_on_bgsave_err = yn;
0722     } else if (!strcasecmp(c->argv[2]->ptr,"repl-ping-slave-period")) {
0723         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
0724         server.repl_ping_slave_period = ll;
0725     } else if (!strcasecmp(c->argv[2]->ptr,"repl-timeout")) {
0726         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
0727         server.repl_timeout = ll;
0728     } else if (!strcasecmp(c->argv[2]->ptr,"watchdog-period")) {
0729         if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
0730         if (ll)
0731             enableWatchdog(ll);
0732         else
0733             disableWatchdog();
0734     } else if (!strcasecmp(c->argv[2]->ptr,"rdbcompression")) {
0735         int yn = yesnotoi(o->ptr);
0736 
0737         if (yn == -1) goto badfmt;
0738         server.rdb_compression = yn;
0739     } else if (!strcasecmp(c->argv[2]->ptr,"rdbchecksum")) {
0740         int yn = yesnotoi(o->ptr);
0741 
0742         if (yn == -1) goto badfmt;
0743         server.rdb_checksum = yn;
0744     } else if (!strcasecmp(c->argv[2]->ptr,"repl-disable-tcp-nodelay")) {
0745         int yn = yesnotoi(o->ptr);
0746 
0747         if (yn == -1) goto badfmt;
0748         server.repl_disable_tcp_nodelay = yn;
0749     } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) {
0750         if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
0751             ll <= 0) goto badfmt;
0752         server.slave_priority = ll;
0753     } else {
0754         addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
0755             (char*)c->argv[2]->ptr);
0756         return;
0757     }
0758     addReply(c,shared.ok);
0759     return;
0760 
0761 badfmt: /* Bad format errors */
0762     addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
0763             (char*)o->ptr,
0764             (char*)c->argv[2]->ptr);
0765 }
0766 
0767 #define config_get_string_field(_name,_var) do { \
0768     if (stringmatch(pattern,_name,0)) { \
0769         addReplyBulkCString(c,_name); \
0770         addReplyBulkCString(c,_var ? _var : ""); \
0771         matches++; \
0772     } \
0773 } while(0);
0774 
0775 #define config_get_bool_field(_name,_var) do { \
0776     if (stringmatch(pattern,_name,0)) { \
0777         addReplyBulkCString(c,_name); \
0778         addReplyBulkCString(c,_var ? "yes" : "no"); \
0779         matches++; \
0780     } \
0781 } while(0);
0782 
0783 #define config_get_numerical_field(_name,_var) do { \
0784     if (stringmatch(pattern,_name,0)) { \
0785         ll2string(buf,sizeof(buf),_var); \
0786         addReplyBulkCString(c,_name); \
0787         addReplyBulkCString(c,buf); \
0788         matches++; \
0789     } \
0790 } while(0);
0791 
0792 void configGetCommand(redisClient *c) {
0793     robj *o = c->argv[2];
0794     void *replylen = addDeferredMultiBulkLength(c);
0795     char *pattern = o->ptr;
0796     char buf[128];
0797     int matches = 0;
0798     redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW);
0799 
0800     /* String values */
0801     config_get_string_field("dbfilename",server.rdb_filename);
0802     config_get_string_field("requirepass",server.requirepass);
0803     config_get_string_field("masterauth",server.masterauth);
0804     config_get_string_field("bind",server.bindaddr);
0805     config_get_string_field("unixsocket",server.unixsocket);
0806     config_get_string_field("logfile",server.logfile);
0807     config_get_string_field("pidfile",server.pidfile);
0808 
0809     /* Numerical values */
0810     config_get_numerical_field("maxmemory",server.maxmemory);
0811     config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
0812     config_get_numerical_field("timeout",server.maxidletime);
0813     config_get_numerical_field("tcp-keepalive",server.tcpkeepalive);
0814     config_get_numerical_field("auto-aof-rewrite-percentage",
0815             server.aof_rewrite_perc);
0816     config_get_numerical_field("auto-aof-rewrite-min-size",
0817             server.aof_rewrite_min_size);
0818     config_get_numerical_field("hash-max-ziplist-entries",
0819             server.hash_max_ziplist_entries);
0820     config_get_numerical_field("hash-max-ziplist-value",
0821             server.hash_max_ziplist_value);
0822     config_get_numerical_field("list-max-ziplist-entries",
0823             server.list_max_ziplist_entries);
0824     config_get_numerical_field("list-max-ziplist-value",
0825             server.list_max_ziplist_value);
0826     config_get_numerical_field("set-max-intset-entries",
0827             server.set_max_intset_entries);
0828     config_get_numerical_field("zset-max-ziplist-entries",
0829             server.zset_max_ziplist_entries);
0830     config_get_numerical_field("zset-max-ziplist-value",
0831             server.zset_max_ziplist_value);
0832     config_get_numerical_field("lua-time-limit",server.lua_time_limit);
0833     config_get_numerical_field("slowlog-log-slower-than",
0834             server.slowlog_log_slower_than);
0835     config_get_numerical_field("slowlog-max-len",
0836             server.slowlog_max_len);
0837     config_get_numerical_field("port",server.port);
0838     config_get_numerical_field("databases",server.dbnum);
0839     config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
0840     config_get_numerical_field("repl-timeout",server.repl_timeout);
0841     config_get_numerical_field("maxclients",server.maxclients);
0842     config_get_numerical_field("watchdog-period",server.watchdog_period);
0843     config_get_numerical_field("slave-priority",server.slave_priority);
0844     config_get_numerical_field("hz",server.hz);
0845 
0846     /* Bool (yes/no) values */
0847     config_get_bool_field("no-appendfsync-on-rewrite",
0848             server.aof_no_fsync_on_rewrite);
0849     config_get_bool_field("slave-serve-stale-data",
0850             server.repl_serve_stale_data);
0851     config_get_bool_field("slave-read-only",
0852             server.repl_slave_ro);
0853     config_get_bool_field("stop-writes-on-bgsave-error",
0854             server.stop_writes_on_bgsave_err);
0855     config_get_bool_field("daemonize", server.daemonize);
0856     config_get_bool_field("rdbcompression", server.rdb_compression);
0857     config_get_bool_field("rdbchecksum", server.rdb_checksum);
0858     config_get_bool_field("activerehashing", server.activerehashing);
0859     config_get_bool_field("repl-disable-tcp-nodelay",
0860             server.repl_disable_tcp_nodelay);
0861     config_get_bool_field("aof-rewrite-incremental-fsync",
0862             server.aof_rewrite_incremental_fsync);
0863 
0864     /* Everything we can't handle with macros follows. */
0865 
0866     if (stringmatch(pattern,"appendonly",0)) {
0867         addReplyBulkCString(c,"appendonly");
0868         addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes");
0869         matches++;
0870     }
0871     if (stringmatch(pattern,"dir",0)) {
0872         char buf[1024];
0873 
0874         if (getcwd(buf,sizeof(buf)) == NULL)
0875             buf[0] = '\0';
0876 
0877         addReplyBulkCString(c,"dir");
0878         addReplyBulkCString(c,buf);
0879         matches++;
0880     }
0881     if (stringmatch(pattern,"maxmemory-policy",0)) {
0882         char *s;
0883 
0884         switch(server.maxmemory_policy) {
0885         case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break;
0886         case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break;
0887         case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break;
0888         case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break;
0889         case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break;
0890         case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break;
0891         default: s = "unknown"; break; /* too harmless to panic */
0892         }
0893         addReplyBulkCString(c,"maxmemory-policy");
0894         addReplyBulkCString(c,s);
0895         matches++;
0896     }
0897     if (stringmatch(pattern,"appendfsync",0)) {
0898         char *policy;
0899 
0900         switch(server.aof_fsync) {
0901         case AOF_FSYNC_NO: policy = "no"; break;
0902         case AOF_FSYNC_EVERYSEC: policy = "everysec"; break;
0903         case AOF_FSYNC_ALWAYS: policy = "always"; break;
0904         default: policy = "unknown"; break; /* too harmless to panic */
0905         }
0906         addReplyBulkCString(c,"appendfsync");
0907         addReplyBulkCString(c,policy);
0908         matches++;
0909     }
0910     if (stringmatch(pattern,"save",0)) {
0911         sds buf = sdsempty();
0912         int j;
0913 
0914         for (j = 0; j < server.saveparamslen; j++) {
0915             buf = sdscatprintf(buf,"%ld %d",
0916                     server.saveparams[j].seconds,
0917                     server.saveparams[j].changes);
0918             if (j != server.saveparamslen-1)
0919                 buf = sdscatlen(buf," ",1);
0920         }
0921         addReplyBulkCString(c,"save");
0922         addReplyBulkCString(c,buf);
0923         sdsfree(buf);
0924         matches++;
0925     }
0926     if (stringmatch(pattern,"loglevel",0)) {
0927         char *s;
0928 
0929         switch(server.verbosity) {
0930         case REDIS_WARNING: s = "warning"; break;
0931         case REDIS_VERBOSE: s = "verbose"; break;
0932         case REDIS_NOTICE: s = "notice"; break;
0933         case REDIS_DEBUG: s = "debug"; break;
0934         default: s = "unknown"; break; /* too harmless to panic */
0935         }
0936         addReplyBulkCString(c,"loglevel");
0937         addReplyBulkCString(c,s);
0938         matches++;
0939     }
0940     if (stringmatch(pattern,"client-output-buffer-limit",0)) {
0941         sds buf = sdsempty();
0942         int j;
0943 
0944         for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {
0945             buf = sdscatprintf(buf,"%s %llu %llu %ld",
0946                     getClientLimitClassName(j),
0947                     server.client_obuf_limits[j].hard_limit_bytes,
0948                     server.client_obuf_limits[j].soft_limit_bytes,
0949                     (long) server.client_obuf_limits[j].soft_limit_seconds);
0950             if (j != REDIS_CLIENT_LIMIT_NUM_CLASSES-1)
0951                 buf = sdscatlen(buf," ",1);
0952         }
0953         addReplyBulkCString(c,"client-output-buffer-limit");
0954         addReplyBulkCString(c,buf);
0955         sdsfree(buf);
0956         matches++;
0957     }
0958     if (stringmatch(pattern,"unixsocketperm",0)) {
0959         char buf[32];
0960         snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
0961         addReplyBulkCString(c,"unixsocketperm");
0962         addReplyBulkCString(c,buf);
0963         matches++;
0964     }
0965     if (stringmatch(pattern,"slaveof",0)) {
0966         char buf[256];
0967 
0968         addReplyBulkCString(c,"slaveof");
0969         if (server.masterhost)
0970             snprintf(buf,sizeof(buf),"%s %d",
0971                 server.masterhost, server.masterport);
0972         else
0973             buf[0] = '\0';
0974         addReplyBulkCString(c,buf);
0975         matches++;
0976     }
0977     setDeferredMultiBulkLength(c,replylen,matches*2);
0978 }
0979 
0980 void configCommand(redisClient *c) {
0981     if (!strcasecmp(c->argv[1]->ptr,"set")) {
0982         if (c->argc != 4) goto badarity;
0983         configSetCommand(c);
0984     } else if (!strcasecmp(c->argv[1]->ptr,"get")) {
0985         if (c->argc != 3) goto badarity;
0986         configGetCommand(c);
0987     } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) {
0988         if (c->argc != 2) goto badarity;
0989         server.stat_keyspace_hits = 0;
0990         server.stat_keyspace_misses = 0;
0991         server.stat_numcommands = 0;
0992         server.stat_numconnections = 0;
0993         server.stat_expiredkeys = 0;
0994         server.stat_rejected_conn = 0;
0995         server.stat_fork_time = 0;
0996         server.aof_delayed_fsync = 0;
0997         resetCommandTableStats();
0998         addReply(c,shared.ok);
0999     } else {
1000         addReplyError(c,
1001             "CONFIG subcommand must be one of GET, SET, RESETSTAT");
1002     }
1003     return;
1004 
1005 badarity:
1006     addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s",
1007         (char*) c->argv[1]->ptr);
1008 }