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