diff --git a/redis.conf b/redis.conf index 1381f1a50..9e72d9c45 100644 --- a/redis.conf +++ b/redis.conf @@ -32,8 +32,17 @@ # If instead you are interested in using includes to override configuration # options, it is better to use include as the last line. # +# Included paths may contain wildcards. All files matching the wildcards will +# be included in alphabetical order. +# Note that if an include path contains a wildcards but no files match it when +# the server is started, the include statement will be ignored and no error will +# be emitted. It is safe, therefore, to include wildcard files from empty +# directories. +# # include /path/to/local.conf # include /path/to/other.conf +# include /path/to/fragments/*.conf +# ################################## MODULES ##################################### diff --git a/src/config.c b/src/config.c index 354cc987a..4324af3d3 100644 --- a/src/config.c +++ b/src/config.c @@ -33,6 +33,8 @@ #include #include +#include +#include /*----------------------------------------------------------------------------- * Config file name-value maps. @@ -648,19 +650,58 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options) { sds config = sdsempty(); char buf[CONFIG_MAX_LINE+1]; FILE *fp; + glob_t globbuf; /* Load the file content */ if (filename) { - if ((fp = fopen(filename,"r")) == NULL) { - serverLog(LL_WARNING, - "Fatal error, can't open config file '%s': %s", - filename, strerror(errno)); - exit(1); + + /* The logic for handling wildcards has slightly different behavior in cases where + * there is a failure to locate the included file. + * Whether or not a wildcard is specified, we should ALWAYS log errors when attempting + * to open included config files. + * + * However, we desire a behavioral difference between instances where a wildcard was + * specified and those where it hasn't: + * no wildcards : attempt to open the specified file and fail with a logged error + * if the file cannot be found and opened. + * with wildcards : attempt to glob the specified pattern; if no files match the + * pattern, then gracefully continue on to the next entry in the + * config file, as if the current entry was never encountered. + * This will allow for empty conf.d directories to be included. */ + + if (strchr(filename, '*') || strchr(filename, '?') || strchr(filename, '[')) { + /* A wildcard character detected in filename, so let us use glob */ + if (glob(filename, 0, NULL, &globbuf) == 0) { + + for (size_t i = 0; i < globbuf.gl_pathc; i++) { + if ((fp = fopen(globbuf.gl_pathv[i], "r")) == NULL) { + serverLog(LL_WARNING, + "Fatal error, can't open config file '%s': %s", + globbuf.gl_pathv[i], strerror(errno)); + exit(1); + } + while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) + config = sdscat(config,buf); + fclose(fp); + } + + globfree(&globbuf); + } + } else { + /* No wildcard in filename means we can use the original logic to read and + * potentially fail traditionally */ + if ((fp = fopen(filename, "r")) == NULL) { + serverLog(LL_WARNING, + "Fatal error, can't open config file '%s': %s", + filename, strerror(errno)); + exit(1); + } + while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) + config = sdscat(config,buf); + fclose(fp); } - while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) - config = sdscat(config,buf); - fclose(fp); } + /* Append content from stdin */ if (config_from_stdin) { serverLog(LL_WARNING,"Reading config from stdin");