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