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 #include "redis.h" 32 33 #include <fcntl.h> 34 #include <sys/stat.h> 35 36 static struct { 37 const char *name; 38 const int value; 39 } validSyslogFacilities[] = { 40 {"user", LOG_USER}, 41 {"local0", LOG_LOCAL0}, 42 {"local1", LOG_LOCAL1}, 43 {"local2", LOG_LOCAL2}, 44 {"local3", LOG_LOCAL3}, 45 {"local4", LOG_LOCAL4}, 46 {"local5", LOG_LOCAL5}, 47 {"local6", LOG_LOCAL6}, 48 {"local7", LOG_LOCAL7}, 49 {NULL, 0} 50 }; 51 52 clientBufferLimitsConfig clientBufferLimitsDefaults[REDIS_CLIENT_LIMIT_NUM_CLASSES] = { 53 {0, 0, 0}, /* normal */ 54 {1024*1024*256, 1024*1024*64, 60}, /* slave */ 55 {1024*1024*32, 1024*1024*8, 60} /* pubsub */ 56 }; 57 58 /*----------------------------------------------------------------------------- 59 * Config file parsing 60 *----------------------------------------------------------------------------*/ 61 62 int yesnotoi(char *s) { 63 if (!strcasecmp(s,"yes")) return 1; 64 else if (!strcasecmp(s,"no")) return 0; 65 else return -1; 66 } 67 68 void appendServerSaveParams(time_t seconds, int changes) { 69 server.saveparams = zrealloc(server.saveparams,sizeof(struct saveparam)*(server.saveparamslen+1)); 70 server.saveparams[server.saveparamslen].seconds = seconds; 71 server.saveparams[server.saveparamslen].changes = changes; 72 server.saveparamslen++; 73 } 74 75 void resetServerSaveParams() { 76 zfree(server.saveparams); 77 server.saveparams = NULL; 78 server.saveparamslen = 0; 79 } 80 81 void loadServerConfigFromString(char *config) { 82 char *err = NULL; 83 int linenum = 0, totlines, i; 84 sds *lines; 85 86 lines = sdssplitlen(config,strlen(config),"\n",1,&totlines); 87 88 for (i = 0; i < totlines; i++) { 89 sds *argv; 90 int argc; 91 92 linenum = i+1; 93 lines[i] = sdstrim(lines[i]," \t\r\n"); 94 95 /* Skip comments and blank lines */ 96 if (lines[i][0] == '#' || lines[i][0] == '\0') continue; 97 98 /* Split into arguments */ 99 argv = sdssplitargs(lines[i],&argc); 100 if (argv == NULL) { 101 err = "Unbalanced quotes in configuration line"; 102 goto loaderr; 103 } 104 105 /* Skip this line if the resulting command vector is empty. */ 106 if (argc == 0) { 107 sdsfreesplitres(argv,argc); 108 return; 109 } 110 sdstolower(argv[0]); 111 112 /* Execute config directives */ 113 if (!strcasecmp(argv[0],"timeout") && argc == 2) { 114 server.maxidletime = atoi(argv[1]); 115 if (server.maxidletime < 0) { 116 err = "Invalid timeout value"; goto loaderr; 117 } 118 } else if (!strcasecmp(argv[0],"tcp-keepalive") && argc == 2) { 119 server.tcpkeepalive = atoi(argv[1]); 120 if (server.tcpkeepalive < 0) { 121 err = "Invalid tcp-keepalive value"; goto loaderr; 122 } 123 } else if (!strcasecmp(argv[0],"port") && argc == 2) { 124 server.port = atoi(argv[1]); 125 if (server.port < 0 || server.port > 65535) { 126 err = "Invalid port"; goto loaderr; 127 } 128 } else if (!strcasecmp(argv[0],"bind") && argc == 2) { 129 server.bindaddr = zstrdup(argv[1]); 130 } else if (!strcasecmp(argv[0],"unixsocket") && argc == 2) { 131 server.unixsocket = zstrdup(argv[1]); 132 } else if (!strcasecmp(argv[0],"unixsocketperm") && argc == 2) { 133 errno = 0; 134 server.unixsocketperm = (mode_t)strtol(argv[1], NULL, 8); 135 if (errno || server.unixsocketperm > 0777) { 136 err = "Invalid socket file permissions"; goto loaderr; 137 } 138 } else if (!strcasecmp(argv[0],"save")) { 139 if (argc == 3) { 140 int seconds = atoi(argv[1]); 141 int changes = atoi(argv[2]); 142 if (seconds < 1 || changes < 0) { 143 err = "Invalid save parameters"; goto loaderr; 144 } 145 appendServerSaveParams(seconds,changes); 146 } else if (argc == 2 && !strcasecmp(argv[1],"")) { 147 resetServerSaveParams(); 148 } 149 } else if (!strcasecmp(argv[0],"dir") && argc == 2) { 150 if (chdir(argv[1]) == -1) { 151 redisLog(REDIS_WARNING,"Can't chdir to '%s': %s", 152 argv[1], strerror(errno)); 153 exit(1); 154 } 155 } else if (!strcasecmp(argv[0],"loglevel") && argc == 2) { 156 if (!strcasecmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG; 157 else if (!strcasecmp(argv[1],"verbose")) server.verbosity = REDIS_VERBOSE; 158 else if (!strcasecmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE; 159 else if (!strcasecmp(argv[1],"warning")) server.verbosity = REDIS_WARNING; 160 else { 161 err = "Invalid log level. Must be one of debug, notice, warning"; 162 goto loaderr; 163 } 164 } else if (!strcasecmp(argv[0],"logfile") && argc == 2) { 165 FILE *logfp; 166 167 zfree(server.logfile); 168 server.logfile = zstrdup(argv[1]); 169 if (server.logfile[0] != '\0') { 170 /* Test if we are able to open the file. The server will not 171 * be able to abort just for this problem later... */ 172 logfp = fopen(server.logfile,"a"); 173 if (logfp == NULL) { 174 err = sdscatprintf(sdsempty(), 175 "Can't open the log file: %s", strerror(errno)); 176 goto loaderr; 177 } 178 fclose(logfp); 179 } 180 } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) { 181 if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) { 182 err = "argument must be 'yes' or 'no'"; goto loaderr; 183 } 184 } else if (!strcasecmp(argv[0],"syslog-ident") && argc == 2) { 185 if (server.syslog_ident) zfree(server.syslog_ident); 186 server.syslog_ident = zstrdup(argv[1]); 187 } else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) { 188 int i; 189 190 for (i = 0; validSyslogFacilities[i].name; i++) { 191 if (!strcasecmp(validSyslogFacilities[i].name, argv[1])) { 192 server.syslog_facility = validSyslogFacilities[i].value; 193 break; 194 } 195 } 196 197 if (!validSyslogFacilities[i].name) { 198 err = "Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7"; 199 goto loaderr; 200 } 201 } else if (!strcasecmp(argv[0],"databases") && argc == 2) { 202 server.dbnum = atoi(argv[1]); 203 if (server.dbnum < 1) { 204 err = "Invalid number of databases"; goto loaderr; 205 } 206 } else if (!strcasecmp(argv[0],"include") && argc == 2) { 207 loadServerConfig(argv[1],NULL); 208 } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) { 209 server.maxclients = atoi(argv[1]); 210 if (server.maxclients < 1) { 211 err = "Invalid max clients limit"; goto loaderr; 212 } 213 } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) { 214 server.maxmemory = memtoll(argv[1],NULL); 215 } else if (!strcasecmp(argv[0],"maxmemory-policy") && argc == 2) { 216 if (!strcasecmp(argv[1],"volatile-lru")) { 217 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU; 218 } else if (!strcasecmp(argv[1],"volatile-random")) { 219 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM; 220 } else if (!strcasecmp(argv[1],"volatile-ttl")) { 221 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL; 222 } else if (!strcasecmp(argv[1],"allkeys-lru")) { 223 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU; 224 } else if (!strcasecmp(argv[1],"allkeys-random")) { 225 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM; 226 } else if (!strcasecmp(argv[1],"noeviction")) { 227 server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION; 228 } else { 229 err = "Invalid maxmemory policy"; 230 goto loaderr; 231 } 232 } else if (!strcasecmp(argv[0],"maxmemory-samples") && argc == 2) { 233 server.maxmemory_samples = atoi(argv[1]); 234 if (server.maxmemory_samples <= 0) { 235 err = "maxmemory-samples must be 1 or greater"; 236 goto loaderr; 237 } 238 } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) { 239 server.masterhost = sdsnew(argv[1]); 240 server.masterport = atoi(argv[2]); 241 server.repl_state = REDIS_REPL_CONNECT; 242 } else if (!strcasecmp(argv[0],"repl-ping-slave-period") && argc == 2) { 243 server.repl_ping_slave_period = atoi(argv[1]); 244 if (server.repl_ping_slave_period <= 0) { 245 err = "repl-ping-slave-period must be 1 or greater"; 246 goto loaderr; 247 } 248 } else if (!strcasecmp(argv[0],"repl-timeout") && argc == 2) { 249 server.repl_timeout = atoi(argv[1]); 250 if (server.repl_timeout <= 0) { 251 err = "repl-timeout must be 1 or greater"; 252 goto loaderr; 253 } 254 } else if (!strcasecmp(argv[0],"repl-disable-tcp-nodelay") && argc==2) { 255 if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) { 256 err = "argument must be 'yes' or 'no'"; goto loaderr; 257 } 258 } else if (!strcasecmp(argv[0],"repl-backlog-size") && argc == 2) { 259 long long size = strtoll(argv[1],NULL,10); 260 if (size <= 0) { 261 err = "repl-backlog-size must be 1 or greater."; 262 goto loaderr; 263 } 264 resizeReplicationBacklog(size); 265 } else if (!strcasecmp(argv[0],"repl-backlog-ttl") && argc == 2) { 266 server.repl_backlog_time_limit = atoi(argv[1]); 267 if (server.repl_backlog_time_limit < 0) { 268 err = "repl-backlog-ttl can't be negative "; 269 goto loaderr; 270 } 271 } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) { 272 server.masterauth = zstrdup(argv[1]); 273 } else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) { 274 if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) { 275 err = "argument must be 'yes' or 'no'"; goto loaderr; 276 } 277 } else if (!strcasecmp(argv[0],"slave-read-only") && argc == 2) { 278 if ((server.repl_slave_ro = yesnotoi(argv[1])) == -1) { 279 err = "argument must be 'yes' or 'no'"; goto loaderr; 280 } 281 } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) { 282 if ((server.rdb_compression = yesnotoi(argv[1])) == -1) { 283 err = "argument must be 'yes' or 'no'"; goto loaderr; 284 } 285 } else if (!strcasecmp(argv[0],"rdbchecksum") && argc == 2) { 286 if ((server.rdb_checksum = yesnotoi(argv[1])) == -1) { 287 err = "argument must be 'yes' or 'no'"; goto loaderr; 288 } 289 } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) { 290 if ((server.activerehashing = yesnotoi(argv[1])) == -1) { 291 err = "argument must be 'yes' or 'no'"; goto loaderr; 292 } 293 } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) { 294 if ((server.daemonize = yesnotoi(argv[1])) == -1) { 295 err = "argument must be 'yes' or 'no'"; goto loaderr; 296 } 297 } else if (!strcasecmp(argv[0],"hz") && argc == 2) { 298 server.hz = atoi(argv[1]); 299 if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ; 300 if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ; 301 } else if (!strcasecmp(argv[0],"appendonly") && argc == 2) { 302 int yes; 303 304 if ((yes = yesnotoi(argv[1])) == -1) { 305 err = "argument must be 'yes' or 'no'"; goto loaderr; 306 } 307 server.aof_state = yes ? REDIS_AOF_ON : REDIS_AOF_OFF; 308 } else if (!strcasecmp(argv[0],"appendfilename") && argc == 2) { 309 zfree(server.aof_filename); 310 server.aof_filename = zstrdup(argv[1]); 311 } else if (!strcasecmp(argv[0],"no-appendfsync-on-rewrite") 312 && argc == 2) { 313 if ((server.aof_no_fsync_on_rewrite= yesnotoi(argv[1])) == -1) { 314 err = "argument must be 'yes' or 'no'"; goto loaderr; 315 } 316 } else if (!strcasecmp(argv[0],"appendfsync") && argc == 2) { 317 if (!strcasecmp(argv[1],"no")) { 318 server.aof_fsync = AOF_FSYNC_NO; 319 } else if (!strcasecmp(argv[1],"always")) { 320 server.aof_fsync = AOF_FSYNC_ALWAYS; 321 } else if (!strcasecmp(argv[1],"everysec")) { 322 server.aof_fsync = AOF_FSYNC_EVERYSEC; 323 } else { 324 err = "argument must be 'no', 'always' or 'everysec'"; 325 goto loaderr; 326 } 327 } else if (!strcasecmp(argv[0],"auto-aof-rewrite-percentage") && 328 argc == 2) 329 { 330 server.aof_rewrite_perc = atoi(argv[1]); 331 if (server.aof_rewrite_perc < 0) { 332 err = "Invalid negative percentage for AOF auto rewrite"; 333 goto loaderr; 334 } 335 } else if (!strcasecmp(argv[0],"auto-aof-rewrite-min-size") && 336 argc == 2) 337 { 338 server.aof_rewrite_min_size = memtoll(argv[1],NULL); 339 } else if (!strcasecmp(argv[0],"aof-rewrite-incremental-fsync") && 340 argc == 2) 341 { 342 if ((server.aof_rewrite_incremental_fsync = yesnotoi(argv[1])) == -1) { 343 err = "argument must be 'yes' or 'no'"; goto loaderr; 344 } 345 } else if (!strcasecmp(argv[0],"requirepass") && argc == 2) { 346 if (strlen(argv[1]) > REDIS_AUTHPASS_MAX_LEN) { 347 err = "Password is longer than REDIS_AUTHPASS_MAX_LEN"; 348 goto loaderr; 349 } 350 server.requirepass = zstrdup(argv[1]); 351 } else if (!strcasecmp(argv[0],"pidfile") && argc == 2) { 352 zfree(server.pidfile); 353 server.pidfile = zstrdup(argv[1]); 354 } else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) { 355 zfree(server.rdb_filename); 356 server.rdb_filename = zstrdup(argv[1]); 357 } else if (!strcasecmp(argv[0],"hash-max-ziplist-entries") && argc == 2) { 358 server.hash_max_ziplist_entries = memtoll(argv[1], NULL); 359 } else if (!strcasecmp(argv[0],"hash-max-ziplist-value") && argc == 2) { 360 server.hash_max_ziplist_value = memtoll(argv[1], NULL); 361 } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){ 362 server.list_max_ziplist_entries = memtoll(argv[1], NULL); 363 } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) { 364 server.list_max_ziplist_value = memtoll(argv[1], NULL); 365 } else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) { 366 server.set_max_intset_entries = memtoll(argv[1], NULL); 367 } else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) { 368 server.zset_max_ziplist_entries = memtoll(argv[1], NULL); 369 } else if (!strcasecmp(argv[0],"zset-max-ziplist-value") && argc == 2) { 370 server.zset_max_ziplist_value = memtoll(argv[1], NULL); 371 } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) { 372 struct redisCommand *cmd = lookupCommand(argv[1]); 373 int retval; 374 375 if (!cmd) { 376 err = "No such command in rename-command"; 377 goto loaderr; 378 } 379 380 /* If the target command name is the empty string we just 381 * remove it from the command table. */ 382 retval = dictDelete(server.commands, argv[1]); 383 redisAssert(retval == DICT_OK); 384 385 /* Otherwise we re-add the command under a different name. */ 386 if (sdslen(argv[2]) != 0) { 387 sds copy = sdsdup(argv[2]); 388 389 retval = dictAdd(server.commands, copy, cmd); 390 if (retval != DICT_OK) { 391 sdsfree(copy); 392 err = "Target command name already exists"; goto loaderr; 393 } 394 } 395 } else if (!strcasecmp(argv[0],"cluster-enabled") && argc == 2) { 396 if ((server.cluster_enabled = yesnotoi(argv[1])) == -1) { 397 err = "argument must be 'yes' or 'no'"; goto loaderr; 398 } 399 } else if (!strcasecmp(argv[0],"cluster-config-file") && argc == 2) { 400 zfree(server.cluster_configfile); 401 server.cluster_configfile = zstrdup(argv[1]); 402 } else if (!strcasecmp(argv[0],"cluster-node-timeout") && argc == 2) { 403 server.cluster_node_timeout = atoi(argv[1]); 404 if (server.cluster_node_timeout <= 0) { 405 err = "cluster node timeout must be 1 or greater"; goto loaderr; 406 } 407 } else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) { 408 server.lua_time_limit = strtoll(argv[1],NULL,10); 409 } else if (!strcasecmp(argv[0],"slowlog-log-slower-than") && 410 argc == 2) 411 { 412 server.slowlog_log_slower_than = strtoll(argv[1],NULL,10); 413 } else if (!strcasecmp(argv[0],"slowlog-max-len") && argc == 2) { 414 server.slowlog_max_len = strtoll(argv[1],NULL,10); 415 } else if (!strcasecmp(argv[0],"client-output-buffer-limit") && 416 argc == 5) 417 { 418 int class = getClientLimitClassByName(argv[1]); 419 unsigned long long hard, soft; 420 int soft_seconds; 421 422 if (class == -1) { 423 err = "Unrecognized client limit class"; 424 goto loaderr; 425 } 426 hard = memtoll(argv[2],NULL); 427 soft = memtoll(argv[3],NULL); 428 soft_seconds = atoi(argv[4]); 429 if (soft_seconds < 0) { 430 err = "Negative number of seconds in soft limit is invalid"; 431 goto loaderr; 432 } 433 server.client_obuf_limits[class].hard_limit_bytes = hard; 434 server.client_obuf_limits[class].soft_limit_bytes = soft; 435 server.client_obuf_limits[class].soft_limit_seconds = soft_seconds; 436 } else if (!strcasecmp(argv[0],"stop-writes-on-bgsave-error") && 437 argc == 2) { 438 if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) { 439 err = "argument must be 'yes' or 'no'"; goto loaderr; 440 } 441 } else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) { 442 server.slave_priority = atoi(argv[1]); 443 } else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) { 444 int flags = keyspaceEventsStringToFlags(argv[1]); 445 446 if (flags == -1) { 447 err = "Invalid event class character. Use 'g$lshzxeA'."; 448 goto loaderr; 449 } 450 server.notify_keyspace_events = flags; 451 } else if (!strcasecmp(argv[0],"sentinel")) { 452 /* argc == 1 is handled by main() as we need to enter the sentinel 453 * mode ASAP. */ 454 if (argc != 1) { 455 if (!server.sentinel_mode) { 456 err = "sentinel directive while not in sentinel mode"; 457 goto loaderr; 458 } 459 err = sentinelHandleConfiguration(argv+1,argc-1); 460 if (err) goto loaderr; 461 } 462 } else { 463 err = "Bad directive or wrong number of arguments"; goto loaderr; 464 } 465 sdsfreesplitres(argv,argc); 466 } 467 sdsfreesplitres(lines,totlines); 468 469 /* Sanity checks. */ 470 if (server.cluster_enabled && server.masterhost) { 471 err = "slaveof directive not allowed in cluster mode"; 472 goto loaderr; 473 } 474 return; 475 476 loaderr: 477 fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n"); 478 fprintf(stderr, "Reading the configuration file, at line %d\n", linenum); 479 fprintf(stderr, ">>> '%s'\n", lines[i]); 480 fprintf(stderr, "%s\n", err); 481 exit(1); 482 } 483 484 /* Load the server configuration from the specified filename. 485 * The function appends the additional configuration directives stored 486 * in the 'options' string to the config file before loading. 487 * 488 * Both filename and options can be NULL, in such a case are considered 489 * empty. This way loadServerConfig can be used to just load a file or 490 * just load a string. */ 491 void loadServerConfig(char *filename, char *options) { 492 sds config = sdsempty(); 493 char buf[REDIS_CONFIGLINE_MAX+1]; 494 495 /* Load the file content */ 496 if (filename) { 497 FILE *fp; 498 499 if (filename[0] == '-' && filename[1] == '\0') { 500 fp = stdin; 501 } else { 502 if ((fp = fopen(filename,"r")) == NULL) { 503 redisLog(REDIS_WARNING, 504 "Fatal error, can't open config file '%s'", filename); 505 exit(1); 506 } 507 } 508 while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) 509 config = sdscat(config,buf); 510 if (fp != stdin) fclose(fp); 511 } 512 /* Append the additional options */ 513 if (options) { 514 config = sdscat(config,"\n"); 515 config = sdscat(config,options); 516 } 517 loadServerConfigFromString(config); 518 sdsfree(config); 519 } 520 521 /*----------------------------------------------------------------------------- 522 * CONFIG SET implementation 523 *----------------------------------------------------------------------------*/ 524 525 void configSetCommand(redisClient *c) { 526 robj *o; 527 long long ll; 528 redisAssertWithInfo(c,c->argv[2],c->argv[2]->encoding == REDIS_ENCODING_RAW); 529 redisAssertWithInfo(c,c->argv[2],c->argv[3]->encoding == REDIS_ENCODING_RAW); 530 o = c->argv[3]; 531 532 if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) { 533 zfree(server.rdb_filename); 534 server.rdb_filename = zstrdup(o->ptr); 535 } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) { 536 if (sdslen(o->ptr) > REDIS_AUTHPASS_MAX_LEN) goto badfmt; 537 zfree(server.requirepass); 538 server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL; 539 } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) { 540 zfree(server.masterauth); 541 server.masterauth = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL; 542 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) { 543 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 544 ll < 0) goto badfmt; 545 server.maxmemory = ll; 546 if (server.maxmemory) { 547 if (server.maxmemory < zmalloc_used_memory()) { 548 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."); 549 } 550 freeMemoryIfNeeded(); 551 } 552 } else if (!strcasecmp(c->argv[2]->ptr,"hz")) { 553 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 554 ll < 0) goto badfmt; 555 server.hz = (int) ll; 556 if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ; 557 if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ; 558 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) { 559 if (!strcasecmp(o->ptr,"volatile-lru")) { 560 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU; 561 } else if (!strcasecmp(o->ptr,"volatile-random")) { 562 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM; 563 } else if (!strcasecmp(o->ptr,"volatile-ttl")) { 564 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL; 565 } else if (!strcasecmp(o->ptr,"allkeys-lru")) { 566 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU; 567 } else if (!strcasecmp(o->ptr,"allkeys-random")) { 568 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM; 569 } else if (!strcasecmp(o->ptr,"noeviction")) { 570 server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION; 571 } else { 572 goto badfmt; 573 } 574 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) { 575 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 576 ll <= 0) goto badfmt; 577 server.maxmemory_samples = ll; 578 } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) { 579 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 580 ll < 0 || ll > LONG_MAX) goto badfmt; 581 server.maxidletime = ll; 582 } else if (!strcasecmp(c->argv[2]->ptr,"tcp-keepalive")) { 583 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 584 ll < 0 || ll > INT_MAX) goto badfmt; 585 server.tcpkeepalive = ll; 586 } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) { 587 if (!strcasecmp(o->ptr,"no")) { 588 server.aof_fsync = AOF_FSYNC_NO; 589 } else if (!strcasecmp(o->ptr,"everysec")) { 590 server.aof_fsync = AOF_FSYNC_EVERYSEC; 591 } else if (!strcasecmp(o->ptr,"always")) { 592 server.aof_fsync = AOF_FSYNC_ALWAYS; 593 } else { 594 goto badfmt; 595 } 596 } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) { 597 int yn = yesnotoi(o->ptr); 598 599 if (yn == -1) goto badfmt; 600 server.aof_no_fsync_on_rewrite = yn; 601 } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) { 602 int enable = yesnotoi(o->ptr); 603 604 if (enable == -1) goto badfmt; 605 if (enable == 0 && server.aof_state != REDIS_AOF_OFF) { 606 stopAppendOnly(); 607 } else if (enable && server.aof_state == REDIS_AOF_OFF) { 608 if (startAppendOnly() == REDIS_ERR) { 609 addReplyError(c, 610 "Unable to turn on AOF. Check server logs."); 611 return; 612 } 613 } 614 } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) { 615 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 616 server.aof_rewrite_perc = ll; 617 } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) { 618 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 619 server.aof_rewrite_min_size = ll; 620 } else if (!strcasecmp(c->argv[2]->ptr,"aof-rewrite-incremental-fsync")) { 621 int yn = yesnotoi(o->ptr); 622 623 if (yn == -1) goto badfmt; 624 server.aof_rewrite_incremental_fsync = yn; 625 } else if (!strcasecmp(c->argv[2]->ptr,"save")) { 626 int vlen, j; 627 sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen); 628 629 /* Perform sanity check before setting the new config: 630 * - Even number of args 631 * - Seconds >= 1, changes >= 0 */ 632 if (vlen & 1) { 633 sdsfreesplitres(v,vlen); 634 goto badfmt; 635 } 636 for (j = 0; j < vlen; j++) { 637 char *eptr; 638 long val; 639 640 val = strtoll(v[j], &eptr, 10); 641 if (eptr[0] != '\0' || 642 ((j & 1) == 0 && val < 1) || 643 ((j & 1) == 1 && val < 0)) { 644 sdsfreesplitres(v,vlen); 645 goto badfmt; 646 } 647 } 648 /* Finally set the new config */ 649 resetServerSaveParams(); 650 for (j = 0; j < vlen; j += 2) { 651 time_t seconds; 652 int changes; 653 654 seconds = strtoll(v[j],NULL,10); 655 changes = strtoll(v[j+1],NULL,10); 656 appendServerSaveParams(seconds, changes); 657 } 658 sdsfreesplitres(v,vlen); 659 } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) { 660 int yn = yesnotoi(o->ptr); 661 662 if (yn == -1) goto badfmt; 663 server.repl_serve_stale_data = yn; 664 } else if (!strcasecmp(c->argv[2]->ptr,"slave-read-only")) { 665 int yn = yesnotoi(o->ptr); 666 667 if (yn == -1) goto badfmt; 668 server.repl_slave_ro = yn; 669 } else if (!strcasecmp(c->argv[2]->ptr,"dir")) { 670 if (chdir((char*)o->ptr) == -1) { 671 addReplyErrorFormat(c,"Changing directory: %s", strerror(errno)); 672 return; 673 } 674 } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-entries")) { 675 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 676 server.hash_max_ziplist_entries = ll; 677 } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) { 678 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 679 server.hash_max_ziplist_value = ll; 680 } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) { 681 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 682 server.list_max_ziplist_entries = ll; 683 } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) { 684 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 685 server.list_max_ziplist_value = ll; 686 } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) { 687 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 688 server.set_max_intset_entries = ll; 689 } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) { 690 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 691 server.zset_max_ziplist_entries = ll; 692 } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) { 693 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 694 server.zset_max_ziplist_value = ll; 695 } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) { 696 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 697 server.lua_time_limit = ll; 698 } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) { 699 if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt; 700 server.slowlog_log_slower_than = ll; 701 } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) { 702 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 703 server.slowlog_max_len = (unsigned)ll; 704 } else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) { 705 if (!strcasecmp(o->ptr,"warning")) { 706 server.verbosity = REDIS_WARNING; 707 } else if (!strcasecmp(o->ptr,"notice")) { 708 server.verbosity = REDIS_NOTICE; 709 } else if (!strcasecmp(o->ptr,"verbose")) { 710 server.verbosity = REDIS_VERBOSE; 711 } else if (!strcasecmp(o->ptr,"debug")) { 712 server.verbosity = REDIS_DEBUG; 713 } else { 714 goto badfmt; 715 } 716 } else if (!strcasecmp(c->argv[2]->ptr,"client-output-buffer-limit")) { 717 int vlen, j; 718 sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen); 719 720 /* We need a multiple of 4: <class> <hard> <soft> <soft_seconds> */ 721 if (vlen % 4) { 722 sdsfreesplitres(v,vlen); 723 goto badfmt; 724 } 725 726 /* Sanity check of single arguments, so that we either refuse the 727 * whole configuration string or accept it all, even if a single 728 * error in a single client class is present. */ 729 for (j = 0; j < vlen; j++) { 730 char *eptr; 731 long val; 732 733 if ((j % 4) == 0) { 734 if (getClientLimitClassByName(v[j]) == -1) { 735 sdsfreesplitres(v,vlen); 736 goto badfmt; 737 } 738 } else { 739 val = strtoll(v[j], &eptr, 10); 740 if (eptr[0] != '\0' || val < 0) { 741 sdsfreesplitres(v,vlen); 742 goto badfmt; 743 } 744 } 745 } 746 /* Finally set the new config */ 747 for (j = 0; j < vlen; j += 4) { 748 int class; 749 unsigned long long hard, soft; 750 int soft_seconds; 751 752 class = getClientLimitClassByName(v[j]); 753 hard = strtoll(v[j+1],NULL,10); 754 soft = strtoll(v[j+2],NULL,10); 755 soft_seconds = strtoll(v[j+3],NULL,10); 756 757 server.client_obuf_limits[class].hard_limit_bytes = hard; 758 server.client_obuf_limits[class].soft_limit_bytes = soft; 759 server.client_obuf_limits[class].soft_limit_seconds = soft_seconds; 760 } 761 sdsfreesplitres(v,vlen); 762 } else if (!strcasecmp(c->argv[2]->ptr,"stop-writes-on-bgsave-error")) { 763 int yn = yesnotoi(o->ptr); 764 765 if (yn == -1) goto badfmt; 766 server.stop_writes_on_bgsave_err = yn; 767 } else if (!strcasecmp(c->argv[2]->ptr,"repl-ping-slave-period")) { 768 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt; 769 server.repl_ping_slave_period = ll; 770 } else if (!strcasecmp(c->argv[2]->ptr,"repl-timeout")) { 771 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt; 772 server.repl_timeout = ll; 773 } else if (!strcasecmp(c->argv[2]->ptr,"repl-backlog-size")) { 774 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt; 775 resizeReplicationBacklog(ll); 776 } else if (!strcasecmp(c->argv[2]->ptr,"repl-backlog-ttl")) { 777 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 778 server.repl_backlog_time_limit = ll; 779 } else if (!strcasecmp(c->argv[2]->ptr,"watchdog-period")) { 780 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 781 if (ll) 782 enableWatchdog(ll); 783 else 784 disableWatchdog(); 785 } else if (!strcasecmp(c->argv[2]->ptr,"rdbcompression")) { 786 int yn = yesnotoi(o->ptr); 787 788 if (yn == -1) goto badfmt; 789 server.rdb_compression = yn; 790 } else if (!strcasecmp(c->argv[2]->ptr,"notify-keyspace-events")) { 791 int flags = keyspaceEventsStringToFlags(o->ptr); 792 793 if (flags == -1) goto badfmt; 794 server.notify_keyspace_events = flags; 795 } else if (!strcasecmp(c->argv[2]->ptr,"repl-disable-tcp-nodelay")) { 796 int yn = yesnotoi(o->ptr); 797 798 if (yn == -1) goto badfmt; 799 server.repl_disable_tcp_nodelay = yn; 800 } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) { 801 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 802 ll <= 0) goto badfmt; 803 server.slave_priority = ll; 804 } else if (!strcasecmp(c->argv[2]->ptr,"cluster-node-timeout")) { 805 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 806 ll <= 0) goto badfmt; 807 server.cluster_node_timeout = ll; 808 } else { 809 addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s", 810 (char*)c->argv[2]->ptr); 811 return; 812 } 813 addReply(c,shared.ok); 814 return; 815 816 badfmt: /* Bad format errors */ 817 addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'", 818 (char*)o->ptr, 819 (char*)c->argv[2]->ptr); 820 } 821 822 /*----------------------------------------------------------------------------- 823 * CONFIG GET implementation 824 *----------------------------------------------------------------------------*/ 825 826 #define config_get_string_field(_name,_var) do { \ 827 if (stringmatch(pattern,_name,0)) { \ 828 addReplyBulkCString(c,_name); \ 829 addReplyBulkCString(c,_var ? _var : ""); \ 830 matches++; \ 831 } \ 832 } while(0); 833 834 #define config_get_bool_field(_name,_var) do { \ 835 if (stringmatch(pattern,_name,0)) { \ 836 addReplyBulkCString(c,_name); \ 837 addReplyBulkCString(c,_var ? "yes" : "no"); \ 838 matches++; \ 839 } \ 840 } while(0); 841 842 #define config_get_numerical_field(_name,_var) do { \ 843 if (stringmatch(pattern,_name,0)) { \ 844 ll2string(buf,sizeof(buf),_var); \ 845 addReplyBulkCString(c,_name); \ 846 addReplyBulkCString(c,buf); \ 847 matches++; \ 848 } \ 849 } while(0); 850 851 void configGetCommand(redisClient *c) { 852 robj *o = c->argv[2]; 853 void *replylen = addDeferredMultiBulkLength(c); 854 char *pattern = o->ptr; 855 char buf[128]; 856 int matches = 0; 857 redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW); 858 859 /* String values */ 860 config_get_string_field("dbfilename",server.rdb_filename); 861 config_get_string_field("requirepass",server.requirepass); 862 config_get_string_field("masterauth",server.masterauth); 863 config_get_string_field("bind",server.bindaddr); 864 config_get_string_field("unixsocket",server.unixsocket); 865 config_get_string_field("logfile",server.logfile); 866 config_get_string_field("pidfile",server.pidfile); 867 868 /* Numerical values */ 869 config_get_numerical_field("maxmemory",server.maxmemory); 870 config_get_numerical_field("maxmemory-samples",server.maxmemory_samples); 871 config_get_numerical_field("timeout",server.maxidletime); 872 config_get_numerical_field("tcp-keepalive",server.tcpkeepalive); 873 config_get_numerical_field("auto-aof-rewrite-percentage", 874 server.aof_rewrite_perc); 875 config_get_numerical_field("auto-aof-rewrite-min-size", 876 server.aof_rewrite_min_size); 877 config_get_numerical_field("hash-max-ziplist-entries", 878 server.hash_max_ziplist_entries); 879 config_get_numerical_field("hash-max-ziplist-value", 880 server.hash_max_ziplist_value); 881 config_get_numerical_field("list-max-ziplist-entries", 882 server.list_max_ziplist_entries); 883 config_get_numerical_field("list-max-ziplist-value", 884 server.list_max_ziplist_value); 885 config_get_numerical_field("set-max-intset-entries", 886 server.set_max_intset_entries); 887 config_get_numerical_field("zset-max-ziplist-entries", 888 server.zset_max_ziplist_entries); 889 config_get_numerical_field("zset-max-ziplist-value", 890 server.zset_max_ziplist_value); 891 config_get_numerical_field("lua-time-limit",server.lua_time_limit); 892 config_get_numerical_field("slowlog-log-slower-than", 893 server.slowlog_log_slower_than); 894 config_get_numerical_field("slowlog-max-len", 895 server.slowlog_max_len); 896 config_get_numerical_field("port",server.port); 897 config_get_numerical_field("databases",server.dbnum); 898 config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period); 899 config_get_numerical_field("repl-timeout",server.repl_timeout); 900 config_get_numerical_field("repl-backlog-size",server.repl_backlog_size); 901 config_get_numerical_field("repl-backlog-ttl",server.repl_backlog_time_limit); 902 config_get_numerical_field("maxclients",server.maxclients); 903 config_get_numerical_field("watchdog-period",server.watchdog_period); 904 config_get_numerical_field("slave-priority",server.slave_priority); 905 config_get_numerical_field("hz",server.hz); 906 config_get_numerical_field("cluster-node-timeout",server.cluster_node_timeout); 907 908 /* Bool (yes/no) values */ 909 config_get_bool_field("no-appendfsync-on-rewrite", 910 server.aof_no_fsync_on_rewrite); 911 config_get_bool_field("slave-serve-stale-data", 912 server.repl_serve_stale_data); 913 config_get_bool_field("slave-read-only", 914 server.repl_slave_ro); 915 config_get_bool_field("stop-writes-on-bgsave-error", 916 server.stop_writes_on_bgsave_err); 917 config_get_bool_field("daemonize", server.daemonize); 918 config_get_bool_field("rdbcompression", server.rdb_compression); 919 config_get_bool_field("rdbchecksum", server.rdb_checksum); 920 config_get_bool_field("activerehashing", server.activerehashing); 921 config_get_bool_field("repl-disable-tcp-nodelay", 922 server.repl_disable_tcp_nodelay); 923 config_get_bool_field("aof-rewrite-incremental-fsync", 924 server.aof_rewrite_incremental_fsync); 925 926 /* Everything we can't handle with macros follows. */ 927 928 if (stringmatch(pattern,"appendonly",0)) { 929 addReplyBulkCString(c,"appendonly"); 930 addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes"); 931 matches++; 932 } 933 if (stringmatch(pattern,"dir",0)) { 934 char buf[1024]; 935 936 if (getcwd(buf,sizeof(buf)) == NULL) 937 buf[0] = '\0'; 938 939 addReplyBulkCString(c,"dir"); 940 addReplyBulkCString(c,buf); 941 matches++; 942 } 943 if (stringmatch(pattern,"maxmemory-policy",0)) { 944 char *s; 945 946 switch(server.maxmemory_policy) { 947 case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break; 948 case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break; 949 case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break; 950 case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break; 951 case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break; 952 case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break; 953 default: s = "unknown"; break; /* too harmless to panic */ 954 } 955 addReplyBulkCString(c,"maxmemory-policy"); 956 addReplyBulkCString(c,s); 957 matches++; 958 } 959 if (stringmatch(pattern,"appendfsync",0)) { 960 char *policy; 961 962 switch(server.aof_fsync) { 963 case AOF_FSYNC_NO: policy = "no"; break; 964 case AOF_FSYNC_EVERYSEC: policy = "everysec"; break; 965 case AOF_FSYNC_ALWAYS: policy = "always"; break; 966 default: policy = "unknown"; break; /* too harmless to panic */ 967 } 968 addReplyBulkCString(c,"appendfsync"); 969 addReplyBulkCString(c,policy); 970 matches++; 971 } 972 if (stringmatch(pattern,"save",0)) { 973 sds buf = sdsempty(); 974 int j; 975 976 for (j = 0; j < server.saveparamslen; j++) { 977 buf = sdscatprintf(buf,"%ld %d", 978 server.saveparams[j].seconds, 979 server.saveparams[j].changes); 980 if (j != server.saveparamslen-1) 981 buf = sdscatlen(buf," ",1); 982 } 983 addReplyBulkCString(c,"save"); 984 addReplyBulkCString(c,buf); 985 sdsfree(buf); 986 matches++; 987 } 988 if (stringmatch(pattern,"loglevel",0)) { 989 char *s; 990 991 switch(server.verbosity) { 992 case REDIS_WARNING: s = "warning"; break; 993 case REDIS_VERBOSE: s = "verbose"; break; 994 case REDIS_NOTICE: s = "notice"; break; 995 case REDIS_DEBUG: s = "debug"; break; 996 default: s = "unknown"; break; /* too harmless to panic */ 997 } 998 addReplyBulkCString(c,"loglevel"); 999 addReplyBulkCString(c,s); 1000 matches++; 1001 } 1002 if (stringmatch(pattern,"client-output-buffer-limit",0)) { 1003 sds buf = sdsempty(); 1004 int j; 1005 1006 for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) { 1007 buf = sdscatprintf(buf,"%s %llu %llu %ld", 1008 getClientLimitClassName(j), 1009 server.client_obuf_limits[j].hard_limit_bytes, 1010 server.client_obuf_limits[j].soft_limit_bytes, 1011 (long) server.client_obuf_limits[j].soft_limit_seconds); 1012 if (j != REDIS_CLIENT_LIMIT_NUM_CLASSES-1) 1013 buf = sdscatlen(buf," ",1); 1014 } 1015 addReplyBulkCString(c,"client-output-buffer-limit"); 1016 addReplyBulkCString(c,buf); 1017 sdsfree(buf); 1018 matches++; 1019 } 1020 if (stringmatch(pattern,"unixsocketperm",0)) { 1021 char buf[32]; 1022 snprintf(buf,sizeof(buf),"%o",server.unixsocketperm); 1023 addReplyBulkCString(c,"unixsocketperm"); 1024 addReplyBulkCString(c,buf); 1025 matches++; 1026 } 1027 if (stringmatch(pattern,"slaveof",0)) { 1028 char buf[256]; 1029 1030 addReplyBulkCString(c,"slaveof"); 1031 if (server.masterhost) 1032 snprintf(buf,sizeof(buf),"%s %d", 1033 server.masterhost, server.masterport); 1034 else 1035 buf[0] = '\0'; 1036 addReplyBulkCString(c,buf); 1037 matches++; 1038 } 1039 if (stringmatch(pattern,"notify-keyspace-events",0)) { 1040 robj *flagsobj = createObject(REDIS_STRING, 1041 keyspaceEventsFlagsToString(server.notify_keyspace_events)); 1042 1043 addReplyBulkCString(c,"notify-keyspace-events"); 1044 addReplyBulk(c,flagsobj); 1045 decrRefCount(flagsobj); 1046 matches++; 1047 } 1048 setDeferredMultiBulkLength(c,replylen,matches*2); 1049 } 1050 1051 /*----------------------------------------------------------------------------- 1052 * CONFIG REWRITE implementation 1053 *----------------------------------------------------------------------------*/ 1054 1055 /* IGNORE: 1056 * 1057 * rename-command 1058 * include 1059 * 1060 * Special handling: 1061 * 1062 * notify-keyspace-events 1063 * client-output-buffer-limit 1064 * save 1065 * appendonly 1066 * appendfsync 1067 * dir 1068 * maxmemory-policy 1069 * loglevel 1070 * unixsocketperm 1071 * slaveof 1072 * 1073 * Type of config directives: 1074 * 1075 * CUSTOM 1076 * VERBATIM 1077 * YESNO 1078 * L 1079 * LL 1080 * 1081 */ 1082 1083 /* We use the following dictionary type to store where a configuration 1084 * option is mentioned in the old configuration file, so it's 1085 * like "maxmemory" -> list of line numbers (first line is zero). */ 1086 unsigned int dictSdsHash(const void *key); 1087 int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2); 1088 void dictSdsDestructor(void *privdata, void *val); 1089 void dictListDestructor(void *privdata, void *val); 1090 1091 dictType optionToLineDictType = { 1092 dictSdsHash, /* hash function */ 1093 NULL, /* key dup */ 1094 NULL, /* val dup */ 1095 dictSdsKeyCompare, /* key compare */ 1096 dictSdsDestructor, /* key destructor */ 1097 dictListDestructor /* val destructor */ 1098 }; 1099 1100 /* The config rewrite state. */ 1101 struct rewriteConfigState { 1102 dict *option_to_line; /* Option -> list of config file lines map */ 1103 int numlines; /* Number of lines in current config */ 1104 sds *lines; /* Current lines as an array of sds strings */ 1105 int has_tail; /* True if we already added directives that were 1106 not present in the original config file. */ 1107 }; 1108 1109 /* Append the new line to the current configuration state. */ 1110 void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) { 1111 state->lines = zrealloc(state->lines, sizeof(char*) * (state->numlines+1)); 1112 state->lines[state->numlines++] = line; 1113 } 1114 1115 /* Populate the option -> list of line numbers map. */ 1116 void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) { 1117 list *l = dictFetchValue(state->option_to_line,option); 1118 1119 if (l == NULL) { 1120 l = listCreate(); 1121 dictAdd(state->option_to_line,sdsdup(option),l); 1122 } 1123 listAddNodeTail(l,(void*)(long)linenum); 1124 } 1125 1126 /* Read the old file, split it into lines to populate a newly created 1127 * config rewrite state, and return it to the caller. 1128 * 1129 * If it is impossible to read the old file, NULL is returned. 1130 * If the old file does not exist at all, an empty state is returned. */ 1131 struct rewriteConfigState *rewriteConfigReadOldFile(char *path) { 1132 FILE *fp = fopen(path,"r"); 1133 struct rewriteConfigState *state = zmalloc(sizeof(*state)); 1134 char buf[REDIS_CONFIGLINE_MAX+1]; 1135 int linenum = -1; 1136 1137 if (fp == NULL && errno != ENOENT) return NULL; 1138 1139 state->option_to_line = dictCreate(&optionToLineDictType,NULL); 1140 state->numlines = 0; 1141 state->lines = NULL; 1142 state->has_tail = 0; 1143 if (fp == NULL) return state; 1144 1145 /* Read the old file line by line, populate the state. */ 1146 while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) { 1147 int argc; 1148 sds *argv; 1149 sds line = sdstrim(sdsnew(buf),"\r\n\t "); 1150 1151 linenum++; /* Zero based, so we init at -1 */ 1152 1153 /* Handle comments and empty lines. */ 1154 if (line[0] == '#' || line[0] == '\0') { 1155 rewriteConfigAppendLine(state,line); 1156 continue; 1157 } 1158 1159 /* Not a comment, split into arguments. */ 1160 argv = sdssplitargs(line,&argc); 1161 if (argv == NULL) { 1162 /* Apparently the line is unparsable for some reason, for 1163 * instance it may have unbalanced quotes. Load it as a 1164 * comment. */ 1165 sds aux = sdsnew("# ??? "); 1166 aux = sdscatsds(aux,line); 1167 sdsfree(line); 1168 rewriteConfigAppendLine(state,aux); 1169 continue; 1170 } 1171 1172 sdstolower(argv[0]); /* We only want lowercase config directives. */ 1173 1174 /* Now we populate the state according to the content of this line. 1175 * Append the line and populate the option -> line numbers map. */ 1176 rewriteConfigAppendLine(state,line); 1177 rewriteConfigAddLineNumberToOption(state,argv[0],linenum); 1178 1179 sdsfreesplitres(argv,argc); 1180 } 1181 fclose(fp); 1182 return state; 1183 } 1184 1185 /* Rewrite the specified configuration option with the new "line". 1186 * It progressively uses lines of the file that were already used for the same 1187 * configuraiton option in the old version of the file, removing that line from 1188 * the map of options -> line numbers. 1189 * 1190 * If there are lines associated with a given configuration option and 1191 * "force" is non-zero, the line is appended to the configuration file. 1192 * Usually "force" is true when an option has not its default value, so it 1193 * must be rewritten even if not present previously. 1194 * 1195 * The first time a line is appended into a configuration file, a comment 1196 * is added to show that starting from that point the config file was generated 1197 * by CONFIG REWRITE. 1198 * 1199 * "line" is either used, or freed, so the caller does not need to free it 1200 * in any way. */ 1201 void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force) { 1202 sds o = sdsnew(option); 1203 list *l = dictFetchValue(state->option_to_line,o); 1204 1205 if (!l && !force) { 1206 /* Option not used previously, and we are not forced to use it. */ 1207 sdsfree(line); 1208 sdsfree(o); 1209 return; 1210 } 1211 1212 if (l) { 1213 listNode *ln = listFirst(l); 1214 int linenum = (long) ln->value; 1215 1216 /* There are still lines in the old configuration file we can reuse 1217 * for this option. Replace the line with the new one. */ 1218 listDelNode(l,ln); 1219 if (listLength(l) == 0) dictDelete(state->option_to_line,o); 1220 sdsfree(state->lines[linenum]); 1221 state->lines[linenum] = line; 1222 } else { 1223 /* Append a new line. */ 1224 if (!state->has_tail) { 1225 rewriteConfigAppendLine(state, 1226 sdsnew("# Generated by CONFIG REWRITE")); 1227 state->has_tail = 1; 1228 } 1229 rewriteConfigAppendLine(state,line); 1230 } 1231 sdsfree(o); 1232 } 1233 1234 /* Rewrite a simple "option-name <bytes>" configuration option. */ 1235 void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) { 1236 int force = value != defvalue; 1237 /* TODO: check if we can write it using MB, GB, or other suffixes. */ 1238 sds line = sdscatprintf(sdsempty(),"%s %lld",option,value); 1239 1240 rewriteConfigRewriteLine(state,option,line,force); 1241 } 1242 1243 /* Rewrite a yes/no option. */ 1244 void rewriteConfigYesNoOption(struct rewriteConfigState *state, char *option, int value, int defvalue) { 1245 int force = value != defvalue; 1246 sds line = sdscatprintf(sdsempty(),"%s %s",option, 1247 value ? "yes" : "no"); 1248 1249 rewriteConfigRewriteLine(state,option,line,force); 1250 } 1251 1252 /* Rewrite a string option. */ 1253 void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, char *value, char *defvalue) { 1254 int force = 1; 1255 sds line; 1256 1257 /* String options set to NULL need to be not present at all in the 1258 * configuration file to be set to NULL again at the next reboot. */ 1259 if (value == NULL) return; 1260 1261 /* Compare the strings as sds strings to have a binary safe comparison. */ 1262 if (defvalue && strcmp(value,defvalue) == 0) force = 0; 1263 1264 line = sdsnew(option); 1265 line = sdscatlen(line, " ", 1); 1266 line = sdscatrepr(line, value, strlen(value)); 1267 1268 rewriteConfigRewriteLine(state,option,line,force); 1269 } 1270 1271 /* Rewrite a numerical (long long range) option. */ 1272 void rewriteConfigNumericalOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) { 1273 int force = value != defvalue; 1274 sds line = sdscatprintf(sdsempty(),"%s %lld",option,value); 1275 1276 rewriteConfigRewriteLine(state,option,line,force); 1277 } 1278 1279 /* Rewrite a octal option. */ 1280 void rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, int value, int defvalue) { 1281 int force = value != defvalue; 1282 sds line = sdscatprintf(sdsempty(),"%s %o",option,value); 1283 1284 rewriteConfigRewriteLine(state,option,line,force); 1285 } 1286 1287 /* Rewrite an enumeration option, after the "value" every enum/value pair 1288 * is specified, terminated by NULL. After NULL the default value is 1289 * specified. See how the function is used for more information. */ 1290 void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int value, ...) { 1291 va_list ap; 1292 char *enum_name, *matching_name; 1293 int enum_val, def_val, force; 1294 sds line; 1295 1296 va_start(ap, value); 1297 while(1) { 1298 enum_name = va_arg(ap,char*); 1299 enum_val = va_arg(ap,int); 1300 if (enum_name == NULL) { 1301 def_val = enum_val; 1302 break; 1303 } 1304 if (value == enum_val) matching_name = enum_name; 1305 } 1306 va_end(ap); 1307 1308 force = value != def_val; 1309 line = sdscatprintf(sdsempty(),"%s %s",option,matching_name); 1310 rewriteConfigRewriteLine(state,option,line,force); 1311 } 1312 1313 /* Rewrite the syslog-fability option. */ 1314 void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) { 1315 int value = server.syslog_facility, j; 1316 int force = value != LOG_LOCAL0; 1317 char *name, *option = "syslog-facility"; 1318 sds line; 1319 1320 for (j = 0; validSyslogFacilities[j].name; j++) { 1321 if (validSyslogFacilities[j].value == value) { 1322 name = (char*) validSyslogFacilities[j].name; 1323 break; 1324 } 1325 } 1326 line = sdscatprintf(sdsempty(),"%s %s",option,name); 1327 rewriteConfigRewriteLine(state,option,line,force); 1328 } 1329 1330 /* Rewrite the save option. */ 1331 void rewriteConfigSaveOption(struct rewriteConfigState *state) { 1332 int j; 1333 sds line; 1334 1335 /* Note that if there are no save parameters at all, all the current 1336 * config line with "save" will be detected as orphaned and deleted, 1337 * resulting into no RDB persistence as expected. */ 1338 for (j = 0; j < server.saveparamslen; j++) { 1339 line = sdscatprintf(sdsempty(),"save %ld %d", 1340 server.saveparams[j].seconds, server.saveparams[j].changes); 1341 rewriteConfigRewriteLine(state,"save",line,1); 1342 } 1343 } 1344 1345 /* Rewrite the dir option, always using absolute paths.*/ 1346 void rewriteConfigDirOption(struct rewriteConfigState *state) { 1347 char cwd[1024]; 1348 1349 if (getcwd(cwd,sizeof(cwd)) == NULL) return; /* no rewrite on error. */ 1350 rewriteConfigStringOption(state,"dir",cwd,NULL); 1351 } 1352 1353 /* Rewrite the slaveof option. */ 1354 void rewriteConfigSlaveofOption(struct rewriteConfigState *state) { 1355 char *option = "slaveof"; 1356 sds line; 1357 1358 /* If this is a master, we want all the slaveof config options 1359 * in the file to be removed. */ 1360 if (server.masterhost == NULL) return; 1361 line = sdscatprintf(sdsempty(),"%s %s %d", option, 1362 server.masterhost, server.masterport); 1363 rewriteConfigRewriteLine(state,option,line,1); 1364 } 1365 1366 /* Rewrite the appendonly option. */ 1367 void rewriteConfigAppendonlyOption(struct rewriteConfigState *state) { 1368 int force = server.aof_state != REDIS_AOF_OFF; 1369 char *option = "appendonly"; 1370 sds line; 1371 1372 line = sdscatprintf(sdsempty(),"%s %s", option, 1373 (server.aof_state == REDIS_AOF_OFF) ? "no" : "yes"); 1374 rewriteConfigRewriteLine(state,option,line,force); 1375 } 1376 1377 /* Rewrite the notify-keyspace-events option. */ 1378 void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) { 1379 int force = server.notify_keyspace_events != 0; 1380 char *option = "notify-keyspace-events"; 1381 sds line, flags; 1382 1383 flags = keyspaceEventsFlagsToString(server.notify_keyspace_events); 1384 line = sdscatprintf(sdsempty(),"%s %s", option, flags); 1385 sdsfree(flags); 1386 rewriteConfigRewriteLine(state,option,line,force); 1387 } 1388 1389 /* Rewrite the client-output-buffer-limit option. */ 1390 void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) { 1391 int j; 1392 char *option = "client-output-buffer-limit"; 1393 1394 for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) { 1395 int force = (server.client_obuf_limits[j].hard_limit_bytes != 1396 clientBufferLimitsDefaults[j].hard_limit_bytes) || 1397 (server.client_obuf_limits[j].soft_limit_bytes != 1398 clientBufferLimitsDefaults[j].soft_limit_bytes) || 1399 (server.client_obuf_limits[j].soft_limit_seconds != 1400 clientBufferLimitsDefaults[j].soft_limit_seconds); 1401 sds line; 1402 1403 line = sdscatprintf(sdsempty(),"%s %s %llu %llu %ld", 1404 option, 1405 getClientLimitClassName(j), 1406 server.client_obuf_limits[j].hard_limit_bytes, 1407 server.client_obuf_limits[j].soft_limit_bytes, 1408 (long) server.client_obuf_limits[j].soft_limit_seconds); 1409 rewriteConfigRewriteLine(state,option,line,force); 1410 } 1411 } 1412 1413 /* Glue together the configuration lines in the current configuration 1414 * rewrite state into a single string, stripping multiple empty lines. */ 1415 sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) { 1416 sds content = sdsempty(); 1417 int j, was_empty = 0; 1418 1419 for (j = 0; j < state->numlines; j++) { 1420 /* Every cluster of empty lines is turned into a single empty line. */ 1421 if (sdslen(state->lines[j]) == 0) { 1422 if (was_empty) continue; 1423 was_empty = 1; 1424 } else { 1425 was_empty = 0; 1426 } 1427 content = sdscatsds(content,state->lines[j]); 1428 content = sdscatlen(content,"\n",1); 1429 } 1430 return content; 1431 } 1432 1433 /* Free the configuration rewrite state. */ 1434 void rewriteConfigReleaseState(struct rewriteConfigState *state) { 1435 sdsfreesplitres(state->lines,state->numlines); 1436 dictRelease(state->option_to_line); 1437 zfree(state); 1438 } 1439 1440 /* At the end of the rewrite process the state contains the remaining 1441 * map between "option name" => "lines in the original config file". 1442 * Lines used by the rewrite process were removed by the function 1443 * rewriteConfigRewriteLine(), all the other lines are "orphaned" and 1444 * should be replaced by empty lines. 1445 * 1446 * This function does just this, iterating all the option names and 1447 * blanking all the lines still associated. */ 1448 void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) { 1449 dictIterator *di = dictGetIterator(state->option_to_line); 1450 dictEntry *de; 1451 1452 while((de = dictNext(di)) != NULL) { 1453 list *l = dictGetVal(de); 1454 1455 while(listLength(l)) { 1456 listNode *ln = listFirst(l); 1457 int linenum = (long) ln->value; 1458 1459 sdsfree(state->lines[linenum]); 1460 state->lines[linenum] = sdsempty(); 1461 listDelNode(l,ln); 1462 } 1463 } 1464 dictReleaseIterator(di); 1465 } 1466 1467 /* This function overwrites the old configuration file with the new content. 1468 * 1469 * 1) The old file length is obtained. 1470 * 2) If the new content is smaller, padding is added. 1471 * 3) A single write(2) call is used to replace the content of the file. 1472 * 4) Later the file is truncated to the length of the new content. 1473 * 1474 * This way we are sure the file is left in a consistent state even if the 1475 * process is stopped between any of the four operations. 1476 * 1477 * The function returns 0 on success, otherwise -1 is returned and errno 1478 * set accordingly. */ 1479 int rewriteConfigOverwriteFile(char *configfile, sds content) { 1480 int retval = 0; 1481 int fd = open(configfile,O_RDWR|O_CREAT,0644); 1482 int content_size = sdslen(content), padding = 0; 1483 struct stat sb; 1484 sds content_padded; 1485 1486 /* 1) Open the old file (or create a new one if it does not 1487 * exist), get the size. */ 1488 if (fd == -1) return -1; /* errno set by open(). */ 1489 if (fstat(fd,&sb) == -1) { 1490 close(fd); 1491 return -1; /* errno set by fstat(). */ 1492 } 1493 1494 /* 2) Pad the content at least match the old file size. */ 1495 content_padded = sdsdup(content); 1496 if (content_size < sb.st_size) { 1497 /* If the old file was bigger, pad the content with 1498 * a newline plus as many "#" chars as required. */ 1499 padding = sb.st_size - content_size; 1500 content_padded = sdsgrowzero(content_padded,sb.st_size); 1501 content_padded[content_size] = '\n'; 1502 memset(content_padded+content_size+1,'#',padding-1); 1503 } 1504 1505 /* 3) Write the new content using a single write(2). */ 1506 if (write(fd,content_padded,strlen(content_padded)) == -1) { 1507 retval = -1; 1508 goto cleanup; 1509 } 1510 1511 /* 4) Truncate the file to the right length if we used padding. */ 1512 if (padding) { 1513 if (ftruncate(fd,content_size) == -1) { 1514 /* Non critical error... */ 1515 } 1516 } 1517 1518 cleanup: 1519 sdsfree(content_padded); 1520 close(fd); 1521 return retval; 1522 } 1523 1524 /* Rewrite the configuration file at "path". 1525 * If the configuration file already exists, we try at best to retain comments 1526 * and overall structure. 1527 * 1528 * Configuration parameters that are at their default value, unless already 1529 * explicitly included in the old configuration file, are not rewritten. 1530 * 1531 * On error -1 is returned and errno is set accordingly, otherwise 0. */ 1532 int rewriteConfig(char *path) { 1533 struct rewriteConfigState *state; 1534 sds newcontent; 1535 int retval; 1536 1537 /* Step 1: read the old config into our rewrite state. */ 1538 if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1; 1539 1540 /* Step 2: rewrite every single option, replacing or appending it inside 1541 * the rewrite state. */ 1542 1543 /* TODO: Turn every default into a define, use it also in 1544 * initServerConfig(). */ 1545 rewriteConfigYesNoOption(state,"daemonize",server.daemonize,0); 1546 rewriteConfigStringOption(state,"pidfile",server.pidfile,REDIS_DEFAULT_PID_FILE); 1547 rewriteConfigNumericalOption(state,"port",server.port,REDIS_SERVERPORT); 1548 rewriteConfigStringOption(state,"bindaddr",server.bindaddr,NULL); 1549 rewriteConfigStringOption(state,"unixsocket",server.unixsocket,NULL); 1550 rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,REDIS_DEFAULT_UNIX_SOCKET_PERM); 1551 rewriteConfigNumericalOption(state,"timeout",server.maxidletime,REDIS_MAXIDLETIME); 1552 rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,REDIS_DEFAULT_TCP_KEEPALIVE); 1553 rewriteConfigEnumOption(state,"loglevel",server.verbosity, 1554 "debug", REDIS_DEBUG, 1555 "verbose", REDIS_VERBOSE, 1556 "notice", REDIS_NOTICE, 1557 "warning", REDIS_WARNING, 1558 NULL, REDIS_DEFAULT_VERBOSITY); 1559 rewriteConfigStringOption(state,"logfile",server.logfile,REDIS_DEFAULT_LOGFILE); 1560 rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,REDIS_DEFAULT_SYSLOG_ENABLED); 1561 rewriteConfigStringOption(state,"syslog-ident",server.syslog_ident,REDIS_DEFAULT_SYSLOG_IDENT); 1562 rewriteConfigSyslogfacilityOption(state); 1563 rewriteConfigSaveOption(state); 1564 rewriteConfigNumericalOption(state,"databases",server.dbnum,REDIS_DEFAULT_DBNUM); 1565 rewriteConfigYesNoOption(state,"stop-writes-on-bgsave-error",server.stop_writes_on_bgsave_err,REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR); 1566 rewriteConfigYesNoOption(state,"rdbcompression",server.rdb_compression,REDIS_DEFAULT_RDB_COMPRESSION); 1567 rewriteConfigYesNoOption(state,"rdbchecksum",server.rdb_checksum,REDIS_DEFAULT_RDB_CHECKSUM); 1568 rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,REDIS_DEFAULT_RDB_FILENAME); 1569 rewriteConfigDirOption(state); 1570 rewriteConfigSlaveofOption(state); 1571 rewriteConfigStringOption(state,"masterauth",server.masterauth,NULL); 1572 rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA); 1573 rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,REDIS_DEFAULT_SLAVE_READ_ONLY); 1574 rewriteConfigNumericalOption(state,"repl-ping-slave-period",server.repl_ping_slave_period,REDIS_REPL_PING_SLAVE_PERIOD); 1575 rewriteConfigNumericalOption(state,"repl-timeout",server.repl_timeout,REDIS_REPL_TIMEOUT); 1576 rewriteConfigNumericalOption(state,"repl-backlog-size",server.repl_backlog_size,REDIS_DEFAULT_REPL_BACKLOG_SIZE); 1577 rewriteConfigBytesOption(state,"repl-backlog-ttl",server.repl_backlog_time_limit,REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT); 1578 rewriteConfigBytesOption(state,"repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay,REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY); 1579 rewriteConfigNumericalOption(state,"slave-priority",server.slave_priority,REDIS_DEFAULT_SLAVE_PRIORITY); 1580 rewriteConfigStringOption(state,"requirepass",server.requirepass,NULL); 1581 rewriteConfigNumericalOption(state,"maxclients",server.maxclients,REDIS_MAX_CLIENTS); 1582 rewriteConfigBytesOption(state,"maxmemory",server.maxmemory,REDIS_DEFAULT_MAXMEMORY); 1583 rewriteConfigEnumOption(state,"maxmemory-policy",server.maxmemory_policy, 1584 "volatile-lru", REDIS_MAXMEMORY_VOLATILE_LRU, 1585 "allkeys-lru", REDIS_MAXMEMORY_ALLKEYS_LRU, 1586 "volatile-random", REDIS_MAXMEMORY_VOLATILE_RANDOM, 1587 "allkeys-random", REDIS_MAXMEMORY_ALLKEYS_RANDOM, 1588 "volatile-ttl", REDIS_MAXMEMORY_VOLATILE_TTL, 1589 "noeviction", REDIS_MAXMEMORY_NO_EVICTION, 1590 NULL, REDIS_DEFAULT_MAXMEMORY_POLICY); 1591 rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,REDIS_DEFAULT_MAXMEMORY_SAMPLES); 1592 rewriteConfigAppendonlyOption(state); 1593 rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync, 1594 "eveysec", AOF_FSYNC_EVERYSEC, 1595 "always", AOF_FSYNC_ALWAYS, 1596 "no", AOF_FSYNC_NO, 1597 NULL, REDIS_DEFAULT_AOF_FSYNC); 1598 rewriteConfigYesNoOption(state,"no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite,REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE); 1599 rewriteConfigNumericalOption(state,"auto-aof-rewrite-percentage",server.aof_rewrite_perc,REDIS_AOF_REWRITE_PERC); 1600 rewriteConfigBytesOption(state,"auto-aof-rewrite-min-size",server.aof_rewrite_min_size,REDIS_AOF_REWRITE_MIN_SIZE); 1601 rewriteConfigNumericalOption(state,"lua-time-limit",server.lua_time_limit,REDIS_LUA_TIME_LIMIT); 1602 rewriteConfigYesNoOption(state,"cluster-enabled",server.cluster_enabled,0); 1603 rewriteConfigStringOption(state,"cluster-config-file",server.cluster_configfile,REDIS_DEFAULT_CLUSTER_CONFIG_FILE); 1604 rewriteConfigNumericalOption(state,"cluster-node-timeout",server.cluster_node_timeout,REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT); 1605 rewriteConfigNumericalOption(state,"slowlog-log-slower-than",server.slowlog_log_slower_than,REDIS_SLOWLOG_LOG_SLOWER_THAN); 1606 rewriteConfigNumericalOption(state,"slowlog-max-len",server.slowlog_max_len,REDIS_SLOWLOG_MAX_LEN); 1607 rewriteConfigNotifykeyspaceeventsOption(state); 1608 rewriteConfigNumericalOption(state,"hash-max-ziplist-entries",server.hash_max_ziplist_entries,REDIS_HASH_MAX_ZIPLIST_ENTRIES); 1609 rewriteConfigNumericalOption(state,"hash-max-ziplist-value",server.hash_max_ziplist_value,REDIS_HASH_MAX_ZIPLIST_VALUE); 1610 rewriteConfigNumericalOption(state,"list-max-ziplist-entries",server.list_max_ziplist_entries,REDIS_LIST_MAX_ZIPLIST_ENTRIES); 1611 rewriteConfigNumericalOption(state,"list-max-ziplist-value",server.list_max_ziplist_value,REDIS_LIST_MAX_ZIPLIST_VALUE); 1612 rewriteConfigNumericalOption(state,"set-max-intset-entries",server.set_max_intset_entries,REDIS_SET_MAX_INTSET_ENTRIES); 1613 rewriteConfigNumericalOption(state,"zset-max-ziplist-entries",server.zset_max_ziplist_entries,REDIS_ZSET_MAX_ZIPLIST_ENTRIES); 1614 rewriteConfigNumericalOption(state,"zset-max-ziplist-value",server.zset_max_ziplist_value,REDIS_ZSET_MAX_ZIPLIST_VALUE); 1615 rewriteConfigYesNoOption(state,"active-rehashing",server.activerehashing,REDIS_DEFAULT_ACTIVE_REHASHING); 1616 rewriteConfigClientoutputbufferlimitOption(state); 1617 rewriteConfigNumericalOption(state,"hz",server.hz,REDIS_DEFAULT_HZ); 1618 rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC); 1619 1620 /* Step 3: remove all the orphaned lines in the old file, that is, lines 1621 * that were used by a config option and are no longer used, like in case 1622 * of multiple "save" options or duplicated options. */ 1623 rewriteConfigRemoveOrphaned(state); 1624 1625 /* Step 4: generate a new configuration file from the modified state 1626 * and write it into the original file. */ 1627 newcontent = rewriteConfigGetContentFromState(state); 1628 retval = rewriteConfigOverwriteFile(server.configfile,newcontent); 1629 1630 sdsfree(newcontent); 1631 rewriteConfigReleaseState(state); 1632 return retval; 1633 } 1634 1635 /*----------------------------------------------------------------------------- 1636 * CONFIG command entry point 1637 *----------------------------------------------------------------------------*/ 1638 1639 void configCommand(redisClient *c) { 1640 if (!strcasecmp(c->argv[1]->ptr,"set")) { 1641 if (c->argc != 4) goto badarity; 1642 configSetCommand(c); 1643 } else if (!strcasecmp(c->argv[1]->ptr,"get")) { 1644 if (c->argc != 3) goto badarity; 1645 configGetCommand(c); 1646 } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) { 1647 if (c->argc != 2) goto badarity; 1648 server.stat_keyspace_hits = 0; 1649 server.stat_keyspace_misses = 0; 1650 server.stat_numcommands = 0; 1651 server.stat_numconnections = 0; 1652 server.stat_expiredkeys = 0; 1653 server.stat_rejected_conn = 0; 1654 server.stat_fork_time = 0; 1655 server.aof_delayed_fsync = 0; 1656 resetCommandTableStats(); 1657 addReply(c,shared.ok); 1658 } else if (!strcasecmp(c->argv[1]->ptr,"rewrite")) { 1659 if (c->argc != 2) goto badarity; 1660 if (server.configfile == NULL) { 1661 addReplyError(c,"The server is running without a config file"); 1662 return; 1663 } 1664 if (rewriteConfig(server.configfile) == -1) { 1665 addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno)); 1666 } else { 1667 addReply(c,shared.ok); 1668 } 1669 } else { 1670 addReplyError(c, 1671 "CONFIG subcommand must be one of GET, SET, RESETSTAT"); 1672 } 1673 return; 1674 1675 badarity: 1676 addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s", 1677 (char*) c->argv[1]->ptr); 1678 } 1679