MXS-304: Check duplicate sections across all files
This commit is contained in:
@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user