From 67c6f0f630e1083f4fdad3efc12be691bf13dd55 Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 1 Dec 2011 13:44:53 +0100 Subject: [PATCH] Support for command line configuration options for redis-server. --- src/config.c | 86 ++++++++++++++++++++++++++++++++-------------------- src/redis.c | 32 ++++++++++++++++--- src/redis.h | 2 +- 3 files changed, 82 insertions(+), 38 deletions(-) diff --git a/src/config.c b/src/config.c index 9f71aaf6396a..acda67bb74c9 100644 --- a/src/config.c +++ b/src/config.c @@ -23,39 +23,25 @@ void resetServerSaveParams() { server.saveparamslen = 0; } -/* I agree, this is a very rudimental way to load a configuration... - will improve later if the config gets more complex */ -void loadServerConfig(char *filename) { - FILE *fp; - char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL; - int linenum = 0; - sds line = NULL; - - if (filename[0] == '-' && filename[1] == '\0') - fp = stdin; - else { - if ((fp = fopen(filename,"r")) == NULL) { - redisLog(REDIS_WARNING, "Fatal error, can't open config file '%s'", filename); - exit(1); - } - } +void loadServerConfigFromString(char *config) { + char *err = NULL; + int linenum = 0, totlines, i; + sds *lines; + + lines = sdssplitlen(config,strlen(config),"\n",1,&totlines); - while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) { + for (i = 0; i < totlines; i++) { sds *argv; - int argc, j; + int argc; - linenum++; - line = sdsnew(buf); - line = sdstrim(line," \t\r\n"); + linenum = i+1; + lines[i] = sdstrim(lines[i]," \t\r\n"); /* Skip comments and blank lines*/ - if (line[0] == '#' || line[0] == '\0') { - sdsfree(line); - continue; - } + if (lines[i][0] == '#' || lines[i][0] == '\0') continue; /* Split into arguments */ - argv = sdssplitargs(line,&argc); + argv = sdssplitargs(lines[i],&argc); sdstolower(argv[0]); /* Execute config directives */ @@ -162,7 +148,7 @@ void loadServerConfig(char *filename) { err = "Invalid number of databases"; goto loaderr; } } else if (!strcasecmp(argv[0],"include") && argc == 2) { - loadServerConfig(argv[1]); + loadServerConfig(argv[1],NULL); } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) { server.maxclients = atoi(argv[1]); } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) { @@ -325,22 +311,56 @@ void loadServerConfig(char *filename) { } else { err = "Bad directive or wrong number of arguments"; goto loaderr; } - for (j = 0; j < argc; j++) - sdsfree(argv[j]); - zfree(argv); - sdsfree(line); + sdsfreesplitres(argv,argc); } - if (fp != stdin) fclose(fp); + sdsfreesplitres(lines,totlines); return; loaderr: fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n"); fprintf(stderr, "Reading the configuration file, at line %d\n", linenum); - fprintf(stderr, ">>> '%s'\n", line); + fprintf(stderr, ">>> '%s'\n", lines[i]); fprintf(stderr, "%s\n", err); exit(1); } +/* Load the server configuration from the specified filename. + * The function appends the additional configuration directives stored + * in the 'options' string to the config file before loading. + * + * Both filename and options can be NULL, in such a case are considered + * emtpy. This way loadServerConfig can be used to just load a file or + * just load a string. */ +void loadServerConfig(char *filename, char *options) { + sds config = sdsempty(); + char buf[REDIS_CONFIGLINE_MAX+1]; + + /* Load the file content */ + if (filename) { + FILE *fp; + + if (filename[0] == '-' && filename[1] == '\0') { + fp = stdin; + } else { + if ((fp = fopen(filename,"r")) == NULL) { + redisLog(REDIS_WARNING, + "Fatal error, can't open config file '%s'", filename); + exit(1); + } + } + while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) + config = sdscat(config,buf); + if (fp != stdin) fclose(fp); + } + /* Append the additional options */ + if (options) { + config = sdscat(config,"\n"); + config = sdscat(config,options); + } + loadServerConfigFromString(config); + sdsfree(config); +} + /*----------------------------------------------------------------------------- * CONFIG command for remote configuration *----------------------------------------------------------------------------*/ diff --git a/src/redis.c b/src/redis.c index 3c287756dbfc..30ba1bce85c7 100644 --- a/src/redis.c +++ b/src/redis.c @@ -2008,15 +2008,39 @@ int main(int argc, char **argv) { zmalloc_enable_thread_safeness(); initServerConfig(); - if (argc == 2) { + if (argc >= 2) { + int j = 1; /* First option to parse in argv[] */ + sds options = sdsempty(); + char *configfile = NULL; + + /* Handle special options --help and --version */ if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) version(); if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) usage(); + /* First argument is the config file name? */ + if (argv[j][0] != '-' || argv[j][1] != '-') + configfile = argv[j++]; + /* All the other options are parsed and conceptually appended to the + * configuration file. For instance --port 6380 will generate the + * string "port 6380\n" to be parsed after the actual file name + * is parsed, if any. */ + while(j != argc) { + if (argv[j][0] == '-' && argv[j][1] == '-') { + /* Option name */ + if (sdslen(options)) options = sdscat(options,"\n"); + options = sdscat(options,argv[j]+2); + options = sdscat(options," "); + } else { + /* Option argument */ + options = sdscatrepr(options,argv[j],strlen(argv[j])); + options = sdscat(options," "); + } + j++; + } resetServerSaveParams(); - loadServerConfig(argv[1]); - } else if ((argc > 2)) { - usage(); + loadServerConfig(configfile,options); + sdsfree(options); } else { redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'"); } diff --git a/src/redis.h b/src/redis.h index 431eeb1404b1..c425691edc4b 100644 --- a/src/redis.h +++ b/src/redis.h @@ -954,7 +954,7 @@ int listMatchPubsubPattern(void *a, void *b); int pubsubPublishMessage(robj *channel, robj *message); /* Configuration */ -void loadServerConfig(char *filename); +void loadServerConfig(char *filename, char *options); void appendServerSaveParams(time_t seconds, int changes); void resetServerSaveParams();