config.c 43.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/* Configuration file parsing and CONFIG GET/SET commands implementation.
 *
 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Redis nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */


32
#include "redis.h"
H
Henry Rawas 已提交
33 34 35
#ifdef _WIN32
#include <direct.h>
#endif
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

/*-----------------------------------------------------------------------------
 * Config file parsing
 *----------------------------------------------------------------------------*/

int yesnotoi(char *s) {
    if (!strcasecmp(s,"yes")) return 1;
    else if (!strcasecmp(s,"no")) return 0;
    else return -1;
}

void appendServerSaveParams(time_t seconds, int changes) {
    server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1));
    server.saveparams[server.saveparamslen].seconds = seconds;
    server.saveparams[server.saveparamslen].changes = changes;
    server.saveparamslen++;
}

void resetServerSaveParams() {
    zfree(server.saveparams);
    server.saveparams = NULL;
    server.saveparamslen = 0;
}

60 61 62 63
void loadServerConfigFromString(char *config) {
    char *err = NULL;
    int linenum = 0, totlines, i;
    sds *lines;
64

H
Henry Rawas 已提交
65
    lines = sdssplitlen(config,(int)strlen(config),"\n",1,&totlines);
66

67
    for (i = 0; i < totlines; i++) {
68
        sds *argv;
69
        int argc;
70

71 72
        linenum = i+1;
        lines[i] = sdstrim(lines[i]," \t\r\n");
73

74
        /* Skip comments and blank lines */
75
        if (lines[i][0] == '#' || lines[i][0] == '\0') continue;
76 77

        /* Split into arguments */
78
        argv = sdssplitargs(lines[i],&argc);
C
charsyam 已提交
79
        if (argv == NULL) {
80
            err = "Unbalanced quotes in configuration line";
C
charsyam 已提交
81 82
            goto loaderr;
        }
83 84 85 86 87 88

        /* Skip this line if the resulting command vector is empty. */
        if (argc == 0) {
            sdsfreesplitres(argv,argc);
            return;
        }
89 90 91 92 93 94 95 96
        sdstolower(argv[0]);

        /* Execute config directives */
        if (!strcasecmp(argv[0],"timeout") && argc == 2) {
            server.maxidletime = atoi(argv[1]);
            if (server.maxidletime < 0) {
                err = "Invalid timeout value"; goto loaderr;
            }
97 98 99 100 101
        } else if (!strcasecmp(argv[0],"tcp-keepalive") && argc == 2) {
            server.tcpkeepalive = atoi(argv[1]);
            if (server.tcpkeepalive < 0) {
                err = "Invalid tcp-keepalive value"; goto loaderr;
            }
102 103
        } else if (!strcasecmp(argv[0],"port") && argc == 2) {
            server.port = atoi(argv[1]);
104
            if (server.port < 0 || server.port > 65535) {
105 106 107 108
                err = "Invalid port"; goto loaderr;
            }
        } else if (!strcasecmp(argv[0],"bind") && argc == 2) {
            server.bindaddr = zstrdup(argv[1]);
109 110
        } else if (!strcasecmp(argv[0],"unixsocket") && argc == 2) {
            server.unixsocket = zstrdup(argv[1]);
111
        } else if (!strcasecmp(argv[0],"unixsocketperm") && argc == 2) {
112
            errno = 0;
113 114 115 116
            server.unixsocketperm = (mode_t)strtol(argv[1], NULL, 8);
            if (errno || server.unixsocketperm > 0777) {
                err = "Invalid socket file permissions"; goto loaderr;
            }
117 118 119 120 121 122 123 124 125 126
        } else if (!strcasecmp(argv[0],"save")) {
            if (argc == 3) {
                int seconds = atoi(argv[1]);
                int changes = atoi(argv[2]);
                if (seconds < 1 || changes < 0) {
                    err = "Invalid save parameters"; goto loaderr;
                }
                appendServerSaveParams(seconds,changes);
            } else if (argc == 2 && !strcasecmp(argv[1],"")) {
                resetServerSaveParams();
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
            }
        } else if (!strcasecmp(argv[0],"dir") && argc == 2) {
            if (chdir(argv[1]) == -1) {
                redisLog(REDIS_WARNING,"Can't chdir to '%s': %s",
                    argv[1], strerror(errno));
                exit(1);
            }
        } else if (!strcasecmp(argv[0],"loglevel") && argc == 2) {
            if (!strcasecmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG;
            else if (!strcasecmp(argv[1],"verbose")) server.verbosity = REDIS_VERBOSE;
            else if (!strcasecmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE;
            else if (!strcasecmp(argv[1],"warning")) server.verbosity = REDIS_WARNING;
            else {
                err = "Invalid log level. Must be one of debug, notice, warning";
                goto loaderr;
            }
        } else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
            FILE *logfp;

            server.logfile = zstrdup(argv[1]);
            if (!strcasecmp(server.logfile,"stdout")) {
                zfree(server.logfile);
                server.logfile = NULL;
            }
            if (server.logfile) {
                /* Test if we are able to open the file. The server will not
                 * be able to abort just for this problem later... */
                logfp = fopen(server.logfile,"a");
                if (logfp == NULL) {
                    err = sdscatprintf(sdsempty(),
                        "Can't open the log file: %s", strerror(errno));
                    goto loaderr;
                }
160 161 162
                else {
                    setLogFile( server.logfile );
                }
163 164
                fclose(logfp);
            }
J
Jonah H. Harris 已提交
165 166 167 168 169 170 171 172
        } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) {
            if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) {
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
        } else if (!strcasecmp(argv[0],"syslog-ident") && argc == 2) {
            if (server.syslog_ident) zfree(server.syslog_ident);
            server.syslog_ident = zstrdup(argv[1]);
        } else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) {
H
Henry Rawas 已提交
173 174 175 176 177
#ifdef _WIN32
            // Skip error - just ignore Syslog
            // err "Syslog is not supported on Windows platform.";
            // goto loaderr;
#else
J
Jonah H. Harris 已提交
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
            struct {
                const char     *name;
                const int       value;
            } validSyslogFacilities[] = {
                {"user",    LOG_USER},
                {"local0",  LOG_LOCAL0},
                {"local1",  LOG_LOCAL1},
                {"local2",  LOG_LOCAL2},
                {"local3",  LOG_LOCAL3},
                {"local4",  LOG_LOCAL4},
                {"local5",  LOG_LOCAL5},
                {"local6",  LOG_LOCAL6},
                {"local7",  LOG_LOCAL7},
                {NULL, 0}
            };
            int i;

            for (i = 0; validSyslogFacilities[i].name; i++) {
                if (!strcasecmp(validSyslogFacilities[i].name, argv[1])) {
                    server.syslog_facility = validSyslogFacilities[i].value;
                    break;
                }
            }

            if (!validSyslogFacilities[i].name) {
                err = "Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7";
                goto loaderr;
            }
H
Henry Rawas 已提交
206
#endif
207 208 209 210 211 212
        } else if (!strcasecmp(argv[0],"databases") && argc == 2) {
            server.dbnum = atoi(argv[1]);
            if (server.dbnum < 1) {
                err = "Invalid number of databases"; goto loaderr;
            }
        } else if (!strcasecmp(argv[0],"include") && argc == 2) {
213
            loadServerConfig(argv[1],NULL);
214 215
        } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
            server.maxclients = atoi(argv[1]);
216 217 218
            if (server.maxclients < 1) {
                err = "Invalid max clients limit"; goto loaderr;
            }
219 220
        } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
            server.maxmemory = memtoll(argv[1],NULL);
