From daf6f06138591621172d77611ad7ae22f6ff4464 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sun, 30 Oct 2016 19:23:03 +0200 Subject: [PATCH] MXS-304: Support nested configuration files Given a config file "config.cnf", we look for the directory "config.cnf.d" and recursively in that hierarhcy load all files whose suffix is ".cnf"; other files are ignored. Currently duplicate sections are checked on a file by file basis. That will be changed so that duplicate sections are not allowed across all the files. --- server/core/config.c | 127 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 9 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 94320f8d1..fa080df3d 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -46,6 +46,7 @@ */ #include #include +#include #include #include #include @@ -62,6 +63,7 @@ #include #include #include +#include #include extern int setipaddress(struct in_addr *, char *); @@ -373,37 +375,108 @@ static bool config_load_single_file(const char* file, CONFIG_CONTEXT* context) if (rval > 0) { snprintf(errorbuffer, sizeof(errorbuffer), - "Failed to parse configuration file. Error on line %d.", rval); + "Failed to parse configuration file %s. Error on line %d.", file, rval); } else if (rval == -1) { snprintf(errorbuffer, sizeof(errorbuffer), - "Failed to parse configuration file. Failed to open file."); + "Failed to parse configuration file %s. Could not open file.", file); } else { snprintf(errorbuffer, sizeof(errorbuffer), - "Failed to parse configuration file. Memory allocation failed."); + "Failed to parse configuration file %s. Memory allocation failed.", file); } MXS_ERROR("%s", errorbuffer); } } + if (rval == 0) + { + MXS_NOTICE("Loaded %s.", file); + } + return rval == 0; } +/** + * The current parsing contex, must be managed explicitly since the ftw callback + * can not have user data. + */ +static CONFIG_CONTEXT *current_context; + +/** + * The nftw callback. + * + * @see man ftw + */ +int config_cb(const char* fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +{ + int rval = 0; + + if (typeflag == FTW_F) // We are only interested in files, + { + const char* filename = fpath + ftwbuf->base; + const char* dot = strrchr(filename, '.'); + + if (dot) // that must have a suffix, + { + const char* suffix = dot + 1; + + if (strcmp(suffix, "cnf") == 0) // that is ".cnf". + { + if (!config_load_single_file(fpath, current_context)) + { + rval = -1; + } + } + } + } + + return rval; +} + +/** + * Loads all configuration files in a directory hierarchy. + * + * Only files with the suffix ".cnf" are considered to be configuration files. + * + * @param dir The directory. + * @param context The configuration context. + * + * @return True, if all configuration files in the directory hierarchy could be loaded, + * otherwise false. + */ +static bool config_load_dir(const char *dir, CONFIG_CONTEXT *context) +{ + // Since there is no way to pass userdata to the callback, we need to store + // the current context into a static variable. Consequently, we need lock. + // Should not matter since config_load() is called once at startup. + static SPINLOCK lock = SPINLOCK_INIT; + + int nopenfd = 5; // Maximum concurrently opened directory descriptors + + spinlock_acquire(&lock); + current_context = context; + int rv = nftw(dir, config_cb, nopenfd, FTW_PHYS); + current_context = NULL; + spinlock_release(&lock); + + return rv == 0; +} + /** * @brief Load the configuration file for the MaxScale * * This function will parse the configuration file, check for duplicate sections, * validate the module parameters and finally turn it into a set of objects. * - * @param file The filename of the configuration file + * @param filename The filename of the configuration file * @return True on success, false on fatal error */ bool -config_load(const char *file) +config_load(const char *filename) { bool rval = false; @@ -416,13 +489,49 @@ config_load(const char *file) CONFIG_CONTEXT config = {.object = ""}; - if (config_load_single_file(file, &config)) + if (config_load_single_file(filename, &config)) { - config_file = file; + const char DIR_SUFFIX[] = ".d"; - if (check_config_objects(config.next) && process_config_context(config.next)) + char dir[strlen(filename) + sizeof(DIR_SUFFIX)]; + strcpy(dir, filename); + strcat(dir, DIR_SUFFIX); + + rval = true; + + struct stat st; + if (stat(dir, &st) == -1) { - rval = true; + if (errno == ENOENT) + { + MXS_NOTICE("%s does not exist, not reading.", dir); + } + else + { + char errbuf[MXS_STRERROR_BUFLEN]; + MXS_WARNING("Could not access %s, not reading: %s", + dir, strerror_r(errno, errbuf, sizeof(errbuf))); + } + } + else + { + if (S_ISDIR(st.st_mode)) + { + rval = config_load_dir(dir, &config); + } + else + { + MXS_WARNING("%s exists, but it is not a directory. Ignoring.", dir); + } + } + + if (rval) + { + if (check_config_objects(config.next) && process_config_context(config.next)) + { + config_file = filename; + rval = true; + } } }