--- libmultipath/config.c | 56 +++++++++++++++++++++++++++++++- libmultipath/config.h | 2 + libmultipath/defaults.h | 1 libmultipath/dict.c | 69 +++++++++++++++++++++++++++++++++++---- libmultipath/parser.c | 78 +++++++++++++++++++++++---------------------- libmultipath/parser.h | 3 - multipath.conf.annotated | 10 +++++ multipath.conf.defaults | 1 multipath/multipath.conf.5 | 7 ++++ 9 files changed, 179 insertions(+), 48 deletions(-) Index: multipath-tools-130222/libmultipath/parser.c =================================================================== --- multipath-tools-130222.orig/libmultipath/parser.c +++ multipath-tools-130222/libmultipath/parser.c @@ -18,6 +18,7 @@ */ #include +#include #include "parser.h" #include "memory.h" @@ -453,14 +454,15 @@ set_value(vector strvec) /* non-recursive configuration stream handler */ static int kw_level = 0; -int warn_on_duplicates(vector uniques, char *str) +int warn_on_duplicates(vector uniques, char *str, char *file) { char *tmp; int i; vector_foreach_slot(uniques, tmp, i) { if (!strcmp(str, tmp)) { - condlog(1, "multipath.conf line %d, duplicate keyword: %s", line_nr, str); + condlog(1, "%s line %d, duplicate keyword: %s", + file, line_nr, str); return 0; } } @@ -496,65 +498,70 @@ is_sublevel_keyword(char *str) } int -validate_config_strvec(vector strvec) +validate_config_strvec(vector strvec, char *file) { char *str; int i; str = VECTOR_SLOT(strvec, 0); if (str == NULL) { - condlog(0, "can't parse option on line %d of config file", - line_nr); + condlog(0, "can't parse option on line %d of %s", + line_nr, file); return -1; } if (*str == '}') { if (VECTOR_SIZE(strvec) > 1) - condlog(0, "ignoring extra data starting with '%s' on line %d of config file", (char *)VECTOR_SLOT(strvec, 1), line_nr); + condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 1), line_nr, file); return 0; } if (*str == '{') { - condlog(0, "invalid keyword '%s' on line %d of config file", str, line_nr); + condlog(0, "invalid keyword '%s' on line %d of %s", + str, line_nr, file); return -1; } if (is_sublevel_keyword(str)) { str = VECTOR_SLOT(strvec, 1); if (str == NULL) - condlog(0, "missing '{' on line %d of config file", line_nr); + condlog(0, "missing '{' on line %d of %s", + line_nr, file); else if (*str != '{') - condlog(0, "expecting '{' on line %d of config file. found '%s'", line_nr, str); + condlog(0, "expecting '{' on line %d of %s. found '%s'", + line_nr, file, str); else if (VECTOR_SIZE(strvec) > 2) - condlog(0, "ignoring extra data starting with '%s' on line %d of config file", (char *)VECTOR_SLOT(strvec, 2), line_nr); + condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file); return 0; } str = VECTOR_SLOT(strvec, 1); if (str == NULL) { - condlog(0, "missing value for option '%s' on line %d of config file", (char *)VECTOR_SLOT(strvec, 0), line_nr); + condlog(0, "missing value for option '%s' on line %d of %s", + (char *)VECTOR_SLOT(strvec, 0), line_nr, file); return -1; } if (*str != '"') { if (VECTOR_SIZE(strvec) > 2) - condlog(0, "ignoring extra data starting with '%s' on line %d of config file", (char *)VECTOR_SLOT(strvec, 2), line_nr); + condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file); return 0; } for (i = 2; i < VECTOR_SIZE(strvec); i++) { str = VECTOR_SLOT(strvec, i); if (str == NULL) { - condlog(0, "can't parse value on line %d of config file", line_nr); + condlog(0, "can't parse value on line %d of %s", + line_nr, file); return -1; } if (*str == '"') { if (VECTOR_SIZE(strvec) > i + 1) - condlog(0, "ignoring extra data starting with '%s' on line %d of config file", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr); + condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr, file); return 0; } } - condlog(0, "missing closing quotes on line %d of config file", - line_nr); + condlog(0, "missing closing quotes on line %d of %s", + line_nr, file); return 0; } -int -process_stream(vector keywords) +static int +process_stream(vector keywords, char *file) { int i; int r = 0; @@ -583,7 +590,7 @@ process_stream(vector keywords) if (!strvec) continue; - if (validate_config_strvec(strvec) != 0) { + if (validate_config_strvec(strvec, file) != 0) { free_strvec(strvec); continue; } @@ -595,8 +602,8 @@ process_stream(vector keywords) free_strvec(strvec); break; } - condlog(0, "unmatched '%s' at line %d of config file", - EOB, line_nr); + condlog(0, "unmatched '%s' at line %d of %s", + EOB, line_nr, file); } for (i = 0; i < VECTOR_SIZE(keywords); i++) { @@ -604,7 +611,7 @@ process_stream(vector keywords) if (!strcmp(keyword->string, str)) { if (keyword->unique && - warn_on_duplicates(uniques, str)) { + warn_on_duplicates(uniques, str, file)) { r = 1; free_strvec(strvec); goto out; @@ -614,15 +621,15 @@ process_stream(vector keywords) if (keyword->sub) { kw_level++; - r += process_stream(keyword->sub); + r += process_stream(keyword->sub, file); kw_level--; } break; } } if (i >= VECTOR_SIZE(keywords)) - condlog(1, "multipath.conf +%d, invalid keyword: %s", - line_nr, str); + condlog(1, "%s line %d, invalid keyword: %s", + file, line_nr, str); free_strvec(strvec); } @@ -646,27 +653,24 @@ int alloc_keywords(void) /* Data initialization */ int -init_data(char *conf_file, void (*init_keywords) (void)) +process_file(char *file) { int r; - stream = fopen(conf_file, "r"); + if (!keywords) { + condlog(0, "No keywords alocated"); + return 1; + } + stream = fopen(file, "r"); if (!stream) { - syslog(LOG_WARNING, "Configuration file open problem"); + condlog(0, "couldn't open configuration file '%s': %s", + file, strerror(errno)); return 1; } - /* Init Keywords structure */ - (*init_keywords) (); - -/* Dump configuration * - vector_dump(keywords); - dump_keywords(keywords, 0); -*/ - /* Stream handling */ line_nr = 0; - r = process_stream(keywords); + r = process_stream(keywords, file); fclose(stream); //free_keywords(keywords); Index: multipath-tools-130222/libmultipath/dict.c =================================================================== --- multipath-tools-130222.orig/libmultipath/dict.c +++ multipath-tools-130222/libmultipath/dict.c @@ -117,6 +117,8 @@ reassign_maps_handler(vector strvec) static int multipath_dir_handler(vector strvec) { + if (conf->multipath_dir) + FREE(conf->multipath_dir); conf->multipath_dir = set_value(strvec); if (!conf->multipath_dir) @@ -128,6 +130,8 @@ multipath_dir_handler(vector strvec) static int def_selector_handler(vector strvec) { + if (conf->selector) + FREE(conf->selector); conf->selector = set_value(strvec); if (!conf->selector) @@ -155,6 +159,8 @@ def_pgpolicy_handler(vector strvec) static int def_uid_attribute_handler(vector strvec) { + if (conf->uid_attribute) + FREE(conf->uid_attribute); conf->uid_attribute = set_value(strvec); if (!conf->uid_attribute) @@ -166,6 +172,8 @@ def_uid_attribute_handler(vector strvec) static int def_prio_handler(vector strvec) { + if (conf->prio_name) + FREE(conf->prio_name); conf->prio_name = set_value(strvec); if (!conf->prio_name) @@ -177,6 +185,8 @@ def_prio_handler(vector strvec) static int def_alias_prefix_handler(vector strvec) { + if (conf->alias_prefix) + FREE(conf->alias_prefix); conf->alias_prefix = set_value(strvec); if (!conf->alias_prefix) @@ -188,6 +198,8 @@ def_alias_prefix_handler(vector strvec) static int def_prio_args_handler(vector strvec) { + if (conf->prio_args) + FREE(conf->prio_args); conf->prio_args = set_value(strvec); if (!conf->prio_args) @@ -199,6 +211,8 @@ def_prio_args_handler(vector strvec) static int def_features_handler(vector strvec) { + if (conf->features) + FREE(conf->features); conf->features = set_value(strvec); if (!conf->features) @@ -210,6 +224,8 @@ def_features_handler(vector strvec) static int def_path_checker_handler(vector strvec) { + if (conf->checker_name) + FREE(conf->checker_name); conf->checker_name = set_value(strvec); if (!conf->checker_name) @@ -432,6 +448,23 @@ def_no_path_retry_handler(vector strvec) return 0; } + +static int +def_config_dir_handler(vector strvec) +{ + /* this is only valid in the main config file */ + if (conf->processed_main_config) + return 0; + if (conf->config_dir) + FREE(conf->config_dir); + conf->config_dir = set_value(strvec); + + if (!conf->config_dir) + return 1; + + return 0; +} + static int def_queue_without_daemon(vector strvec) { @@ -611,6 +644,8 @@ def_names_handler(vector strvec) static int bindings_file_handler(vector strvec) { + if (conf->bindings_file) + FREE(conf->bindings_file); conf->bindings_file = set_value(strvec); if (!conf->bindings_file) @@ -622,6 +657,8 @@ bindings_file_handler(vector strvec) static int wwids_file_handler(vector strvec) { + if (conf->wwids_file) + FREE(conf->wwids_file); conf->wwids_file = set_value(strvec); if (!conf->wwids_file) @@ -770,9 +807,12 @@ def_ignore_new_boot_devs_handler(vector static int blacklist_handler(vector strvec) { - conf->blist_devnode = vector_alloc(); - conf->blist_wwid = vector_alloc(); - conf->blist_device = vector_alloc(); + if (!conf->blist_devnode) + conf->blist_devnode = vector_alloc(); + if (!conf->blist_wwid) + conf->blist_wwid = vector_alloc(); + if (!conf->blist_device) + conf->blist_device = vector_alloc(); if (!conf->blist_devnode || !conf->blist_wwid || !conf->blist_device) return 1; @@ -783,9 +823,12 @@ blacklist_handler(vector strvec) static int blacklist_exceptions_handler(vector strvec) { - conf->elist_devnode = vector_alloc(); - conf->elist_wwid = vector_alloc(); - conf->elist_device = vector_alloc(); + if (!conf->elist_devnode) + conf->elist_devnode = vector_alloc(); + if (!conf->elist_wwid) + conf->elist_wwid = vector_alloc(); + if (!conf->elist_device) + conf->elist_device = vector_alloc(); if (!conf->elist_devnode || !conf->elist_wwid || !conf->elist_device) return 1; @@ -1480,7 +1523,8 @@ hw_deferred_remove_handler(vector strvec static int multipaths_handler(vector strvec) { - conf->mptable = vector_alloc(); + if (!conf->mptable) + conf->mptable = vector_alloc(); if (!conf->mptable) return 1; @@ -2945,6 +2989,16 @@ snprint_def_ignore_new_boot_devs(char * return snprintf(buff, len, "no"); } + +static int +snprint_def_config_dir (char * buff, int len, void * data) +{ + if (!conf->config_dir) + return 0; + + return snprintf(buff, len, "\"%s\"", conf->config_dir); +} + static int snprint_ble_simple (char * buff, int len, void * data) { @@ -3016,6 +3070,7 @@ init_keywords(void) install_keyword("force_sync", &def_force_sync_handler, &snprint_def_force_sync); install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove); install_keyword("ignore_new_boot_devs", &def_ignore_new_boot_devs_handler, &snprint_def_ignore_new_boot_devs); + install_keyword("config_dir", &def_config_dir_handler, &snprint_def_config_dir); __deprecated install_keyword("default_selector", &def_selector_handler, NULL); __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL); Index: multipath-tools-130222/libmultipath/parser.h =================================================================== --- multipath-tools-130222.orig/libmultipath/parser.h +++ multipath-tools-130222/libmultipath/parser.h @@ -76,9 +76,8 @@ extern int read_line(char *buf, int size extern vector read_value_block(void); extern int alloc_value_block(vector strvec, void (*alloc_func) (vector)); extern void *set_value(vector strvec); -extern int process_stream(vector keywords); extern int alloc_keywords(void); -extern int init_data(char *conf_file, void (*init_keywords) (void)); +extern int process_file(char *conf_file); extern struct keyword * find_keyword(vector v, char * name); void set_current_keywords (vector *k); int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, Index: multipath-tools-130222/libmultipath/config.c =================================================================== --- multipath-tools-130222.orig/libmultipath/config.c +++ multipath-tools-130222/libmultipath/config.c @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include "checkers.h" #include "memory.h" @@ -556,6 +559,7 @@ free_config (struct config * conf) if (conf->wwids_file) FREE(conf->wwids_file); + if (conf->prio_name) FREE(conf->prio_name); @@ -567,6 +571,10 @@ free_config (struct config * conf) if (conf->checker_name) FREE(conf->checker_name); + + if (conf->config_dir) + FREE(conf->config_dir); + if (conf->reservation_key) FREE(conf->reservation_key); @@ -584,6 +592,43 @@ free_config (struct config * conf) FREE(conf); } +/* if multipath fails to process the config directory, it should continue, + * with just a warning message */ +static void +process_config_dir(vector keywords, char *dir) +{ + struct dirent **namelist; + int i, n; + char path[LINE_MAX]; + int old_hwtable_size; + + if (dir[0] != '/') { + condlog(1, "config_dir '%s' must be a fully qualified path", + dir); + return; + } + n = scandir(dir, &namelist, NULL, alphasort); + if (n < 0) { + if (errno == ENOENT) + condlog(3, "No configuration dir '%s'", dir); + else + condlog(0, "couldn't open configuration dir '%s': %s", + dir, strerror(errno)); + return; + } + for (i = 0; i < n; i++) { + if (!strstr(namelist[i]->d_name, ".conf")) + continue; + old_hwtable_size = VECTOR_SIZE(conf->hwtable); + snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name); + path[LINE_MAX-1] = '\0'; + process_file(path); + if (VECTOR_SIZE(conf->hwtable) > old_hwtable_size) + factorize_hwtable(conf->hwtable, old_hwtable_size); + + } +} + int load_config (char * file, struct udev *udev) { @@ -623,6 +668,7 @@ load_config (char * file, struct udev *u conf->hw_strmatch = 0; conf->force_sync = 0; conf->ignore_new_boot_devs = 0; + conf->processed_main_config = 0; /* * preload default hwtable @@ -641,11 +687,12 @@ load_config (char * file, struct udev *u */ set_current_keywords(&conf->keywords); alloc_keywords(); + init_keywords(); if (filepresent(file)) { int builtin_hwtable_size; builtin_hwtable_size = VECTOR_SIZE(conf->hwtable); - if (init_data(file, init_keywords)) { + if (process_file(file)) { condlog(0, "error parsing config file"); goto out; } @@ -658,7 +705,6 @@ load_config (char * file, struct udev *u } } else { - init_keywords(); condlog(0, "/etc/multipath.conf does not exist, blacklisting all devices."); condlog(0, "run \"/sbin/mpathconf --enable\" to create a default /etc/multipath.conf"); if (conf->blist_devnode == NULL) { @@ -675,6 +721,12 @@ load_config (char * file, struct udev *u } } + conf->processed_main_config = 1; + if (conf->config_dir == NULL) + conf->config_dir = set_default(DEFAULT_CONFIG_DIR); + if (conf->config_dir && conf->config_dir[0] != '\0') + process_config_dir(conf->keywords, conf->config_dir); + /* * fill the voids left in the config file */ Index: multipath-tools-130222/libmultipath/config.h =================================================================== --- multipath-tools-130222.orig/libmultipath/config.h +++ multipath-tools-130222/libmultipath/config.h @@ -132,6 +132,7 @@ struct config { int force_sync; int deferred_remove; int ignore_new_boot_devs; + int processed_main_config; unsigned int version[3]; char * dev; @@ -147,6 +148,7 @@ struct config { char * prio_args; char * checker_name; char * alias_prefix; + char * config_dir; unsigned char * reservation_key; vector keywords; Index: multipath-tools-130222/libmultipath/defaults.h =================================================================== --- multipath-tools-130222.orig/libmultipath/defaults.h +++ multipath-tools-130222/libmultipath/defaults.h @@ -31,5 +31,6 @@ #define DEFAULT_CONFIGFILE "/etc/multipath.conf" #define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings" #define DEFAULT_WWIDS_FILE "/etc/multipath/wwids" +#define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d" char * set_default (char * str); Index: multipath-tools-130222/multipath.conf.annotated =================================================================== --- multipath-tools-130222.orig/multipath.conf.annotated +++ multipath-tools-130222/multipath.conf.annotated @@ -232,6 +232,16 @@ # # values : yes|no # # default : no # force_sync yes +# +# # +# # name : config_dir +# # scope : multipath & multipathd +# # desc : If not set to an empty string, multipath will search +# # this directory alphabetically for files ending in ".conf" +# # and it will read configuration information from these +# # files, just as if it was in /etc/multipath.conf +# # values : "" or a fully qualified pathname +# # default : "/etc/multipath/conf.d" #} # ## Index: multipath-tools-130222/multipath.conf.defaults =================================================================== --- multipath-tools-130222.orig/multipath.conf.defaults +++ multipath-tools-130222/multipath.conf.defaults @@ -26,6 +26,7 @@ # log_checker_err always # retain_attached_hw_handler no # detect_prio no +# config_dir "/etc/multipath/conf.d" #} #blacklist { # devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" Index: multipath-tools-130222/multipath/multipath.conf.5 =================================================================== --- multipath-tools-130222.orig/multipath/multipath.conf.5 +++ multipath-tools-130222/multipath/multipath.conf.5 @@ -452,6 +452,13 @@ still in use, it will be freed when the to the multipath device before the last user closes it, the deferred remove will be canceled. Default is .I no +.TP +.B config_dir +If set to anything other than "", multipath will search this directory +alphabetically for file ending in ".conf" and it will read configuration +information from them, just as if it was in /etc/multipath.conf. config_dir +must either be "" or a fully qualified directory name. Default is +.I "/etc/multipath/conf.d" . .SH "blacklist section" The