Blame SOURCES/0112-RHBZ-1194917-add-config_dir-option.patch

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