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 if ((server.notify_keyspace_events = yesnotoi(argv[1])) == -1) { 396 err = "argument must be 'yes' or 'no'"; goto loaderr; 397 } 398 } else if (!strcasecmp(argv[0],"sentinel")) { 399 /* argc == 1 is handled by main() as we need to enter the sentinel 400 * mode ASAP. */ 401 if (argc != 1) { 402 if (!server.sentinel_mode) { 403 err = "sentinel directive while not in sentinel mode"; 404 goto loaderr; 405 } 406 err = sentinelHandleConfiguration(argv+1,argc-1); 407 if (err) goto loaderr; 408 } 409 } else { 410 err = "Bad directive or wrong number of arguments"; goto loaderr; 411 } 412 sdsfreesplitres(argv,argc); 413 } 414 sdsfreesplitres(lines,totlines); 415 return; 416 417 loaderr: 418 fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n"); 419 fprintf(stderr, "Reading the configuration file, at line %d\n", linenum); 420 fprintf(stderr, ">>> '%s'\n", lines[i]); 421 fprintf(stderr, "%s\n", err); 422 exit(1); 423 } 424 425 /* Load the server configuration from the specified filename. 426 * The function appends the additional configuration directives stored 427 * in the 'options' string to the config file before loading. 428 * 429 * Both filename and options can be NULL, in such a case are considered 430 * empty. This way loadServerConfig can be used to just load a file or 431 * just load a string. */ 432 void loadServerConfig(char *filename, char *options) { 433 sds config = sdsempty(); 434 char buf[REDIS_CONFIGLINE_MAX+1]; 435 436 /* Load the file content */ 437 if (filename) { 438 FILE *fp; 439 440 if (filename[0] == '-' && filename[1] == '\0') { 441 fp = stdin; 442 } else { 443 if ((fp = fopen(filename,"r")) == NULL) { 444 redisLog(REDIS_WARNING, 445 "Fatal error, can't open config file '%s'", filename); 446 exit(1); 447 } 448 } 449 while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) 450 config = sdscat(config,buf); 451 if (fp != stdin) fclose(fp); 452 } 453 /* Append the additional options */ 454 if (options) { 455 config = sdscat(config,"\n"); 456 config = sdscat(config,options); 457 } 458 loadServerConfigFromString(config); 459 sdsfree(config); 460 } 461 462 /*----------------------------------------------------------------------------- 463 * CONFIG command for remote configuration 464 *----------------------------------------------------------------------------*/ 465 466 void configSetCommand(redisClient *c) { 467 robj *o; 468 long long ll; 469 redisAssertWithInfo(c,c->argv[2],c->argv[2]->encoding == REDIS_ENCODING_RAW); 470 redisAssertWithInfo(c,c->argv[2],c->argv[3]->encoding == REDIS_ENCODING_RAW); 471 o = c->argv[3]; 472 473 if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) { 474 zfree(server.rdb_filename); 475 server.rdb_filename = zstrdup(o->ptr); 476 } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) { 477 if (sdslen(o->ptr) > REDIS_AUTHPASS_MAX_LEN) goto badfmt; 478 zfree(server.requirepass); 479 server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL; 480 } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) { 481 zfree(server.masterauth); 482 server.masterauth = zstrdup(o->ptr); 483 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) { 484 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 485 ll < 0) goto badfmt; 486 server.maxmemory = ll; 487 if (server.maxmemory) { 488 if (server.maxmemory < zmalloc_used_memory()) { 489 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."); 490 } 491 freeMemoryIfNeeded(); 492 } 493 } else if (!strcasecmp(c->argv[2]->ptr,"hz")) { 494 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 495 ll < 0) goto badfmt; 496 server.hz = (int) ll; 497 if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ; 498 if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ; 499 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) { 500 if (!strcasecmp(o->ptr,"volatile-lru")) { 501 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU; 502 } else if (!strcasecmp(o->ptr,"volatile-random")) { 503 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM; 504 } else if (!strcasecmp(o->ptr,"volatile-ttl")) { 505 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL; 506 } else if (!strcasecmp(o->ptr,"allkeys-lru")) { 507 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU; 508 } else if (!strcasecmp(o->ptr,"allkeys-random")) { 509 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM; 510 } else if (!strcasecmp(o->ptr,"noeviction")) { 511 server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION; 512 } else { 513 goto badfmt; 514 } 515 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) { 516 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 517 ll <= 0) goto badfmt; 518 server.maxmemory_samples = ll; 519 } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) { 520 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 521 ll < 0 || ll > LONG_MAX) goto badfmt; 522 server.maxidletime = ll; 523 } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) { 524 if (!strcasecmp(o->ptr,"no")) { 525 server.aof_fsync = AOF_FSYNC_NO; 526 } else if (!strcasecmp(o->ptr,"everysec")) { 527 server.aof_fsync = AOF_FSYNC_EVERYSEC; 528 } else if (!strcasecmp(o->ptr,"always")) { 529 server.aof_fsync = AOF_FSYNC_ALWAYS; 530 } else { 531 goto badfmt; 532 } 533 } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) { 534 int yn = yesnotoi(o->ptr); 535 536 if (yn == -1) goto badfmt; 537 server.aof_no_fsync_on_rewrite = yn; 538 } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) { 539 int enable = yesnotoi(o->ptr); 540 541 if (enable == -1) goto badfmt; 542 if (enable == 0 && server.aof_state != REDIS_AOF_OFF) { 543 stopAppendOnly(); 544 } else if (enable && server.aof_state == REDIS_AOF_OFF) { 545 if (startAppendOnly() == REDIS_ERR) { 546 addReplyError(c, 547 "Unable to turn on AOF. Check server logs."); 548 return; 549 } 550 } 551 } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) { 552 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 553 server.aof_rewrite_perc = ll; 554 } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) { 555 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 556 server.aof_rewrite_min_size = ll; 557 } else if (!strcasecmp(c->argv[2]->ptr,"save")) { 558 int vlen, j; 559 sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen); 560 561 /* Perform sanity check before setting the new config: 562 * - Even number of args 563 * - Seconds >= 1, changes >= 0 */ 564 if (vlen & 1) { 565 sdsfreesplitres(v,vlen); 566 goto badfmt; 567 } 568 for (j = 0; j < vlen; j++) { 569 char *eptr; 570 long val; 571 572 val = strtoll(v[j], &eptr, 10); 573 if (eptr[0] != '\0' || 574 ((j & 1) == 0 && val < 1) || 575 ((j & 1) == 1 && val < 0)) { 576 sdsfreesplitres(v,vlen); 577 goto badfmt; 578 } 579 } 580 /* Finally set the new config */ 581 resetServerSaveParams(); 582 for (j = 0; j < vlen; j += 2) { 583 time_t seconds; 584 int changes; 585 586 seconds = strtoll(v[j],NULL,10); 587 changes = strtoll(v[j+1],NULL,10); 588 appendServerSaveParams(seconds, changes); 589 } 590 sdsfreesplitres(v,vlen); 591 } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) { 592 int yn = yesnotoi(o->ptr); 593 594 if (yn == -1) goto badfmt; 595 server.repl_serve_stale_data = yn; 596 } else if (!strcasecmp(c->argv[2]->ptr,"slave-read-only")) { 597 int yn = yesnotoi(o->ptr); 598 599 if (yn == -1) goto badfmt; 600 server.repl_slave_ro = yn; 601 } else if (!strcasecmp(c->argv[2]->ptr,"dir")) { 602 if (chdir((char*)o->ptr) == -1) { 603 addReplyErrorFormat(c,"Changing directory: %s", strerror(errno)); 604 return; 605 } 606 } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-entries")) { 607 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 608 server.hash_max_ziplist_entries = ll; 609 } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) { 610 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 611 server.hash_max_ziplist_value = ll; 612 } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) { 613 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 614 server.list_max_ziplist_entries = ll; 615 } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) { 616 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 617 server.list_max_ziplist_value = ll; 618 } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) { 619 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 620 server.set_max_intset_entries = ll; 621 } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) { 622 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 623 server.zset_max_ziplist_entries = ll; 624 } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) { 625 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 626 server.zset_max_ziplist_value = ll; 627 } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) { 628 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 629 server.lua_time_limit = ll; 630 } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) { 631 if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt; 632 server.slowlog_log_slower_than = ll; 633 } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) { 634 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 635 server.slowlog_max_len = (unsigned)ll; 636 } else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) { 637 if (!strcasecmp(o->ptr,"warning")) { 638 server.verbosity = REDIS_WARNING; 639 } else if (!strcasecmp(o->ptr,"notice")) { 640 server.verbosity = REDIS_NOTICE; 641 } else if (!strcasecmp(o->ptr,"verbose")) { 642 server.verbosity = REDIS_VERBOSE; 643 } else if (!strcasecmp(o->ptr,"debug")) { 644 server.verbosity = REDIS_DEBUG; 645 } else { 646 goto badfmt; 647 } 648 } else if (!strcasecmp(c->argv[2]->ptr,"client-output-buffer-limit")) { 649 int vlen, j; 650 sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen); 651 652 /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */ 653 if (vlen % 4) { 654 sdsfreesplitres(v,vlen); 655 goto badfmt; 656 } 657 658 /* Sanity check of single arguments, so that we either refuse the 659 * whole configuration string or accept it all, even if a single 660 * error in a single client class is present. */ 661 for (j = 0; j < vlen; j++) { 662 char *eptr; 663 long val; 664 665 if ((j % 4) == 0) { 666 if (getClientLimitClassByName(v[j]) == -1) { 667 sdsfreesplitres(v,vlen); 668 goto badfmt; 669 } 670 } else { 671 val = strtoll(v[j], &eptr, 10); 672 if (eptr[0] != '\0' || val < 0) { 673 sdsfreesplitres(v,vlen); 674 goto badfmt; 675 } 676 } 677 } 678 /* Finally set the new config */ 679 for (j = 0; j < vlen; j += 4) { 680 int class; 681 unsigned long long hard, soft; 682 int soft_seconds; 683 684 class = getClientLimitClassByName(v[j]); 685 hard = strtoll(v[j+1],NULL,10); 686 soft = strtoll(v[j+2],NULL,10); 687 soft_seconds = strtoll(v[j+3],NULL,10); 688 689 server.client_obuf_limits[class].hard_limit_bytes = hard; 690 server.client_obuf_limits[class].soft_limit_bytes = soft; 691 server.client_obuf_limits[class].soft_limit_seconds = soft_seconds; 692 } 693 sdsfreesplitres(v,vlen); 694 } else if (!strcasecmp(c->argv[2]->ptr,"stop-writes-on-bgsave-error")) { 695 int yn = yesnotoi(o->ptr); 696 697 if (yn == -1) goto badfmt; 698 server.stop_writes_on_bgsave_err = yn; 699 } else if (!strcasecmp(c->argv[2]->ptr,"repl-ping-slave-period")) { 700 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt; 701 server.repl_ping_slave_period = ll; 702 } else if (!strcasecmp(c->argv[2]->ptr,"repl-timeout")) { 703 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt; 704 server.repl_timeout = ll; 705 } else if (!strcasecmp(c->argv[2]->ptr,"watchdog-period")) { 706 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 707 if (ll) 708 enableWatchdog(ll); 709 else 710 disableWatchdog(); 711 } else if (!strcasecmp(c->argv[2]->ptr,"rdbcompression")) { 712 int yn = yesnotoi(o->ptr); 713 714 if (yn == -1) goto badfmt; 715 server.rdb_compression = yn; 716 } else if (!strcasecmp(c->argv[2]->ptr,"notify-keyspace-events")) { 717 int yn = yesnotoi(o->ptr); 718 719 if (yn == -1) goto badfmt; 720 server.notify_keyspace_events = yn; 721 } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) { 722 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 723 ll <= 0) goto badfmt; 724 server.slave_priority = ll; 725 } else { 726 addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s", 727 (char*)c->argv[2]->ptr); 728 return; 729 } 730 addReply(c,shared.ok); 731 return; 732 733 badfmt: /* Bad format errors */ 734 addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'", 735 (char*)o->ptr, 736 (char*)c->argv[2]->ptr); 737 } 738 739 #define config_get_string_field(_name,_var) do { \ 740 if (stringmatch(pattern,_name,0)) { \ 741 addReplyBulkCString(c,_name); \ 742 addReplyBulkCString(c,_var ? _var : ""); \ 743 matches++; \ 744 } \ 745 } while(0); 746 747 #define config_get_bool_field(_name,_var) do { \ 748 if (stringmatch(pattern,_name,0)) { \ 749 addReplyBulkCString(c,_name); \ 750 addReplyBulkCString(c,_var ? "yes" : "no"); \ 751 matches++; \ 752 } \ 753 } while(0); 754 755 #define config_get_numerical_field(_name,_var) do { \ 756 if (stringmatch(pattern,_name,0)) { \ 757 ll2string(buf,sizeof(buf),_var); \ 758 addReplyBulkCString(c,_name); \ 759 addReplyBulkCString(c,buf); \ 760 matches++; \ 761 } \ 762 } while(0); 763 764 void configGetCommand(redisClient *c) { 765 robj *o = c->argv[2]; 766 void *replylen = addDeferredMultiBulkLength(c); 767 char *pattern = o->ptr; 768 char buf[128]; 769 int matches = 0; 770 redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW); 771 772 /* String values */ 773 config_get_string_field("dbfilename",server.rdb_filename); 774 config_get_string_field("requirepass",server.requirepass); 775 config_get_string_field("masterauth",server.requirepass); 776 config_get_string_field("bind",server.bindaddr); 777 config_get_string_field("unixsocket",server.unixsocket); 778 config_get_string_field("logfile",server.logfile); 779 config_get_string_field("pidfile",server.pidfile); 780 781 /* Numerical values */ 782 config_get_numerical_field("maxmemory",server.maxmemory); 783 config_get_numerical_field("maxmemory-samples",server.maxmemory_samples); 784 config_get_numerical_field("timeout",server.maxidletime); 785 config_get_numerical_field("auto-aof-rewrite-percentage", 786 server.aof_rewrite_perc); 787 config_get_numerical_field("auto-aof-rewrite-min-size", 788 server.aof_rewrite_min_size); 789 config_get_numerical_field("hash-max-ziplist-entries", 790 server.hash_max_ziplist_entries); 791 config_get_numerical_field("hash-max-ziplist-value", 792 server.hash_max_ziplist_value); 793 config_get_numerical_field("list-max-ziplist-entries", 794 server.list_max_ziplist_entries); 795 config_get_numerical_field("list-max-ziplist-value", 796 server.list_max_ziplist_value); 797 config_get_numerical_field("set-max-intset-entries", 798 server.set_max_intset_entries); 799 config_get_numerical_field("zset-max-ziplist-entries", 800 server.zset_max_ziplist_entries); 801 config_get_numerical_field("zset-max-ziplist-value", 802 server.zset_max_ziplist_value); 803 config_get_numerical_field("lua-time-limit",server.lua_time_limit); 804 config_get_numerical_field("slowlog-log-slower-than", 805 server.slowlog_log_slower_than); 806 config_get_numerical_field("slowlog-max-len", 807 server.slowlog_max_len); 808 config_get_numerical_field("port",server.port); 809 config_get_numerical_field("databases",server.dbnum); 810 config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period); 811 config_get_numerical_field("repl-timeout",server.repl_timeout); 812 config_get_numerical_field("maxclients",server.maxclients); 813 config_get_numerical_field("watchdog-period",server.watchdog_period); 814 config_get_numerical_field("slave-priority",server.slave_priority); 815 config_get_numerical_field("hz",server.hz); 816 817 /* Bool (yes/no) values */ 818 config_get_bool_field("no-appendfsync-on-rewrite", 819 server.aof_no_fsync_on_rewrite); 820 config_get_bool_field("slave-serve-stale-data", 821 server.repl_serve_stale_data); 822 config_get_bool_field("slave-read-only", 823 server.repl_slave_ro); 824 config_get_bool_field("stop-writes-on-bgsave-error", 825 server.stop_writes_on_bgsave_err); 826 config_get_bool_field("daemonize", server.daemonize); 827 config_get_bool_field("rdbcompression", server.rdb_compression); 828 config_get_bool_field("rdbchecksum", server.rdb_checksum); 829 config_get_bool_field("activerehashing", server.activerehashing); 830 config_get_bool_field("notify-keyspace-events", server.notify_keyspace_events); 831 832 /* Everything we can't handle with macros follows. */ 833 834 if (stringmatch(pattern,"appendonly",0)) { 835 addReplyBulkCString(c,"appendonly"); 836 addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes"); 837 matches++; 838 } 839 if (stringmatch(pattern,"dir",0)) { 840 char buf[1024]; 841 842 if (getcwd(buf,sizeof(buf)) == NULL) 843 buf[0] = '\0'; 844 845 addReplyBulkCString(c,"dir"); 846 addReplyBulkCString(c,buf); 847 matches++; 848 } 849 if (stringmatch(pattern,"maxmemory-policy",0)) { 850 char *s; 851 852 switch(server.maxmemory_policy) { 853 case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break; 854 case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break; 855 case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break; 856 case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break; 857 case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break; 858 case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break; 859 default: s = "unknown"; break; /* too harmless to panic */ 860 } 861 addReplyBulkCString(c,"maxmemory-policy"); 862 addReplyBulkCString(c,s); 863 matches++; 864 } 865 if (stringmatch(pattern,"appendfsync",0)) { 866 char *policy; 867 868 switch(server.aof_fsync) { 869 case AOF_FSYNC_NO: policy = "no"; break; 870 case AOF_FSYNC_EVERYSEC: policy = "everysec"; break; 871 case AOF_FSYNC_ALWAYS: policy = "always"; break; 872 default: policy = "unknown"; break; /* too harmless to panic */ 873 } 874 addReplyBulkCString(c,"appendfsync"); 875 addReplyBulkCString(c,policy); 876 matches++; 877 } 878 if (stringmatch(pattern,"save",0)) { 879 sds buf = sdsempty(); 880 int j; 881 882 for (j = 0; j < server.saveparamslen; j++) { 883 buf = sdscatprintf(buf,"%ld %d", 884 server.saveparams[j].seconds, 885 server.saveparams[j].changes); 886 if (j != server.saveparamslen-1) 887 buf = sdscatlen(buf," ",1); 888 } 889 addReplyBulkCString(c,"save"); 890 addReplyBulkCString(c,buf); 891 sdsfree(buf); 892 matches++; 893 } 894 if (stringmatch(pattern,"loglevel",0)) { 895 char *s; 896 897 switch(server.verbosity) { 898 case REDIS_WARNING: s = "warning"; break; 899 case REDIS_VERBOSE: s = "verbose"; break; 900 case REDIS_NOTICE: s = "notice"; break; 901 case REDIS_DEBUG: s = "debug"; break; 902 default: s = "unknown"; break; /* too harmless to panic */ 903 } 904 addReplyBulkCString(c,"loglevel"); 905 addReplyBulkCString(c,s); 906 matches++; 907 } 908 if (stringmatch(pattern,"client-output-buffer-limit",0)) { 909 sds buf = sdsempty(); 910 int j; 911 912 for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) { 913 buf = sdscatprintf(buf,"%s %llu %llu %ld", 914 getClientLimitClassName(j), 915 server.client_obuf_limits[j].hard_limit_bytes, 916 server.client_obuf_limits[j].soft_limit_bytes, 917 (long) server.client_obuf_limits[j].soft_limit_seconds); 918 if (j != REDIS_CLIENT_LIMIT_NUM_CLASSES-1) 919 buf = sdscatlen(buf," ",1); 920 } 921 addReplyBulkCString(c,"client-output-buffer-limit"); 922 addReplyBulkCString(c,buf); 923 sdsfree(buf); 924 matches++; 925 } 926 if (stringmatch(pattern,"unixsocketperm",0)) { 927 char buf[32]; 928 snprintf(buf,sizeof(buf),"%o",server.unixsocketperm); 929 addReplyBulkCString(c,"unixsocketperm"); 930 addReplyBulkCString(c,buf); 931 matches++; 932 } 933 if (stringmatch(pattern,"slaveof",0)) { 934 char buf[256]; 935 936 addReplyBulkCString(c,"slaveof"); 937 if (server.masterhost) 938 snprintf(buf,sizeof(buf),"%s %d", 939 server.masterhost, server.masterport); 940 else 941 buf[0] = '\0'; 942 addReplyBulkCString(c,buf); 943 matches++; 944 } 945 setDeferredMultiBulkLength(c,replylen,matches*2); 946 } 947 948 void configCommand(redisClient *c) { 949 if (!strcasecmp(c->argv[1]->ptr,"set")) { 950 if (c->argc != 4) goto badarity; 951 configSetCommand(c); 952 } else if (!strcasecmp(c->argv[1]->ptr,"get")) { 953 if (c->argc != 3) goto badarity; 954 configGetCommand(c); 955 } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) { 956 if (c->argc != 2) goto badarity; 957 server.stat_keyspace_hits = 0; 958 server.stat_keyspace_misses = 0; 959 server.stat_numcommands = 0; 960 server.stat_numconnections = 0; 961 server.stat_expiredkeys = 0; 962 server.stat_rejected_conn = 0; 963 server.stat_fork_time = 0; 964 server.aof_delayed_fsync = 0; 965 resetCommandTableStats(); 966 addReply(c,shared.ok); 967 } else { 968 addReplyError(c, 969 "CONFIG subcommand must be one of GET, SET, RESETSTAT"); 970 } 971 return; 972 973 badarity: 974 addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s", 975 (char*) c->argv[1]->ptr); 976 } 977