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