221 222 223 224 225 226 227 228 229 230 231
        } else if (!strcasecmp(argv[0],"maxmemory-policy") && argc == 2) {
            if (!strcasecmp(argv[1],"volatile-lru")) {
                server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
            } else if (!strcasecmp(argv[1],"volatile-random")) {
                server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
            } else if (!strcasecmp(argv[1],"volatile-ttl")) {
                server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
            } else if (!strcasecmp(argv[1],"allkeys-lru")) {
                server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
            } else if (!strcasecmp(argv[1],"allkeys-random")) {
                server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
232 233
            } else if (!strcasecmp(argv[1],"noeviction")) {
                server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
234 235 236 237
            } else {
                err = "Invalid maxmemory policy";
                goto loaderr;
            }
238 239 240 241 242 243
        } else if (!strcasecmp(argv[0],"maxmemory-samples") && argc == 2) {
            server.maxmemory_samples = atoi(argv[1]);
            if (server.maxmemory_samples <= 0) {
                err = "maxmemory-samples must be 1 or greater";
                goto loaderr;
            }
244 245 246
        } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
            server.masterhost = sdsnew(argv[1]);
            server.masterport = atoi(argv[2]);
A
antirez 已提交
247
            server.repl_state = REDIS_REPL_CONNECT;
A
7c6da73  
antirez 已提交
248 249 250 251 252 253 254 255 256 257 258 259
        } else if (!strcasecmp(argv[0],"repl-ping-slave-period") && argc == 2) {
            server.repl_ping_slave_period = atoi(argv[1]);
            if (server.repl_ping_slave_period <= 0) {
                err = "repl-ping-slave-period must be 1 or greater";
                goto loaderr;
            }
        } else if (!strcasecmp(argv[0],"repl-timeout") && argc == 2) {
            server.repl_timeout = atoi(argv[1]);
            if (server.repl_timeout <= 0) {
                err = "repl-timeout must be 1 or greater";
                goto loaderr;
            }
260 261 262 263
        } else if (!strcasecmp(argv[0],"repl-disable-tcp-nodelay") && argc==2) {
            if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) {
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
264 265
        } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) {
        	server.masterauth = zstrdup(argv[1]);
266 267 268 269
        } else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) {
            if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) {
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
270 271 272 273
        } else if (!strcasecmp(argv[0],"slave-read-only") && argc == 2) {
            if ((server.repl_slave_ro = yesnotoi(argv[1])) == -1) {
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
274
        } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
A
antirez 已提交
275
            if ((server.rdb_compression = yesnotoi(argv[1])) == -1) {
276 277
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
278 279 280 281
        } else if (!strcasecmp(argv[0],"rdbchecksum") && argc == 2) {
            if ((server.rdb_checksum = yesnotoi(argv[1])) == -1) {
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
282 283 284 285 286 287 288 289
        } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) {
            if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
        } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
            if ((server.daemonize = yesnotoi(argv[1])) == -1) {
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
290 291 292 293
        } else if (!strcasecmp(argv[0],"hz") && argc == 2) {
            server.hz = atoi(argv[1]);
            if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
            if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
294
        } else if (!strcasecmp(argv[0],"appendonly") && argc == 2) {
295 296 297
            int yes;

            if ((yes = yesnotoi(argv[1])) == -1) {
298 299
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
300
            server.aof_state = yes ? REDIS_AOF_ON : REDIS_AOF_OFF;
301
        } else if (!strcasecmp(argv[0],"appendfilename") && argc == 2) {
302 303
            zfree(server.aof_filename);
            server.aof_filename = zstrdup(argv[1]);
304 305
        } else if (!strcasecmp(argv[0],"no-appendfsync-on-rewrite")
                   && argc == 2) {
306
            if ((server.aof_no_fsync_on_rewrite= yesnotoi(argv[1])) == -1) {
307 308 309 310
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
        } else if (!strcasecmp(argv[0],"appendfsync") && argc == 2) {
            if (!strcasecmp(argv[1],"no")) {
311
                server.aof_fsync = AOF_FSYNC_NO;
312
            } else if (!strcasecmp(argv[1],"always")) {
313
                server.aof_fsync = AOF_FSYNC_ALWAYS;
314
            } else if (!strcasecmp(argv[1],"everysec")) {
315
                server.aof_fsync = AOF_FSYNC_EVERYSEC;
316 317 318 319
            } else {
                err = "argument must be 'no', 'always' or 'everysec'";
                goto loaderr;
            }
320 321 322
        } else if (!strcasecmp(argv[0],"auto-aof-rewrite-percentage") &&
                   argc == 2)
        {
323 324
            server.aof_rewrite_perc = atoi(argv[1]);
            if (server.aof_rewrite_perc < 0) {
325 326 327 328 329 330
                err = "Invalid negative percentage for AOF auto rewrite";
                goto loaderr;
            }
        } else if (!strcasecmp(argv[0],"auto-aof-rewrite-min-size") &&
                   argc == 2)
        {
331
            server.aof_rewrite_min_size = memtoll(argv[1],NULL);
332 333 334 335 336 337
        } else if (!strcasecmp(argv[0],"aof-rewrite-incremental-fsync") &&
                   argc == 2)
        {
            if ((server.aof_rewrite_incremental_fsync = yesnotoi(argv[1])) == -1) {
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
338
        } else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
339 340 341 342
            if (strlen(argv[1]) > REDIS_AUTHPASS_MAX_LEN) {
                err = "Password is longer than REDIS_AUTHPASS_MAX_LEN";
                goto loaderr;
            }
343 344 345 346 347
            server.requirepass = zstrdup(argv[1]);
        } else if (!strcasecmp(argv[0],"pidfile") && argc == 2) {
            zfree(server.pidfile);
            server.pidfile = zstrdup(argv[1]);
        } else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) {
A
antirez 已提交
348 349
            zfree(server.rdb_filename);
            server.rdb_filename = zstrdup(argv[1]);
350 351 352 353
        } else if (!strcasecmp(argv[0],"hash-max-ziplist-entries") && argc == 2) {
            server.hash_max_ziplist_entries = memtoll(argv[1], NULL);
        } else if (!strcasecmp(argv[0],"hash-max-ziplist-value") && argc == 2) {
            server.hash_max_ziplist_value = memtoll(argv[1], NULL);
354 355
        } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
            server.list_max_ziplist_entries = memtoll(argv[1], NULL);
356
        } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
357
            server.list_max_ziplist_value = memtoll(argv[1], NULL);
358
        } else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) {
359
            server.set_max_intset_entries = memtoll(argv[1], NULL);
360 361 362 363
        } else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) {
            server.zset_max_ziplist_entries = memtoll(argv[1], NULL);
        } else if (!strcasecmp(argv[0],"zset-max-ziplist-value") && argc == 2) {
            server.zset_max_ziplist_value = memtoll(argv[1], NULL);
364 365 366 367 368 369 370 371 372
        } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) {
            struct redisCommand *cmd = lookupCommand(argv[1]);
            int retval;

            if (!cmd) {
                err = "No such command in rename-command";
                goto loaderr;
            }

G
guiquanz 已提交
373
            /* If the target command name is the empty string we just
374 375 376 377 378 379 380 381 382 383 384 385 386 387
             * remove it from the command table. */
            retval = dictDelete(server.commands, argv[1]);
            redisAssert(retval == DICT_OK);

            /* Otherwise we re-add the command under a different name. */
            if (sdslen(argv[2]) != 0) {
                sds copy = sdsdup(argv[2]);

                retval = dictAdd(server.commands, copy, cmd);
                if (retval != DICT_OK) {
                    sdsfree(copy);
                    err = "Target command name already exists"; goto loaderr;
                }
            }
A
antirez 已提交
388 389
        } else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) {
            server.lua_time_limit = strtoll(argv[1],NULL,10);
A
antirez 已提交
390 391 392 393 394 395
        } else if (!strcasecmp(argv[0],"slowlog-log-slower-than") &&
                   argc == 2)
        {
            server.slowlog_log_slower_than = strtoll(argv[1],NULL,10);
        } else if (!strcasecmp(argv[0],"slowlog-max-len") && argc == 2) {
            server.slowlog_max_len = strtoll(argv[1],NULL,10);
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
        } else if (!strcasecmp(argv[0],"client-output-buffer-limit") &&
                   argc == 5)
        {
            int class = getClientLimitClassByName(argv[1]);
            unsigned long long hard, soft;
            int soft_seconds;

            if (class == -1) {
                err = "Unrecognized client limit class";
                goto loaderr;
            }
            hard = memtoll(argv[2],NULL);
            soft = memtoll(argv[3],NULL);
            soft_seconds = atoi(argv[4]);
            if (soft_seconds < 0) {
G
guiquanz 已提交
411
                err = "Negative number of seconds in soft limit is invalid";
412 413 414 415 416
                goto loaderr;
            }
            server.client_obuf_limits[class].hard_limit_bytes = hard;
            server.client_obuf_limits[class].soft_limit_bytes = soft;
            server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
417 418 419 420 421
        } else if (!strcasecmp(argv[0],"stop-writes-on-bgsave-error") &&
                   argc == 2) {
            if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) {
                err = "argument must be 'yes' or 'no'"; goto loaderr;
            }
