Support glob pattern matching for config include files (#8980)

This will allow distros to use an "include conf.d/*.conf" statement in the default configuration file
which will facilitate customization across upgrades/downgrades.

The change itself is trivial: instead of opening an individual file, the glob call creates a vector of files to open, and each file is opened in turn, and its content is added to the configuration.
This commit is contained in:
Sam Bortman 2021-06-16 14:59:38 -04:00 committed by GitHub
parent 362786c58a
commit c2b93ff83f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 8 deletions

View File

@ -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 #####################################

View File

@ -33,6 +33,8 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <glob.h>
#include <string.h>
/*-----------------------------------------------------------------------------
* 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");