MXS-304: Check duplicate sections across all files

This commit is contained in:
Johan Wikman
2016-10-30 21:50:36 +02:00
parent 2116c1549c
commit f71f41b0b8

View File

@ -96,7 +96,7 @@ int config_get_ifaddr(unsigned char *output);
static int config_get_release_string(char* release); static int config_get_release_string(char* release);
FEEDBACK_CONF *config_get_feedback_data(); FEEDBACK_CONF *config_get_feedback_data();
void config_add_param(CONFIG_CONTEXT*, char*, char*); void config_add_param(CONFIG_CONTEXT*, char*, char*);
bool config_has_duplicate_sections(const char* config); bool config_has_duplicate_sections(const char* config, DUPLICATE_CONTEXT* context);
int create_new_service(CONFIG_CONTEXT *obj); int create_new_service(CONFIG_CONTEXT *obj);
int create_new_server(CONFIG_CONTEXT *obj); int create_new_server(CONFIG_CONTEXT *obj);
int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE* monitorhash); int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE* monitorhash);
@ -423,17 +423,24 @@ ini_handler(void *userdata, const char *section, const char *name, const char *v
* Load single configuration file. * Load single configuration file.
* *
* @param file The file to load. * @param file The file to load.
* @param context The context used when parsing. * @param dcontext The context object used when tracking duplicate sections.
* @param ccontext The context object used when parsing.
* *
* @return True if the file could be parsed, false otherwise. * @return True if the file could be parsed, false otherwise.
*/ */
static bool config_load_single_file(const char* file, CONFIG_CONTEXT* context) static bool config_load_single_file(const char* file,
DUPLICATE_CONTEXT* dcontext,
CONFIG_CONTEXT* ccontext)
{ {
int rval = -1; int rval = -1;
if (!config_has_duplicate_sections(file)) // With multiple configuration files being loaded, we need to log the file
// currently being loaded so that the context is clear in case of errors.
MXS_NOTICE("Loading %s.", file);
if (!config_has_duplicate_sections(file, dcontext))
{ {
if ((rval = ini_parse(file, ini_handler, context)) != 0) if ((rval = ini_parse(file, ini_handler, ccontext)) != 0)
{ {
char errorbuffer[1024 + 1]; char errorbuffer[1024 + 1];
@ -457,19 +464,15 @@ static bool config_load_single_file(const char* file, CONFIG_CONTEXT* context)
} }
} }
if (rval == 0)
{
MXS_NOTICE("Loaded %s.", file);
}
return rval == 0; return rval == 0;
} }
/** /**
* The current parsing contex, must be managed explicitly since the ftw callback * The current parsing contexts must be managed explicitly since the ftw callback
* can not have user data. * can not have user data.
*/ */
static CONFIG_CONTEXT *current_context; static CONFIG_CONTEXT *current_ccontext;
static DUPLICATE_CONTEXT *current_dcontext;
/** /**
* The nftw callback. * The nftw callback.
@ -491,7 +494,10 @@ int config_cb(const char* fpath, const struct stat *sb, int typeflag, struct FTW
if (strcmp(suffix, "cnf") == 0) // that is ".cnf". if (strcmp(suffix, "cnf") == 0) // that is ".cnf".
{ {
if (!config_load_single_file(fpath, current_context)) ss_dassert(current_dcontext);
ss_dassert(current_ccontext);
if (!config_load_single_file(fpath, current_dcontext, current_ccontext))
{ {
rval = -1; rval = -1;
} }
@ -508,12 +514,13 @@ int config_cb(const char* fpath, const struct stat *sb, int typeflag, struct FTW
* Only files with the suffix ".cnf" are considered to be configuration files. * Only files with the suffix ".cnf" are considered to be configuration files.
* *
* @param dir The directory. * @param dir The directory.
* @param context The configuration context. * @param dcontext The duplicate section context.
* @param ccontext The configuration context.
* *
* @return True, if all configuration files in the directory hierarchy could be loaded, * @return True, if all configuration files in the directory hierarchy could be loaded,
* otherwise false. * otherwise false.
*/ */
static bool config_load_dir(const char *dir, CONFIG_CONTEXT *context) static bool config_load_dir(const char *dir, DUPLICATE_CONTEXT *dcontext, CONFIG_CONTEXT *ccontext)
{ {
// Since there is no way to pass userdata to the callback, we need to store // 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. // the current context into a static variable. Consequently, we need lock.
@ -523,9 +530,11 @@ static bool config_load_dir(const char *dir, CONFIG_CONTEXT *context)
int nopenfd = 5; // Maximum concurrently opened directory descriptors int nopenfd = 5; // Maximum concurrently opened directory descriptors
spinlock_acquire(&lock); spinlock_acquire(&lock);
current_context = context; current_dcontext = dcontext;
current_ccontext = ccontext;
int rv = nftw(dir, config_cb, nopenfd, FTW_PHYS); int rv = nftw(dir, config_cb, nopenfd, FTW_PHYS);
current_context = NULL; current_ccontext = NULL;
current_dcontext = NULL;
spinlock_release(&lock); spinlock_release(&lock);
return rv == 0; return rv == 0;
@ -552,9 +561,13 @@ config_load(const char *filename)
global_defaults(); global_defaults();
feedback_defaults(); feedback_defaults();
CONFIG_CONTEXT config = {.object = ""}; DUPLICATE_CONTEXT dcontext;
if (config_load_single_file(filename, &config)) if (duplicate_context_init(&dcontext))
{
CONFIG_CONTEXT ccontext = {.object = ""};
if (config_load_single_file(filename, &dcontext, &ccontext))
{ {
const char DIR_SUFFIX[] = ".d"; const char DIR_SUFFIX[] = ".d";
@ -582,7 +595,7 @@ config_load(const char *filename)
{ {
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
{ {
rval = config_load_dir(dir, &config); rval = config_load_dir(dir, &dcontext, &ccontext);
} }
else else
{ {
@ -592,7 +605,7 @@ config_load(const char *filename)
if (rval) if (rval)
{ {
if (check_config_objects(config.next) && process_config_context(config.next)) if (check_config_objects(ccontext.next) && process_config_context(ccontext.next))
{ {
config_file = filename; config_file = filename;
rval = true; rval = true;
@ -600,7 +613,10 @@ config_load(const char *filename)
} }
} }
free_config_context(config.next); free_config_context(ccontext.next);
duplicate_context_finish(&dcontext);
}
return rval; return rval;
} }
@ -612,15 +628,24 @@ config_load(const char *filename)
int int
config_reload() config_reload()
{ {
CONFIG_CONTEXT config; int rval = 0;
int rval;
if (!config_file) if (config_file)
{ {
return 0; return 0;
} }
if (config_has_duplicate_sections(config_file)) DUPLICATE_CONTEXT dcontext;
if (!duplicate_context_init(&dcontext))
{
return 0;
}
bool duplicates = config_has_duplicate_sections(config_file, &dcontext);
duplicate_context_finish(&dcontext);
if (duplicates)
{ {
return 0; return 0;
} }
@ -632,6 +657,7 @@ config_reload()
global_defaults(); global_defaults();
CONFIG_CONTEXT config;
config.object = ""; config.object = "";
config.next = NULL; config.next = NULL;
@ -2268,40 +2294,43 @@ GATEWAY_CONF* config_get_global_options()
/** /**
* Check if sections are defined multiple times in the configuration file. * Check if sections are defined multiple times in the configuration file.
* @param config Path to the configuration file *
* @param filename Path to the configuration file
* @param context The context object used for tracking the duplication
* section information.
*
* @return True if duplicate sections were found or an error occurred * @return True if duplicate sections were found or an error occurred
*/ */
bool config_has_duplicate_sections(const char* config) bool config_has_duplicate_sections(const char* filename, DUPLICATE_CONTEXT* context)
{ {
bool rval = false; bool rval = false;
DUPLICATE_CONTEXT context;
int size = 1024; int size = 1024;
char *buffer = MXS_MALLOC(size * sizeof(char)); char *buffer = MXS_MALLOC(size * sizeof(char));
if (buffer && duplicate_context_init(&context)) if (buffer)
{ {
FILE* file = fopen(config, "r"); FILE* file = fopen(filename, "r");
if (file) if (file)
{ {
while (maxscale_getline(&buffer, &size, file) > 0) while (maxscale_getline(&buffer, &size, file) > 0)
{ {
if (pcre2_match(context.re, (PCRE2_SPTR) buffer, if (pcre2_match(context->re, (PCRE2_SPTR) buffer,
PCRE2_ZERO_TERMINATED, 0, 0, PCRE2_ZERO_TERMINATED, 0, 0,
context.mdata, NULL) > 0) context->mdata, NULL) > 0)
{ {
/** /**
* Neither of the PCRE2 calls will fail since we know the pattern * Neither of the PCRE2 calls will fail since we know the pattern
* beforehand and we allocate enough memory from the stack * beforehand and we allocate enough memory from the stack
*/ */
PCRE2_SIZE len; PCRE2_SIZE len;
pcre2_substring_length_bynumber(context.mdata, 1, &len); pcre2_substring_length_bynumber(context->mdata, 1, &len);
len += 1; /** one for the null terminator */ len += 1; /** one for the null terminator */
PCRE2_UCHAR section[len]; PCRE2_UCHAR section[len];
pcre2_substring_copy_bynumber(context.mdata, 1, section, &len); pcre2_substring_copy_bynumber(context->mdata, 1, section, &len);
if (hashtable_add(context.hash, section, "") == 0) if (hashtable_add(context->hash, section, "") == 0)
{ {
MXS_ERROR("Duplicate section found: %s", section); MXS_ERROR("Duplicate section found: %s", section);
rval = true; rval = true;
@ -2313,12 +2342,10 @@ bool config_has_duplicate_sections(const char* config)
else else
{ {
char errbuf[MXS_STRERROR_BUFLEN]; char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to open file '%s': %s", config, MXS_ERROR("Failed to open file '%s': %s", filename,
strerror_r(errno, errbuf, sizeof(errbuf))); strerror_r(errno, errbuf, sizeof(errbuf)));
rval = true; rval = true;
} }
duplicate_context_finish(&context);
} }
else else
{ {