422 423
        } else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) {
            server.slave_priority = atoi(argv[1]);
424 425 426 427 428 429 430 431 432 433 434
        } else if (!strcasecmp(argv[0],"sentinel")) {
            /* argc == 1 is handled by main() as we need to enter the sentinel
             * mode ASAP. */
            if (argc != 1) {
                if (!server.sentinel_mode) {
                    err = "sentinel directive while not in sentinel mode";
                    goto loaderr;
                }
                err = sentinelHandleConfiguration(argv+1,argc-1);
                if (err) goto loaderr;
            }
435 436 437
        } else {
            err = "Bad directive or wrong number of arguments"; goto loaderr;
        }
438
        sdsfreesplitres(argv,argc);
439
    }
440
    sdsfreesplitres(lines,totlines);
441 442 443 444 445
    return;

loaderr:
    fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n");
    fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
446
    fprintf(stderr, ">>> '%s'\n", lines[i]);
447 448 449 450
    fprintf(stderr, "%s\n", err);
    exit(1);
}

451 452 453 454 455
/* Load the server configuration from the specified filename.
 * The function appends the additional configuration directives stored
 * in the 'options' string to the config file before loading.
 *
 * Both filename and options can be NULL, in such a case are considered
G
guiquanz 已提交
456
 * empty. This way loadServerConfig can be used to just load a file or
457 458 459 460 461 462 463 464 465 466 467 468
 * just load a string. */
