Blob Blame History Raw
---
 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 <syslog.h>
+#include <errno.h>
 
 #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 <stdio.h>
 #include <string.h>
 #include <libudev.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
 
 #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, "A default multipath.conf file is located at");
 		condlog(0, "/usr/share/doc/device-mapper-multipath-%d.%d.%d/multipath.conf", MULTIPATH_VERSION(VERSION_CODE));
@@ -677,6 +723,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