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 redisLog(REDIS_WARNING, "Deprecated configuration directive: \"%s\"", argv[0]); 263 server.hash_max_ziplist_entries = memtoll(argv[1], NULL); 264 } else if (!strcasecmp(argv[0],"hash-max-zipmap-value") && argc == 2) { 265 redisLog(REDIS_WARNING, "Deprecated configuration directive: \"%s\"", argv[0]); 266 server.hash_max_ziplist_value = memtoll(argv[1], NULL); 267 } else if (!strcasecmp(argv[0],"hash-max-ziplist-entries") && argc == 2) { 268 server.hash_max_ziplist_entries = memtoll(argv[1], NULL); 269 } else if (!strcasecmp(argv[0],"hash-max-ziplist-value") && argc == 2) { 270 server.hash_max_ziplist_value = memtoll(argv[1], NULL); 271 } else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){ 272 server.list_max_ziplist_entries = memtoll(argv[1], NULL); 273 } else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) { 274 server.list_max_ziplist_value = memtoll(argv[1], NULL); 275 } else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) { 276 server.set_max_intset_entries = memtoll(argv[1], NULL); 277 } else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) { 278 server.zset_max_ziplist_entries = memtoll(argv[1], NULL); 279 } else if (!strcasecmp(argv[0],"zset-max-ziplist-value") && argc == 2) { 280 server.zset_max_ziplist_value = memtoll(argv[1], NULL); 281 } else if (!strcasecmp(argv[0],"rename-command") && argc == 3) { 282 struct redisCommand *cmd = lookupCommand(argv[1]); 283 int retval; 284 285 if (!cmd) { 286 err = "No such command in rename-command"; 287 goto loaderr; 288 } 289 290 /* If the target command name is the emtpy string we just 291 * remove it from the command table. */ 292 retval = dictDelete(server.commands, argv[1]); 293 redisAssert(retval == DICT_OK); 294 295 /* Otherwise we re-add the command under a different name. */ 296 if (sdslen(argv[2]) != 0) { 297 sds copy = sdsdup(argv[2]); 298 299 retval = dictAdd(server.commands, copy, cmd); 300 if (retval != DICT_OK) { 301 sdsfree(copy); 302 err = "Target command name already exists"; goto loaderr; 303 } 304 } 305 } else if (!strcasecmp(argv[0],"cluster-enabled") && argc == 2) { 306 if ((server.cluster_enabled = yesnotoi(argv[1])) == -1) { 307 err = "argument must be 'yes' or 'no'"; goto loaderr; 308 } 309 } else if (!strcasecmp(argv[0],"cluster-config-file") && argc == 2) { 310 zfree(server.cluster.configfile); 311 server.cluster.configfile = zstrdup(argv[1]); 312 } else if (!strcasecmp(argv[0],"lua-time-limit") && argc == 2) { 313 server.lua_time_limit = strtoll(argv[1],NULL,10); 314 } else if (!strcasecmp(argv[0],"slowlog-log-slower-than") && 315 argc == 2) 316 { 317 server.slowlog_log_slower_than = strtoll(argv[1],NULL,10); 318 } else if (!strcasecmp(argv[0],"slowlog-max-len") && argc == 2) { 319 server.slowlog_max_len = strtoll(argv[1],NULL,10); 320 } else { 321 err = "Bad directive or wrong number of arguments"; goto loaderr; 322 } 323 sdsfreesplitres(argv,argc); 324 } 325 sdsfreesplitres(lines,totlines); 326 return; 327 328 loaderr: 329 fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n"); 330 fprintf(stderr, "Reading the configuration file, at line %d\n", linenum); 331 fprintf(stderr, ">>> '%s'\n", lines[i]); 332 fprintf(stderr, "%s\n", err); 333 exit(1); 334 } 335 336 /* Load the server configuration from the specified filename. 337 * The function appends the additional configuration directives stored 338 * in the 'options' string to the config file before loading. 339 * 340 * Both filename and options can be NULL, in such a case are considered 341 * emtpy. This way loadServerConfig can be used to just load a file or 342 * just load a string. */ 343 void loadServerConfig(char *filename, char *options) { 344 sds config = sdsempty(); 345 char buf[REDIS_CONFIGLINE_MAX+1]; 346 347 /* Load the file content */ 348 if (filename) { 349 FILE *fp; 350 351 if (filename[0] == '-' && filename[1] == '\0') { 352 fp = stdin; 353 } else { 354 if ((fp = fopen(filename,"r")) == NULL) { 355 redisLog(REDIS_WARNING, 356 "Fatal error, can't open config file '%s'", filename); 357 exit(1); 358 } 359 } 360 while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) 361 config = sdscat(config,buf); 362 if (fp != stdin) fclose(fp); 363 } 364 /* Append the additional options */ 365 if (options) { 366 config = sdscat(config,"\n"); 367 config = sdscat(config,options); 368 } 369 loadServerConfigFromString(config); 370 sdsfree(config); 371 } 372 373 /*----------------------------------------------------------------------------- 374 * CONFIG command for remote configuration 375 *----------------------------------------------------------------------------*/ 376 377 void configSetCommand(redisClient *c) { 378 robj *o; 379 long long ll; 380 redisAssertWithInfo(c,c->argv[2],c->argv[2]->encoding == REDIS_ENCODING_RAW); 381 redisAssertWithInfo(c,c->argv[2],c->argv[3]->encoding == REDIS_ENCODING_RAW); 382 o = c->argv[3]; 383 384 if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) { 385 zfree(server.rdb_filename); 386 server.rdb_filename = zstrdup(o->ptr); 387 } else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) { 388 zfree(server.requirepass); 389 server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL; 390 } else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) { 391 zfree(server.masterauth); 392 server.masterauth = zstrdup(o->ptr); 393 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) { 394 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 395 ll < 0) goto badfmt; 396 server.maxmemory = ll; 397 if (server.maxmemory) freeMemoryIfNeeded(); 398 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) { 399 if (!strcasecmp(o->ptr,"volatile-lru")) { 400 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU; 401 } else if (!strcasecmp(o->ptr,"volatile-random")) { 402 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_RANDOM; 403 } else if (!strcasecmp(o->ptr,"volatile-ttl")) { 404 server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_TTL; 405 } else if (!strcasecmp(o->ptr,"allkeys-lru")) { 406 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_LRU; 407 } else if (!strcasecmp(o->ptr,"allkeys-random")) { 408 server.maxmemory_policy = REDIS_MAXMEMORY_ALLKEYS_RANDOM; 409 } else if (!strcasecmp(o->ptr,"noeviction")) { 410 server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION; 411 } else { 412 goto badfmt; 413 } 414 } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-samples")) { 415 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 416 ll <= 0) goto badfmt; 417 server.maxmemory_samples = ll; 418 } else if (!strcasecmp(c->argv[2]->ptr,"timeout")) { 419 if (getLongLongFromObject(o,&ll) == REDIS_ERR || 420 ll < 0 || ll > LONG_MAX) goto badfmt; 421 server.maxidletime = ll; 422 } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) { 423 if (!strcasecmp(o->ptr,"no")) { 424 server.aof_fsync = AOF_FSYNC_NO; 425 } else if (!strcasecmp(o->ptr,"everysec")) { 426 server.aof_fsync = AOF_FSYNC_EVERYSEC; 427 } else if (!strcasecmp(o->ptr,"always")) { 428 server.aof_fsync = AOF_FSYNC_ALWAYS; 429 } else { 430 goto badfmt; 431 } 432 } else if (!strcasecmp(c->argv[2]->ptr,"no-appendfsync-on-rewrite")) { 433 int yn = yesnotoi(o->ptr); 434 435 if (yn == -1) goto badfmt; 436 server.aof_no_fsync_on_rewrite = yn; 437 } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) { 438 int enable = yesnotoi(o->ptr); 439 440 if (enable == -1) goto badfmt; 441 if (enable == 0 && server.aof_state != REDIS_AOF_OFF) { 442 stopAppendOnly(); 443 } else if (enable && server.aof_state == REDIS_AOF_OFF) { 444 if (startAppendOnly() == REDIS_ERR) { 445 addReplyError(c, 446 "Unable to turn on AOF. Check server logs."); 447 return; 448 } 449 } 450 } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) { 451 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 452 server.aof_rewrite_perc = ll; 453 } else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) { 454 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 455 server.aof_rewrite_min_size = ll; 456 } else if (!strcasecmp(c->argv[2]->ptr,"save")) { 457 int vlen, j; 458 sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen); 459 460 /* Perform sanity check before setting the new config: 461 * - Even number of args 462 * - Seconds >= 1, changes >= 0 */ 463 if (vlen & 1) { 464 sdsfreesplitres(v,vlen); 465 goto badfmt; 466 } 467 for (j = 0; j < vlen; j++) { 468 char *eptr; 469 long val; 470 471 val = strtoll(v[j], &eptr, 10); 472 if (eptr[0] != '\0' || 473 ((j & 1) == 0 && val < 1) || 474 ((j & 1) == 1 && val < 0)) { 475 sdsfreesplitres(v,vlen); 476 goto badfmt; 477 } 478 } 479 /* Finally set the new config */ 480 resetServerSaveParams(); 481 for (j = 0; j < vlen; j += 2) { 482 time_t seconds; 483 int changes; 484 485 seconds = strtoll(v[j],NULL,10); 486 changes = strtoll(v[j+1],NULL,10); 487 appendServerSaveParams(seconds, changes); 488 } 489 sdsfreesplitres(v,vlen); 490 } else if (!strcasecmp(c->argv[2]->ptr,"slave-serve-stale-data")) { 491 int yn = yesnotoi(o->ptr); 492 493 if (yn == -1) goto badfmt; 494 server.repl_serve_stale_data = yn; 495 } else if (!strcasecmp(c->argv[2]->ptr,"dir")) { 496 if (chdir((char*)o->ptr) == -1) { 497 addReplyErrorFormat(c,"Changing directory: %s", strerror(errno)); 498 return; 499 } 500 } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-entries")) { 501 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 502 server.hash_max_ziplist_entries = ll; 503 } else if (!strcasecmp(c->argv[2]->ptr,"hash-max-ziplist-value")) { 504 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 505 server.hash_max_ziplist_value = ll; 506 } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) { 507 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 508 server.list_max_ziplist_entries = ll; 509 } else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) { 510 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 511 server.list_max_ziplist_value = ll; 512 } else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) { 513 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 514 server.set_max_intset_entries = ll; 515 } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) { 516 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 517 server.zset_max_ziplist_entries = ll; 518 } else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) { 519 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 520 server.zset_max_ziplist_value = ll; 521 } else if (!strcasecmp(c->argv[2]->ptr,"lua-time-limit")) { 522 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 523 server.lua_time_limit = ll; 524 } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-log-slower-than")) { 525 if (getLongLongFromObject(o,&ll) == REDIS_ERR) goto badfmt; 526 server.slowlog_log_slower_than = ll; 527 } else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) { 528 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; 529 server.slowlog_max_len = (unsigned)ll; 530 } else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) { 531 if (!strcasecmp(o->ptr,"warning")) { 532 server.verbosity = REDIS_WARNING; 533 } else if (!strcasecmp(o->ptr,"notice")) { 534 server.verbosity = REDIS_NOTICE; 535 } else if (!strcasecmp(o->ptr,"verbose")) { 536 server.verbosity = REDIS_VERBOSE; 537 } else if (!strcasecmp(o->ptr,"debug")) { 538 server.verbosity = REDIS_DEBUG; 539 } else { 540 goto badfmt; 541 } 542 } else { 543 addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s", 544 (char*)c->argv[2]->ptr); 545 return; 546 } 547 addReply(c,shared.ok); 548 return; 549 550 badfmt: /* Bad format errors */ 551 addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'", 552 (char*)o->ptr, 553 (char*)c->argv[2]->ptr); 554 } 555 556 void configGetCommand(redisClient *c) { 557 robj *o = c->argv[2]; 558 void *replylen = addDeferredMultiBulkLength(c); 559 char *pattern = o->ptr; 560 char buf[128]; 561 int matches = 0; 562 redisAssertWithInfo(c,o,o->encoding == REDIS_ENCODING_RAW); 563 564 if (stringmatch(pattern,"dir",0)) { 565 char buf[1024]; 566 567 if (getcwd(buf,sizeof(buf)) == NULL) 568 buf[0] = '\0'; 569 570 addReplyBulkCString(c,"dir"); 571 addReplyBulkCString(c,buf); 572 matches++; 573 } 574 if (stringmatch(pattern,"dbfilename",0)) { 575 addReplyBulkCString(c,"dbfilename"); 576 addReplyBulkCString(c,server.rdb_filename); 577 matches++; 578 } 579 if (stringmatch(pattern,"requirepass",0)) { 580 addReplyBulkCString(c,"requirepass"); 581 addReplyBulkCString(c,server.requirepass); 582 matches++; 583 } 584 if (stringmatch(pattern,"masterauth",0)) { 585 addReplyBulkCString(c,"masterauth"); 586 addReplyBulkCString(c,server.masterauth); 587 matches++; 588 } 589 if (stringmatch(pattern,"maxmemory",0)) { 590 ll2string(buf,sizeof(buf),server.maxmemory); 591 addReplyBulkCString(c,"maxmemory"); 592 addReplyBulkCString(c,buf); 593 matches++; 594 } 595 if (stringmatch(pattern,"maxmemory-policy",0)) { 596 char *s; 597 598 switch(server.maxmemory_policy) { 599 case REDIS_MAXMEMORY_VOLATILE_LRU: s = "volatile-lru"; break; 600 case REDIS_MAXMEMORY_VOLATILE_TTL: s = "volatile-ttl"; break; 601 case REDIS_MAXMEMORY_VOLATILE_RANDOM: s = "volatile-random"; break; 602 case REDIS_MAXMEMORY_ALLKEYS_LRU: s = "allkeys-lru"; break; 603 case REDIS_MAXMEMORY_ALLKEYS_RANDOM: s = "allkeys-random"; break; 604 case REDIS_MAXMEMORY_NO_EVICTION: s = "noeviction"; break; 605 default: s = "unknown"; break; /* too harmless to panic */ 606 } 607 addReplyBulkCString(c,"maxmemory-policy"); 608 addReplyBulkCString(c,s); 609 matches++; 610 } 611 if (stringmatch(pattern,"maxmemory-samples",0)) { 612 ll2string(buf,sizeof(buf),server.maxmemory_samples); 613 addReplyBulkCString(c,"maxmemory-samples"); 614 addReplyBulkCString(c,buf); 615 matches++; 616 } 617 if (stringmatch(pattern,"timeout",0)) { 618 ll2string(buf,sizeof(buf),server.maxidletime); 619 addReplyBulkCString(c,"timeout"); 620 addReplyBulkCString(c,buf); 621 matches++; 622 } 623 if (stringmatch(pattern,"appendonly",0)) { 624 addReplyBulkCString(c,"appendonly"); 625 addReplyBulkCString(c,server.aof_state == REDIS_AOF_OFF ? "no" : "yes"); 626 matches++; 627 } 628 if (stringmatch(pattern,"no-appendfsync-on-rewrite",0)) { 629 addReplyBulkCString(c,"no-appendfsync-on-rewrite"); 630 addReplyBulkCString(c,server.aof_no_fsync_on_rewrite ? "yes" : "no"); 631 matches++; 632 } 633 if (stringmatch(pattern,"appendfsync",0)) { 634 char *policy; 635 636 switch(server.aof_fsync) { 637 case AOF_FSYNC_NO: policy = "no"; break; 638 case AOF_FSYNC_EVERYSEC: policy = "everysec"; break; 639 case AOF_FSYNC_ALWAYS: policy = "always"; break; 640 default: policy = "unknown"; break; /* too harmless to panic */ 641 } 642 addReplyBulkCString(c,"appendfsync"); 643 addReplyBulkCString(c,policy); 644 matches++; 645 } 646 if (stringmatch(pattern,"save",0)) { 647 sds buf = sdsempty(); 648 int j; 649 650 for (j = 0; j < server.saveparamslen; j++) { 651 buf = sdscatprintf(buf,"%ld %d", 652 server.saveparams[j].seconds, 653 server.saveparams[j].changes); 654 if (j != server.saveparamslen-1) 655 buf = sdscatlen(buf," ",1); 656 } 657 addReplyBulkCString(c,"save"); 658 addReplyBulkCString(c,buf); 659 sdsfree(buf); 660 matches++; 661 } 662 if (stringmatch(pattern,"auto-aof-rewrite-percentage",0)) { 663 addReplyBulkCString(c,"auto-aof-rewrite-percentage"); 664 addReplyBulkLongLong(c,server.aof_rewrite_perc); 665 matches++; 666 } 667 if (stringmatch(pattern,"auto-aof-rewrite-min-size",0)) { 668 addReplyBulkCString(c,"auto-aof-rewrite-min-size"); 669 addReplyBulkLongLong(c,server.aof_rewrite_min_size); 670 matches++; 671 } 672 if (stringmatch(pattern,"slave-serve-stale-data",0)) { 673 addReplyBulkCString(c,"slave-serve-stale-data"); 674 addReplyBulkCString(c,server.repl_serve_stale_data ? "yes" : "no"); 675 matches++; 676 } 677 if (stringmatch(pattern,"hash-max-ziplist-entries",0)) { 678 addReplyBulkCString(c,"hash-max-ziplist-entries"); 679 addReplyBulkLongLong(c,server.hash_max_ziplist_entries); 680 matches++; 681 } 682 if (stringmatch(pattern,"hash-max-ziplist-value",0)) { 683 addReplyBulkCString(c,"hash-max-ziplist-value"); 684 addReplyBulkLongLong(c,server.hash_max_ziplist_value); 685 matches++; 686 } 687 if (stringmatch(pattern,"list-max-ziplist-entries",0)) { 688 addReplyBulkCString(c,"list-max-ziplist-entries"); 689 addReplyBulkLongLong(c,server.list_max_ziplist_entries); 690 matches++; 691 } 692 if (stringmatch(pattern,"list-max-ziplist-value",0)) { 693 addReplyBulkCString(c,"list-max-ziplist-value"); 694 addReplyBulkLongLong(c,server.list_max_ziplist_value); 695 matches++; 696 } 697 if (stringmatch(pattern,"set-max-intset-entries",0)) { 698 addReplyBulkCString(c,"set-max-intset-entries"); 699 addReplyBulkLongLong(c,server.set_max_intset_entries); 700 matches++; 701 } 702 if (stringmatch(pattern,"zset-max-ziplist-entries",0)) { 703 addReplyBulkCString(c,"zset-max-ziplist-entries"); 704 addReplyBulkLongLong(c,server.zset_max_ziplist_entries); 705 matches++; 706 } 707 if (stringmatch(pattern,"zset-max-ziplist-value",0)) { 708 addReplyBulkCString(c,"zset-max-ziplist-value"); 709 addReplyBulkLongLong(c,server.zset_max_ziplist_value); 710 matches++; 711 } 712 if (stringmatch(pattern,"lua-time-limit",0)) { 713 addReplyBulkCString(c,"lua-time-limit"); 714 addReplyBulkLongLong(c,server.lua_time_limit); 715 matches++; 716 } 717 if (stringmatch(pattern,"slowlog-log-slower-than",0)) { 718 addReplyBulkCString(c,"slowlog-log-slower-than"); 719 addReplyBulkLongLong(c,server.slowlog_log_slower_than); 720 matches++; 721 } 722 if (stringmatch(pattern,"slowlog-max-len",0)) { 723 addReplyBulkCString(c,"slowlog-max-len"); 724 addReplyBulkLongLong(c,server.slowlog_max_len); 725 matches++; 726 } 727 if (stringmatch(pattern,"loglevel",0)) { 728 char *s; 729 730 switch(server.verbosity) { 731 case REDIS_WARNING: s = "warning"; break; 732 case REDIS_VERBOSE: s = "verbose"; break; 733 case REDIS_NOTICE: s = "notice"; break; 734 case REDIS_DEBUG: s = "debug"; break; 735 default: s = "unknown"; break; /* too harmless to panic */ 736 } 737 addReplyBulkCString(c,"loglevel"); 738 addReplyBulkCString(c,s); 739 matches++; 740 } 741 setDeferredMultiBulkLength(c,replylen,matches*2); 742 } 743 744 void configCommand(redisClient *c) { 745 if (!strcasecmp(c->argv[1]->ptr,"set")) { 746 if (c->argc != 4) goto badarity; 747 configSetCommand(c); 748 } else if (!strcasecmp(c->argv[1]->ptr,"get")) { 749 if (c->argc != 3) goto badarity; 750 configGetCommand(c); 751 } else if (!strcasecmp(c->argv[1]->ptr,"resetstat")) { 752 if (c->argc != 2) goto badarity; 753 server.stat_keyspace_hits = 0; 754 server.stat_keyspace_misses = 0; 755 server.stat_numcommands = 0; 756 server.stat_numconnections = 0; 757 server.stat_expiredkeys = 0; 758 resetCommandTableStats(); 759 addReply(c,shared.ok); 760 } else { 761 addReplyError(c, 762 "CONFIG subcommand must be one of GET, SET, RESETSTAT"); 763 } 764 return; 765 766 badarity: 767 addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s", 768 (char*) c->argv[1]->ptr); 769 } 770