void loadServerConfig(char *filename, char *options) {
    sds config = sdsempty();
    char buf[REDIS_CONFIGLINE_MAX+1];

    /* Load the file content */
    if (filename) {
        FILE *fp;

        if (filename[0] == '-' && filename[1] == '\0') {
            fp = stdin;
        } else {
H
Henry Rawas 已提交
469 470 471
#ifdef _WIN32
            if ((fp = fopen(filename,"rb")) == NULL) {
#else
472
            if ((fp = fopen(filename,"r")) == NULL) {
H
Henry Rawas 已提交
473
#endif
474 475 476 477 478
                redisLog(REDIS_WARNING,
                    "Fatal error, can't open config file '%s'", filename);
                exit(1);
            }
        }
H
Henry Rawas 已提交
479 480 481 482 483 484 485

        if (fread(buf, 1, 3, fp) == 3) {
            /* if BOM for UTF-8 skip over it, else seek to 0 */
            if (buf[0] != (char)0xEF || buf[1] != (char)0xBB || buf[2] != (char)0xBF) {
                fseek(fp, 0, SEEK_SET);
            }
        }
486 487 488 489 490 491 492 493 494 495 496 497 498
        while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL)
            config = sdscat(config,buf);
        if (fp != stdin) fclose(fp);
    }
    /* Append the additional options */
    if (options) {
        config = sdscat(config,"\n");
        config = sdscat(config,options);
    }
    loadServerConfigFromString(config);
    sdsfree(config);
}

499 500 501 502 503
/*-----------------------------------------------------------------------------
 * CONFIG command for remote configuration
 *----------------------------------------------------------------------------*/

void configSetCommand(redisClient *c) {
504
    robj *o;
505
    long long ll;
506 507
    redisAssertWithInfo(c,c->argv[2],c->argv[2]->encoding == REDIS_ENCODING_RAW);
    redisAssertWithInfo(c,c->argv[2],c->argv[3]->encoding == REDIS_ENCODING_RAW);
508
    o = c->argv[3];
509 510

    if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) {
A
antirez 已提交
511 512
        zfree(server.rdb_filename);
        server.rdb_filename = zstrdup(o->ptr);
513
    } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) {
514
        if (sdslen(o->ptr) > REDIS_AUTHPASS_MAX_LEN) goto badfmt;
515
        zfree(server.requirepass);
516
        server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
517 518
    } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) {
        zfree(server.masterauth);
519
        server.masterauth = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
520 521 522 523
    } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
            ll < 0) goto badfmt;
        server.maxmemory = ll;
