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