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