524 525 526 527 528 529
        if (server.maxmemory) {
            if (server.maxmemory < zmalloc_used_memory()) {
                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.");
            }
            freeMemoryIfNeeded();
        }
530 531 532 533 534 535
    } else if (!strcasecmp(c->argv[2]->ptr,"hz")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
            ll < 0) goto badfmt;
        server.hz = (int) ll;
        if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
        if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
536 537 538 539 540 541 542 543 544 545 546
    } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) {
        if (!strcasecmp(o->ptr,"volatile-lru")) {
            server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
        } else if (!strcasecmp(o->ptr,"volatile-random")) {
            server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM;
        } else if (!strcasecmp(o->ptr,"volatile-ttl")) {
            server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL;
        } else if (!strcasecmp(o->ptr,"allkeys-lru")) {
            server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU;
        } else if (!strcasecmp(o->ptr,"allkeys-random")) {
            server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM;
547 548
        } else if (!strcasecmp(o->ptr,"noeviction")) {
            server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
549 550 551
        } else {
            goto badfmt;
        }
552 553 554 555
    } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
            ll <= 0) goto badfmt;
        server.maxmemory_samples = ll;
556 557 558 559
    } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
            ll < 0 || ll > LONG_MAX) goto badfmt;
        server.maxidletime = ll;
560 561 562 563
    } else if (!strcasecmp(c->argv[2]->ptr,"tcp-keepalive")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
            ll < 0 || ll > INT_MAX) goto badfmt;
        server.tcpkeepalive = ll;
564 565
    } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) {
        if (!strcasecmp(o->ptr,"no")) {
566
            server.aof_fsync = AOF_FSYNC_NO;
567
        } else if (!strcasecmp(o->ptr,"everysec")) {
568
            server.aof_fsync = AOF_FSYNC_EVERYSEC;
569
        } else if (!strcasecmp(o->ptr,"always")) {
570
            server.aof_fsync = AOF_FSYNC_ALWAYS;
571 572 573 574 575 576 577
        } else {
            goto badfmt;
        }
    } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) {
        int yn = yesnotoi(o->ptr);

        if (yn == -1) goto badfmt;
578
        server.aof_no_fsync_on_rewrite = yn;
579
    } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) {
580 581 582 583 584 585 586 587 588 589
        int enable = yesnotoi(o->ptr);

        if (enable == -1) goto badfmt;
        if (enable == 0 && server.aof_state != REDIS_AOF_OFF) {
            stopAppendOnly();
        } else if (enable && server.aof_state == REDIS_AOF_OFF) {
            if (startAppendOnly() == REDIS_ERR) {
                addReplyError(c,
                    "Unable to turn on AOF. Check server logs.");
                return;
590 591
            }
        }
592 593
    } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
594
        server.aof_rewrite_perc = ll;
595 596
    } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
597
        server.aof_rewrite_min_size = ll;
598 599 600 601 602
    } else if (!strcasecmp(c->argv[2]->ptr,"aof-rewrite-incremental-fsync")) {
        int yn = yesnotoi(o->ptr);

        if (yn == -1) goto badfmt;
        server.aof_rewrite_incremental_fsync = yn;
603 604
    } else if (!strcasecmp(c->argv[2]->ptr,"save")) {
        int vlen, j;
H
Henry Rawas 已提交
605
        sds *v = sdssplitlen(o->ptr,(int)sdslen(o->ptr)," ",1,&vlen);
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636

        /* Perform sanity check before setting the new config:
         * - Even number of args
         * - Seconds >= 1, changes >= 0 */
        if (vlen & 1) {
            sdsfreesplitres(v,vlen);
            goto badfmt;
        }
        for (j = 0; j < vlen; j++) {
            char *eptr;
            long val;

            val = strtoll(v[j], &eptr, 10);
            if (eptr[0] != '\0' ||
                ((j & 1) == 0 && val < 1) ||
                ((j & 1) == 1 && val < 0)) {
                sdsfreesplitres(v,vlen);
                goto badfmt;
            }
        }
        /* Finally set the new config */
        resetServerSaveParams();
        for (j = 0; j < vlen; j += 2) {
            time_t seconds;
            int changes;

            seconds = strtoll(v[j],NULL,10);
            changes = strtoll(v[j+1],NULL,10);
            appendServerSaveParams(seconds, changes);
        }
        sdsfreesplitres(v,vlen);
637 638 639 640 641
    } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) {
        int yn = yesnotoi(o->ptr);

        if (yn == -1) goto badfmt;
        server.repl_serve_stale_data = yn;
642 643 644 645 646
    } else if (!strcasecmp(c->argv[2]->ptr,"slave-read-only")) {
        int yn = yesnotoi(o->ptr);

        if (yn == -1) goto badfmt;
        server.repl_slave_ro = yn;
A
antirez 已提交
647 648 649 650 651
    } else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
        if (chdir((char*)o->ptr) == -1) {
            addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
            return;
        }
