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") && argc == 3) { 69 int seconds = atoi(argv[1]); 70 int changes = atoi(argv[2]); 71 if (seconds < 1 || changes < 0) { 72 err = "Invalid save parameters"; goto loaderr; 73 } 74 appendServerSaveParams(seconds,changes); 75 } else if (!strcasecmp(argv[0],"dir") && argc == 2) { 76 if (chdir(argv[1]) == -1) { 77 redisLog(REDIS_WARNING,"Can't chdir to '%s': %s", 78 argv[1], strerror(errno)); 79 exit(1); 80 } 81 } else if (!strcasecmp(argv[0],"loglevel") && argc == 2) { 82 if (!strcasecmp(argv[1],"debug")) server.verbosity = REDIS_DEBUG; 83 else if (!strcasecmp(argv[1],"verbose")) server.verbosity = REDIS_VERBOSE; 84 else if (!strcasecmp(argv[1],"notice")) server.verbosity = REDIS_NOTICE; 85 else if (!strcasecmp(argv[1],"warning")) server.verbosity = REDIS_WARNING; 86 else { 87 err = "Invalid log level. Must be one of debug, notice, warning"; 88 goto loaderr; 89 } 90 } else if (!strcasecmp(argv[0],"logfile") && argc == 2) { 91 FILE *logfp; 92 93 server.logfile = zstrdup(argv[1]); 94 if (!strcasecmp(server.logfile,"stdout")) { 95 zfree(server.logfile); 96 server.logfile = NULL; 97 } 98 if (server.logfile) { 99 /* Test if we are able to open the file. The server will not 100 * be able to abort just for this problem later... */ 101 logfp = fopen(server.logfile,"a"); 102 if (logfp == NULL) { 103 err = sdscatprintf(sdsempty(), 104 "Can't open the log file: %s", strerror(errno)); 105 goto loaderr; 106 } 107 fclose(logfp); 108 } 109 } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) { 110 if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) { 111 err = "argument must be 'yes' or 'no'"; goto loaderr; 112 } 113 } else if (!strcasecmp(argv[0],"syslog-ident") && argc == 2) { 114 if (server.syslog_ident) zfree(server.syslog_ident); 115 server.syslog_ident = zstrdup(argv[1]); 116 } else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) { 117 struct { 118 const char *name; 119 const int value; 120 } validSyslogFacilities[] = { 121 {"user", LOG_USER}, 122 {"local0", LOG_LOCAL0}, 123 {"local1", LOG_LOCAL1}, 124 {"local2", LOG_LOCAL2}, 125 {"local3", LOG_LOCAL3}, 126 {"local4", LOG_LOCAL4}, 127 {"local5", LOG_LOCAL5}, 128 {"local6", LOG_LOCAL6}, 129 {"local7", LOG_LOCAL7}, 130 {NULL, 0} 131 }; 132 int i; 133 134 for (i = 0; validSyslogFacilities[i].name; i++) { 135 if (!strcasecmp(validSyslogFacilities[i].name, argv[1])) { 136 server.syslog_facility = validSyslogFacilities[i].value; 137 break; 138 } 139 } 140 141 if (!validSyslogFacilities[i].name) { 142 err = "Invalid log facility. Must be one of USER or between LOCAL0-LOCAL7"; 143 goto loaderr; 144 } 145 } else if (!strcasecmp(argv[0],"databases") && argc == 2) { 146 server.dbnum = atoi(argv[1]); 147 if (server.dbnum < 1) { 148 err = "Invalid number of databases"; goto loaderr; 149 } 150 } else if (!strcasecmp(argv[0],"include") && argc == 2) { 151 loadServerConfig(argv[1],NULL); 152 } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) { 153 server.maxclients = atoi(argv[1]); 154 } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) { 155 server.maxmemory = memtoll(argv[1],NULL); 156 } else if (!strcasecmp(argv[0],"maxmemory-policy") && argc == 2) { 157 if (!strcasecmp(argv[1],"volatile-lru")) { 158 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU; 159 } else if (!strcasecmp(argv[1],"volatile-random")) { 160 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM; 161 } else if (!strcasecmp(argv[1],"volatile-ttl")) { 162 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL; 163 } else if (!strcasecmp(argv[1],"allkeys-lru")) { 164 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU; 165 } else if (!strcasecmp(argv[1],"allkeys-random")) { 166 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM; 167 } else if (!strcasecmp(argv[1],"noeviction")) { 168 server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION; 169 } else { 170 err = "Invalid maxmemory policy"; 171 goto loaderr; 172 } 173 } else if (!strcasecmp(argv[0],"maxmemory-samples") && argc == 2) { 174 server.maxmemory_samples = atoi(argv[1]); 175 if (server.maxmemory_samples <= 0) { 176 err = "maxmemory-samples must be 1 or greater"; 177 goto loaderr; 178 } 179 } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) { 180 server.masterhost = sdsnew(argv[1]); 181 server.masterport = atoi(argv[2]); 182 server.repl_state = REDIS_REPL_CONNECT; 183 } else if (!strcasecmp(argv[0],"repl-ping-slave-period") && argc == 2) { 184 server.repl_ping_slave_period = atoi(argv[1]); 185 if (server.repl_ping_slave_period <= 0) { 186 err = "repl-ping-slave-period must be 1 or greater"; 187 goto loaderr; 188 } 189 } else if (!strcasecmp(argv[0],"repl-timeout") && argc == 2) { 190 server.repl_timeout = atoi(argv[1]); 191 if (server.repl_timeout <= 0) { 192 err = "repl-timeout must be 1 or greater"; 193 goto loaderr; 194 } 195 } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) { 196 server.masterauth = zstrdup(argv[1]); 197 } else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) { 198 if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) { 199 err = "argument must be 'yes' or 'no'"; goto loaderr; 200 } 201 } else if (!strcasecmp(argv[0],"glueoutputbuf")) { 202 redisLog(REDIS_WARNING, "Deprecated configuration directive: \"%s\"", argv[0]); 203 } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) { 204 if ((server.rdb_compression = yesnotoi(argv[1])) == -1) { 205 err = "argument must be 'yes' or 'no'"; goto loaderr; 206 } 207 } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) { 208 if ((server.activerehashing = yesnotoi(argv[1])) == -1) { 209 err = "argument must be 'yes' or 'no'"; goto loaderr; 210 } 211 } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) { 212 if ((server.daemonize = yesnotoi(argv[1])) == -1) { 213 err = "argument must be 'yes' or 'no'"; goto loaderr; 214 } 215 } else if (!strcasecmp(argv[0],"appendonly") && argc == 2) { 216 int yes; 217 218 if ((yes = yesnotoi(argv[1])) == -1) { 219 err = "argument must be 'yes' or 'no'"; goto loaderr; 220 } 221 server.aof_state = yes ? REDIS_AOF_ON : REDIS_AOF_OFF; 222 } else if (!strcasecmp(argv[0],"appendfilename") && argc == 2) { 223 zfree(server.aof_filename); 224 server.aof_filename = zstrdup(argv[1]); 225 } else if (!strcasecmp(argv[0],"no-appendfsync-on-rewrite") 226 && argc == 2) { 227 if ((server.aof_no_fsync_on_rewrite= yesnotoi(argv[1])) == -1) { 228 err = "argument must be 'yes' or 'no'"; goto loaderr; 229 } 230 } else if (!strcasecmp(argv[0],"appendfsync") && argc == 2) { 231 if (!strcasecmp(argv[1],"no")) { 232 server.aof_fsync = AOF_FSYNC_NO; 233 } else if (!strcasecmp(argv[1],"always")) { 234 server.aof_fsync = AOF_FSYNC_ALWAYS; 235 } else if (!strcasecmp(argv[1],"everysec")) { 236 server.aof_fsync = AOF_FSYNC_EVERYSEC; 237 } else { 238 err = "argument must be 'no', 'always' or 'everysec'"; 239 goto loaderr; 240 } 241 } else if (!strcasecmp(argv[0],"auto-aof-rewrite-percentage") && 242 argc == 2) 243 { 244 server.aof_rewrite_perc = atoi(argv[1]); 245 if (server.aof_rewrite_perc < 0) { 246 err = "Invalid negative percentage for AOF auto rewrite"; 247 goto loaderr; 248 } 249 } else if (!strcasecmp(argv[0],"auto-aof-rewrite-min-size") && 250 argc == 2) 251 { 252 server.aof_rewrite_min_size = memtoll(argv[1],NULL); 253 } else if (!strcasecmp(argv[0],"requirepass") && argc == 2) { 254 server.requirepass = zstrdup(argv[1]); 255 } else if (!strcasecmp(argv[0],"pidfile") && argc == 2) { 256 zfree(server.pidfile); 257 server.pidfile = zstrdup(argv[1]); 258 } else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) { 259 zfree(server.rdb_filename); 260 server.rdb_filename = zstrdup(argv[1]); 261 } else if (!strcasecmp(argv[0],"hash-max-zipmap-entries") && argc == 2) { 262 server.hash_max_zipmap_entries = memtoll(argv[1], NULL); 263 } else if (!strcasecmp(argv[0],"hash-max-zipmap-value") && argc == 2) { 264 server.hash_max_zipmap_value = memtoll(argv[1], NULL); 265 } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){ 266 server.list_max_ziplist_entries = memtoll(argv[1], NULL); 267 } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) { 268 server.list_max_ziplist_value = memtoll(argv[1], NULL); 269 } else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) { 270 server.set_max_intset_entries = memtoll(argv[1], NULL); 271 } else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) { 272 server.zset_max_ziplist_entries = memtoll(argv[1], NULL); 273 } else if (!strcasecmp(argv[0],"zset-max-ziplist-value") && argc == 2) { 274 server.zset_max_ziplist_value = memtoll(argv[1], NULL); 275 } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) { 276 struct redisCommand *cmd = lookupCommand(argv[1]); 277 int retval; 278 279 if (!cmd) { 280 err = "No such command in rename-command"; 281 goto loaderr; 282 } 283 284 /* If the target command name is the emtpy string we just 285 * remove it from the command table. */ 286 retval = dictDelete(server.commands, argv[1]); 287 redisAssert(retval == DICT_OK); 288 289 /* Otherwise we re-add the command under a different name. */ 290 if (sdslen(argv[2]) != 0) { 291 sds copy = sdsdup(argv[2]); 292 293 retval = dictAdd(server.commands, copy, cmd); 294 if (retval != DICT_OK) { 295 sdsfree(copy); 296 err = "Target command name already exists"; goto loaderr; 297 } 298 } 299 } else if (!strcasecmp(argv[0],"cluster-enabled") && argc == 2) { 300 if ((server.cluster_enabled = yesnotoi(argv[1])) == -1) { 301 err = "argument must be 'yes' or 'no'"; goto loaderr; 302 } 303 } else if (!strcasecmp(argv[0],"cluster-config-file") && argc == 2) { 304 zfree(server.cluster.configfile); 305 server.cluster.configfile = zstrdup(argv[1]); 306 } else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) { 307 server.lua_time_limit = strtoll(argv[1],NULL,10); 308 } else if (!strcasecmp(argv[0],"slowlog-log-slower-than") && 309 argc == 2) 310 { 311 server.slowlog_log_slower_than = strtoll(argv[1],NULL,10); 312 } else if (!strcasecmp(argv[0],"slowlog-max-len") && argc == 2) { 313 server.slowlog_max_len = strtoll(argv[1],NULL,10); 314 } else { 315 err = "Bad directive or wrong number of arguments"; goto loaderr; 316 } 317 sdsfreesplitres(argv,argc); 318 } 319 sdsfreesplitres(lines,totlines); 320 return; 321 322 loaderr: 323 fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n"); 324 fprintf(stderr, "Reading the configuration file, at line %d\n", linenum); 325 fprintf(stderr, ">>> '%s'\n", lines[i]); 326 fprintf(stderr, "%s\n", err); 327 exit(1); 328 } 329 330 /* Load the server configuration from the specified filename. 331 * The function appends the additional configuration directives stored 332 * in the 'options' string to the config file before loading. 333 * 334 * Both filename and options can be NULL, in such a case are considered 335 * emtpy. This way loadServerConfig can be used to just load a file or 336 * just load a string. */ 337 void loadServerConfig(char *filename, char *options) { 338 sds config = sdsempty(); 339 char buf[REDIS_CONFIGLINE_MAX+1]; 340 341 /* Load the file content */ 342 if (filename) { 343 FILE *fp; 344 345 if (filename[0] == '-' && filename[1] == '\0') { 346 fp = stdin; 347 } else { 348 if ((fp = fopen(filename,"r")) == NULL) { 349 redisLog(REDIS_WARNING, 350 "Fatal error, can't open config file '%s'", filename); 351 exit(1); 352 } 353 } 354 while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) 355 config = sdscat(config,buf); 356 if (fp != stdin) fclose(fp); 357 } 358 /* Append the additional options */ 359 if (options) { 360 config = sdscat(config,"\n"); 361 config = sdscat(config,options); 362 } 363 loadServerConfigFromString(config); 364 sdsfree(config); 365 } 366 367 /*----------------------------------------------------------------------------- 368 * CONFIG command for remote configuration 369 *----------------------------------------------------------------------------*/ 370 371 void configSetCommand(redisClient *c) { 372 robj *o; 373 long long ll; 374 redisAssertWithInfo(c,c->argv[2],c->argv[2]->encoding == REDIS_ENCODING_RAW); 375 redisAssertWithInfo(c,c->argv[2],c->argv[3]->encoding == REDIS_ENCODING_RAW); 376 o = c->argv[3]; 377 378 if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) { 379 zfree(server.rdb_filename); 380 server.rdb_filename = zstrdup(o->ptr); 381 } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) { 382 zfree(server.requirepass); 383 server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL; 384 } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) { 385 zfree(server.masterauth); 386 server.masterauth = zstrdup(o->ptr); 387 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) { 388 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 389 ll < 0) goto badfmt; 390 server.maxmemory = ll; 391 if (server.maxmemory) freeMemoryIfNeeded(); 392 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) { 393 if (!strcasecmp(o->ptr,"volatile-lru")) { 394 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU; 395 } else if (!strcasecmp(o->ptr,"volatile-random")) { 396 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM; 397 } else if (!strcasecmp(o->ptr,"volatile-ttl")) { 398 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL; 399 } else if (!strcasecmp(o->ptr,"allkeys-lru")) { 400 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU; 401 } else if (!strcasecmp(o->ptr,"allkeys-random")) { 402 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM; 403 } else if (!strcasecmp(o->ptr,"noeviction")) { 404 server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION; 405 } else { 406 goto badfmt; 407 } 408 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) { 409 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 410 ll <= 0) goto badfmt; 411 server.maxmemory_samples = ll; 412 } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) { 413 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 414 ll < 0 || ll > LONG_MAX) goto badfmt; 415 server.maxidletime = ll; 416 } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) { 417 if (!strcasecmp(o->ptr,"no")) { 418 server.aof_fsync = AOF_FSYNC_NO; 419 } else if (!strcasecmp(o->ptr,"everysec")) { 420 server.aof_fsync = AOF_FSYNC_EVERYSEC; 421 } else if (!strcasecmp(o->ptr,"always")) { 422 server.aof_fsync = AOF_FSYNC_ALWAYS; 423 } else { 424 goto badfmt; 425 } 426 } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) { 427 int yn = yesnotoi(o->ptr); 428 429 if (yn == -1) goto badfmt; 430 server.aof_no_fsync_on_rewrite = yn; 431 } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) { 432 int enable = yesnotoi(o->ptr); 433 434 if (enable == -1) goto badfmt; 435 if (enable == 0 && server.aof_state != REDIS_AOF_OFF) { 436 stopAppendOnly(); 437 } else if (enable && server.aof_state == REDIS_AOF_OFF) { 438 if (startAppendOnly() == REDIS_ERR) { 439 addReplyError(c, 440 "Unable to turn on AOF. Check server logs."); 441 return; 442 } 443 } 444 } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) { 445 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 446 server.aof_rewrite_perc = ll; 447 } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) { 448 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 449 server.aof_rewrite_min_size = ll; 450 } else if (!strcasecmp(c->argv[2]->ptr,"save")) { 451 int vlen, j; 452 sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen); 453 454 /* Perform sanity check before setting the new config: 455 * - Even number of args 456 * - Seconds >= 1, changes >= 0 */ 457 if (vlen & 1) { 458 sdsfreesplitres(v,vlen); 459 goto badfmt; 460 } 461 for (j = 0; j < vlen; j++) { 462 char *eptr; 463 long val; 464 465 val = strtoll(v[j], &eptr, 10); 466 if (eptr[0] != '\0' || 467 ((j & 1) == 0 && val < 1) || 468 ((j & 1) == 1 && val < 0)) { 469 sdsfreesplitres(v,vlen); 470 goto badfmt; 471 } 472 } 473 /* Finally set the new config */ 474 resetServerSaveParams(); 475 for (j = 0; j < vlen; j += 2) { 476 time_t seconds; 477 int changes; 478 479 seconds = strtoll(v[j],NULL,10); 480 changes = strtoll(v[j+1],NULL,10); 481 appendServerSaveParams(seconds, changes); 482 } 483 sdsfreesplitres(v,vlen); 484 } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) { 485 int yn = yesnotoi(o->ptr); 486 487 if (yn == -1) goto badfmt; 488 server.repl_serve_stale_data = yn; 489 } else if (!strcasecmp(c->argv[2]->ptr,"dir")) { 490 if (chdir((char*)o->ptr) == -1) { 491 addReplyErrorFormat(c,"Changing directory: %s", strerror(errno)); 492 return; 493 } 494 } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-zipmap-entries")) { 495 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 496 server.hash_max_zipmap_entries = ll; 497 } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-zipmap-value")) { 498 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 499 server.hash_max_zipmap_value = ll; 500 } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) { 501 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 502 server.list_max_ziplist_entries = ll; 503 } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) { 504 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 505 server.list_max_ziplist_value = ll; 506 } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) { 507 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 508 server.set_max_intset_entries = ll; 509 } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) { 510 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 511 server.zset_max_ziplist_entries = ll; 512 } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) { 513 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 514 server.zset_max_ziplist_value = ll; 515 } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) { 516 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 517 server.lua_time_limit = ll; 518 } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) { 519 if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt; 520 server.slowlog_log_slower_than = ll; 521 } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) { 522 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 523 server.slowlog_max_len = (unsigned)ll; 524 } else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) { 525 if (!strcasecmp(o->ptr,"warning")) { 526 server.verbosity = REDIS_WARNING; 527 } else if (!strcasecmp(o->ptr,"notice")) { 528 server.verbosity = REDIS_NOTICE; 529 } else if (!strcasecmp(o->ptr,"verbose")) { 530 server.verbosity = REDIS_VERBOSE; 531 } else if (!strcasecmp(o->ptr,"debug")) { 532 server.verbosity = REDIS_DEBUG; 533 } else { 534 goto badfmt; 535 } 536 } else { 537 addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s", 538 (char*)c->argv[2]->ptr); 539 return; 540 } 541 addReply(c,shared.ok); 542 return; 543 544 badfmt: /* Bad format errors */ 545 addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'", 546 (char*)o->ptr, 547 (char*)c->argv[2]->ptr); 548 } 549 550 void configGetCommand(redisClient *c) { 551 robj *o = c->argv[2]; 552 void *replylen = addDeferredMultiBulkLength(c); 553 char *pattern = o->ptr; 554 char buf[128]; 555 int matches = 0; 556 redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW); 557 558 if (stringmatch(pattern,"dir",0)) { 559 char buf[1024]; 560 561 if (getcwd(buf,sizeof(buf)) == NULL) 562 buf[0] = '\0'; 563 564 addReplyBulkCString(c,"dir"); 565 addReplyBulkCString(c,buf); 566 matches++; 567 } 568 if (stringmatch(pattern,"dbfilename",0)) { 569 addReplyBulkCString(c,"dbfilename"); 570 addReplyBulkCString(c,server.rdb_filename); 571 matches++; 572 } 573 if (stringmatch(pattern,"requirepass",0)) { 574 addReplyBulkCString(c,"requirepass"); 575 addReplyBulkCString(c,server.requirepass); 576 matches++; 577 } 578 if (stringmatch(pattern,"masterauth",0)) { 579 addReplyBulkCString(c,"masterauth"); 580 addReplyBulkCString(c,server.masterauth); 581 matches++; 582 } 583 if (stringmatch(pattern,"maxmemory",0)) { 584 ll2string(buf,sizeof(buf),server.maxmemory); 585 addReplyBulkCString(c,"maxmemory"); 586 addReplyBulkCString(c,buf); 587 matches++; 588 } 589 if (stringmatch(pattern,"maxmemory-policy",0)) { 590 char *s; 591 592 switch(server.maxmemory_policy) { 593 case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break; 594 case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break; 595 case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break; 596 case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break; 597 case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break; 598 case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break; 599 default: s = "unknown"; break; /* too harmless to panic */ 600 } 601 addReplyBulkCString(c,"maxmemory-policy"); 602 addReplyBulkCString(c,s); 603 matches++; 604 } 605 if (stringmatch(pattern,"maxmemory-samples",0)) { 606 ll2string(buf,sizeof(buf),server.maxmemory_samples); 607 addReplyBulkCString(c,"maxmemory-samples"); 608 addReplyBulkCString(c,buf); 609 matches++; 610 } 611 if (stringmatch(pattern,"timeout",0)) { 612 ll2string(buf,sizeof(buf),server.maxidletime); 613 addReplyBulkCString(c,"timeout"); 614 addReplyBulkCString(c,buf); 615 matches++; 616 } 617 if (stringmatch(pattern,"appendonly",0)) { 618 addReplyBulkCString(c,"appendonly"); 619 addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes"); 620 matches++; 621 } 622 if (stringmatch(pattern,"no-appendfsync-on-rewrite",0)) { 623 addReplyBulkCString(c,"no-appendfsync-on-rewrite"); 624 addReplyBulkCString(c,server.aof_no_fsync_on_rewrite ? "yes" : "no"); 625 matches++; 626 } 627 if (stringmatch(pattern,"appendfsync",0)) { 628 char *policy; 629 630 switch(server.aof_fsync) { 631 case AOF_FSYNC_NO: policy = "no"; break; 632 case AOF_FSYNC_EVERYSEC: policy = "everysec"; break; 633 case AOF_FSYNC_ALWAYS: policy = "always"; break; 634 default: policy = "unknown"; break; /* too harmless to panic */ 635 } 636 addReplyBulkCString(c,"appendfsync"); 637 addReplyBulkCString(c,policy); 638 matches++; 639 } 640 if (stringmatch(pattern,"save",0)) { 641 sds buf = sdsempty(); 642 int j; 643 644 for (j = 0; j < server.saveparamslen; j++) { 645 buf = sdscatprintf(buf,"%ld %d", 646 server.saveparams[j].seconds, 647 server.saveparams[j].changes); 648 if (j != server.saveparamslen-1) 649 buf = sdscatlen(buf," ",1); 650 } 651 addReplyBulkCString(c,"save"); 652 addReplyBulkCString(c,buf); 653 sdsfree(buf); 654 matches++; 655 } 656 if (stringmatch(pattern,"auto-aof-rewrite-percentage",0)) { 657 addReplyBulkCString(c,"auto-aof-rewrite-percentage"); 658 addReplyBulkLongLong(c,server.aof_rewrite_perc); 659 matches++; 660 } 661 if (stringmatch(pattern,"auto-aof-rewrite-min-size",0)) { 662 addReplyBulkCString(c,"auto-aof-rewrite-min-size"); 663 addReplyBulkLongLong(c,server.aof_rewrite_min_size); 664 matches++; 665 } 666 if (stringmatch(pattern,"slave-serve-stale-data",0)) { 667 addReplyBulkCString(c,"slave-serve-stale-data"); 668 addReplyBulkCString(c,server.repl_serve_stale_data ? "yes" : "no"); 669 matches++; 670 } 671 if (stringmatch(pattern,"hash-max-zipmap-entries",0)) { 672 addReplyBulkCString(c,"hash-max-zipmap-entries"); 673 addReplyBulkLongLong(c,server.hash_max_zipmap_entries); 674 matches++; 675 } 676 if (stringmatch(pattern,"hash-max-zipmap-value",0)) { 677 addReplyBulkCString(c,"hash-max-zipmap-value"); 678 addReplyBulkLongLong(c,server.hash_max_zipmap_value); 679 matches++; 680 } 681 if (stringmatch(pattern,"list-max-ziplist-entries",0)) { 682 addReplyBulkCString(c,"list-max-ziplist-entries"); 683 addReplyBulkLongLong(c,server.list_max_ziplist_entries); 684 matches++; 685 } 686 if (stringmatch(pattern,"list-max-ziplist-value",0)) { 687 addReplyBulkCString(c,"list-max-ziplist-value"); 688 addReplyBulkLongLong(c,server.list_max_ziplist_value); 689 matches++; 690 } 691 if (stringmatch(pattern,"set-max-intset-entries",0)) { 692 addReplyBulkCString(c,"set-max-intset-entries"); 693 addReplyBulkLongLong(c,server.set_max_intset_entries); 694 matches++; 695 } 696 if (stringmatch(pattern,"zset-max-ziplist-entries",0)) { 697 addReplyBulkCString(c,"zset-max-ziplist-entries"); 698 addReplyBulkLongLong(c,server.zset_max_ziplist_entries); 699 matches++; 700 } 701 if (stringmatch(pattern,"zset-max-ziplist-value",0)) { 702 addReplyBulkCString(c,"zset-max-ziplist-value"); 703 addReplyBulkLongLong(c,server.zset_max_ziplist_value); 704 matches++; 705 } 706 if (stringmatch(pattern,"lua-time-limit",0)) { 707 addReplyBulkCString(c,"lua-time-limit"); 708 addReplyBulkLongLong(c,server.lua_time_limit); 709 matches++; 710 } 711 if (stringmatch(pattern,"slowlog-log-slower-than",0)) { 712 addReplyBulkCString(c,"slowlog-log-slower-than"); 713 addReplyBulkLongLong(c,server.slowlog_log_slower_than); 714 matches++; 715 } 716 if (stringmatch(pattern,"slowlog-max-len",0)) { 717 addReplyBulkCString(c,"slowlog-max-len"); 718 addReplyBulkLongLong(c,server.slowlog_max_len); 719 matches++; 720 } 721 if (stringmatch(pattern,"loglevel",0)) { 722 char *s; 723 724 switch(server.verbosity) { 725 case REDIS_WARNING: s = "warning"; break; 726 case REDIS_VERBOSE: s = "verbose"; break; 727 case REDIS_NOTICE: s = "notice"; break; 728 case REDIS_DEBUG: s = "debug"; break; 729 default: s = "unknown"; break; /* too harmless to panic */ 730 } 731 addReplyBulkCString(c,"loglevel"); 732 addReplyBulkCString(c,s); 733 matches++; 734 } 735 setDeferredMultiBulkLength(c,replylen,matches*2); 736 } 737 738 void configCommand(redisClient *c) { 739 if (!strcasecmp(c->argv[1]->ptr,"set")) { 740 if (c->argc != 4) goto badarity; 741 configSetCommand(c); 742 } else if (!strcasecmp(c->argv[1]->ptr,"get")) { 743 if (c->argc != 3) goto badarity; 744 configGetCommand(c); 745 } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) { 746 if (c->argc != 2) goto badarity; 747 server.stat_keyspace_hits = 0; 748 server.stat_keyspace_misses = 0; 749 server.stat_numcommands = 0; 750 server.stat_numconnections = 0; 751 server.stat_expiredkeys = 0; 752 resetCommandTableStats(); 753 addReply(c,shared.ok); 754 } else { 755 addReplyError(c, 756 "CONFIG subcommand must be one of GET, SET, RESETSTAT"); 757 } 758 return; 759 760 badarity: 761 addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s", 762 (char*) c->argv[1]->ptr); 763 } 764