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