652
    } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-entries")) {
653
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
654 655
        server.hash_max_ziplist_entries = ll;
    } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) {
656
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
657
        server.hash_max_ziplist_value = ll;
658 659 660 661 662 663 664 665 666
    } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
        server.list_max_ziplist_entries = ll;
    } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
        server.list_max_ziplist_value = ll;
    } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
        server.set_max_intset_entries = ll;
667 668 669 670 671 672
    } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
        server.zset_max_ziplist_entries = ll;
    } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
        server.zset_max_ziplist_value = ll;
A
antirez 已提交
673 674 675
    } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
        server.lua_time_limit = ll;
A
antirez 已提交
676 677 678 679 680 681
    } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt;
        server.slowlog_log_slower_than = ll;
    } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
        server.slowlog_max_len = (unsigned)ll;
A
antirez 已提交
682 683 684 685 686 687 688 689 690 691 692 693
    } else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) {
        if (!strcasecmp(o->ptr,"warning")) {
            server.verbosity = REDIS_WARNING;
        } else if (!strcasecmp(o->ptr,"notice")) {
            server.verbosity = REDIS_NOTICE;
        } else if (!strcasecmp(o->ptr,"verbose")) {
            server.verbosity = REDIS_VERBOSE;
        } else if (!strcasecmp(o->ptr,"debug")) {
            server.verbosity = REDIS_DEBUG;
        } else {
            goto badfmt;
        }
694
        setLogVerbosityLevel(server.verbosity);
695 696
    } else if (!strcasecmp(c->argv[2]->ptr,"client-output-buffer-limit")) {
        int vlen, j;
H
Henry Rawas 已提交
697
        sds *v = sdssplitlen(o->ptr,(int)sdslen(o->ptr)," ",1,&vlen);
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740

        /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */
        if (vlen % 4) {
            sdsfreesplitres(v,vlen);
            goto badfmt;
        }

        /* Sanity check of single arguments, so that we either refuse the
         * whole configuration string or accept it all, even if a single
         * error in a single client class is present. */
        for (j = 0; j < vlen; j++) {
            char *eptr;
            long val;

            if ((j % 4) == 0) {
                if (getClientLimitClassByName(v[j]) == -1) {
                    sdsfreesplitres(v,vlen);
                    goto badfmt;
                }
            } else {
                val = strtoll(v[j], &eptr, 10);
                if (eptr[0] != '\0' || val < 0) {
                    sdsfreesplitres(v,vlen);
                    goto badfmt;
                }
            }
        }
        /* Finally set the new config */
        for (j = 0; j < vlen; j += 4) {
            int class;
            unsigned long long hard, soft;
            int soft_seconds;

            class = getClientLimitClassByName(v[j]);
            hard = strtoll(v[j+1],NULL,10);
            soft = strtoll(v[j+2],NULL,10);
            soft_seconds = strtoll(v[j+3],NULL,10);

            server.client_obuf_limits[class].hard_limit_bytes = hard;
            server.client_obuf_limits[class].soft_limit_bytes = soft;
            server.client_obuf_limits[class].soft_limit_seconds = soft_seconds;
        }
        sdsfreesplitres(v,vlen);
741 742
    } else if (!strcasecmp(c->argv[2]->ptr,"stop-writes-on-bgsave-error")) {
        int yn = yesnotoi(o->ptr);
743

744 745
        if (yn == -1) goto badfmt;
        server.stop_writes_on_bgsave_err = yn;
746 747 748 749 750 751
    } else if (!strcasecmp(c->argv[2]->ptr,"repl-ping-slave-period")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
        server.repl_ping_slave_period = ll;
    } else if (!strcasecmp(c->argv[2]->ptr,"repl-timeout")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
        server.repl_timeout = ll;
A
antirez 已提交
752 753 754
    } else if (!strcasecmp(c->argv[2]->ptr,"watchdog-period")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
        if (ll)
H
Henry Rawas 已提交
755
            enableWatchdog((int)ll);
A
antirez 已提交
756 757
        else
            disableWatchdog();
758 759 760 761 762 763 764 765 766 767
    } else if (!strcasecmp(c->argv[2]->ptr,"rdbcompression")) {
        int yn = yesnotoi(o->ptr);

        if (yn == -1) goto badfmt;
        server.rdb_compression = yn;
    } else if (!strcasecmp(c->argv[2]->ptr,"rdbchecksum")) {
        int yn = yesnotoi(o->ptr);

        if (yn == -1) goto badfmt;
        server.rdb_checksum = yn;
768 769
    } else if (!strcasecmp(c->argv[2]->ptr,"repl-disable-tcp-nodelay")) {
        int yn = yesnotoi(o->ptr);
770

771 772
        if (yn == -1) goto badfmt;
        server.repl_disable_tcp_nodelay = yn;
773 774 775 776
    } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) {
        if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
            ll <= 0) goto badfmt;
        server.slave_priority = ll;
777
    } else {
778 779
        addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
            (char*)c->argv[2]->ptr);
780 781 782 783 784 785
        return;
    }
    addReply(c,shared.ok);
    return;

badfmt: /* Bad format errors */
786
    addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
787
            (char*)o->ptr,
788
            (char*)c->argv[2]->ptr);
789 790
}

791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
#define config_get_string_field(_name,_var) do { \
    if (stringmatch(pattern,_name,0)) { \
        addReplyBulkCString(c,_name); \
        addReplyBulkCString(c,_var ? _var : ""); \
        matches++; \
    } \
} while(0);

#define config_get_bool_field(_name,_var) do { \
    if (stringmatch(pattern,_name,0)) { \
        addReplyBulkCString(c,_name); \
        addReplyBulkCString(c,_var ? "yes" : "no"); \
        matches++; \
    } \
} while(0);

#define config_get_numerical_field(_name,_var) do { \
    if (stringmatch(pattern,_name,0)) { \
        ll2string(buf,sizeof(buf),_var); \
        addReplyBulkCString(c,_name); \
        addReplyBulkCString(c,buf); \
        matches++; \
    } \
} while(0);

816
void configGetCommand(redisClient *c) {
817
    robj *o = c->argv[2];
818
    void *replylen = addDeferredMultiBulkLength(c);
819
    char *pattern = o->ptr;
820
    char buf[128];
821
    int matches = 0;
822
    redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW);
823

824 825 826
    /* String values */
    config_get_string_field("dbfilename",server.rdb_filename);
    config_get_string_field("requirepass",server.requirepass);
一个手艺人's avatar
一个手艺人 已提交
827
    config_get_string_field("masterauth",server.masterauth);
828 829 830 831 832 833 834 835 836
    config_get_string_field("bind",server.bindaddr);
    config_get_string_field("unixsocket",server.unixsocket);
    config_get_string_field("logfile",server.logfile);
    config_get_string_field("pidfile",server.pidfile);

    /* Numerical values */
    config_get_numerical_field("maxmemory",server.maxmemory);
    config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
    config_get_numerical_field("timeout",server.maxidletime);
837
    config_get_numerical_field("tcp-keepalive",server.tcpkeepalive);
838 839 840 841
    config_get_numerical_field("auto-aof-rewrite-percentage",
            server.aof_rewrite_perc);
    config_get_numerical_field("auto-aof-rewrite-min-size",
            server.aof_rewrite_min_size);
A
antirez 已提交
842 843 844 845
    config_get_numerical_field("hash-max-ziplist-entries",
            server.hash_max_ziplist_entries);
    config_get_numerical_field("hash-max-ziplist-value",
            server.hash_max_ziplist_value);
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
    config_get_numerical_field("list-max-ziplist-entries",
            server.list_max_ziplist_entries);
    config_get_numerical_field("list-max-ziplist-value",
            server.list_max_ziplist_value);
    config_get_numerical_field("set-max-intset-entries",
            server.set_max_intset_entries);
    config_get_numerical_field("zset-max-ziplist-entries",
            server.zset_max_ziplist_entries);
    config_get_numerical_field("zset-max-ziplist-value",
            server.zset_max_ziplist_value);
    config_get_numerical_field("lua-time-limit",server.lua_time_limit);
    config_get_numerical_field("slowlog-log-slower-than",
            server.slowlog_log_slower_than);
    config_get_numerical_field("slowlog-max-len",
            server.slowlog_max_len);
    config_get_numerical_field("port",server.port);
    config_get_numerical_field("databases",server.dbnum);
    config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
    config_get_numerical_field("repl-timeout",server.repl_timeout);
    config_get_numerical_field("maxclients",server.maxclients);
A
antirez 已提交
866
    config_get_numerical_field("watchdog-period",server.watchdog_period);
867
    config_get_numerical_field("slave-priority",server.slave_priority);
868
    config_get_numerical_field("hz",server.hz);
869 870 871 872 873 874

    /* Bool (yes/no) values */
    config_get_bool_field("no-appendfsync-on-rewrite",
            server.aof_no_fsync_on_rewrite);
    config_get_bool_field("slave-serve-stale-data",
            server.repl_serve_stale_data);
875 876
    config_get_bool_field("slave-read-only",
            server.repl_slave_ro);
877 878 879 880
    config_get_bool_field("stop-writes-on-bgsave-error",
            server.stop_writes_on_bgsave_err);
    config_get_bool_field("daemonize", server.daemonize);
    config_get_bool_field("rdbcompression", server.rdb_compression);
881
    config_get_bool_field("rdbchecksum", server.rdb_checksum);
882
    config_get_bool_field("activerehashing", server.activerehashing);
883 884
    config_get_bool_field("repl-disable-tcp-nodelay",
            server.repl_disable_tcp_nodelay);
885 886
    config_get_bool_field("aof-rewrite-incremental-fsync",
            server.aof_rewrite_incremental_fsync);
887 888 889 890 891 892 893 894

    /* Everything we can't handle with macros follows. */

    if (stringmatch(pattern,"appendonly",0)) {
        addReplyBulkCString(c,"appendonly");
        addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes");
        matches++;
    }
A
antirez 已提交
895 896 897
    if (stringmatch(pattern,"dir",0)) {
        char buf[1024];

898
        if (getcwd(buf,sizeof(buf)) == NULL)
A
antirez 已提交
899
            buf[0] = '\0';
900 901 902

        addReplyBulkCString(c,"dir");
        addReplyBulkCString(c,buf);
A
antirez 已提交
903 904
        matches++;
    }
905 906 907 908 909 910 911 912 913
    if (stringmatch(pattern,"maxmemory-policy",0)) {
        char *s;

        switch(server.maxmemory_policy) {
        case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break;
        case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break;
        case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break;
        case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break;
        case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break;
914
        case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break;
915 916 917 918 919 920
        default: s = "unknown"; break; /* too harmless to panic */
        }
        addReplyBulkCString(c,"maxmemory-policy");
        addReplyBulkCString(c,s);
        matches++;
    }
921 922 923
    if (stringmatch(pattern,"appendfsync",0)) {
        char *policy;

924 925 926 927
        switch(server.aof_fsync) {
        case AOF_FSYNC_NO: policy = "no"; break;
        case AOF_FSYNC_EVERYSEC: policy = "everysec"; break;
        case AOF_FSYNC_ALWAYS: policy = "always"; break;
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
        default: policy = "unknown"; break; /* too harmless to panic */
        }
        addReplyBulkCString(c,"appendfsync");
        addReplyBulkCString(c,policy);
        matches++;
    }
    if (stringmatch(pattern,"save",0)) {
        sds buf = sdsempty();
        int j;

        for (j = 0; j < server.saveparamslen; j++) {
            buf = sdscatprintf(buf,"%ld %d",
                    server.saveparams[j].seconds,
                    server.saveparams[j].changes);
            if (j != server.saveparamslen-1)
                buf = sdscatlen(buf," ",1);
        }
        addReplyBulkCString(c,"save");
        addReplyBulkCString(c,buf);
        sdsfree(buf);
        matches++;
    }
A
antirez 已提交
950 951 952 953 954 955 956 957 958 959 960 961 962 963
    if (stringmatch(pattern,"loglevel",0)) {
        char *s;

        switch(server.verbosity) {
        case REDIS_WARNING: s = "warning"; break;
        case REDIS_VERBOSE: s = "verbose"; break;
        case REDIS_NOTICE: s = "notice"; break;
        case REDIS_DEBUG: s = "debug"; break;
        default: s = "unknown"; break; /* too harmless to panic */
        }
        addReplyBulkCString(c,"loglevel");
        addReplyBulkCString(c,s);
        matches++;
    }
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
    if (stringmatch(pattern,"client-output-buffer-limit",0)) {
        sds buf = sdsempty();
        int j;

        for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {
            buf = sdscatprintf(buf,"%s %llu %llu %ld",
                    getClientLimitClassName(j),
                    server.client_obuf_limits[j].hard_limit_bytes,
                    server.client_obuf_limits[j].soft_limit_bytes,
                    (long) server.client_obuf_limits[j].soft_limit_seconds);
            if (j != REDIS_CLIENT_LIMIT_NUM_CLASSES-1)
                buf = sdscatlen(buf," ",1);
        }
        addReplyBulkCString(c,"client-output-buffer-limit");
        addReplyBulkCString(c,buf);
        sdsfree(buf);
        matches++;
    }
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
    if (stringmatch(pattern,"unixsocketperm",0)) {
        char buf[32];
        snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
        addReplyBulkCString(c,"unixsocketperm");
        addReplyBulkCString(c,buf);
        matches++;
    }
    if (stringmatch(pattern,"slaveof",0)) {
        char buf[256];

        addReplyBulkCString(c,"slaveof");
        if (server.masterhost)
            snprintf(buf,sizeof(buf),"%s %d",
                server.masterhost, server.masterport);
        else
            buf[0] = '\0';
        addReplyBulkCString(c,buf);
999 1000
        matches++;
    }
1001
    setDeferredMultiBulkLength(c,replylen,matches*2);
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
}

void configCommand(redisClient *c) {
    if (!strcasecmp(c->argv[1]->ptr,"set")) {
        if (c->argc != 4) goto badarity;
        configSetCommand(c);
    } else if (!strcasecmp(c->argv[1]->ptr,"get")) {
        if (c->argc != 3) goto badarity;
        configGetCommand(c);
    } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) {
        if (c->argc != 2) goto badarity;
1013 1014
        server.stat_keyspace_hits = 0;
        server.stat_keyspace_misses = 0;
1015 1016 1017
        server.stat_numcommands = 0;
        server.stat_numconnections = 0;
        server.stat_expiredkeys = 0;
1018 1019
        server.stat_rejected_conn = 0;
        server.stat_fork_time = 0;
1020
        server.aof_delayed_fsync = 0;
1021
        resetCommandTableStats();
1022 1023
        addReply(c,shared.ok);
    } else {
1024 1025
        addReplyError(c,
            "CONFIG subcommand must be one of GET, SET, RESETSTAT");
1026 1027 1028 1029
    }
    return;

badarity:
1030 1031
    addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s",
        (char*) c->argv[1]->ptr);
1032
}