diff --git a/SOURCES/0166-RHBZ-1323429-dont-allow-new-wwid.patch b/SOURCES/0166-RHBZ-1323429-dont-allow-new-wwid.patch index b0fc664..bcc61cc 100644 --- a/SOURCES/0166-RHBZ-1323429-dont-allow-new-wwid.patch +++ b/SOURCES/0166-RHBZ-1323429-dont-allow-new-wwid.patch @@ -15,7 +15,7 @@ Index: multipath-tools-130222/libmultipath/dmparser.c + * path uid_attribute after creating a device + */ + else if (strcmp(pp->wwid, mpp->wwid) != 0) { -+ condlog(0, "%s: path wwid appears to have changed. Using old wwid.\n", pp->dev_t); ++ condlog(0, "%s: path wwid appears to have changed. Using map wwid.\n", pp->dev_t); + strncpy(pp->wwid, mpp->wwid, WWID_SIZE); + } + diff --git a/SOURCES/0189-RHBZ-1368211-remove-retries.patch b/SOURCES/0189-RHBZ-1368211-remove-retries.patch new file mode 100644 index 0000000..253d572 --- /dev/null +++ b/SOURCES/0189-RHBZ-1368211-remove-retries.patch @@ -0,0 +1,168 @@ +--- + libmultipath/config.c | 1 + + libmultipath/config.h | 1 + + libmultipath/devmapper.c | 35 +++++++++++++++++++---------------- + libmultipath/dict.c | 25 +++++++++++++++++++++++++ + multipath.conf.defaults | 1 + + multipath/multipath.conf.5 | 5 +++++ + 6 files changed, 52 insertions(+), 16 deletions(-) + +Index: multipath-tools-130222/libmultipath/devmapper.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/devmapper.c ++++ multipath-tools-130222/libmultipath/devmapper.c +@@ -803,10 +803,11 @@ dm_flush_map_nopaths(const char * mapnam + extern int + dm_suspend_and_flush_map (const char * mapname) + { +- int s = 0, queue_if_no_path = 0; ++ int need_reset = 0, queue_if_no_path = 0; + unsigned long long mapsize; + char params[PARAMS_SIZE] = {0}; + int udev_flags = 0; ++ int retries = conf->remove_retries; + + if (!dm_is_mpath(mapname)) + return 0; /* nothing to do */ +@@ -821,22 +822,24 @@ dm_suspend_and_flush_map (const char * m + queue_if_no_path = 1; + } + +- if (queue_if_no_path) +- s = dm_queue_if_no_path((char *)mapname, 0); +- /* Leave queue_if_no_path alone if unset failed */ +- if (s) +- queue_if_no_path = 0; +- else +- s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 0, 0); +- +- if (!dm_flush_map(mapname)) { +- condlog(4, "multipath map %s removed", mapname); +- return 0; +- } ++ if (queue_if_no_path && dm_queue_if_no_path((char *)mapname, 0) == 0) ++ need_reset = 1; ++ ++ do { ++ if (!queue_if_no_path || need_reset) ++ dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 0, 0); ++ ++ if (!dm_flush_map(mapname)) { ++ condlog(4, "multipath map %s removed", mapname); ++ return 0; ++ } ++ dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags); ++ if (retries) ++ sleep(1); ++ } while (retries-- > 0); + condlog(2, "failed to remove multipath map %s", mapname); +- dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags); +- if (queue_if_no_path) +- s = dm_queue_if_no_path((char *)mapname, 1); ++ if (need_reset) ++ dm_queue_if_no_path((char *)mapname, 1); + return 1; + } + +Index: multipath-tools-130222/libmultipath/config.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/config.c ++++ multipath-tools-130222/libmultipath/config.c +@@ -680,6 +680,7 @@ load_config (char * file, struct udev *u + conf->new_bindings_in_boot = 0; + conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT; + conf->skip_kpartx = DEFAULT_SKIP_KPARTX; ++ conf->remove_retries = 0; + + /* + * preload default hwtable +Index: multipath-tools-130222/libmultipath/config.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/config.h ++++ multipath-tools-130222/libmultipath/config.h +@@ -146,6 +146,7 @@ struct config { + int delayed_reconfig; + int uev_wait_timeout; + int skip_kpartx; ++ int remove_retries; + unsigned int version[3]; + + char * dev; +Index: multipath-tools-130222/libmultipath/dict.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/dict.c ++++ multipath-tools-130222/libmultipath/dict.c +@@ -935,6 +935,24 @@ def_new_bindings_in_boot_handler(vector + return 0; + } + ++static int ++def_remove_retries_handler(vector strvec) ++{ ++ char *buff; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ conf->remove_retries = atoi(buff); ++ if (conf->remove_retries < 0) ++ conf->remove_retries = 0; ++ FREE(buff); ++ ++ return 0; ++} ++ + /* + * blacklist block handlers + */ +@@ -3405,6 +3423,12 @@ snprint_def_new_bindings_in_boot(char * + } + + static int ++snprint_def_remove_retries (char * buff, int len, void * data) ++{ ++ return snprintf(buff, len, "%i", conf->remove_retries); ++} ++ ++static int + snprint_ble_simple (char * buff, int len, void * data) + { + struct blentry * ble = (struct blentry *)data; +@@ -3483,6 +3507,7 @@ init_keywords(void) + install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay); + install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout); + install_keyword("new_bindings_in_boot", &def_new_bindings_in_boot_handler, &snprint_def_new_bindings_in_boot); ++ install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries); + __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/multipath.conf.defaults +=================================================================== +--- multipath-tools-130222.orig/multipath.conf.defaults ++++ multipath-tools-130222/multipath.conf.defaults +@@ -41,6 +41,7 @@ + # retrigger_delay 10 + # missing_uev_wait_timeout 30 + # new_bindings_in_boot no ++# remove_retries 0 + #} + #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 +@@ -556,6 +556,11 @@ user_friendly_names. When multipathd is + regular filesystem, the device will be renamed to a user_friendly_name. The + default is + .I no ++.TP ++.B remove_retries ++This sets how may times multipath will retry removing a device that is in-use. ++Between each attempt, multipath will sleep 1 second. The default is ++.I 0 + . + .SH "blacklist section" + The diff --git a/SOURCES/0189-RHBZ-1395298-rbd-lock-on-read.patch b/SOURCES/0189-RHBZ-1395298-rbd-lock-on-read.patch deleted file mode 100644 index 584d37f..0000000 --- a/SOURCES/0189-RHBZ-1395298-rbd-lock-on-read.patch +++ /dev/null @@ -1,38 +0,0 @@ ---- - libmultipath/checkers/rbd.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -Index: multipath-tools-130222/libmultipath/checkers/rbd.c -=================================================================== ---- multipath-tools-130222.orig/libmultipath/checkers/rbd.c -+++ multipath-tools-130222/libmultipath/checkers/rbd.c -@@ -45,6 +45,7 @@ struct rbd_checker_context { - char *username; - int remapped; - int blacklisted; -+ int lock_on_read:1; - - rados_t cluster; - -@@ -141,6 +142,9 @@ int libcheck_init(struct checker * c) - goto free_addr; - } - -+ if (strstr(config_info, "lock_on_read")) -+ ct->lock_on_read = 1; -+ - ct->config_info = strdup(config_info); - if (!ct->config_info) - goto free_addr; -@@ -397,7 +401,10 @@ static int rbd_remap(struct rbd_checker_ - case 0: - argv[i++] = "rbd"; - argv[i++] = "map"; -- argv[i++] = "-o noshare"; -+ if (ct->lock_on_read) -+ argv[i++] = "-o noshare,lock_on_read"; -+ else -+ argv[i++] = "-o noshare"; - if (ct->username) { - argv[i++] = "--id"; - argv[i++] = ct->username; diff --git a/SOURCES/0190-RHBZ-1380602-rbd-lock-on-read.patch b/SOURCES/0190-RHBZ-1380602-rbd-lock-on-read.patch new file mode 100644 index 0000000..584d37f --- /dev/null +++ b/SOURCES/0190-RHBZ-1380602-rbd-lock-on-read.patch @@ -0,0 +1,38 @@ +--- + libmultipath/checkers/rbd.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +Index: multipath-tools-130222/libmultipath/checkers/rbd.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/checkers/rbd.c ++++ multipath-tools-130222/libmultipath/checkers/rbd.c +@@ -45,6 +45,7 @@ struct rbd_checker_context { + char *username; + int remapped; + int blacklisted; ++ int lock_on_read:1; + + rados_t cluster; + +@@ -141,6 +142,9 @@ int libcheck_init(struct checker * c) + goto free_addr; + } + ++ if (strstr(config_info, "lock_on_read")) ++ ct->lock_on_read = 1; ++ + ct->config_info = strdup(config_info); + if (!ct->config_info) + goto free_addr; +@@ -397,7 +401,10 @@ static int rbd_remap(struct rbd_checker_ + case 0: + argv[i++] = "rbd"; + argv[i++] = "map"; +- argv[i++] = "-o noshare"; ++ if (ct->lock_on_read) ++ argv[i++] = "-o noshare,lock_on_read"; ++ else ++ argv[i++] = "-o noshare"; + if (ct->username) { + argv[i++] = "--id"; + argv[i++] = ct->username; diff --git a/SOURCES/0190-RHBZ-1429885-max-sectors-kb.patch b/SOURCES/0190-RHBZ-1429885-max-sectors-kb.patch deleted file mode 100644 index 228cc9a..0000000 --- a/SOURCES/0190-RHBZ-1429885-max-sectors-kb.patch +++ /dev/null @@ -1,474 +0,0 @@ ---- - libmultipath/config.c | 3 + - libmultipath/config.h | 3 + - libmultipath/configure.c | 1 - libmultipath/defaults.h | 1 - libmultipath/devmapper.c | 4 +- - libmultipath/dict.c | 87 +++++++++++++++++++++++++++++++++++++++++++++ - libmultipath/discovery.c | 60 +++++++++++++++++++++++++++++++ - libmultipath/discovery.h | 1 - libmultipath/propsel.c | 25 ++++++++++++ - libmultipath/propsel.h | 1 - libmultipath/structs.h | 7 +++ - multipath/multipath.conf.5 | 8 ++++ - 12 files changed, 200 insertions(+), 1 deletion(-) - -Index: multipath-tools-130222/libmultipath/config.c -=================================================================== ---- multipath-tools-130222.orig/libmultipath/config.c -+++ multipath-tools-130222/libmultipath/config.c -@@ -344,6 +344,7 @@ merge_hwe (struct hwentry * dst, struct - merge_num(delay_watch_checks); - merge_num(delay_wait_checks); - merge_num(skip_kpartx); -+ merge_num(max_sectors_kb); - - /* - * Make sure features is consistent with -@@ -405,6 +406,7 @@ overwrite_hwe (struct hwentry * dst, str - overwrite_num(delay_watch_checks); - overwrite_num(delay_wait_checks); - overwrite_num(skip_kpartx); -+ overwrite_num(max_sectors_kb); - - /* - * Make sure features is consistent with -@@ -680,6 +682,7 @@ load_config (char * file, struct udev *u - conf->new_bindings_in_boot = 0; - conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT; - conf->skip_kpartx = DEFAULT_SKIP_KPARTX; -+ conf->max_sectors_kb = DEFAULT_MAX_SECTORS_KB; - - /* - * preload default hwtable -Index: multipath-tools-130222/libmultipath/config.h -=================================================================== ---- multipath-tools-130222.orig/libmultipath/config.h -+++ multipath-tools-130222/libmultipath/config.h -@@ -65,6 +65,7 @@ struct hwentry { - int delay_watch_checks; - int delay_wait_checks; - int skip_kpartx; -+ int max_sectors_kb; - char * bl_product; - }; - -@@ -92,6 +93,7 @@ struct mpentry { - int delay_watch_checks; - int delay_wait_checks; - int skip_kpartx; -+ int max_sectors_kb; - uid_t uid; - gid_t gid; - mode_t mode; -@@ -146,6 +148,7 @@ struct config { - int delayed_reconfig; - int uev_wait_timeout; - int skip_kpartx; -+ int max_sectors_kb; - unsigned int version[3]; - - char * dev; -Index: multipath-tools-130222/libmultipath/configure.c -=================================================================== ---- multipath-tools-130222.orig/libmultipath/configure.c -+++ multipath-tools-130222/libmultipath/configure.c -@@ -295,6 +295,7 @@ setup_map (struct multipath * mpp, char - select_delay_watch_checks(mpp); - select_delay_wait_checks(mpp); - select_skip_kpartx(mpp); -+ select_max_sectors_kb(mpp); - - sysfs_set_scsi_tmo(mpp); - /* -Index: multipath-tools-130222/libmultipath/defaults.h -=================================================================== ---- multipath-tools-130222.orig/libmultipath/defaults.h -+++ multipath-tools-130222/libmultipath/defaults.h -@@ -25,6 +25,7 @@ - #define DEFAULT_RETRIGGER_TRIES 3 - #define DEFAULT_UEV_WAIT_TIMEOUT 30 - #define DEFAULT_SKIP_KPARTX SKIP_KPARTX_OFF -+#define DEFAULT_MAX_SECTORS_KB MAX_SECTORS_KB_UNDEF - - #define DEFAULT_CHECKINT 5 - #define MAX_CHECKINT(a) (a << 2) -Index: multipath-tools-130222/libmultipath/dict.c -=================================================================== ---- multipath-tools-130222.orig/libmultipath/dict.c -+++ multipath-tools-130222/libmultipath/dict.c -@@ -935,6 +935,22 @@ def_new_bindings_in_boot_handler(vector - return 0; - } - -+static int -+def_max_sectors_kb_handler(vector strvec) -+{ -+ char * buff; -+ -+ buff = set_value(strvec); -+ if (!buff) -+ return 1; -+ -+ if ((conf->max_sectors_kb = atoi(buff)) < MAX_SECTORS_KB_MIN) -+ conf->max_sectors_kb = MAX_SECTORS_KB_UNDEF; -+ -+ FREE(buff); -+ return 0; -+} -+ - /* - * blacklist block handlers - */ -@@ -1724,6 +1740,26 @@ hw_delay_wait_checks_handler(vector strv - return 0; - } - -+static int -+hw_max_sectors_kb_handler(vector strvec) -+{ -+ struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); -+ char * buff; -+ -+ if (!hwe) -+ return 1; -+ -+ buff = set_value(strvec); -+ if (!buff) -+ return 1; -+ -+ if ((hwe->max_sectors_kb = atoi(buff)) < MAX_SECTORS_KB_MIN) -+ hwe->max_sectors_kb = MAX_SECTORS_KB_UNDEF; -+ -+ FREE(buff); -+ return 0; -+} -+ - /* - * multipaths block handlers - */ -@@ -2275,6 +2311,26 @@ mp_delay_wait_checks_handler(vector strv - return 0; - } - -+static int -+mp_max_sectors_kb_handler(vector strvec) -+{ -+ struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); -+ char * buff; -+ -+ if (!mpe) -+ return 1; -+ -+ buff = set_value(strvec); -+ if (!buff) -+ return 1; -+ -+ if ((mpe->max_sectors_kb = atoi(buff)) < MAX_SECTORS_KB_MIN) -+ mpe->max_sectors_kb = MAX_SECTORS_KB_UNDEF; -+ -+ FREE(buff); -+ return 0; -+} -+ - /* - * config file keywords printing - */ -@@ -2574,6 +2630,16 @@ snprint_mp_delay_wait_checks(char * buff - } - - static int -+snprint_mp_max_sectors_kb(char * buff, int len, void * data) -+{ -+ struct mpentry * mpe = (struct mpentry *)data; -+ -+ if (mpe->max_sectors_kb == MAX_SECTORS_KB_UNDEF) -+ return 0; -+ return snprintf(buff, len, "%d", mpe->max_sectors_kb); -+} -+ -+static int - snprint_hw_fast_io_fail(char * buff, int len, void * data) - { - struct hwentry * hwe = (struct hwentry *)data; -@@ -2952,6 +3018,16 @@ snprint_detect_prio(char * buff, int len - } - - static int -+snprint_hw_max_sectors_kb(char * buff, int len, void * data) -+{ -+ struct hwentry * hwe = (struct hwentry *)data; -+ -+ if (hwe->max_sectors_kb == MAX_SECTORS_KB_UNDEF) -+ return 0; -+ return snprintf(buff, len, "%d", hwe->max_sectors_kb); -+} -+ -+static int - snprint_def_polling_interval (char * buff, int len, void * data) - { - return snprintf(buff, len, "%i", conf->checkint); -@@ -3405,6 +3481,14 @@ snprint_def_new_bindings_in_boot(char * - } - - static int -+snprint_def_max_sectors_kb(char * buff, int len, void * data) -+{ -+ if (conf->max_sectors_kb == MAX_SECTORS_KB_UNDEF) -+ return 0; -+ return snprintf(buff, len, "%d", conf->max_sectors_kb); -+} -+ -+static int - snprint_ble_simple (char * buff, int len, void * data) - { - struct blentry * ble = (struct blentry *)data; -@@ -3483,6 +3567,7 @@ init_keywords(void) - install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay); - install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout); - install_keyword("new_bindings_in_boot", &def_new_bindings_in_boot_handler, &snprint_def_new_bindings_in_boot); -+ install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb); - __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); -@@ -3551,6 +3636,7 @@ init_keywords(void) - install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks); - install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks); - install_keyword("skip_kpartx", &hw_skip_kpartx_handler, &snprint_hw_skip_kpartx); -+ install_keyword("max_sectors_kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb); - install_sublevel_end(); - - install_keyword_root("multipaths", &multipaths_handler); -@@ -3579,5 +3665,6 @@ init_keywords(void) - install_keyword("delay_watch_checks", &mp_delay_watch_checks_handler, &snprint_mp_delay_watch_checks); - install_keyword("delay_wait_checks", &mp_delay_wait_checks_handler, &snprint_mp_delay_wait_checks); - install_keyword("skip_kpartx", &mp_skip_kpartx_handler, &snprint_mp_skip_kpartx); -+ install_keyword("max_sectors_kb", &mp_max_sectors_kb_handler, &snprint_mp_max_sectors_kb); - install_sublevel_end(); - } -Index: multipath-tools-130222/libmultipath/discovery.c -=================================================================== ---- multipath-tools-130222.orig/libmultipath/discovery.c -+++ multipath-tools-130222/libmultipath/discovery.c -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - - #include "checkers.h" - #include "vector.h" -@@ -27,6 +28,7 @@ - #include "discovery.h" - #include "prio.h" - #include "defaults.h" -+#include "devmapper.h" - - int - store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice, -@@ -166,6 +168,64 @@ declare_sysfs_get_str(rev); - declare_sysfs_get_str(dev); - - int -+sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload) -+{ -+ struct pathgroup * pgp; -+ struct path *pp; -+ char buff[11]; -+ struct udev_device *udevice = NULL; -+ int i, j, len, ret; -+ int max_sectors_kb; -+ -+ if (mpp->max_sectors_kb == MAX_SECTORS_KB_UNDEF) -+ return 0; -+ max_sectors_kb = mpp->max_sectors_kb; -+ if (is_reload) { -+ if (!mpp->dmi && dm_get_info(mpp->alias, &mpp->dmi) != 0) { -+ condlog(0, "failed to get dm info on %s to set max_sectors_kb", mpp->alias); -+ return 1; -+ } -+ udevice = udev_device_new_from_devnum(conf->udev, 'b', -+ makedev(mpp->dmi->major, -+ mpp->dmi->minor)); -+ if (!udevice) { -+ condlog(0, "failed to get udev device to set max_sectors_kb for %s", mpp->alias); -+ return 1; -+ } -+ if (sysfs_attr_get_value(udevice, "queue/max_sectors_kb", -+ buff, sizeof(buff)) <= 0) { -+ condlog(0, "failed to get current max_sectors_kb from %s", mpp->alias); -+ goto fail_reload; -+ } -+ if (sscanf(buff, "%u\n", &max_sectors_kb) != 1) { -+ condlog(0, "can't parse current max_sectors_kb from %s", -+ mpp->alias); -+ goto fail_reload; -+ } -+ udev_device_unref(udevice); -+ } -+ snprintf(buff, 11, "%d", max_sectors_kb); -+ len = strlen(buff); -+ -+ vector_foreach_slot (mpp->pg, pgp, i) { -+ vector_foreach_slot (pgp->paths, pp, j) { -+ ret = sysfs_attr_set_value(pp->udev, -+ "queue/max_sectors_kb", -+ buff, len); -+ if (ret < 0) { -+ condlog(0, "failed setting max_sectors_kb on %s : %s", pp->dev, strerror(-ret)); -+ return 1; -+ } -+ } -+ } -+ return 0; -+ -+fail_reload: -+ udev_device_unref(udevice); -+ return 1; -+} -+ -+int - sysfs_get_timeout(struct path *pp, unsigned int *timeout) - { - const char *attr = NULL; -Index: multipath-tools-130222/libmultipath/discovery.h -=================================================================== ---- multipath-tools-130222.orig/libmultipath/discovery.h -+++ multipath-tools-130222/libmultipath/discovery.h -@@ -41,6 +41,7 @@ int store_pathinfo (vector pathvec, vect - struct udev_device *udevice, int flag, - struct path **pp_ptr); - int sysfs_set_scsi_tmo (struct multipath *mpp); -+int sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload); - int sysfs_get_timeout(struct path *pp, unsigned int *timeout); - int sysfs_get_host_pci_name(struct path *pp, char *pci_name); - int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address); -Index: multipath-tools-130222/libmultipath/propsel.c -=================================================================== ---- multipath-tools-130222.orig/libmultipath/propsel.c -+++ multipath-tools-130222/libmultipath/propsel.c -@@ -880,3 +880,28 @@ select_skip_kpartx (struct multipath * m - condlog(3, "skip_kpartx = DISABLED (internal default)"); - return 0; - } -+ -+extern int -+select_max_sectors_kb (struct multipath * mp) -+{ -+ if (mp->mpe && mp->mpe->max_sectors_kb != MAX_SECTORS_KB_UNDEF) { -+ mp->max_sectors_kb = mp->mpe->max_sectors_kb; -+ condlog(3, "max_sectors_kb = %i (multipath setting)", -+ mp->max_sectors_kb); -+ return 0; -+ } -+ if (mp->hwe && mp->hwe->max_sectors_kb != MAX_SECTORS_KB_UNDEF) { -+ mp->max_sectors_kb = mp->hwe->max_sectors_kb; -+ condlog(3, "max_sectors_kb = %i (controler setting)", -+ mp->max_sectors_kb); -+ return 0; -+ } -+ if (conf->max_sectors_kb != MAX_SECTORS_KB_UNDEF) { -+ mp->max_sectors_kb = conf->max_sectors_kb; -+ condlog(3, "max_sectors_kb = %i (config file default)", -+ mp->max_sectors_kb); -+ return 0; -+ } -+ mp->max_sectors_kb = MAX_SECTORS_KB_UNDEF; -+ return 0; -+} -Index: multipath-tools-130222/libmultipath/propsel.h -=================================================================== ---- multipath-tools-130222.orig/libmultipath/propsel.h -+++ multipath-tools-130222/libmultipath/propsel.h -@@ -24,3 +24,4 @@ int select_deferred_remove(struct multip - int select_delay_watch_checks (struct multipath * mp); - int select_delay_wait_checks (struct multipath * mp); - int select_skip_kpartx (struct multipath * mp); -+int select_max_sectors_kb (struct multipath * mp); -Index: multipath-tools-130222/libmultipath/structs.h -=================================================================== ---- multipath-tools-130222.orig/libmultipath/structs.h -+++ multipath-tools-130222/libmultipath/structs.h -@@ -128,6 +128,12 @@ enum skip_kpartx_states { - SKIP_KPARTX_ON, - }; - -+ -+enum max_sectors_kb_states { -+ MAX_SECTORS_KB_UNDEF = 0, -+ MAX_SECTORS_KB_MIN = 4, /* can't be smaller than page size */ -+}; -+ - enum scsi_protocol { - SCSI_PROTOCOL_FCP = 0, /* Fibre Channel */ - SCSI_PROTOCOL_SPI = 1, /* parallel SCSI */ -@@ -244,6 +250,7 @@ struct multipath { - int delay_wait_checks; - int force_udev_reload; - int skip_kpartx; -+ int max_sectors_kb; - unsigned int dev_loss; - uid_t uid; - gid_t gid; -Index: multipath-tools-130222/multipath/multipath.conf.5 -=================================================================== ---- multipath-tools-130222.orig/multipath/multipath.conf.5 -+++ multipath-tools-130222/multipath/multipath.conf.5 -@@ -556,6 +556,10 @@ user_friendly_names. When multipathd is - regular filesystem, the device will be renamed to a user_friendly_name. The - default is - .I no -+.TP -+.B max_sectors_kb -+Sets the max_sectors_kb device parameter on all path devices and the multipath -+device to the specified value. Default is device dependent. - . - .SH "blacklist section" - The -@@ -667,6 +671,8 @@ section: - .B delay_wait_checks - .TP - .B skip_kpartx -+.TP -+.B max_sectors_kb - .RE - .PD - .LP -@@ -767,6 +773,8 @@ section: - .B delay_wait_checks - .TP - .B skip_kpartx -+.TP -+.B max_sectors_kb - .RE - .PD - .LP -Index: multipath-tools-130222/libmultipath/devmapper.c -=================================================================== ---- multipath-tools-130222.orig/libmultipath/devmapper.c -+++ multipath-tools-130222/libmultipath/devmapper.c -@@ -21,7 +21,7 @@ - #include "devmapper.h" - #include "config.h" - #include "sysfs.h" -- -+#include "discovery.h" - #include "log_pthread.h" - #include - #include -@@ -330,6 +330,7 @@ extern int - dm_addmap_create (struct multipath *mpp, char * params) { - int ro; - -+ sysfs_set_max_sectors_kb(mpp, 0); - for (ro = 0; ro <= 1; ro++) { - int err; - -@@ -356,6 +357,7 @@ dm_addmap_create (struct multipath *mpp, - - extern int - dm_addmap_reload (struct multipath *mpp, char *params) { -+ sysfs_set_max_sectors_kb(mpp, 1); - if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW, SKIP_KPARTX_OFF)) - return 1; - if (errno != EROFS) diff --git a/SOURCES/0191-RHBZ-1169168-disable-changed-paths.patch b/SOURCES/0191-RHBZ-1169168-disable-changed-paths.patch new file mode 100644 index 0000000..e7e9286 --- /dev/null +++ b/SOURCES/0191-RHBZ-1169168-disable-changed-paths.patch @@ -0,0 +1,210 @@ +--- + libmultipath/config.c | 1 + + libmultipath/config.h | 1 + + libmultipath/dict.c | 33 +++++++++++++++++++++++++++++++++ + libmultipath/discovery.c | 11 ++++++----- + libmultipath/discovery.h | 1 + + libmultipath/structs.h | 1 + + multipathd/main.c | 26 ++++++++++++++++++++++++++ + 7 files changed, 69 insertions(+), 5 deletions(-) + +Index: multipath-tools-130222/libmultipath/discovery.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/discovery.c ++++ multipath-tools-130222/libmultipath/discovery.c +@@ -1155,8 +1155,8 @@ free_dev: + return ret; + } + +-static int +-get_uid (struct path * pp) ++int ++get_uid (struct path * pp, struct udev_device *udev) + { + char *c; + const char *value; +@@ -1165,7 +1165,7 @@ get_uid (struct path * pp) + if (!pp->uid_attribute) + select_getuid(pp); + +- if (!pp->udev) { ++ if (!udev) { + condlog(1, "%s: no udev information", pp->dev); + return 1; + } +@@ -1180,7 +1180,7 @@ get_uid (struct path * pp) + pp->tick = conf->retrigger_delay; + } + } else { +- value = udev_device_get_property_value(pp->udev, ++ value = udev_device_get_property_value(udev, + pp->uid_attribute); + if ((!value || strlen(value) == 0) && + conf->cmd == CMD_VALID_PATH) +@@ -1194,6 +1194,7 @@ get_uid (struct path * pp) + len = strlen(value); + } + strncpy(pp->wwid, value, len); ++ condlog(4, "%s: got wwid of '%s'", pp->dev, pp->wwid); + pp->missing_udev_info = INFO_OK; + pp->tick = 0; + } else { +@@ -1282,7 +1283,7 @@ pathinfo (struct path *pp, vector hwtabl + } + + if ((mask & DI_WWID) && !strlen(pp->wwid)) +- get_uid(pp); ++ get_uid(pp, pp->udev); + if (mask & DI_BLACKLIST && mask & DI_WWID) { + if (filter_wwid(conf->blist_wwid, conf->elist_wwid, + pp->wwid) > 0) { +Index: multipath-tools-130222/libmultipath/discovery.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/discovery.h ++++ multipath-tools-130222/libmultipath/discovery.h +@@ -44,6 +44,7 @@ int sysfs_set_scsi_tmo (struct multipath + int sysfs_get_timeout(struct path *pp, unsigned int *timeout); + int sysfs_get_host_pci_name(struct path *pp, char *pci_name); + int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address); ++int get_uid (struct path * pp, struct udev_device *udev); + + /* + * discovery bitmask +Index: multipath-tools-130222/libmultipath/structs.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/structs.h ++++ multipath-tools-130222/libmultipath/structs.h +@@ -209,6 +209,7 @@ struct path { + int fd; + int missing_udev_info; + int retriggers; ++ int wwid_changed; + + /* configlet pointers */ + struct hwentry * hwe; +Index: multipath-tools-130222/multipathd/main.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/main.c ++++ multipath-tools-130222/multipathd/main.c +@@ -784,6 +784,26 @@ uev_update_path (struct uevent *uev, str + if (pp->missing_udev_info == INFO_REQUESTED) + return uev_add_path(uev, vecs); + ++ if (conf->disable_changed_wwids && ++ (strlen(pp->wwid) || pp->wwid_changed)) { ++ char wwid[WWID_SIZE]; ++ ++ strcpy(wwid, pp->wwid); ++ get_uid(pp, uev->udev); ++ if (strcmp(wwid, pp->wwid) != 0) { ++ condlog(0, "%s: path wwid changed from '%s' to '%s'. disallowing", uev->kernel, wwid, pp->wwid); ++ strcpy(pp->wwid, wwid); ++ if (!pp->wwid_changed) { ++ pp->wwid_changed = 1; ++ pp->tick = 1; ++ dm_fail_path(pp->mpp->alias, pp->dev_t); ++ } ++ } ++ else { ++ pp->wwid_changed = 0; ++ } ++ } ++ + /* reinit the prio values on change event, in case something is + * different */ + prio_init(&pp->prio); +@@ -1284,6 +1304,12 @@ check_path (struct vectors * vecs, struc + else + checker_clear_message(&pp->checker); + ++ if (pp->wwid_changed) { ++ condlog(2, "%s: path wwid has changed. Refusing to use", ++ pp->dev); ++ newstate = PATH_DOWN; ++ } ++ + if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) { + condlog(2, "%s: unusable path", pp->dev); + pathinfo(pp, conf->hwtable, 0); +Index: multipath-tools-130222/libmultipath/config.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/config.c ++++ multipath-tools-130222/libmultipath/config.c +@@ -681,6 +681,7 @@ load_config (char * file, struct udev *u + conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT; + conf->skip_kpartx = DEFAULT_SKIP_KPARTX; + conf->remove_retries = 0; ++ conf->disable_changed_wwids = 0; + + /* + * preload default hwtable +Index: multipath-tools-130222/libmultipath/config.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/config.h ++++ multipath-tools-130222/libmultipath/config.h +@@ -147,6 +147,7 @@ struct config { + int uev_wait_timeout; + int skip_kpartx; + int remove_retries; ++ int disable_changed_wwids; + unsigned int version[3]; + + char * dev; +Index: multipath-tools-130222/libmultipath/dict.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/dict.c ++++ multipath-tools-130222/libmultipath/dict.c +@@ -953,6 +953,29 @@ def_remove_retries_handler(vector strvec + return 0; + } + ++static int ++def_disable_changed_wwids_handler(vector strvec) ++{ ++ char * buff; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ if ((strlen(buff) == 2 && !strcmp(buff, "no")) || ++ (strlen(buff) == 1 && !strcmp(buff, "0"))) ++ conf->disable_changed_wwids = 0; ++ else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) || ++ (strlen(buff) == 1 && !strcmp(buff, "1"))) ++ conf->disable_changed_wwids = 1; ++ else ++ conf->disable_changed_wwids = 0; ++ ++ FREE(buff); ++ return 0; ++} ++ + /* + * blacklist block handlers + */ +@@ -3429,6 +3452,15 @@ snprint_def_remove_retries (char * buff, + } + + static int ++snprint_def_disable_changed_wwids(char * buff, int len, void * data) ++{ ++ if (conf->disable_changed_wwids == 1) ++ return snprintf(buff, len, "yes"); ++ else ++ return snprintf(buff, len, "no"); ++} ++ ++static int + snprint_ble_simple (char * buff, int len, void * data) + { + struct blentry * ble = (struct blentry *)data; +@@ -3508,6 +3540,7 @@ init_keywords(void) + install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout); + install_keyword("new_bindings_in_boot", &def_new_bindings_in_boot_handler, &snprint_def_new_bindings_in_boot); + install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries); ++ install_keyword("disable_changed_wwids", &def_disable_changed_wwids_handler, &snprint_def_disable_changed_wwids); + __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); diff --git a/SOURCES/0192-RHBZ-1362409-infinibox-config.patch b/SOURCES/0192-RHBZ-1362409-infinibox-config.patch new file mode 100644 index 0000000..dd56f8b --- /dev/null +++ b/SOURCES/0192-RHBZ-1362409-infinibox-config.patch @@ -0,0 +1,34 @@ +--- + libmultipath/hwtable.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +Index: multipath-tools-130222/libmultipath/hwtable.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/hwtable.c ++++ multipath-tools-130222/libmultipath/hwtable.c +@@ -1168,6 +1168,25 @@ static struct hwentry default_hw[] = { + .prio_name = PRIO_ALUA, + .prio_args = NULL, + }, ++ /* ++ * Infinidat ++ */ ++ { ++ .vendor = "NFINIDAT", ++ .product = "InfiniBox.*", ++ .features = DEFAULT_FEATURES, ++ .hwhandler = DEFAULT_HWHANDLER, ++ .pgpolicy = GROUP_BY_PRIO, ++ .pgfailback = 30, ++ .rr_weight = RR_WEIGHT_PRIO, ++ .no_path_retry = NO_PATH_RETRY_FAIL, ++ .checker_name = TUR, ++ .prio_name = PRIO_ALUA, ++ .prio_args = NULL, ++ .selector = "round-robin 0", ++ .flush_on_last_del = FLUSH_ENABLED, ++ .dev_loss = 30, ++ }, + { + .vendor = "XtremIO", + .product = "XtremApp", diff --git a/SOURCES/0194-RHBZ-1351964-kpartx-recurse.patch b/SOURCES/0194-RHBZ-1351964-kpartx-recurse.patch new file mode 100644 index 0000000..be5b22e --- /dev/null +++ b/SOURCES/0194-RHBZ-1351964-kpartx-recurse.patch @@ -0,0 +1,17 @@ +--- + kpartx/dos.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: multipath-tools-130222/kpartx/dos.c +=================================================================== +--- multipath-tools-130222.orig/kpartx/dos.c ++++ multipath-tools-130222/kpartx/dos.c +@@ -46,7 +46,7 @@ read_extended_partition(int fd, struct p + for (i=0; i<2; i++) { + memcpy(&p, bp + 0x1be + i * sizeof (p), sizeof (p)); + if (is_extended(p.sys_type)) { +- if (p.nr_sects && !moretodo) { ++ if (p.start_sect && p.nr_sects && !moretodo) { + next = start + sector_size_mul * le32_to_cpu(p.start_sect); + moretodo = 1; + } diff --git a/SOURCES/0195-RHBZ-1359510-no-daemon-msg.patch b/SOURCES/0195-RHBZ-1359510-no-daemon-msg.patch new file mode 100644 index 0000000..667f10d --- /dev/null +++ b/SOURCES/0195-RHBZ-1359510-no-daemon-msg.patch @@ -0,0 +1,111 @@ +--- + libmultipath/configure.c | 21 ++++++++++++++++++++- + libmultipath/configure.h | 1 + + multipath/main.c | 21 +++++++++++++++++++++ + 3 files changed, 42 insertions(+), 1 deletion(-) + +Index: multipath-tools-130222/libmultipath/configure.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/configure.c ++++ multipath-tools-130222/libmultipath/configure.c +@@ -743,7 +743,8 @@ deadmap (struct multipath * mpp) + return 1; /* dead */ + } + +-int check_daemon(void) ++extern int ++check_daemon(void) + { + int fd; + char *reply; +@@ -776,6 +777,7 @@ coalesce_paths (struct vectors * vecs, v + { + int r = 1; + int k, i; ++ int map_processed = 0; + char empty_buff[WWID_SIZE]; + char params[PARAMS_SIZE]; + struct multipath * mpp; +@@ -936,6 +938,13 @@ coalesce_paths (struct vectors * vecs, v + else + remove_map(mpp, vecs, 0); + } ++ ++ /* By now at least one multipath device map is processed, ++ * so set map_processed = 1 ++ */ ++ if (!map_processed) ++ map_processed = 1; ++ + } + /* + * Flush maps with only dead paths (ie not in sysfs) +@@ -963,6 +972,16 @@ coalesce_paths (struct vectors * vecs, v + condlog(2, "%s: remove (dead)", alias); + } + } ++ ++ /* If there is at least one multipath device map processed then ++ * check if 'multipathd' service is running or not? ++ */ ++ if (map_processed) { ++ if (!conf->daemon && !check_daemon()) ++ condlog(0, "'multipathd' service is currently not " ++ "running, IO failover/failback will not work"); ++ } ++ + return 0; + } + +Index: multipath-tools-130222/libmultipath/configure.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/configure.h ++++ multipath-tools-130222/libmultipath/configure.h +@@ -27,6 +27,7 @@ enum actions { + int setup_map (struct multipath * mpp, char * params, int params_size ); + int domap (struct multipath * mpp, char * params); + int reinstate_paths (struct multipath *mpp); ++int check_daemon(void); + int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload); + int get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid); + int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh); +Index: multipath-tools-130222/multipath/main.c +=================================================================== +--- multipath-tools-130222.orig/multipath/main.c ++++ multipath-tools-130222/multipath/main.c +@@ -178,6 +178,7 @@ static int + get_dm_mpvec (vector curmp, vector pathvec, char * refwwid) + { + int i; ++ int maps_present = 0; + struct multipath * mpp; + char params[PARAMS_SIZE], status[PARAMS_SIZE]; + +@@ -226,7 +227,27 @@ get_dm_mpvec (vector curmp, vector pathv + + if (conf->cmd == CMD_CREATE) + reinstate_paths(mpp); ++ ++ /* At this place we have found at least one multipath ++ * device map, so set maps_present = 1 ++ */ ++ if (!maps_present) ++ maps_present = 1; ++ + } ++ ++ /* If there is at least one multipath device map present then ++ * check if 'multipathd' service is running or not? ++ */ ++ if (maps_present) { ++ if (!conf->daemon && !check_daemon()) { ++ condlog(0, "multipath device maps are present, but " ++ "'multipathd' service is not running"); ++ condlog(0, "IO failover/failback will not work without " ++ "'multipathd' service running"); ++ } ++ } ++ + return 0; + } + diff --git a/SOURCES/0196-RHBZ-1239173-dont-set-flag.patch b/SOURCES/0196-RHBZ-1239173-dont-set-flag.patch new file mode 100644 index 0000000..269edc8 --- /dev/null +++ b/SOURCES/0196-RHBZ-1239173-dont-set-flag.patch @@ -0,0 +1,38 @@ +--- + libmultipath/configure.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +Index: multipath-tools-130222/libmultipath/configure.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/configure.c ++++ multipath-tools-130222/libmultipath/configure.c +@@ -257,7 +257,7 @@ extern int + setup_map (struct multipath * mpp, char * params, int params_size) + { + struct pathgroup * pgp; +- int i; ++ int i, old_nr_active; + + /* + * don't bother if devmap size is unknown +@@ -311,8 +311,12 @@ setup_map (struct multipath * mpp, char + if (mpp->pgpolicyfn && mpp->pgpolicyfn(mpp)) + return 1; + ++ old_nr_active = mpp->nr_active; + mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); + ++ if (mpp->nr_active && !old_nr_active) ++ mpp->force_udev_reload = 1; ++ + /* + * ponders each path group and determine highest prio pg + * to switch over (default to first) +@@ -445,7 +449,6 @@ select_action (struct multipath * mpp, v + mpp->alias); + return; + } +- mpp->force_udev_reload = !pathcount(mpp, PATH_WILD); + if (cmpp->size != mpp->size) { + mpp->force_udev_reload = 1; + mpp->action = ACT_RESIZE; diff --git a/SOURCES/0197-RHBZ-1394059-max-sectors-kb.patch b/SOURCES/0197-RHBZ-1394059-max-sectors-kb.patch new file mode 100644 index 0000000..7ed778d --- /dev/null +++ b/SOURCES/0197-RHBZ-1394059-max-sectors-kb.patch @@ -0,0 +1,474 @@ +--- + libmultipath/config.c | 3 + + libmultipath/config.h | 3 + + libmultipath/configure.c | 1 + libmultipath/defaults.h | 1 + libmultipath/devmapper.c | 4 +- + libmultipath/dict.c | 87 +++++++++++++++++++++++++++++++++++++++++++++ + libmultipath/discovery.c | 60 +++++++++++++++++++++++++++++++ + libmultipath/discovery.h | 1 + libmultipath/propsel.c | 25 ++++++++++++ + libmultipath/propsel.h | 1 + libmultipath/structs.h | 7 +++ + multipath/multipath.conf.5 | 8 ++++ + 12 files changed, 200 insertions(+), 1 deletion(-) + +Index: multipath-tools-130222/libmultipath/config.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/config.c ++++ multipath-tools-130222/libmultipath/config.c +@@ -344,6 +344,7 @@ merge_hwe (struct hwentry * dst, struct + merge_num(delay_watch_checks); + merge_num(delay_wait_checks); + merge_num(skip_kpartx); ++ merge_num(max_sectors_kb); + + /* + * Make sure features is consistent with +@@ -405,6 +406,7 @@ overwrite_hwe (struct hwentry * dst, str + overwrite_num(delay_watch_checks); + overwrite_num(delay_wait_checks); + overwrite_num(skip_kpartx); ++ overwrite_num(max_sectors_kb); + + /* + * Make sure features is consistent with +@@ -682,6 +684,7 @@ load_config (char * file, struct udev *u + conf->skip_kpartx = DEFAULT_SKIP_KPARTX; + conf->remove_retries = 0; + conf->disable_changed_wwids = 0; ++ conf->max_sectors_kb = DEFAULT_MAX_SECTORS_KB; + + /* + * preload default hwtable +Index: multipath-tools-130222/libmultipath/config.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/config.h ++++ multipath-tools-130222/libmultipath/config.h +@@ -65,6 +65,7 @@ struct hwentry { + int delay_watch_checks; + int delay_wait_checks; + int skip_kpartx; ++ int max_sectors_kb; + char * bl_product; + }; + +@@ -92,6 +93,7 @@ struct mpentry { + int delay_watch_checks; + int delay_wait_checks; + int skip_kpartx; ++ int max_sectors_kb; + uid_t uid; + gid_t gid; + mode_t mode; +@@ -148,6 +150,7 @@ struct config { + int skip_kpartx; + int remove_retries; + int disable_changed_wwids; ++ int max_sectors_kb; + unsigned int version[3]; + + char * dev; +Index: multipath-tools-130222/libmultipath/configure.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/configure.c ++++ multipath-tools-130222/libmultipath/configure.c +@@ -295,6 +295,7 @@ setup_map (struct multipath * mpp, char + select_delay_watch_checks(mpp); + select_delay_wait_checks(mpp); + select_skip_kpartx(mpp); ++ select_max_sectors_kb(mpp); + + sysfs_set_scsi_tmo(mpp); + /* +Index: multipath-tools-130222/libmultipath/defaults.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/defaults.h ++++ multipath-tools-130222/libmultipath/defaults.h +@@ -25,6 +25,7 @@ + #define DEFAULT_RETRIGGER_TRIES 3 + #define DEFAULT_UEV_WAIT_TIMEOUT 30 + #define DEFAULT_SKIP_KPARTX SKIP_KPARTX_OFF ++#define DEFAULT_MAX_SECTORS_KB MAX_SECTORS_KB_UNDEF + + #define DEFAULT_CHECKINT 5 + #define MAX_CHECKINT(a) (a << 2) +Index: multipath-tools-130222/libmultipath/dict.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/dict.c ++++ multipath-tools-130222/libmultipath/dict.c +@@ -976,6 +976,22 @@ def_disable_changed_wwids_handler(vector + return 0; + } + ++static int ++def_max_sectors_kb_handler(vector strvec) ++{ ++ char * buff; ++ ++ buff = set_value(strvec); ++ if (!buff) ++ return 1; ++ ++ if ((conf->max_sectors_kb = atoi(buff)) < MAX_SECTORS_KB_MIN) ++ conf->max_sectors_kb = MAX_SECTORS_KB_UNDEF; ++ ++ FREE(buff); ++ return 0; ++} ++ + /* + * blacklist block handlers + */ +@@ -1765,6 +1781,26 @@ hw_delay_wait_checks_handler(vector strv + return 0; + } + ++static int ++hw_max_sectors_kb_handler(vector strvec) ++{ ++ struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); ++ char * buff; ++ ++ if (!hwe) ++ return 1; ++ ++ buff = set_value(strvec); ++ if (!buff) ++ return 1; ++ ++ if ((hwe->max_sectors_kb = atoi(buff)) < MAX_SECTORS_KB_MIN) ++ hwe->max_sectors_kb = MAX_SECTORS_KB_UNDEF; ++ ++ FREE(buff); ++ return 0; ++} ++ + /* + * multipaths block handlers + */ +@@ -2316,6 +2352,26 @@ mp_delay_wait_checks_handler(vector strv + return 0; + } + ++static int ++mp_max_sectors_kb_handler(vector strvec) ++{ ++ struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable); ++ char * buff; ++ ++ if (!mpe) ++ return 1; ++ ++ buff = set_value(strvec); ++ if (!buff) ++ return 1; ++ ++ if ((mpe->max_sectors_kb = atoi(buff)) < MAX_SECTORS_KB_MIN) ++ mpe->max_sectors_kb = MAX_SECTORS_KB_UNDEF; ++ ++ FREE(buff); ++ return 0; ++} ++ + /* + * config file keywords printing + */ +@@ -2615,6 +2671,16 @@ snprint_mp_delay_wait_checks(char * buff + } + + static int ++snprint_mp_max_sectors_kb(char * buff, int len, void * data) ++{ ++ struct mpentry * mpe = (struct mpentry *)data; ++ ++ if (mpe->max_sectors_kb == MAX_SECTORS_KB_UNDEF) ++ return 0; ++ return snprintf(buff, len, "%d", mpe->max_sectors_kb); ++} ++ ++static int + snprint_hw_fast_io_fail(char * buff, int len, void * data) + { + struct hwentry * hwe = (struct hwentry *)data; +@@ -2993,6 +3059,16 @@ snprint_detect_prio(char * buff, int len + } + + static int ++snprint_hw_max_sectors_kb(char * buff, int len, void * data) ++{ ++ struct hwentry * hwe = (struct hwentry *)data; ++ ++ if (hwe->max_sectors_kb == MAX_SECTORS_KB_UNDEF) ++ return 0; ++ return snprintf(buff, len, "%d", hwe->max_sectors_kb); ++} ++ ++static int + snprint_def_polling_interval (char * buff, int len, void * data) + { + return snprintf(buff, len, "%i", conf->checkint); +@@ -3461,6 +3537,14 @@ snprint_def_disable_changed_wwids(char * + } + + static int ++snprint_def_max_sectors_kb(char * buff, int len, void * data) ++{ ++ if (conf->max_sectors_kb == MAX_SECTORS_KB_UNDEF) ++ return 0; ++ return snprintf(buff, len, "%d", conf->max_sectors_kb); ++} ++ ++static int + snprint_ble_simple (char * buff, int len, void * data) + { + struct blentry * ble = (struct blentry *)data; +@@ -3541,6 +3625,7 @@ init_keywords(void) + install_keyword("new_bindings_in_boot", &def_new_bindings_in_boot_handler, &snprint_def_new_bindings_in_boot); + install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries); + install_keyword("disable_changed_wwids", &def_disable_changed_wwids_handler, &snprint_def_disable_changed_wwids); ++ install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb); + __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); +@@ -3609,6 +3694,7 @@ init_keywords(void) + install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks); + install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks); + install_keyword("skip_kpartx", &hw_skip_kpartx_handler, &snprint_hw_skip_kpartx); ++ install_keyword("max_sectors_kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb); + install_sublevel_end(); + + install_keyword_root("multipaths", &multipaths_handler); +@@ -3637,5 +3723,6 @@ init_keywords(void) + install_keyword("delay_watch_checks", &mp_delay_watch_checks_handler, &snprint_mp_delay_watch_checks); + install_keyword("delay_wait_checks", &mp_delay_wait_checks_handler, &snprint_mp_delay_wait_checks); + install_keyword("skip_kpartx", &mp_skip_kpartx_handler, &snprint_mp_skip_kpartx); ++ install_keyword("max_sectors_kb", &mp_max_sectors_kb_handler, &snprint_mp_max_sectors_kb); + install_sublevel_end(); + } +Index: multipath-tools-130222/libmultipath/discovery.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/discovery.c ++++ multipath-tools-130222/libmultipath/discovery.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #include "checkers.h" + #include "vector.h" +@@ -27,6 +28,7 @@ + #include "discovery.h" + #include "prio.h" + #include "defaults.h" ++#include "devmapper.h" + + int + store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice, +@@ -166,6 +168,64 @@ declare_sysfs_get_str(rev); + declare_sysfs_get_str(dev); + + int ++sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload) ++{ ++ struct pathgroup * pgp; ++ struct path *pp; ++ char buff[11]; ++ struct udev_device *udevice = NULL; ++ int i, j, len, ret; ++ int max_sectors_kb; ++ ++ if (mpp->max_sectors_kb == MAX_SECTORS_KB_UNDEF) ++ return 0; ++ max_sectors_kb = mpp->max_sectors_kb; ++ if (is_reload) { ++ if (!mpp->dmi && dm_get_info(mpp->alias, &mpp->dmi) != 0) { ++ condlog(0, "failed to get dm info on %s to set max_sectors_kb", mpp->alias); ++ return 1; ++ } ++ udevice = udev_device_new_from_devnum(conf->udev, 'b', ++ makedev(mpp->dmi->major, ++ mpp->dmi->minor)); ++ if (!udevice) { ++ condlog(0, "failed to get udev device to set max_sectors_kb for %s", mpp->alias); ++ return 1; ++ } ++ if (sysfs_attr_get_value(udevice, "queue/max_sectors_kb", ++ buff, sizeof(buff)) <= 0) { ++ condlog(0, "failed to get current max_sectors_kb from %s", mpp->alias); ++ goto fail_reload; ++ } ++ if (sscanf(buff, "%u\n", &max_sectors_kb) != 1) { ++ condlog(0, "can't parse current max_sectors_kb from %s", ++ mpp->alias); ++ goto fail_reload; ++ } ++ udev_device_unref(udevice); ++ } ++ snprintf(buff, 11, "%d", max_sectors_kb); ++ len = strlen(buff); ++ ++ vector_foreach_slot (mpp->pg, pgp, i) { ++ vector_foreach_slot (pgp->paths, pp, j) { ++ ret = sysfs_attr_set_value(pp->udev, ++ "queue/max_sectors_kb", ++ buff, len); ++ if (ret < 0) { ++ condlog(0, "failed setting max_sectors_kb on %s : %s", pp->dev, strerror(-ret)); ++ return 1; ++ } ++ } ++ } ++ return 0; ++ ++fail_reload: ++ udev_device_unref(udevice); ++ return 1; ++} ++ ++int + sysfs_get_timeout(struct path *pp, unsigned int *timeout) + { + const char *attr = NULL; +Index: multipath-tools-130222/libmultipath/discovery.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/discovery.h ++++ multipath-tools-130222/libmultipath/discovery.h +@@ -41,6 +41,7 @@ int store_pathinfo (vector pathvec, vect + struct udev_device *udevice, int flag, + struct path **pp_ptr); + int sysfs_set_scsi_tmo (struct multipath *mpp); ++int sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload); + int sysfs_get_timeout(struct path *pp, unsigned int *timeout); + int sysfs_get_host_pci_name(struct path *pp, char *pci_name); + int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address); +Index: multipath-tools-130222/libmultipath/propsel.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/propsel.c ++++ multipath-tools-130222/libmultipath/propsel.c +@@ -880,3 +880,28 @@ select_skip_kpartx (struct multipath * m + condlog(3, "skip_kpartx = DISABLED (internal default)"); + return 0; + } ++ ++extern int ++select_max_sectors_kb (struct multipath * mp) ++{ ++ if (mp->mpe && mp->mpe->max_sectors_kb != MAX_SECTORS_KB_UNDEF) { ++ mp->max_sectors_kb = mp->mpe->max_sectors_kb; ++ condlog(3, "max_sectors_kb = %i (multipath setting)", ++ mp->max_sectors_kb); ++ return 0; ++ } ++ if (mp->hwe && mp->hwe->max_sectors_kb != MAX_SECTORS_KB_UNDEF) { ++ mp->max_sectors_kb = mp->hwe->max_sectors_kb; ++ condlog(3, "max_sectors_kb = %i (controler setting)", ++ mp->max_sectors_kb); ++ return 0; ++ } ++ if (conf->max_sectors_kb != MAX_SECTORS_KB_UNDEF) { ++ mp->max_sectors_kb = conf->max_sectors_kb; ++ condlog(3, "max_sectors_kb = %i (config file default)", ++ mp->max_sectors_kb); ++ return 0; ++ } ++ mp->max_sectors_kb = MAX_SECTORS_KB_UNDEF; ++ return 0; ++} +Index: multipath-tools-130222/libmultipath/propsel.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/propsel.h ++++ multipath-tools-130222/libmultipath/propsel.h +@@ -24,3 +24,4 @@ int select_deferred_remove(struct multip + int select_delay_watch_checks (struct multipath * mp); + int select_delay_wait_checks (struct multipath * mp); + int select_skip_kpartx (struct multipath * mp); ++int select_max_sectors_kb (struct multipath * mp); +Index: multipath-tools-130222/libmultipath/structs.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/structs.h ++++ multipath-tools-130222/libmultipath/structs.h +@@ -128,6 +128,12 @@ enum skip_kpartx_states { + SKIP_KPARTX_ON, + }; + ++ ++enum max_sectors_kb_states { ++ MAX_SECTORS_KB_UNDEF = 0, ++ MAX_SECTORS_KB_MIN = 4, /* can't be smaller than page size */ ++}; ++ + enum scsi_protocol { + SCSI_PROTOCOL_FCP = 0, /* Fibre Channel */ + SCSI_PROTOCOL_SPI = 1, /* parallel SCSI */ +@@ -245,6 +251,7 @@ struct multipath { + int delay_wait_checks; + int force_udev_reload; + int skip_kpartx; ++ int max_sectors_kb; + unsigned int dev_loss; + uid_t uid; + gid_t gid; +Index: multipath-tools-130222/multipath/multipath.conf.5 +=================================================================== +--- multipath-tools-130222.orig/multipath/multipath.conf.5 ++++ multipath-tools-130222/multipath/multipath.conf.5 +@@ -561,6 +561,10 @@ default is + This sets how may times multipath will retry removing a device that is in-use. + Between each attempt, multipath will sleep 1 second. The default is + .I 0 ++.TP ++.B max_sectors_kb ++Sets the max_sectors_kb device parameter on all path devices and the multipath ++device to the specified value. Default is device dependent. + . + .SH "blacklist section" + The +@@ -672,6 +676,8 @@ section: + .B delay_wait_checks + .TP + .B skip_kpartx ++.TP ++.B max_sectors_kb + .RE + .PD + .LP +@@ -772,6 +778,8 @@ section: + .B delay_wait_checks + .TP + .B skip_kpartx ++.TP ++.B max_sectors_kb + .RE + .PD + .LP +Index: multipath-tools-130222/libmultipath/devmapper.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/devmapper.c ++++ multipath-tools-130222/libmultipath/devmapper.c +@@ -21,7 +21,7 @@ + #include "devmapper.h" + #include "config.h" + #include "sysfs.h" +- ++#include "discovery.h" + #include "log_pthread.h" + #include + #include +@@ -330,6 +330,7 @@ extern int + dm_addmap_create (struct multipath *mpp, char * params) { + int ro; + ++ sysfs_set_max_sectors_kb(mpp, 0); + for (ro = 0; ro <= 1; ro++) { + int err; + +@@ -356,6 +357,7 @@ dm_addmap_create (struct multipath *mpp, + + extern int + dm_addmap_reload (struct multipath *mpp, char *params) { ++ sysfs_set_max_sectors_kb(mpp, 1); + if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW, SKIP_KPARTX_OFF)) + return 1; + if (errno != EROFS) diff --git a/SOURCES/0198-RHBZ-1372032-detect-path-checker.patch b/SOURCES/0198-RHBZ-1372032-detect-path-checker.patch new file mode 100644 index 0000000..48fcfce --- /dev/null +++ b/SOURCES/0198-RHBZ-1372032-detect-path-checker.patch @@ -0,0 +1,377 @@ +--- + libmultipath/config.c | 4 ++ + libmultipath/config.h | 2 + + libmultipath/defaults.h | 1 + libmultipath/dict.c | 74 +++++++++++++++++++++++++++++++++++++++++++++ + libmultipath/discovery.c | 1 + libmultipath/hwtable.c | 1 + libmultipath/propsel.c | 65 +++++++++++++++++++++++++++++++-------- + libmultipath/propsel.h | 1 + libmultipath/structs.h | 7 ++++ + multipath/multipath.conf.5 | 9 +++++ + 10 files changed, 152 insertions(+), 13 deletions(-) + +Index: multipath-tools-130222/libmultipath/config.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/config.c ++++ multipath-tools-130222/libmultipath/config.c +@@ -340,6 +340,7 @@ merge_hwe (struct hwentry * dst, struct + merge_num(user_friendly_names); + merge_num(retain_hwhandler); + merge_num(detect_prio); ++ merge_num(detect_checker); + merge_num(deferred_remove); + merge_num(delay_watch_checks); + merge_num(delay_wait_checks); +@@ -402,6 +403,7 @@ overwrite_hwe (struct hwentry * dst, str + overwrite_num(user_friendly_names); + overwrite_num(retain_hwhandler); + overwrite_num(detect_prio); ++ overwrite_num(detect_checker); + overwrite_num(deferred_remove); + overwrite_num(delay_watch_checks); + overwrite_num(delay_wait_checks); +@@ -476,6 +478,7 @@ store_hwe (vector hwtable, struct hwentr + hwe->user_friendly_names = dhwe->user_friendly_names; + hwe->retain_hwhandler = dhwe->retain_hwhandler; + hwe->detect_prio = dhwe->detect_prio; ++ hwe->detect_checker = dhwe->detect_checker; + + if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product))) + goto out; +@@ -672,6 +675,7 @@ load_config (char * file, struct udev *u + conf->fast_io_fail = DEFAULT_FAST_IO_FAIL; + conf->retain_hwhandler = DEFAULT_RETAIN_HWHANDLER; + conf->detect_prio = DEFAULT_DETECT_PRIO; ++ conf->detect_checker = DEFAULT_DETECT_CHECKER; + conf->deferred_remove = DEFAULT_DEFERRED_REMOVE; + conf->hw_strmatch = 0; + conf->force_sync = 0; +Index: multipath-tools-130222/libmultipath/config.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/config.h ++++ multipath-tools-130222/libmultipath/config.h +@@ -61,6 +61,7 @@ struct hwentry { + int user_friendly_names; + int retain_hwhandler; + int detect_prio; ++ int detect_checker; + int deferred_remove; + int delay_watch_checks; + int delay_wait_checks; +@@ -136,6 +137,7 @@ struct config { + int reassign_maps; + int retain_hwhandler; + int detect_prio; ++ int detect_checker; + int force_sync; + int deferred_remove; + int ignore_new_boot_devs; +Index: multipath-tools-130222/libmultipath/defaults.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/defaults.h ++++ multipath-tools-130222/libmultipath/defaults.h +@@ -19,6 +19,7 @@ + #define DEFAULT_FAST_IO_FAIL 5 + #define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_OFF + #define DEFAULT_DETECT_PRIO DETECT_PRIO_OFF ++#define DEFAULT_DETECT_CHECKER DETECT_CHECKER_OFF + #define DEFAULT_DEFERRED_REMOVE DEFERRED_REMOVE_OFF + #define DEFAULT_DELAY_CHECKS DELAY_CHECKS_OFF + #define DEFAULT_RETRIGGER_DELAY 10 +Index: multipath-tools-130222/libmultipath/dict.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/dict.c ++++ multipath-tools-130222/libmultipath/dict.c +@@ -714,6 +714,29 @@ def_detect_prio_handler(vector strvec) + } + + static int ++def_detect_checker_handler(vector strvec) ++{ ++ char * buff; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ if ((strlen(buff) == 2 && !strcmp(buff, "no")) || ++ (strlen(buff) == 1 && !strcmp(buff, "0"))) ++ conf->detect_checker = DETECT_CHECKER_OFF; ++ else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) || ++ (strlen(buff) == 1 && !strcmp(buff, "1"))) ++ conf->detect_checker = DETECT_CHECKER_ON; ++ else ++ conf->detect_checker = DETECT_CHECKER_UNDEF; ++ ++ FREE(buff); ++ return 0; ++} ++ ++static int + def_hw_strmatch_handler(vector strvec) + { + char *buff; +@@ -1682,6 +1705,33 @@ hw_detect_prio_handler(vector strvec) + } + + static int ++hw_detect_checker_handler(vector strvec) ++{ ++ struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); ++ char * buff; ++ ++ if (!hwe) ++ return 1; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ if ((strlen(buff) == 2 && !strcmp(buff, "no")) || ++ (strlen(buff) == 1 && !strcmp(buff, "0"))) ++ hwe->detect_checker = DETECT_CHECKER_OFF; ++ else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) || ++ (strlen(buff) == 1 && !strcmp(buff, "1"))) ++ hwe->detect_checker = DETECT_CHECKER_ON; ++ else ++ hwe->detect_checker = DETECT_CHECKER_UNDEF; ++ ++ FREE(buff); ++ return 0; ++} ++ ++static int + hw_deferred_remove_handler(vector strvec) + { + struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable); +@@ -3059,6 +3109,19 @@ snprint_detect_prio(char * buff, int len + } + + static int ++snprint_detect_checker(char * buff, int len, void * data) ++{ ++ struct hwentry * hwe = (struct hwentry *)data; ++ ++ if (hwe->detect_checker == DETECT_CHECKER_ON) ++ return snprintf(buff, len, "yes"); ++ else if (hwe->detect_checker == DETECT_CHECKER_OFF) ++ return snprintf(buff, len, "no"); ++ else ++ return 0; ++} ++ ++static int + snprint_hw_max_sectors_kb(char * buff, int len, void * data) + { + struct hwentry * hwe = (struct hwentry *)data; +@@ -3424,6 +3487,15 @@ snprint_def_detect_prio(char * buff, int + } + + static int ++snprint_def_detect_checker(char * buff, int len, void * data) ++{ ++ if (conf->detect_checker == DETECT_PRIO_ON) ++ return snprintf(buff, len, "yes"); ++ else ++ return snprintf(buff, len, "no"); ++} ++ ++static int + snprint_def_hw_strmatch(char * buff, int len, void * data) + { + if (conf->hw_strmatch) +@@ -3611,6 +3683,7 @@ init_keywords(void) + install_keyword("find_multipaths", &def_find_multipaths_handler, &snprint_def_find_multipaths); + install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler_handler); + install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio); ++ install_keyword("detect_path_checker", &def_detect_checker_handler, &snprint_def_detect_checker); + install_keyword("hw_str_match", &def_hw_strmatch_handler, &snprint_def_hw_strmatch); + install_keyword("force_sync", &def_force_sync_handler, &snprint_def_force_sync); + install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove); +@@ -3690,6 +3763,7 @@ init_keywords(void) + install_keyword("user_friendly_names", &hw_names_handler, &snprint_hw_user_friendly_names); + install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler_handler); + install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_detect_prio); ++ install_keyword("detect_path_checker", &hw_detect_checker_handler, &snprint_detect_checker); + install_keyword("deferred_remove", &hw_deferred_remove_handler, &snprint_hw_deferred_remove); + install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks); + install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks); +Index: multipath-tools-130222/libmultipath/discovery.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/discovery.c ++++ multipath-tools-130222/libmultipath/discovery.c +@@ -1107,6 +1107,7 @@ get_state (struct path * pp, int daemon) + return PATH_UNCHECKED; + } + } ++ select_detect_checker(pp); + select_checker(pp); + if (!checker_selected(c)) { + condlog(3, "%s: No checker selected", pp->dev); +Index: multipath-tools-130222/libmultipath/hwtable.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/hwtable.c ++++ multipath-tools-130222/libmultipath/hwtable.c +@@ -289,6 +289,7 @@ static struct hwentry default_hw[] = { + .prio_args = NULL, + .retain_hwhandler = RETAIN_HWHANDLER_ON, + .detect_prio = DETECT_PRIO_ON, ++ .detect_checker = DETECT_CHECKER_ON, + }, + { + .vendor = "EMC", +Index: multipath-tools-130222/libmultipath/propsel.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/propsel.c ++++ multipath-tools-130222/libmultipath/propsel.c +@@ -335,11 +335,43 @@ select_hwhandler (struct multipath * mp) + return 0; + } + ++int ++detect_alua(struct path * pp) ++{ ++ int ret; ++ int tpgs = 0; ++ ++ if ((tpgs = get_target_port_group_support(pp->fd)) <= 0) ++ return 0; ++ pp->tpgs = tpgs; ++ ret = get_target_port_group(pp->fd, NULL); ++ if (ret < 0) ++ return 0; ++ if (get_asymmetric_access_state(pp->fd, ret, NULL) < 0) ++ return 0; ++ return 1; ++} ++ ++void ++detect_checker(struct path * pp) ++{ ++ if (detect_alua(pp)) ++ checker_get(&pp->checker, TUR); ++} ++ + extern int + select_checker(struct path *pp) + { + struct checker * c = &pp->checker; + ++ if (pp->detect_checker == DETECT_CHECKER_ON) { ++ detect_checker(pp); ++ if (checker_selected(c)) { ++ condlog(3, "%s: path checker = %s (detected setting)", ++ pp->dev, checker_name(c)); ++ goto out; ++ } ++ } + if (pp->hwe && pp->hwe->checker_name) { + checker_get(c, pp->hwe->checker_name); + condlog(3, "%s: path checker = %s (controller setting)", +@@ -396,19 +428,8 @@ select_getuid (struct path * pp) + void + detect_prio(struct path * pp) + { +- int ret; +- struct prio *p = &pp->prio; +- int tpgs = 0; +- +- if ((tpgs = get_target_port_group_support(pp->fd)) <= 0) +- return; +- pp->tpgs = tpgs; +- ret = get_target_port_group(pp->fd, NULL); +- if (ret < 0) +- return; +- if (get_asymmetric_access_state(pp->fd, ret, NULL) < 0) +- return; +- prio_get(p, PRIO_ALUA, DEFAULT_PRIO_ARGS); ++ if (detect_alua(pp)) ++ prio_get(&pp->prio, PRIO_ALUA, DEFAULT_PRIO_ARGS); + } + + extern int +@@ -803,6 +824,24 @@ select_detect_prio (struct path * pp) + return 0; + } + ++extern int ++select_detect_checker (struct path * pp) ++{ ++ if (pp->hwe && pp->hwe->detect_checker) { ++ pp->detect_checker = pp->hwe->detect_checker; ++ condlog(3, "%s: detect_checker = %d (controller default)", pp->dev, pp->detect_checker); ++ return 0; ++ } ++ if (conf->detect_checker) { ++ pp->detect_checker = conf->detect_checker; ++ condlog(3, "%s: detect_checker = %d (config file default)", pp->dev, pp->detect_checker); ++ return 0; ++ } ++ pp->detect_checker = DEFAULT_DETECT_CHECKER; ++ condlog(3, "%s: detect_checker = %d (compiled in default)", pp->dev, pp->detect_checker); ++ return 0; ++} ++ + extern int + select_delay_watch_checks (struct multipath * mp) + { +Index: multipath-tools-130222/libmultipath/propsel.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/propsel.h ++++ multipath-tools-130222/libmultipath/propsel.h +@@ -20,6 +20,7 @@ int select_dev_loss(struct multipath *mp + int select_reservation_key(struct multipath *mp); + int select_retain_hwhandler (struct multipath * mp); + int select_detect_prio(struct path * pp); ++int select_detect_checker(struct path * pp); + int select_deferred_remove(struct multipath *mp); + int select_delay_watch_checks (struct multipath * mp); + int select_delay_wait_checks (struct multipath * mp); +Index: multipath-tools-130222/libmultipath/structs.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/structs.h ++++ multipath-tools-130222/libmultipath/structs.h +@@ -115,6 +115,12 @@ enum detect_prio_states { + DETECT_PRIO_ON, + }; + ++enum detect_checker_states { ++ DETECT_CHECKER_UNDEF, ++ DETECT_CHECKER_OFF, ++ DETECT_CHECKER_ON, ++}; ++ + enum deferred_remove_states { + DEFERRED_REMOVE_UNDEF, + DEFERRED_REMOVE_OFF, +@@ -204,6 +210,7 @@ struct path { + int priority; + int pgindex; + int detect_prio; ++ int detect_checker; + int watch_checks; + int wait_checks; + int tpgs; +Index: multipath-tools-130222/multipath/multipath.conf.5 +=================================================================== +--- multipath-tools-130222.orig/multipath/multipath.conf.5 ++++ multipath-tools-130222/multipath/multipath.conf.5 +@@ -448,6 +448,15 @@ will automatically use the + prioritizer. If not, the prioritizer will be selected as usual. Default is + .I no + .TP ++.B detect_checker ++If set to ++.I yes ++, multipath will try to detect if the device supports ALUA. If so, the device ++will automatically use the ++.I tur ++checker. If not, the prioritizer will be selected as ususal. Default is ++.I no ++.TP + .B hw_str_match + If set to + .I yes diff --git a/SOURCES/0199-RHBZ-1279355-3pardata-config.patch b/SOURCES/0199-RHBZ-1279355-3pardata-config.patch new file mode 100644 index 0000000..037de3d --- /dev/null +++ b/SOURCES/0199-RHBZ-1279355-3pardata-config.patch @@ -0,0 +1,17 @@ +--- + libmultipath/hwtable.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: multipath-tools-130222/libmultipath/hwtable.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/hwtable.c ++++ multipath-tools-130222/libmultipath/hwtable.c +@@ -69,7 +69,7 @@ static struct hwentry default_hw[] = { + .pgpolicy = MULTIBUS, + .pgfailback = FAILBACK_UNDEF, + .rr_weight = RR_WEIGHT_NONE, +- .no_path_retry = NO_PATH_RETRY_UNDEF, ++ .no_path_retry = 12, + .checker_name = DEFAULT_CHECKER, + .prio_name = DEFAULT_PRIO, + .prio_args = NULL, diff --git a/SOURCES/0200-RHBZ-1402092-orphan-status.patch b/SOURCES/0200-RHBZ-1402092-orphan-status.patch new file mode 100644 index 0000000..67830ca --- /dev/null +++ b/SOURCES/0200-RHBZ-1402092-orphan-status.patch @@ -0,0 +1,29 @@ +--- + libmultipath/print.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +Index: multipath-tools-130222/libmultipath/print.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/print.c ++++ multipath-tools-130222/libmultipath/print.c +@@ -386,7 +386,9 @@ snprint_dev_t (char * buff, size_t len, + static int + snprint_offline (char * buff, size_t len, struct path * pp) + { +- if (pp->offline) ++ if (!pp || !pp->mpp) ++ return snprintf(buff, len, "unknown"); ++ else if (pp->offline) + return snprintf(buff, len, "offline"); + else + return snprintf(buff, len, "running"); +@@ -395,6 +397,9 @@ snprint_offline (char * buff, size_t len + static int + snprint_chk_state (char * buff, size_t len, struct path * pp) + { ++ if (!pp || !pp->mpp) ++ return snprintf(buff, len, "undef"); ++ + switch (pp->state) { + case PATH_UP: + return snprintf(buff, len, "ready"); diff --git a/SOURCES/0201-RHBZ-1403552-silence-warning.patch b/SOURCES/0201-RHBZ-1403552-silence-warning.patch new file mode 100644 index 0000000..c816ea0 --- /dev/null +++ b/SOURCES/0201-RHBZ-1403552-silence-warning.patch @@ -0,0 +1,59 @@ +--- + libmultipath/discovery.c | 15 +++++++++++---- + multipathd/main.c | 10 ++++++++++ + 2 files changed, 21 insertions(+), 4 deletions(-) + +Index: multipath-tools-130222/libmultipath/discovery.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/discovery.c ++++ multipath-tools-130222/libmultipath/discovery.c +@@ -84,10 +84,6 @@ path_discover (vector pathvec, struct co + if (!devname) + return PATHINFO_FAILED; + +- if (filter_devnode(conf->blist_devnode, conf->elist_devnode, +- (char *)devname) > 0) +- return PATHINFO_SKIPPED; +- + pp = find_path_by_dev(pathvec, (char *)devname); + if (!pp) { + return store_pathinfo(pathvec, conf->hwtable, +@@ -1286,6 +1282,17 @@ pathinfo (struct path *pp, vector hwtabl + if (!pp) + return PATHINFO_FAILED; + ++ /* ++ * For behavior backward-compatibility with multipathd, ++ * the blacklisting by filter_devnode() is not ++ * limited by DI_BLACKLIST and occurs before this debug ++ * message with the mask value. ++ */ ++ if (filter_devnode(conf->blist_devnode, ++ conf->elist_devnode, ++ pp->dev) > 0) ++ return PATHINFO_SKIPPED; ++ + condlog(3, "%s: mask = 0x%x", pp->dev, mask); + + /* +Index: multipath-tools-130222/multipathd/main.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/main.c ++++ multipath-tools-130222/multipathd/main.c +@@ -776,6 +776,16 @@ uev_update_path (struct uevent *uev, str + + pp = find_path_by_dev(vecs->pathvec, uev->kernel); + if (!pp) { ++ /* If the path is blacklisted, print a debug/non-default verbosity message. */ ++ if (uev->udev) { ++ int flag = DI_SYSFS | DI_WWID; ++ ++ if (store_pathinfo(NULL, conf->hwtable, uev->udev, flag, NULL) == PATHINFO_SKIPPED) { ++ condlog(3, "%s: spurious uevent, path is blacklisted", uev->kernel); ++ return 0; ++ } ++ } ++ + condlog(0, "%s: spurious uevent, path not found", + uev->kernel); + return 1; diff --git a/SOURCES/0202-RHBZ-1362120-skip-prio.patch b/SOURCES/0202-RHBZ-1362120-skip-prio.patch new file mode 100644 index 0000000..8a187ad --- /dev/null +++ b/SOURCES/0202-RHBZ-1362120-skip-prio.patch @@ -0,0 +1,18 @@ +--- + multipathd/main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +Index: multipath-tools-130222/multipathd/main.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/main.c ++++ multipath-tools-130222/multipathd/main.c +@@ -1248,7 +1248,8 @@ int update_prio(struct path *pp, int ref + return changed; + } + oldpriority = pp->priority; +- pathinfo(pp, conf->hwtable, DI_PRIO); ++ if (pp->state != PATH_DOWN) ++ pathinfo(pp, conf->hwtable, DI_PRIO); + + if (pp->priority == oldpriority) + return 0; diff --git a/SOURCES/0203-RHBZ-1363718-add-msgs.patch b/SOURCES/0203-RHBZ-1363718-add-msgs.patch new file mode 100644 index 0000000..9eaadc0 --- /dev/null +++ b/SOURCES/0203-RHBZ-1363718-add-msgs.patch @@ -0,0 +1,24 @@ +--- + multipathd/main.c | 2 ++ + 1 file changed, 2 insertions(+) + +Index: multipath-tools-130222/multipathd/main.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/main.c ++++ multipath-tools-130222/multipathd/main.c +@@ -337,6 +337,7 @@ ev_add_map (char * dev, char * alias, st + + if (mpp) { + if (mpp->wait_for_udev > 1) { ++ condlog(2, "%s: performing delayed actions", mpp->alias); + if (update_map(mpp, vecs)) + /* setup multipathd removed the map */ + return 1; +@@ -535,6 +536,7 @@ ev_add_path (struct path * pp, struct ve + pp->tpgs == TPGS_IMPLICIT)) + mpp->force_udev_reload = 1; + else { ++ condlog(2, "%s : delaying path addition until %s is fully initialized", pp->dev, mpp->alias); + mpp->wait_for_udev = 2; + orphan_path(pp); + return 0; diff --git a/SOURCES/0204-RHBZ-1406226-nimble-config.patch b/SOURCES/0204-RHBZ-1406226-nimble-config.patch new file mode 100644 index 0000000..c3f2e90 --- /dev/null +++ b/SOURCES/0204-RHBZ-1406226-nimble-config.patch @@ -0,0 +1,28 @@ +--- + libmultipath/hwtable.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +Index: multipath-tools-130222/libmultipath/hwtable.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/hwtable.c ++++ multipath-tools-130222/libmultipath/hwtable.c +@@ -1189,6 +1189,19 @@ static struct hwentry default_hw[] = { + .dev_loss = 30, + }, + { ++ .vendor = "Nimble", ++ .product = "Server", ++ .features = "1 queue_if_no_path", ++ .hwhandler = "1 alua", ++ .pgpolicy = GROUP_BY_PRIO, ++ .prio_name = PRIO_ALUA, ++ .prio_args = NULL, ++ .pgfailback = -FAILBACK_IMMEDIATE, ++ .selector = "round-robin 0", ++ .dev_loss = MAX_DEV_LOSS_TMO, ++ .fast_io_fail = 1, ++ }, ++ { + .vendor = "XtremIO", + .product = "XtremApp", + .features = DEFAULT_FEATURES, diff --git a/SOURCES/0205-RHBZ-1416569-reset-stats.patch b/SOURCES/0205-RHBZ-1416569-reset-stats.patch new file mode 100644 index 0000000..bb414ba --- /dev/null +++ b/SOURCES/0205-RHBZ-1416569-reset-stats.patch @@ -0,0 +1,108 @@ +--- + multipathd/cli.c | 2 ++ + multipathd/cli_handlers.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ + multipathd/cli_handlers.h | 2 ++ + multipathd/main.c | 2 ++ + 4 files changed, 50 insertions(+) + +Index: multipath-tools-130222/multipathd/cli.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/cli.c ++++ multipath-tools-130222/multipathd/cli.c +@@ -482,6 +482,8 @@ cli_init (void) { + add_handler(LIST+BLACKLIST, NULL); + add_handler(LIST+DEVICES, NULL); + add_handler(LIST+WILDCARDS, NULL); ++ add_handler(RESET+MAPS+STATS, NULL); ++ add_handler(RESET+MAP+STATS, NULL); + add_handler(ADD+PATH, NULL); + add_handler(DEL+PATH, NULL); + add_handler(ADD+MAP, NULL); +Index: multipath-tools-130222/multipathd/cli_handlers.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/cli_handlers.c ++++ multipath-tools-130222/multipathd/cli_handlers.c +@@ -233,6 +233,17 @@ show_config (char ** r, int * len) + return 0; + } + ++void ++reset_stats(struct multipath * mpp) ++{ ++ mpp->stat_switchgroup = 0; ++ mpp->stat_path_failures = 0; ++ mpp->stat_map_loads = 0; ++ mpp->stat_total_queueing_time = 0; ++ mpp->stat_queueing_timeouts = 0; ++ mpp->stat_map_failures = 0; ++} ++ + int + cli_list_config (void * v, char ** reply, int * len, void * data) + { +@@ -501,6 +512,39 @@ cli_list_daemon (void * v, char ** reply + } + + int ++cli_reset_maps_stats (void * v, char ** reply, int * len, void * data) ++{ ++ struct vectors * vecs = (struct vectors *)data; ++ int i; ++ struct multipath * mpp; ++ ++ condlog(3, "reset multipaths stats (operator)"); ++ ++ vector_foreach_slot(vecs->mpvec, mpp, i) { ++ reset_stats(mpp); ++ } ++ return 0; ++} ++ ++int ++cli_reset_map_stats (void * v, char ** reply, int * len, void * data) ++{ ++ struct vectors * vecs = (struct vectors *)data; ++ struct multipath * mpp; ++ char * param = get_keyparam(v, MAP); ++ ++ param = convert_dev(param, 0); ++ mpp = find_mp_by_str(vecs->mpvec, param); ++ ++ if (!mpp) ++ return 1; ++ ++ condlog(3, "reset multipath %s stats (operator)", param); ++ reset_stats(mpp); ++ return 0; ++} ++ ++int + cli_add_path (void * v, char ** reply, int * len, void * data) + { + struct vectors * vecs = (struct vectors *)data; +Index: multipath-tools-130222/multipathd/cli_handlers.h +=================================================================== +--- multipath-tools-130222.orig/multipathd/cli_handlers.h ++++ multipath-tools-130222/multipathd/cli_handlers.h +@@ -16,6 +16,8 @@ int cli_list_config (void * v, char ** r + int cli_list_blacklist (void * v, char ** reply, int * len, void * data); + int cli_list_devices (void * v, char ** reply, int * len, void * data); + int cli_list_wildcards (void * v, char ** reply, int * len, void * data); ++int cli_reset_maps_stats (void * v, char ** reply, int * len, void * data); ++int cli_reset_map_stats (void * v, char ** reply, int * len, void * data); + int cli_add_path (void * v, char ** reply, int * len, void * data); + int cli_del_path (void * v, char ** reply, int * len, void * data); + int cli_add_map (void * v, char ** reply, int * len, void * data); +Index: multipath-tools-130222/multipathd/main.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/main.c ++++ multipath-tools-130222/multipathd/main.c +@@ -1011,6 +1011,8 @@ uxlsnrloop (void * ap) + set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); + set_handler_callback(LIST+DEVICES, cli_list_devices); + set_handler_callback(LIST+WILDCARDS, cli_list_wildcards); ++ set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats); ++ set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats); + set_handler_callback(ADD+PATH, cli_add_path); + set_handler_callback(DEL+PATH, cli_del_path); + set_handler_callback(ADD+MAP, cli_add_map); diff --git a/SOURCES/0206-RHBZ-1239173-pt2-no-paths.patch b/SOURCES/0206-RHBZ-1239173-pt2-no-paths.patch new file mode 100644 index 0000000..c38be56 --- /dev/null +++ b/SOURCES/0206-RHBZ-1239173-pt2-no-paths.patch @@ -0,0 +1,121 @@ +--- + libmultipath/configure.c | 2 - + libmultipath/devmapper.h | 6 ++++ + multipath/11-dm-mpath.rules | 61 +++++++++++++++++++++++++++++++++----------- + 3 files changed, 54 insertions(+), 15 deletions(-) + +Index: multipath-tools-130222/libmultipath/configure.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/configure.c ++++ multipath-tools-130222/libmultipath/configure.c +@@ -615,7 +615,7 @@ extern int + domap (struct multipath * mpp, char * params) + { + int r = 0; +- uint16_t udev_flags = ((mpp->force_udev_reload)? 0 : MPATH_UDEV_RELOAD_FLAG) | ((mpp->skip_kpartx == SKIP_KPARTX_ON)? MPATH_UDEV_NO_KPARTX_FLAG : 0); ++ uint16_t udev_flags = ((mpp->force_udev_reload)? 0 : MPATH_UDEV_RELOAD_FLAG) | ((mpp->skip_kpartx == SKIP_KPARTX_ON)? MPATH_UDEV_NO_KPARTX_FLAG : 0) | ((mpp->nr_active)? 0 : MPATH_UDEV_NO_PATHS_FLAG); + + /* + * last chance to quit before touching the devmaps +Index: multipath-tools-130222/libmultipath/devmapper.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/devmapper.h ++++ multipath-tools-130222/libmultipath/devmapper.h +@@ -18,6 +18,12 @@ + #define MPATH_UDEV_NO_KPARTX_FLAG 0 + #endif + ++#ifdef DM_SUBSYSTEM_UDEV_FLAG2 ++#define MPATH_UDEV_NO_PATHS_FLAG DM_SUBSYSTEM_UDEV_FLAG2 ++#else ++#define MPATH_UDEV_NO_PATHS_FLAG 0 ++#endif ++ + void dm_init(void); + int dm_prereq (void); + int dm_drv_version (unsigned int * version, char * str); +Index: multipath-tools-130222/multipath/11-dm-mpath.rules +=================================================================== +--- multipath-tools-130222.orig/multipath/11-dm-mpath.rules ++++ multipath-tools-130222/multipath/11-dm-mpath.rules +@@ -2,33 +2,66 @@ ACTION!="add|change", GOTO="mpath_end" + ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="mpath_end" + ENV{DM_UUID}!="mpath-?*", GOTO="mpath_end" + ++IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD" ++IMPORT{db}="MPATH_DEVICE_READY" ++ ++# If this uevent didn't come from dm, don't try to update the ++# device state ++ENV{DM_COOKIE}!="?*", ENV{DM_ACTION}!="PATH_*", IMPORT{db}="DM_UDEV_DISABLE_OTHER_RULES_FLAG", IMPORT{db}="DM_NOSCAN", GOTO="scan_import" ++ ++ENV{.MPATH_DEVICE_READY_OLD}="$env{MPATH_DEVICE_READY}" ++ ++# multipath sets DM_SUBSYSTEM_UDEV_FLAG2 when it reloads a ++# table with no active devices. If this happens, mark the ++# device not ready ++ENV{DM_SUBSYSTEM_UDEV_FLAG2}=="1", ENV{MPATH_DEVICE_READY}="0",\ ++ GOTO="mpath_action" ++ ++# If the last path has failed mark the device not ready ++ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0",\ ++ ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action" ++ ++# Don't mark a device ready on a PATH_FAILED event. even if ++# DM_NR_VALID_PATHS is greater than 0. Just keep the existing ++# value ++ENV{DM_ACTION}=="PATH_FAILED", GOTO="mpath_action" ++ ++# This event is either a PATH_REINSTATED or a table reload where ++# there are active paths. Mark the device ready ++ENV{MPATH_DEVICE_READY}="" ++ ++LABEL="mpath_action" ++# DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem. ++# Drop the DM_ACTIVATION flag here as mpath reloads tables if any of its ++# paths are lost/recovered. For any stack above the mpath device, this is not ++# something that should be reacted upon since it would be useless extra work. ++# It's exactly mpath's job to provide *seamless* device access to any of the ++# paths that are available underneath. ++ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_ACTIVATION}="0" ++ + # Do not initiate scanning if no path is available, + # otherwise there would be a hang or IO error on access. + # We'd like to avoid this, especially within udev processing. +-ENV{DM_NR_VALID_PATHS}!="?*", IMPORT{db}="DM_NR_VALID_PATHS" +-ENV{DM_NR_VALID_PATHS}=="0", ENV{DM_NOSCAN}="1" ++ENV{MPATH_DEVICE_READY}=="0", ENV{DM_NOSCAN}="1" + + # Also skip all foreign rules if no path is available. + # Remember the original value of DM_DISABLE_OTHER_RULES_FLAG + # and restore it back once we have at least one path available. +-IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD" +-ENV{DM_ACTION}=="PATH_FAILED",\ +- ENV{DM_NR_VALID_PATHS}=="0",\ ++ENV{MPATH_DEVICE_READY}=="0", ENV{.MPATH_DEVICE_READY_OLD}!="0",\ + ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="",\ + ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}",\ + ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1" +-ENV{DM_ACTION}=="PATH_REINSTATED",\ +- ENV{DM_NR_VALID_PATHS}=="1",\ ++ENV{MPATH_DEVICE_READY}!="0", ENV{.MPATH_DEVICE_READY_OLD}=="0",\ + ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\ + ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\ + ENV{DM_ACTIVATION}="1" + +-# DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem. +-# Drop the DM_ACTIVATION flag here as mpath reloads tables if any of its +-# paths are lost/recovered. For any stack above the mpath device, this is not +-# something that should be reacted upon since it would be useless extra work. +-# It's exactly mpath's job to provide *seamless* device access to any of the +-# paths that are available underneath. +-ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_ACTIVATION}="0" ++LABEL="scan_import" ++ENV{DM_NOSCAN}!="1", GOTO="mpath_end" ++ENV{ID_FS_TYPE}!="?*", IMPORT{db}="ID_FS_TYPE" ++ENV{ID_FS_USAGE}!="?*", IMPORT{db}="ID_FS_USAGE" ++ENV{ID_FS_UUID}!="?*", IMPORT{db}="ID_FS_UUID" ++ENV{ID_FS_ENC}!="?*", IMPORT{db}="ID_FS_UUID_ENC" ++ENV{ID_FS_VERSION}!="?*", IMPORT{db}="ID_FS_VERSION" + + LABEL="mpath_end" diff --git a/SOURCES/0207-UP-add-libmpathcmd.patch b/SOURCES/0207-UP-add-libmpathcmd.patch new file mode 100644 index 0000000..ee19d65 --- /dev/null +++ b/SOURCES/0207-UP-add-libmpathcmd.patch @@ -0,0 +1,840 @@ +From c146b5840bbd7ad89c8a8de6192590ad0595a977 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 7 Apr 2016 18:19:58 -0500 +Subject: [PATCH] Add libmpathcmd library and use it internally + +Other programs would like to communicate with multipathd to issue +command or check status. Instead of having them exec multipathd, +I've pulled the code that sends commands and receives replies from +multipathd into its own library. I've made the multipath tools use +this library internally as well. + +Signed-off-by: Benjamin Marzinski +--- + Makefile | 1 + Makefile.inc | 2 + libmpathcmd/Makefile | 32 +++++++ + libmpathcmd/mpath_cmd.c | 178 +++++++++++++++++++++++++++++++++++++++ + libmpathcmd/mpath_cmd.h | 125 +++++++++++++++++++++++++++ + libmpathpersist/Makefile | 5 - + libmpathpersist/mpath_updatepr.c | 30 +++--- + libmultipath/Makefile | 4 + libmultipath/config.c | 1 + libmultipath/configure.c | 10 +- + libmultipath/uxsock.c | 88 +++---------------- + libmultipath/uxsock.h | 6 - + mpathpersist/Makefile | 2 + multipath/Makefile | 5 - + multipathd/Makefile | 4 + multipathd/uxclnt.c | 13 +- + multipathd/uxlsnr.c | 9 - + 17 files changed, 401 insertions(+), 114 deletions(-) + create mode 100644 libmpathcmd/Makefile + create mode 100644 libmpathcmd/mpath_cmd.c + create mode 100644 libmpathcmd/mpath_cmd.h + +Index: multipath-tools-130222/Makefile +=================================================================== +--- multipath-tools-130222.orig/Makefile ++++ multipath-tools-130222/Makefile +@@ -20,6 +20,7 @@ export KRNLSRC + export KRNLOBJ + + BUILDDIRS = \ ++ libmpathcmd \ + libmultipath \ + libmultipath/prioritizers \ + libmultipath/checkers \ +Index: multipath-tools-130222/Makefile.inc +=================================================================== +--- multipath-tools-130222.orig/Makefile.inc ++++ multipath-tools-130222/Makefile.inc +@@ -34,6 +34,8 @@ syslibdir = $(prefix)/usr/$(LIB) + libdir = $(prefix)/usr/$(LIB)/multipath + unitdir = $(prefix)/lib/systemd/system + mpathpersistdir = $(TOPDIR)/libmpathpersist ++includedir = $(prefix)/usr/include ++mpathcmddir = $(TOPDIR)/libmpathcmd + + GZIP = /bin/gzip -9 -c + INSTALL_PROGRAM = install +Index: multipath-tools-130222/libmpathcmd/Makefile +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libmpathcmd/Makefile +@@ -0,0 +1,32 @@ ++# Makefile ++# ++include ../Makefile.inc ++ ++SONAME=0 ++DEVLIB = libmpathcmd.so ++LIBS = $(DEVLIB).$(SONAME) ++ ++CFLAGS += -fPIC ++ ++OBJS = mpath_cmd.o ++ ++all: $(LIBS) ++ ++$(LIBS): $(OBJS) ++ $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) $(LIBDEPS) ++ ln -sf $@ $(DEVLIB) ++ ++install: $(LIBS) ++ $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir) ++ $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) ++ ln -sf $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB) ++ $(INSTALL_PROGRAM) -d $(DESTDIR)$(includedir) ++ $(INSTALL_PROGRAM) -m 644 mpath_cmd.h $(DESTDIR)$(includedir) ++ ++uninstall: ++ rm -f $(DESTDIR)$(syslibdir)/$(LIBS) ++ rm -f $(DESTDIR)$(syslibdir)/$(DEVLIB) ++ rm -f $(DESTDIR)$(includedir)/mpath_cmd.h ++ ++clean: ++ rm -f core *.a *.o *.gz *.so *.so.* +Index: multipath-tools-130222/libmpathcmd/mpath_cmd.c +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libmpathcmd/mpath_cmd.c +@@ -0,0 +1,178 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mpath_cmd.h" ++ ++/* ++ * keep reading until its all read ++ */ ++static ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout) ++{ ++ size_t total = 0; ++ ssize_t n; ++ int ret; ++ struct pollfd pfd; ++ ++ while (len) { ++ pfd.fd = fd; ++ pfd.events = POLLIN; ++ ret = poll(&pfd, 1, timeout); ++ if (!ret) { ++ errno = ETIMEDOUT; ++ return -1; ++ } else if (ret < 0) { ++ if (errno == EINTR) ++ continue; ++ return -1; ++ } else if (!(pfd.revents & POLLIN)) ++ continue; ++ n = read(fd, buf, len); ++ if (n < 0) { ++ if ((errno == EINTR) || (errno == EAGAIN)) ++ continue; ++ return -1; ++ } ++ if (!n) ++ return total; ++ buf = n + (char *)buf; ++ len -= n; ++ total += n; ++ } ++ return total; ++} ++ ++/* ++ * keep writing until it's all sent ++ */ ++static size_t write_all(int fd, const void *buf, size_t len) ++{ ++ size_t total = 0; ++ ++ while (len) { ++ ssize_t n = write(fd, buf, len); ++ if (n < 0) { ++ if ((errno == EINTR) || (errno == EAGAIN)) ++ continue; ++ return total; ++ } ++ if (!n) ++ return total; ++ buf = n + (char *)buf; ++ len -= n; ++ total += n; ++ } ++ return total; ++} ++ ++/* ++ * connect to a unix domain socket ++ */ ++int mpath_connect(void) ++{ ++ int fd, len; ++ struct sockaddr_un addr; ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.sun_family = AF_LOCAL; ++ addr.sun_path[0] = '\0'; ++ len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t); ++ strncpy(&addr.sun_path[1], DEFAULT_SOCKET, len); ++ ++ fd = socket(AF_LOCAL, SOCK_STREAM, 0); ++ if (fd == -1) ++ return -1; ++ ++ if (connect(fd, (struct sockaddr *)&addr, len) == -1) { ++ close(fd); ++ return -1; ++ } ++ ++ return fd; ++} ++ ++int mpath_disconnect(int fd) ++{ ++ return close(fd); ++} ++ ++ssize_t mpath_recv_reply_len(int fd, unsigned int timeout) ++{ ++ size_t len; ++ ssize_t ret; ++ ++ ret = read_all(fd, &len, sizeof(len), timeout); ++ if (ret < 0) ++ return ret; ++ if (ret != sizeof(len)) { ++ errno = EIO; ++ return -1; ++ } ++ return len; ++} ++ ++int mpath_recv_reply_data(int fd, char *reply, size_t len, ++ unsigned int timeout) ++{ ++ ssize_t ret; ++ ++ ret = read_all(fd, reply, len, timeout); ++ if (ret < 0) ++ return ret; ++ if (ret != len) { ++ errno = EIO; ++ return -1; ++ } ++ reply[len - 1] = '\0'; ++ return 0; ++} ++ ++int mpath_recv_reply(int fd, char **reply, unsigned int timeout) ++{ ++ int err; ++ ssize_t len; ++ ++ *reply = NULL; ++ len = mpath_recv_reply_len(fd, timeout); ++ if (len <= 0) ++ return -1; ++ *reply = malloc(len); ++ if (!*reply) ++ return -1; ++ err = mpath_recv_reply_data(fd, *reply, len, timeout); ++ if (err) { ++ free(*reply); ++ *reply = NULL; ++ return -1; ++ } ++ return 0; ++} ++ ++int mpath_send_cmd(int fd, const char *cmd) ++{ ++ size_t len; ++ ++ if (cmd != NULL) ++ len = strlen(cmd) + 1; ++ else ++ len = 0; ++ if (write_all(fd, &len, sizeof(len)) != sizeof(len)) ++ return -1; ++ if (len && write_all(fd, cmd, len) != len) ++ return -1; ++ return 0; ++} ++ ++int mpath_process_cmd(int fd, const char *cmd, char **reply, ++ unsigned int timeout) ++{ ++ if (mpath_send_cmd(fd, cmd) != 0) ++ return -1; ++ return mpath_recv_reply(fd, reply, timeout); ++} +Index: multipath-tools-130222/libmpathcmd/mpath_cmd.h +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libmpathcmd/mpath_cmd.h +@@ -0,0 +1,125 @@ ++/* ++ * Copyright (C) 2015 Red Hat, Inc. ++ * ++ * This file is part of the device-mapper multipath userspace tools. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, ++ * USA. ++ */ ++ ++#ifndef LIB_MPATH_CMD_H ++#define LIB_MPATH_CMD_H ++ ++#ifdef __cpluscplus ++extern "C" { ++#endif ++ ++#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd" ++#define DEFAULT_REPLY_TIMEOUT 10000 ++ ++ ++/* ++ * DESCRIPTION: ++ * Connect to the running multipathd daemon. On systems with the ++ * multipathd.socket systemd unit file installed, this command will ++ * start multipathd if it is not already running. This function ++ * must be run before any of the others in this library ++ * ++ * RETURNS: ++ * A file descriptor on success. -1 on failure (with errno set). ++ */ ++int mpath_connect(void); ++ ++ ++/* ++ * DESCRIPTION: ++ * Disconnect from the multipathd daemon. This function must be ++ * run after after processing all the multipath commands. ++ * ++ * RETURNS: ++ * 0 on success. -1 on failure (with errno set). ++ */ ++int mpath_disconnect(int fd); ++ ++ ++/* ++ * DESCRIPTION ++ * Send multipathd a command and return the reply. This function ++ * does the same as calling mpath_send_cmd() and then ++ * mpath_recv_reply() ++ * ++ * RETURNS: ++ * 0 on successs, and reply will either be NULL (if there was no ++ * reply data), or point to the reply string, which must be freed by ++ * the caller. -1 on failure (with errno set). ++ */ ++int mpath_process_cmd(int fd, const char *cmd, char **reply, ++ unsigned int timeout); ++ ++ ++/* ++ * DESCRIPTION: ++ * Send a command to multipathd ++ * ++ * RETURNS: ++ * 0 on success. -1 on failure (with errno set) ++ */ ++int mpath_send_cmd(int fd, const char *cmd); ++ ++ ++/* ++ * DESCRIPTION: ++ * Return a reply from multipathd for a previously sent command. ++ * This is equivalent to calling mpath_recv_reply_len(), allocating ++ * a buffer of the appropriate size, and then calling ++ * mpath_recv_reply_data() with that buffer. ++ * ++ * RETURNS: ++ * 0 on success, and reply will either be NULL (if there was no ++ * reply data), or point to the reply string, which must be freed by ++ * the caller, -1 on failure (with errno set). ++ */ ++int mpath_recv_reply(int fd, char **reply, unsigned int timeout); ++ ++ ++/* ++ * DESCRIPTION: ++ * Return the size of the upcoming reply data from the sent multipath ++ * command. This must be called before calling mpath_recv_reply_data(). ++ * ++ * RETURNS: ++ * The required size of the reply data buffer on success. -1 on ++ * failure (with errno set). ++ */ ++ssize_t mpath_recv_reply_len(int fd, unsigned int timeout); ++ ++ ++/* ++ * DESCRIPTION: ++ * Return the reply data from the sent multipath command. ++ * mpath_recv_reply_len must be called first. reply must point to a ++ * buffer of len size. ++ * ++ * RETURNS: ++ * 0 on success, and reply will contain the reply data string. -1 ++ * on failure (with errno set). ++ */ ++int mpath_recv_reply_data(int fd, char *reply, size_t len, ++ unsigned int timeout); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif /* LIB_MPATH_CMD_H */ +Index: multipath-tools-130222/libmpathpersist/Makefile +=================================================================== +--- multipath-tools-130222.orig/libmpathpersist/Makefile ++++ multipath-tools-130222/libmpathpersist/Makefile +@@ -10,8 +10,9 @@ DEVLIB = libmpathpersist.so + LIBS = $(DEVLIB).$(SONAME) + + +-CFLAGS += -fPIC -I$(multipathdir) -I$(mpathpersistdir) +-LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath ++CFLAGS += -fPIC -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir) ++LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath \ ++ -L$(mpathcmddir) -lmpathcmd + + OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o + +Index: multipath-tools-130222/libmpathpersist/mpath_updatepr.c +=================================================================== +--- multipath-tools-130222.orig/libmpathpersist/mpath_updatepr.c ++++ multipath-tools-130222/libmpathpersist/mpath_updatepr.c +@@ -12,9 +12,9 @@ + #include + #include + #include ++#include ++#include + #include "memory.h" +-#include "../libmultipath/uxsock.h" +-#include "../libmultipath/defaults.h" + + unsigned long mem_allocated; /* Total memory used in Bytes */ + +@@ -23,10 +23,9 @@ int update_prflag(char * arg1, char * ar + int fd; + char str[64]; + char *reply; +- size_t len; + int ret = 0; + +- fd = ux_socket_connect(DEFAULT_SOCKET); ++ fd = mpath_connect(); + if (fd == -1) { + condlog (0, "ux socket connect error"); + return 1 ; +@@ -34,18 +33,23 @@ int update_prflag(char * arg1, char * ar + + snprintf(str,sizeof(str),"map %s %s", arg1, arg2); + condlog (2, "%s: pr flag message=%s", arg1, str); +- send_packet(fd, str, strlen(str) + 1); +- recv_packet(fd, &reply, &len); +- +- condlog (2, "%s: message=%s reply=%s", arg1, str, reply); +- if (!reply || strncmp(reply,"ok", 2) == 0) +- ret = -1; +- else if (strncmp(reply, "fail", 4) == 0) ++ send_packet(fd, str); ++ ret = recv_packet(fd, &reply); ++ if (ret < 0) { ++ condlog(2, "%s: message=%s recv error=%d", arg1, str, errno); + ret = -2; +- else{ +- ret = atoi(reply); ++ } else { ++ condlog (2, "%s: message=%s reply=%s", arg1, str, reply); ++ if (!reply || strncmp(reply,"ok", 2) == 0) ++ ret = -1; ++ else if (strncmp(reply, "fail", 4) == 0) ++ ret = -2; ++ else{ ++ ret = atoi(reply); ++ } + } + + free(reply); ++ mpath_disconnect(fd); + return ret; + } +Index: multipath-tools-130222/libmultipath/Makefile +=================================================================== +--- multipath-tools-130222.orig/libmultipath/Makefile ++++ multipath-tools-130222/libmultipath/Makefile +@@ -7,8 +7,8 @@ include ../Makefile.inc + SONAME=0 + DEVLIB = libmultipath.so + LIBS = $(DEVLIB).$(SONAME) +-LIBDEPS = -lpthread -ldl -ldevmapper -ludev +-CFLAGS += -fPIC ++LIBDEPS = -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd ++CFLAGS += -fPIC -I$(mpathcmddir) + + OBJS = memory.o parser.o vector.o devmapper.o \ + hwtable.o blacklist.o util.o dmparser.o config.o \ +Index: multipath-tools-130222/libmultipath/config.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/config.c ++++ multipath-tools-130222/libmultipath/config.c +@@ -25,6 +25,7 @@ + #include "prio.h" + #include "devmapper.h" + #include "version.h" ++#include "mpath_cmd.h" + + static int + hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2) +Index: multipath-tools-130222/libmultipath/configure.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/configure.c ++++ multipath-tools-130222/libmultipath/configure.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include "checkers.h" + #include "vector.h" +@@ -752,16 +753,15 @@ check_daemon(void) + { + int fd; + char *reply; +- size_t len; + int ret = 0; + +- fd = ux_socket_connect(DEFAULT_SOCKET); ++ fd = mpath_connect(); + if (fd == -1) + return 0; + +- if (send_packet(fd, "show daemon", 12) != 0) ++ if (send_packet(fd, "show daemon") != 0) + goto out; +- if (recv_packet(fd, &reply, &len) != 0) ++ if (recv_packet(fd, &reply) != 0) + goto out; + + if (strstr(reply, "shutdown")) +@@ -772,7 +772,7 @@ check_daemon(void) + out_free: + FREE(reply); + out: +- close(fd); ++ mpath_disconnect(fd); + return ret; + } + +Index: multipath-tools-130222/libmultipath/uxsock.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/uxsock.c ++++ multipath-tools-130222/libmultipath/uxsock.c +@@ -16,37 +16,10 @@ + #include + #include + #include ++#include + + #include "memory.h" + #include "uxsock.h" +- +-/* +- * connect to a unix domain socket +- */ +-int ux_socket_connect(const char *name) +-{ +- int fd, len; +- struct sockaddr_un addr; +- +- memset(&addr, 0, sizeof(addr)); +- addr.sun_family = AF_LOCAL; +- addr.sun_path[0] = '\0'; +- len = strlen(name) + 1 + sizeof(sa_family_t); +- strncpy(&addr.sun_path[1], name, len); +- +- fd = socket(AF_LOCAL, SOCK_STREAM, 0); +- if (fd == -1) { +- return -1; +- } +- +- if (connect(fd, (struct sockaddr *)&addr, len) == -1) { +- close(fd); +- return -1; +- } +- +- return fd; +-} +- + /* + * create a unix domain socket and start listening on it + * return a file descriptor open on the socket +@@ -102,32 +75,9 @@ size_t write_all(int fd, const void *buf + } + + /* +- * keep reading until its all read +- */ +-size_t read_all(int fd, void *buf, size_t len) +-{ +- size_t total = 0; +- +- while (len) { +- ssize_t n = read(fd, buf, len); +- if (n < 0) { +- if ((errno == EINTR) || (errno == EAGAIN)) +- continue; +- return total; +- } +- if (!n) +- return total; +- buf = n + (char *)buf; +- len -= n; +- total += n; +- } +- return total; +-} +- +-/* + * send a packet in length prefix format + */ +-int send_packet(int fd, const char *buf, size_t len) ++int send_packet(int fd, const char *buf) + { + int ret = 0; + sigset_t set, old; +@@ -137,10 +87,7 @@ int send_packet(int fd, const char *buf, + sigaddset(&set, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &set, &old); + +- if (write_all(fd, &len, sizeof(len)) != sizeof(len)) +- ret = -1; +- if (!ret && write_all(fd, buf, len) != len) +- ret = -1; ++ ret = mpath_send_cmd(fd, buf); + + /* And unblock it again */ + pthread_sigmask(SIG_SETMASK, &old, NULL); +@@ -151,25 +98,24 @@ int send_packet(int fd, const char *buf, + /* + * receive a packet in length prefix format + */ +-int recv_packet(int fd, char **buf, size_t *len) ++int recv_packet(int fd, char **buf) + { +- if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) { +- (*buf) = NULL; +- *len = 0; +- return -1; +- } +- if (len == 0) { +- (*buf) = NULL; +- return 0; +- } +- (*buf) = MALLOC(*len); ++ int err; ++ ssize_t len; ++ unsigned int timeout = DEFAULT_REPLY_TIMEOUT; ++ ++ *buf = NULL; ++ len = mpath_recv_reply_len(fd, timeout); ++ if (len <= 0) ++ return len; ++ (*buf) = MALLOC(len); + if (!*buf) +- return -1; +- if (read_all(fd, *buf, *len) != *len) { ++ return -ENOMEM; ++ err = mpath_recv_reply_data(fd, *buf, len, timeout); ++ if (err) { + FREE(*buf); + (*buf) = NULL; +- *len = 0; +- return -1; ++ return err; + } + return 0; + } +Index: multipath-tools-130222/libmultipath/uxsock.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/uxsock.h ++++ multipath-tools-130222/libmultipath/uxsock.h +@@ -1,7 +1,5 @@ + /* some prototypes */ +-int ux_socket_connect(const char *name); + int ux_socket_listen(const char *name); +-int send_packet(int fd, const char *buf, size_t len); +-int recv_packet(int fd, char **buf, size_t *len); ++int send_packet(int fd, const char *buf); ++int recv_packet(int fd, char **buf); + size_t write_all(int fd, const void *buf, size_t len); +-size_t read_all(int fd, void *buf, size_t len); +Index: multipath-tools-130222/mpathpersist/Makefile +=================================================================== +--- multipath-tools-130222.orig/mpathpersist/Makefile ++++ multipath-tools-130222/mpathpersist/Makefile +@@ -5,7 +5,7 @@ include ../Makefile.inc + OBJS = main.o + + CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) +-LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath -ludev ++LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -L$(mpathcmddir) -lmpathcmd -lmultipath -ludev + + EXEC = mpathpersist + +Index: multipath-tools-130222/multipath/Makefile +=================================================================== +--- multipath-tools-130222.orig/multipath/Makefile ++++ multipath-tools-130222/multipath/Makefile +@@ -6,8 +6,9 @@ include ../Makefile.inc + + OBJS = main.o + +-CFLAGS += -fPIC -I$(multipathdir) +-LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev ++CFLAGS += -I$(multipathdir) -I$(mpathcmddir) ++LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev \ ++ -L$(mpathcmddir) -lmpathcmd + + EXEC = multipath + +Index: multipath-tools-130222/multipathd/Makefile +=================================================================== +--- multipath-tools-130222.orig/multipathd/Makefile ++++ multipath-tools-130222/multipathd/Makefile +@@ -5,10 +5,10 @@ include ../Makefile.inc + # + # basic flags setting + # +-CFLAGS += -fPIE -DPIE -I$(multipathdir) -I$(mpathpersistdir) ++CFLAGS += -fPIE -DPIE -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir) + LDFLAGS += -lpthread -ldevmapper -lreadline -ludev -ldl \ + -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \ +- -Wl,-z,now -pie ++ -L$(mpathcmddir) -lmpathcmd -Wl,-z,now -pie + + # + # debuging stuff +Index: multipath-tools-130222/multipathd/uxclnt.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/uxclnt.c ++++ multipath-tools-130222/multipathd/uxclnt.c +@@ -17,6 +17,7 @@ + #include + #include + ++#include + #include + #include + #include +@@ -49,7 +50,6 @@ static void process(int fd) + rl_readline_name = "multipathd"; + rl_completion_entry_function = key_generator; + while ((line = readline("multipathd> "))) { +- size_t len; + size_t llen = strlen(line); + + if (!llen) { +@@ -61,8 +61,8 @@ static void process(int fd) + if (!strncmp(line, "quit", 4) && llen == 4) + break; + +- if (send_packet(fd, line, llen + 1) != 0) break; +- if (recv_packet(fd, &reply, &len) != 0) break; ++ if (send_packet(fd, line) != 0) break; ++ if (recv_packet(fd, &reply) != 0) break; + + print_reply(reply); + +@@ -77,13 +77,12 @@ static void process(int fd) + static void process_req(int fd, char * inbuf) + { + char *reply; +- size_t len; + +- if (send_packet(fd, inbuf, strlen(inbuf) + 1) != 0) { ++ if (send_packet(fd, inbuf) != 0) { + printf("cannot send packet\n"); + return; + } +- if (recv_packet(fd, &reply, &len) != 0) ++ if (recv_packet(fd, &reply) != 0) + printf("error receiving packet\n"); + else { + printf("%s", reply); +@@ -98,7 +97,7 @@ int uxclnt(char * inbuf) + { + int fd; + +- fd = ux_socket_connect(DEFAULT_SOCKET); ++ fd = mpath_connect(); + if (fd == -1) { + perror("ux_socket_connect"); + exit(1); +Index: multipath-tools-130222/multipathd/uxlsnr.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/uxlsnr.c ++++ multipath-tools-130222/multipathd/uxlsnr.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + + #include "main.h" + #include "cli.h" +@@ -108,7 +109,6 @@ void * uxsock_listen(int (*uxsock_trigge + void * trigger_data) + { + int ux_sock; +- size_t len; + int rlen; + char *inbuf; + char *reply; +@@ -171,16 +171,15 @@ void * uxsock_listen(int (*uxsock_trigge + struct client *next = c->next; + + if (polls[i].revents & POLLIN) { +- if (recv_packet(c->fd, &inbuf, &len) != 0) { ++ if (recv_packet(c->fd, &inbuf) != 0) { + dead_client(c); + } else { +- inbuf[len - 1] = 0; + condlog(4, "Got request [%s]", inbuf); + uxsock_trigger(inbuf, &reply, &rlen, + trigger_data); + if (reply) { +- if (send_packet(c->fd, reply, +- rlen) != 0) { ++ if (send_packet(c->fd, ++ reply) != 0) { + dead_client(c); + } + condlog(4, "Reply [%d bytes]", diff --git a/SOURCES/0208-UPBZ-1430097-multipathd-IPC-changes.patch b/SOURCES/0208-UPBZ-1430097-multipathd-IPC-changes.patch new file mode 100644 index 0000000..cddf641 --- /dev/null +++ b/SOURCES/0208-UPBZ-1430097-multipathd-IPC-changes.patch @@ -0,0 +1,280 @@ +[PATCH] Multipath: Remove duplicated memset() for multipathd show command. +[PATCH] multipath-tools: New way to limit the IPC command length. +[PATCH] multipath-tools: Perform socket client uid check on IPC commands. + +Signed-off-by: Gris Ge +--- + libmultipath/print.c | 10 ---------- + libmultipath/uxsock.c | 38 +++++++++++++++++++++++++++++--------- + libmultipath/uxsock.h | 9 +++++++++ + multipathd/main.c | 15 +++++++++++++-- + multipathd/uxlsnr.c | 31 ++++++++++++++++++++++++++----- + multipathd/uxlsnr.h | 8 +++++--- + 6 files changed, 82 insertions(+), 29 deletions(-) + +Index: multipath-tools-130222/libmultipath/print.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/print.c ++++ multipath-tools-130222/libmultipath/print.c +@@ -771,8 +771,6 @@ snprint_multipath_header (char * line, i + int fwd; + struct multipath_data * data; + +- memset(line, 0, len); +- + do { + if (!TAIL) + break; +@@ -806,8 +804,6 @@ snprint_multipath (char * line, int len, + struct multipath_data * data; + char buff[MAX_FIELD_LEN] = {}; + +- memset(line, 0, len); +- + do { + if (!TAIL) + break; +@@ -842,8 +838,6 @@ snprint_path_header (char * line, int le + int fwd; + struct path_data * data; + +- memset(line, 0, len); +- + do { + if (!TAIL) + break; +@@ -877,8 +871,6 @@ snprint_path (char * line, int len, char + struct path_data * data; + char buff[MAX_FIELD_LEN]; + +- memset(line, 0, len); +- + do { + if (!TAIL) + break; +@@ -914,8 +906,6 @@ snprint_pathgroup (char * line, int len, + struct pathgroup_data * data; + char buff[MAX_FIELD_LEN]; + +- memset(line, 0, len); +- + do { + if (!TAIL) + break; +Index: multipath-tools-130222/libmultipath/uxsock.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/uxsock.c ++++ multipath-tools-130222/libmultipath/uxsock.c +@@ -20,6 +20,15 @@ + + #include "memory.h" + #include "uxsock.h" ++ ++/* ++ * Code is similar with mpath_recv_reply() with data size limitation ++ * and debug-able malloc. ++ * When limit == 0, it means no limit on data size, used for socket client ++ * to receiving data from multipathd. ++ */ ++static int _recv_packet(int fd, char **buf, ssize_t limit); ++ + /* + * create a unix domain socket and start listening on it + * return a file descriptor open on the socket +@@ -95,27 +104,38 @@ int send_packet(int fd, const char *buf) + return ret; + } + +-/* +- * receive a packet in length prefix format +- */ +-int recv_packet(int fd, char **buf) ++static int _recv_packet(int fd, char **buf, ssize_t limit) + { +- int err; +- ssize_t len; ++ int err = 0; ++ ssize_t len = 0; + unsigned int timeout = DEFAULT_REPLY_TIMEOUT; + + *buf = NULL; + len = mpath_recv_reply_len(fd, timeout); + if (len <= 0) + return len; ++ if ((limit > 0) && (len > limit)) ++ return -EINVAL; + (*buf) = MALLOC(len); + if (!*buf) + return -ENOMEM; + err = mpath_recv_reply_data(fd, *buf, len, timeout); +- if (err) { ++ if (err != 0) { + FREE(*buf); + (*buf) = NULL; +- return err; + } +- return 0; ++ return err; ++} ++ ++/* ++ * receive a packet in length prefix format ++ */ ++int recv_packet(int fd, char **buf) ++{ ++ return _recv_packet(fd, buf, 0 /* no limit */); ++} ++ ++int recv_packet_from_client(int fd, char **buf) ++{ ++ return _recv_packet(fd, buf, _MAX_CMD_LEN); + } +Index: multipath-tools-130222/libmultipath/uxsock.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/uxsock.h ++++ multipath-tools-130222/libmultipath/uxsock.h +@@ -3,3 +3,12 @@ int ux_socket_listen(const char *name); + int send_packet(int fd, const char *buf); + int recv_packet(int fd, char **buf); + size_t write_all(int fd, const void *buf, size_t len); ++ ++#define _MAX_CMD_LEN 512 ++ ++/* ++ * Used for receiving socket command from untrusted socket client where data ++ * size is restricted to 512(_MAX_CMD_LEN) at most. ++ * Return -EINVAL if data length requested by client exceeded the _MAX_CMD_LEN. ++ */ ++int recv_packet_from_client(int fd, char **buf); +Index: multipath-tools-130222/multipathd/main.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/main.c ++++ multipath-tools-130222/multipathd/main.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include "prioritizers/alua_rtpg.h" + +@@ -859,7 +860,8 @@ map_discovery (struct vectors * vecs) + } + + int +-uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data) ++uxsock_trigger (char * str, char ** reply, int * len, bool is_root, ++ void * trigger_data) + { + struct vectors * vecs; + int r; +@@ -872,6 +874,15 @@ uxsock_trigger (char * str, char ** repl + lock(vecs->lock); + pthread_testcancel(); + ++ if ((str != NULL) && (is_root == false) && ++ (strncmp(str, "list", strlen("list")) != 0) && ++ (strncmp(str, "show", strlen("show")) != 0)) { ++ *reply = STRDUP("permission deny: need to be root"); ++ *len = strlen(*reply) + 1; ++ r = 1; ++ goto out; ++ } ++ + r = parse_cmd(str, reply, len, vecs); + + if (r > 0) { +@@ -885,7 +896,7 @@ uxsock_trigger (char * str, char ** repl + r = 0; + } + /* else if (r < 0) leave *reply alone */ +- ++out: + lock_cleanup_pop(vecs->lock); + return r; + } +Index: multipath-tools-130222/multipathd/uxlsnr.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/uxlsnr.c ++++ multipath-tools-130222/multipathd/uxlsnr.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -48,6 +49,23 @@ struct pollfd *polls; + volatile sig_atomic_t reconfig_sig = 0; + volatile sig_atomic_t log_reset_sig = 0; + ++static bool _socket_client_is_root(int fd); ++ ++static bool _socket_client_is_root(int fd) ++{ ++ socklen_t len = 0; ++ struct ucred uc; ++ ++ len = sizeof(struct ucred); ++ if ((fd >= 0) && ++ (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) && ++ (uc.uid == 0)) ++ return true; ++ ++ /* Treat error as not root client */ ++ return false; ++} ++ + /* + * handle a new client joining + */ +@@ -105,8 +123,7 @@ void uxsock_cleanup(void *arg) + /* + * entry point + */ +-void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *), +- void * trigger_data) ++void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) + { + int ux_sock; + int rlen; +@@ -171,12 +188,16 @@ void * uxsock_listen(int (*uxsock_trigge + struct client *next = c->next; + + if (polls[i].revents & POLLIN) { +- if (recv_packet(c->fd, &inbuf) != 0) { ++ if (recv_packet_from_client(c->fd, ++ &inbuf) != 0) { + dead_client(c); ++ } else if (!inbuf) { ++ condlog(4, "recv_packet_from_client " ++ "get null request"); ++ continue; + } else { + condlog(4, "Got request [%s]", inbuf); +- uxsock_trigger(inbuf, &reply, &rlen, +- trigger_data); ++ uxsock_trigger(inbuf, &reply, &rlen, _socket_client_is_root(c->fd), trigger_data); + if (reply) { + if (send_packet(c->fd, + reply) != 0) { +Index: multipath-tools-130222/multipathd/uxlsnr.h +=================================================================== +--- multipath-tools-130222.orig/multipathd/uxlsnr.h ++++ multipath-tools-130222/multipathd/uxlsnr.h +@@ -1,9 +1,11 @@ + #ifndef _UXLSNR_H + #define _UXLSNR_H + +-void * uxsock_listen(int (*uxsock_trigger) +- (char *, char **, int *, void *), +- void * trigger_data); ++#include ++ ++typedef int (uxsock_trigger_fn)(char *, char **, int *, bool, void *); ++ ++void *uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data); + + extern volatile sig_atomic_t reconfig_sig; + extern volatile sig_atomic_t log_reset_sig; diff --git a/SOURCES/0209-UPBZ-1430097-multipath-C-API.patch b/SOURCES/0209-UPBZ-1430097-multipath-C-API.patch new file mode 100644 index 0000000..08a6f1d --- /dev/null +++ b/SOURCES/0209-UPBZ-1430097-multipath-C-API.patch @@ -0,0 +1,5632 @@ +From 4335abb36f33f12eadc943729901fac31f3dc012 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Fri, 24 Feb 2017 20:50:26 +0800 +Subject: [PATCH] multipath-tools: Introducing multipath C API + +Features: + + * Use mpath_cmd.h for IPC connection and use output of 'show maps json'. + * Library user guide will be 'man 3 libdmmp.h'. + * Every public function has its own manpage in section 3 which is + generated by linux 'kernel-doc' tool. + +Usage: + + make -j5 + sudo make install \ + bindir=/usr/sbin/ \ + syslibdir=/usr/lib64/ \ + libdir=/usr/lib64/multipath \ + rcdir=/etc/rc.d/init.d \ + unitdir=/usr/lib/systemd/system \ + includedir=/usr/include + make -C libdmmp check + make -C libdmmp speed_test + + man libdmmp.h + man dmmp_mpath_array_get + man + +Performance: + + * 10k scsi_debug sdX with 2 disks per mpath (i7-6820HQ 16GiB RAM): + $ make -C libdmmp speed_test + Got 5000 mpath + real 3.22 + user 0.15 + sys 0.01 + +Misc: + * Developer note is libdmmp/DEV_NOTES. + +Changes since V4: + + * Add new function dmmp_mpath_kdev_name_get() to query the '/dev/dm-01' for + mpath. + * Updated manpages. + * Rebased to current master ea4367159d32444e48a409a4f1c4f18324b737a9. + +Signed-off-by: Gris Ge +--- + .gitignore | 8 + Makefile | 1 + Makefile.inc | 6 + libdmmp/DEV_NOTES | 41 + libdmmp/Makefile | 84 + + libdmmp/docs/doc-preclean.pl | 28 + libdmmp/docs/kernel-doc | 3156 ++++++++++++++++++++++++++++++++++++++ + libdmmp/docs/libdmmp.h.3 | 113 + + libdmmp/docs/split-man.pl | 40 + libdmmp/libdmmp.c | 285 +++ + libdmmp/libdmmp.pc.in | 9 + libdmmp/libdmmp/libdmmp.h | 653 +++++++ + libdmmp/libdmmp_misc.c | 87 + + libdmmp/libdmmp_mp.c | 159 + + libdmmp/libdmmp_path.c | 115 + + libdmmp/libdmmp_pg.c | 208 ++ + libdmmp/libdmmp_private.h | 208 ++ + libdmmp/test/Makefile | 30 + libdmmp/test/libdmmp_speed_test.c | 49 + libdmmp/test/libdmmp_test.c | 147 + + 20 files changed, 5426 insertions(+), 1 deletion(-) + create mode 100644 libdmmp/DEV_NOTES + create mode 100644 libdmmp/Makefile + create mode 100644 libdmmp/docs/doc-preclean.pl + create mode 100644 libdmmp/docs/kernel-doc + create mode 100644 libdmmp/docs/libdmmp.h.3 + create mode 100644 libdmmp/docs/split-man.pl + create mode 100644 libdmmp/libdmmp.c + create mode 100644 libdmmp/libdmmp.pc.in + create mode 100644 libdmmp/libdmmp/libdmmp.h + create mode 100644 libdmmp/libdmmp_misc.c + create mode 100644 libdmmp/libdmmp_mp.c + create mode 100644 libdmmp/libdmmp_path.c + create mode 100644 libdmmp/libdmmp_pg.c + create mode 100644 libdmmp/libdmmp_private.h + create mode 100644 libdmmp/test/Makefile + create mode 100644 libdmmp/test/libdmmp_speed_test.c + create mode 100644 libdmmp/test/libdmmp_test.c + +Index: multipath-tools-130222/.gitignore +=================================================================== +--- multipath-tools-130222.orig/.gitignore ++++ multipath-tools-130222/.gitignore +@@ -10,3 +10,11 @@ multipath/multipath + multipathd/multipathd + mpathpersist/mpathpersist + .nfs* ++*.swp ++*.patch ++*.rej ++*.orig ++libdmmp/docs/man/*.3.gz ++libdmmp/*.so.* ++libdmmp/test/libdmmp_test ++libdmmp/test/libdmmp_speed_test +Index: multipath-tools-130222/Makefile +=================================================================== +--- multipath-tools-130222.orig/Makefile ++++ multipath-tools-130222/Makefile +@@ -25,6 +25,7 @@ BUILDDIRS = \ + libmultipath/prioritizers \ + libmultipath/checkers \ + libmpathpersist \ ++ libdmmp \ + multipath \ + multipathd \ + mpathpersist \ +Index: multipath-tools-130222/Makefile.inc +=================================================================== +--- multipath-tools-130222.orig/Makefile.inc ++++ multipath-tools-130222/Makefile.inc +@@ -36,8 +36,12 @@ unitdir = $(prefix)/lib/systemd/syst + mpathpersistdir = $(TOPDIR)/libmpathpersist + includedir = $(prefix)/usr/include + mpathcmddir = $(TOPDIR)/libmpathcmd ++libdmmpdir = $(TOPDIR)/libdmmp ++pkgconfdir = $(prefix)/usr/$(LIB)/pkgconfig + +-GZIP = /bin/gzip -9 -c ++GZIP = /bin/gzip -9 -c ++RM = rm -f ++LN = ln -sf + INSTALL_PROGRAM = install + + ifndef RPM_OPT_FLAGS +Index: multipath-tools-130222/libdmmp/DEV_NOTES +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/DEV_NOTES +@@ -0,0 +1,41 @@ ++== Planed features == ++ * Expose all properties used by /usr/bin/multipath ++ ++== Code style == ++ * Keep things as simple as possible. ++ * Linux Kernel code style. ++ * Don't use typedef. ++ * Don't use enum. ++ * We are not smarter than API user, so don't create wrapping function like: ++ ++ ``` ++ dmmp_mpath_search_by_id(struct dmmp_context *ctx, ++ struct dmmp_mpath **dmmp_mp, ++ uint32_t dmmp_mp_count, const char *id) ++ ++ dmmp_path_group_id_search(struct dmmp_mpath *dmmp_mp, ++ const char *blk_name) ++ ``` ++ * The performance is the same for query single mpath and query all mpaths, ++ so no `dmmp_mpath_of_wwid(struct dmmp_context *ctx, const char *wwid)` yet. ++ ++== Naming scheme == ++ * Public constants should be named as `DMMP_XXX_YYY`. ++ * Public functions should be named as `dmmp__`. ++ * Private constants should be named as `_DMMP_XXX_YYY`. ++ * Private functions should be named as `_dmmp__`. ++ ++== Code Layout == ++ * libdmmp_private.h ++ Internal functions or macros. ++ * libdmmp.c ++ Handling multipathd IPC and generate dmmp_context and ++ dmmp_mpath_array_get(). ++ * libdmmp_mp.c ++ For `struct dmmp_mpath` ++ * libdmmp_pg.c ++ For `struct dmmp_path_group` ++ * libdmmp_path.c ++ For `struct dmmp_path` ++ * libdmmp_misc.c ++ Misc functions. +Index: multipath-tools-130222/libdmmp/Makefile +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/Makefile +@@ -0,0 +1,84 @@ ++# Makefile ++# ++# Copyright (C) 2015 - 2016 Red Hat, Inc. ++# Gris Ge ++# ++include ../Makefile.inc ++ ++LIBDMMP_VERSION=0.1.0 ++SONAME=$(LIBDMMP_VERSION) ++DEVLIB = libdmmp.so ++LIBS = $(DEVLIB).$(SONAME) ++LIBDEPS = -pthread ++PKGFILE = libdmmp.pc ++EXTRA_MAN_FILES = libdmmp.h.3 ++HEADERS = libdmmp/libdmmp.h ++OBJS = libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o ++ ++CFLAGS += -fPIC -fvisibility=hidden -I$(libdmmpdir) -I$(mpathcmddir) \ ++ $(shell pkg-config --cflags json-c) ++LDFLAGS += $(shell pkg-config --libs json-c) -L$(mpathcmddir) -lmpathcmd ++ ++all: $(LIBS) doc ++ ++$(LIBS): $(OBJS) ++ $(CC) $(LDFLAGS) $(SHARED_FLAGS) \ ++ -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) $(LIBDEPS) ++ $(LN) $@ $(DEVLIB) ++ ++install: ++ $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) ++ $(INSTALL_PROGRAM) -m 644 -D \ ++ $(HEADERS) $(DESTDIR)$(includedir)/$(HEADERS) ++ $(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB) ++ $(INSTALL_PROGRAM) -m 644 -D \ ++ $(PKGFILE).in $(DESTDIR)$(pkgconfdir)/$(PKGFILE) ++ perl -i -pe 's|__VERSION__|$(LIBDMMP_VERSION)|g' \ ++ $(DESTDIR)$(pkgconfdir)/$(PKGFILE) ++ perl -i -pe 's|__LIBDIR__|$(syslibdir)|g' \ ++ $(DESTDIR)$(pkgconfdir)/$(PKGFILE) ++ perl -i -pe 's|__INCLUDEDIR__|$(includedir)|g' \ ++ $(DESTDIR)$(pkgconfdir)/$(PKGFILE) ++ @for file in docs/man/*.3.gz; do \ ++ $(INSTALL_PROGRAM) -m 644 -D \ ++ $$file \ ++ $(DESTDIR)$(man3dir)/ || exit $?; \ ++ done ++ ++uninstall: ++ $(RM) $(DESTDIR)$(syslibdir)/$(LIBS) ++ $(RM) $(DESTDIR)$(includedir)/$(HEADERS) ++ $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB) ++ @for file in $(DESTDIR)$(man3dir)/dmmp_*; do \ ++ $(RM) $$file; \ ++ done ++ $(RM) $(DESTDIR)$(man3dir)/libdmmp.h* ++ ++clean: ++ $(RM) core *.a *.o *.gz *.so *.so.* ++ $(RM) docs/man/*.3.gz ++ $(MAKE) -C test clean ++ ++check: all ++ $(MAKE) -C test check ++ ++speed_test: all ++ $(MAKE) -C test speed_test ++ ++doc: docs/man/$(EXTRA_MAN_FILES).gz ++ ++TEMPFILE := $(shell mktemp) ++ ++docs/man/$(EXTRA_MAN_FILES).gz: $(HEADERS) ++ @for file in $(EXTRA_MAN_FILES); do \ ++ $(INSTALL_PROGRAM) -v -m 644 -D docs/$$file docs/man/$$file; \ ++ done ++ cat $(HEADERS) | \ ++ perl docs/doc-preclean.pl > $(TEMPFILE) ++ perl docs/kernel-doc -man $(TEMPFILE) | \ ++ perl docs/split-man.pl docs/man ++ -rm -f $(TEMPFILE) ++ @for file in docs/man/*.3; do \ ++ gzip -f $$file; \ ++ done ++ find docs/man -type f -name \*[0-9].gz +Index: multipath-tools-130222/libdmmp/docs/doc-preclean.pl +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/docs/doc-preclean.pl +@@ -0,0 +1,28 @@ ++#!/usr/bin/perl ++# Copyright (C) 2016 Red Hat, Inc. ++# ++# This program is free software: you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++# ++# Author: Gris Ge ++ ++use strict; ++ ++my @REMOVE_KEY_LIST=("DMMP_DLL_EXPORT"); ++ ++while (<>) { ++ for my $key (@REMOVE_KEY_LIST) { ++ (s/$key//g); ++ } ++ print; ++} +Index: multipath-tools-130222/libdmmp/docs/kernel-doc +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/docs/kernel-doc +@@ -0,0 +1,3156 @@ ++#!/usr/bin/perl -w ++ ++use strict; ++ ++## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## ++## Copyright (C) 2000, 1 Tim Waugh ## ++## Copyright (C) 2001 Simon Huggins ## ++## Copyright (C) 2005-2012 Randy Dunlap ## ++## Copyright (C) 2012 Dan Luedtke ## ++## ## ++## #define enhancements by Armin Kuster ## ++## Copyright (c) 2000 MontaVista Software, Inc. ## ++## ## ++## This software falls under the GNU General Public License. ## ++## Please read the COPYING file for more information ## ++ ++# 18/01/2001 - Cleanups ++# Functions prototyped as foo(void) same as foo() ++# Stop eval'ing where we don't need to. ++# -- huggie@earth.li ++ ++# 27/06/2001 - Allowed whitespace after initial "/**" and ++# allowed comments before function declarations. ++# -- Christian Kreibich ++ ++# Still to do: ++# - add perldoc documentation ++# - Look more closely at some of the scarier bits :) ++ ++# 26/05/2001 - Support for separate source and object trees. ++# Return error code. ++# Keith Owens ++ ++# 23/09/2001 - Added support for typedefs, structs, enums and unions ++# Support for Context section; can be terminated using empty line ++# Small fixes (like spaces vs. \s in regex) ++# -- Tim Jansen ++ ++# 25/07/2012 - Added support for HTML5 ++# -- Dan Luedtke ++ ++sub usage { ++ my $message = <<"EOF"; ++Usage: $0 [OPTION ...] FILE ... ++ ++Read C language source or header FILEs, extract embedded documentation comments, ++and print formatted documentation to standard output. ++ ++The documentation comments are identified by "/**" opening comment mark. See ++Documentation/kernel-doc-nano-HOWTO.txt for the documentation comment syntax. ++ ++Output format selection (mutually exclusive): ++ -docbook Output DocBook format. ++ -html Output HTML format. ++ -html5 Output HTML5 format. ++ -list Output symbol list format. This is for use by docproc. ++ -man Output troff manual page format. This is the default. ++ -rst Output reStructuredText format. ++ -text Output plain text format. ++ ++Output selection (mutually exclusive): ++ -export Only output documentation for symbols that have been ++ exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() ++ in any input FILE or -export-file FILE. ++ -internal Only output documentation for symbols that have NOT been ++ exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() ++ in any input FILE or -export-file FILE. ++ -function NAME Only output documentation for the given function(s) ++ or DOC: section title(s). All other functions and DOC: ++ sections are ignored. May be specified multiple times. ++ -nofunction NAME Do NOT output documentation for the given function(s); ++ only output documentation for the other functions and ++ DOC: sections. May be specified multiple times. ++ ++Output selection modifiers: ++ -no-doc-sections Do not output DOC: sections. ++ -enable-lineno Enable output of #define LINENO lines. Only works with ++ reStructuredText format. ++ -export-file FILE Specify an additional FILE in which to look for ++ EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with ++ -export or -internal. May be specified multiple times. ++ ++Other parameters: ++ -v Verbose output, more warnings and other information. ++ -h Print this help. ++ ++EOF ++ print $message; ++ exit 1; ++} ++ ++# ++# format of comments. ++# In the following table, (...)? signifies optional structure. ++# (...)* signifies 0 or more structure elements ++# /** ++# * function_name(:)? (- short description)? ++# (* @parameterx: (description of parameter x)?)* ++# (* a blank line)? ++# * (Description:)? (Description of function)? ++# * (section header: (section description)? )* ++# (*)?*/ ++# ++# So .. the trivial example would be: ++# ++# /** ++# * my_function ++# */ ++# ++# If the Description: header tag is omitted, then there must be a blank line ++# after the last parameter specification. ++# e.g. ++# /** ++# * my_function - does my stuff ++# * @my_arg: its mine damnit ++# * ++# * Does my stuff explained. ++# */ ++# ++# or, could also use: ++# /** ++# * my_function - does my stuff ++# * @my_arg: its mine damnit ++# * Description: Does my stuff explained. ++# */ ++# etc. ++# ++# Besides functions you can also write documentation for structs, unions, ++# enums and typedefs. Instead of the function name you must write the name ++# of the declaration; the struct/union/enum/typedef must always precede ++# the name. Nesting of declarations is not supported. ++# Use the argument mechanism to document members or constants. ++# e.g. ++# /** ++# * struct my_struct - short description ++# * @a: first member ++# * @b: second member ++# * ++# * Longer description ++# */ ++# struct my_struct { ++# int a; ++# int b; ++# /* private: */ ++# int c; ++# }; ++# ++# All descriptions can be multiline, except the short function description. ++# ++# For really longs structs, you can also describe arguments inside the ++# body of the struct. ++# eg. ++# /** ++# * struct my_struct - short description ++# * @a: first member ++# * @b: second member ++# * ++# * Longer description ++# */ ++# struct my_struct { ++# int a; ++# int b; ++# /** ++# * @c: This is longer description of C ++# * ++# * You can use paragraphs to describe arguments ++# * using this method. ++# */ ++# int c; ++# }; ++# ++# This should be use only for struct/enum members. ++# ++# You can also add additional sections. When documenting kernel functions you ++# should document the "Context:" of the function, e.g. whether the functions ++# can be called form interrupts. Unlike other sections you can end it with an ++# empty line. ++# A non-void function should have a "Return:" section describing the return ++# value(s). ++# Example-sections should contain the string EXAMPLE so that they are marked ++# appropriately in DocBook. ++# ++# Example: ++# /** ++# * user_function - function that can only be called in user context ++# * @a: some argument ++# * Context: !in_interrupt() ++# * ++# * Some description ++# * Example: ++# * user_function(22); ++# */ ++# ... ++# ++# ++# All descriptive text is further processed, scanning for the following special ++# patterns, which are highlighted appropriately. ++# ++# 'funcname()' - function ++# '$ENVVAR' - environmental variable ++# '&struct_name' - name of a structure (up to two words including 'struct') ++# '@parameter' - name of a parameter ++# '%CONST' - name of a constant. ++ ++## init lots of data ++ ++ ++my $errors = 0; ++my $warnings = 0; ++my $anon_struct_union = 0; ++ ++# match expressions used to find embedded type information ++my $type_constant = '\%([-_\w]+)'; ++my $type_func = '(\w+)\(\)'; ++my $type_param = '\@(\w+(\.\.\.)?)'; ++my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params ++my $type_struct = '\&((struct\s*)*[_\w]+)'; ++my $type_struct_xml = '\\&((struct\s*)*[_\w]+)'; ++my $type_env = '(\$\w+)'; ++my $type_enum_full = '\&(enum)\s*([_\w]+)'; ++my $type_struct_full = '\&(struct)\s*([_\w]+)'; ++my $type_typedef_full = '\&(typedef)\s*([_\w]+)'; ++my $type_union_full = '\&(union)\s*([_\w]+)'; ++my $type_member = '\&([_\w]+)((\.|->)[_\w]+)'; ++my $type_member_func = $type_member . '\(\)'; ++ ++# Output conversion substitutions. ++# One for each output format ++ ++# these work fairly well ++my @highlights_html = ( ++ [$type_constant, "\$1"], ++ [$type_func, "\$1"], ++ [$type_struct_xml, "\$1"], ++ [$type_env, "\$1"], ++ [$type_param, "\$1"] ++ ); ++my $local_lt = "\\\\\\\\lt:"; ++my $local_gt = "\\\\\\\\gt:"; ++my $blankline_html = $local_lt . "p" . $local_gt; # was "

" ++ ++# html version 5 ++my @highlights_html5 = ( ++ [$type_constant, "\$1"], ++ [$type_func, "\$1"], ++ [$type_struct_xml, "\$1"], ++ [$type_env, "\$1"], ++ [$type_param, "\$1]"] ++ ); ++my $blankline_html5 = $local_lt . "br /" . $local_gt; ++ ++# XML, docbook format ++my @highlights_xml = ( ++ ["([^=])\\\"([^\\\"<]+)\\\"", "\$1\$2"], ++ [$type_constant, "\$1"], ++ [$type_struct_xml, "\$1"], ++ [$type_param, "\$1"], ++ [$type_func, "\$1"], ++ [$type_env, "\$1"] ++ ); ++my $blankline_xml = $local_lt . "/para" . $local_gt . $local_lt . "para" . $local_gt . "\n"; ++ ++# gnome, docbook format ++my @highlights_gnome = ( ++ [$type_constant, "\$1"], ++ [$type_func, "\$1"], ++ [$type_struct, "\$1"], ++ [$type_env, "\$1"], ++ [$type_param, "\$1" ] ++ ); ++my $blankline_gnome = "\n"; ++ ++# these are pretty rough ++my @highlights_man = ( ++ [$type_constant, "\$1"], ++ [$type_func, "\\\\fB\$1\\\\fP"], ++ [$type_struct, "\\\\fI\$1\\\\fP"], ++ [$type_param, "\\\\fI\$1\\\\fP"] ++ ); ++my $blankline_man = ""; ++ ++# text-mode ++my @highlights_text = ( ++ [$type_constant, "\$1"], ++ [$type_func, "\$1"], ++ [$type_struct, "\$1"], ++ [$type_param, "\$1"] ++ ); ++my $blankline_text = ""; ++ ++# rst-mode ++my @highlights_rst = ( ++ [$type_constant, "``\$1``"], ++ # Note: need to escape () to avoid func matching later ++ [$type_member_func, "\\:c\\:type\\:`\$1\$2\\\\(\\\\) <\$1>`"], ++ [$type_member, "\\:c\\:type\\:`\$1\$2 <\$1>`"], ++ [$type_fp_param, "**\$1\\\\(\\\\)**"], ++ [$type_func, "\\:c\\:func\\:`\$1()`"], ++ [$type_struct_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], ++ [$type_enum_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], ++ [$type_typedef_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], ++ [$type_union_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], ++ # in rst this can refer to any type ++ [$type_struct, "\\:c\\:type\\:`\$1`"], ++ [$type_param, "**\$1**"] ++ ); ++my $blankline_rst = "\n"; ++ ++# list mode ++my @highlights_list = ( ++ [$type_constant, "\$1"], ++ [$type_func, "\$1"], ++ [$type_struct, "\$1"], ++ [$type_param, "\$1"] ++ ); ++my $blankline_list = ""; ++ ++# read arguments ++if ($#ARGV == -1) { ++ usage(); ++} ++ ++my $kernelversion; ++my $dohighlight = ""; ++ ++my $verbose = 0; ++my $output_mode = "man"; ++my $output_preformatted = 0; ++my $no_doc_sections = 0; ++my $enable_lineno = 0; ++my @highlights = @highlights_man; ++my $blankline = $blankline_man; ++my $modulename = "Kernel API"; ++ ++use constant { ++ OUTPUT_ALL => 0, # output all symbols and doc sections ++ OUTPUT_INCLUDE => 1, # output only specified symbols ++ OUTPUT_EXCLUDE => 2, # output everything except specified symbols ++ OUTPUT_EXPORTED => 3, # output exported symbols ++ OUTPUT_INTERNAL => 4, # output non-exported symbols ++}; ++my $output_selection = OUTPUT_ALL; ++my $show_not_found = 0; ++ ++my @export_file_list; ++ ++my @build_time; ++if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) && ++ (my $seconds = `date -d"${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') { ++ @build_time = gmtime($seconds); ++} else { ++ @build_time = localtime; ++} ++ ++my $man_date = ('January', 'February', 'March', 'April', 'May', 'June', ++ 'July', 'August', 'September', 'October', ++ 'November', 'December')[$build_time[4]] . ++ " " . ($build_time[5]+1900); ++ ++# Essentially these are globals. ++# They probably want to be tidied up, made more localised or something. ++# CAVEAT EMPTOR! Some of the others I localised may not want to be, which ++# could cause "use of undefined value" or other bugs. ++my ($function, %function_table, %parametertypes, $declaration_purpose); ++my $declaration_start_line; ++my ($type, $declaration_name, $return_type); ++my ($newsection, $newcontents, $prototype, $brcount, %source_map); ++ ++if (defined($ENV{'KBUILD_VERBOSE'})) { ++ $verbose = "$ENV{'KBUILD_VERBOSE'}"; ++} ++ ++# Generated docbook code is inserted in a template at a point where ++# docbook v3.1 requires a non-zero sequence of RefEntry's; see: ++# http://www.oasis-open.org/docbook/documentation/reference/html/refentry.html ++# We keep track of number of generated entries and generate a dummy ++# if needs be to ensure the expanded template can be postprocessed ++# into html. ++my $section_counter = 0; ++ ++my $lineprefix=""; ++ ++# Parser states ++use constant { ++ STATE_NORMAL => 0, # normal code ++ STATE_NAME => 1, # looking for function name ++ STATE_FIELD => 2, # scanning field start ++ STATE_PROTO => 3, # scanning prototype ++ STATE_DOCBLOCK => 4, # documentation block ++ STATE_INLINE => 5, # gathering documentation outside main block ++}; ++my $state; ++my $in_doc_sect; ++ ++# Inline documentation state ++use constant { ++ STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE) ++ STATE_INLINE_NAME => 1, # looking for member name (@foo:) ++ STATE_INLINE_TEXT => 2, # looking for member documentation ++ STATE_INLINE_END => 3, # done ++ STATE_INLINE_ERROR => 4, # error - Comment without header was found. ++ # Spit a warning as it's not ++ # proper kernel-doc and ignore the rest. ++}; ++my $inline_doc_state; ++ ++#declaration types: can be ++# 'function', 'struct', 'union', 'enum', 'typedef' ++my $decl_type; ++ ++my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start. ++my $doc_end = '\*/'; ++my $doc_com = '\s*\*\s*'; ++my $doc_com_body = '\s*\* ?'; ++my $doc_decl = $doc_com . '(\w+)'; ++# @params and a strictly limited set of supported section names ++my $doc_sect = $doc_com . ++ '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:(.*)'; ++my $doc_content = $doc_com_body . '(.*)'; ++my $doc_block = $doc_com . 'DOC:\s*(.*)?'; ++my $doc_inline_start = '^\s*/\*\*\s*$'; ++my $doc_inline_sect = '\s*\*\s*(@[\w\s]+):(.*)'; ++my $doc_inline_end = '^\s*\*/\s*$'; ++my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$'; ++my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;'; ++ ++my %parameterdescs; ++my %parameterdesc_start_lines; ++my @parameterlist; ++my %sections; ++my @sectionlist; ++my %section_start_lines; ++my $sectcheck; ++my $struct_actual; ++ ++my $contents = ""; ++my $new_start_line = 0; ++ ++# the canonical section names. see also $doc_sect above. ++my $section_default = "Description"; # default section ++my $section_intro = "Introduction"; ++my $section = $section_default; ++my $section_context = "Context"; ++my $section_return = "Return"; ++ ++my $undescribed = "-- undescribed --"; ++ ++reset_state(); ++ ++while ($ARGV[0] =~ m/^-(.*)/) { ++ my $cmd = shift @ARGV; ++ if ($cmd eq "-html") { ++ $output_mode = "html"; ++ @highlights = @highlights_html; ++ $blankline = $blankline_html; ++ } elsif ($cmd eq "-html5") { ++ $output_mode = "html5"; ++ @highlights = @highlights_html5; ++ $blankline = $blankline_html5; ++ } elsif ($cmd eq "-man") { ++ $output_mode = "man"; ++ @highlights = @highlights_man; ++ $blankline = $blankline_man; ++ } elsif ($cmd eq "-text") { ++ $output_mode = "text"; ++ @highlights = @highlights_text; ++ $blankline = $blankline_text; ++ } elsif ($cmd eq "-rst") { ++ $output_mode = "rst"; ++ @highlights = @highlights_rst; ++ $blankline = $blankline_rst; ++ } elsif ($cmd eq "-docbook") { ++ $output_mode = "xml"; ++ @highlights = @highlights_xml; ++ $blankline = $blankline_xml; ++ } elsif ($cmd eq "-list") { ++ $output_mode = "list"; ++ @highlights = @highlights_list; ++ $blankline = $blankline_list; ++ } elsif ($cmd eq "-gnome") { ++ $output_mode = "gnome"; ++ @highlights = @highlights_gnome; ++ $blankline = $blankline_gnome; ++ } elsif ($cmd eq "-module") { # not needed for XML, inherits from calling document ++ $modulename = shift @ARGV; ++ } elsif ($cmd eq "-function") { # to only output specific functions ++ $output_selection = OUTPUT_INCLUDE; ++ $function = shift @ARGV; ++ $function_table{$function} = 1; ++ } elsif ($cmd eq "-nofunction") { # output all except specific functions ++ $output_selection = OUTPUT_EXCLUDE; ++ $function = shift @ARGV; ++ $function_table{$function} = 1; ++ } elsif ($cmd eq "-export") { # only exported symbols ++ $output_selection = OUTPUT_EXPORTED; ++ %function_table = (); ++ } elsif ($cmd eq "-internal") { # only non-exported symbols ++ $output_selection = OUTPUT_INTERNAL; ++ %function_table = (); ++ } elsif ($cmd eq "-export-file") { ++ my $file = shift @ARGV; ++ push(@export_file_list, $file); ++ } elsif ($cmd eq "-v") { ++ $verbose = 1; ++ } elsif (($cmd eq "-h") || ($cmd eq "--help")) { ++ usage(); ++ } elsif ($cmd eq '-no-doc-sections') { ++ $no_doc_sections = 1; ++ } elsif ($cmd eq '-enable-lineno') { ++ $enable_lineno = 1; ++ } elsif ($cmd eq '-show-not-found') { ++ $show_not_found = 1; ++ } ++} ++ ++# continue execution near EOF; ++ ++# get kernel version from env ++sub get_kernel_version() { ++ my $version = 'unknown kernel version'; ++ ++ if (defined($ENV{'KERNELVERSION'})) { ++ $version = $ENV{'KERNELVERSION'}; ++ } ++ return $version; ++} ++ ++# ++sub print_lineno { ++ my $lineno = shift; ++ if ($enable_lineno && defined($lineno)) { ++ print "#define LINENO " . $lineno . "\n"; ++ } ++} ++## ++# dumps section contents to arrays/hashes intended for that purpose. ++# ++sub dump_section { ++ my $file = shift; ++ my $name = shift; ++ my $contents = join "\n", @_; ++ ++ if ($name =~ m/$type_param/) { ++ $name = $1; ++ $parameterdescs{$name} = $contents; ++ $sectcheck = $sectcheck . $name . " "; ++ $parameterdesc_start_lines{$name} = $new_start_line; ++ $new_start_line = 0; ++ } elsif ($name eq "@\.\.\.") { ++ $name = "..."; ++ $parameterdescs{$name} = $contents; ++ $sectcheck = $sectcheck . $name . " "; ++ $parameterdesc_start_lines{$name} = $new_start_line; ++ $new_start_line = 0; ++ } else { ++ if (defined($sections{$name}) && ($sections{$name} ne "")) { ++ # Only warn on user specified duplicate section names. ++ if ($name ne $section_default) { ++ print STDERR "${file}:$.: warning: duplicate section name '$name'\n"; ++ ++$warnings; ++ } ++ $sections{$name} .= $contents; ++ } else { ++ $sections{$name} = $contents; ++ push @sectionlist, $name; ++ $section_start_lines{$name} = $new_start_line; ++ $new_start_line = 0; ++ } ++ } ++} ++ ++## ++# dump DOC: section after checking that it should go out ++# ++sub dump_doc_section { ++ my $file = shift; ++ my $name = shift; ++ my $contents = join "\n", @_; ++ ++ if ($no_doc_sections) { ++ return; ++ } ++ ++ if (($output_selection == OUTPUT_ALL) || ++ ($output_selection == OUTPUT_INCLUDE && ++ defined($function_table{$name})) || ++ ($output_selection == OUTPUT_EXCLUDE && ++ !defined($function_table{$name}))) ++ { ++ dump_section($file, $name, $contents); ++ output_blockhead({'sectionlist' => \@sectionlist, ++ 'sections' => \%sections, ++ 'module' => $modulename, ++ 'content-only' => ($output_selection != OUTPUT_ALL), }); ++ } ++} ++ ++## ++# output function ++# ++# parameterdescs, a hash. ++# function => "function name" ++# parameterlist => @list of parameters ++# parameterdescs => %parameter descriptions ++# sectionlist => @list of sections ++# sections => %section descriptions ++# ++ ++sub output_highlight { ++ my $contents = join "\n",@_; ++ my $line; ++ ++# DEBUG ++# if (!defined $contents) { ++# use Carp; ++# confess "output_highlight got called with no args?\n"; ++# } ++ ++ if ($output_mode eq "html" || $output_mode eq "html5" || ++ $output_mode eq "xml") { ++ $contents = local_unescape($contents); ++ # convert data read & converted thru xml_escape() into &xyz; format: ++ $contents =~ s/\\\\\\/\&/g; ++ } ++# print STDERR "contents b4:$contents\n"; ++ eval $dohighlight; ++ die $@ if $@; ++# print STDERR "contents af:$contents\n"; ++ ++# strip whitespaces when generating html5 ++ if ($output_mode eq "html5") { ++ $contents =~ s/^\s+//; ++ $contents =~ s/\s+$//; ++ } ++ foreach $line (split "\n", $contents) { ++ if (! $output_preformatted) { ++ $line =~ s/^\s*//; ++ } ++ if ($line eq ""){ ++ if (! $output_preformatted) { ++ print $lineprefix, local_unescape($blankline); ++ } ++ } else { ++ $line =~ s/\\\\\\/\&/g; ++ if ($output_mode eq "man" && substr($line, 0, 1) eq ".") { ++ print "\\&$line"; ++ } else { ++ print $lineprefix, $line; ++ } ++ } ++ print "\n"; ++ } ++} ++ ++# output sections in html ++sub output_section_html(%) { ++ my %args = %{$_[0]}; ++ my $section; ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ print "

$section

\n"; ++ print "
\n"; ++ output_highlight($args{'sections'}{$section}); ++ print "
\n"; ++ } ++} ++ ++# output enum in html ++sub output_enum_html(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $count; ++ print "

enum " . $args{'enum'} . "

\n"; ++ ++ print "enum " . $args{'enum'} . " {
\n"; ++ $count = 0; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print " " . $parameter . ""; ++ if ($count != $#{$args{'parameterlist'}}) { ++ $count++; ++ print ",\n"; ++ } ++ print "
"; ++ } ++ print "};
\n"; ++ ++ print "

Constants

\n"; ++ print "
\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print "
" . $parameter . "\n"; ++ print "
"; ++ output_highlight($args{'parameterdescs'}{$parameter}); ++ } ++ print "
\n"; ++ output_section_html(@_); ++ print "
\n"; ++} ++ ++# output typedef in html ++sub output_typedef_html(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $count; ++ print "

typedef " . $args{'typedef'} . "

\n"; ++ ++ print "typedef " . $args{'typedef'} . "\n"; ++ output_section_html(@_); ++ print "
\n"; ++} ++ ++# output struct in html ++sub output_struct_html(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ ++ print "

" . $args{'type'} . " " . $args{'struct'} . " - " . $args{'purpose'} . "

\n"; ++ print "" . $args{'type'} . " " . $args{'struct'} . " {
\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ if ($parameter =~ /^#/) { ++ print "$parameter
\n"; ++ next; ++ } ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print "    $1$parameter) ($2);
\n"; ++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) { ++ # bitfield ++ print "    $1 $parameter$2;
\n"; ++ } else { ++ print "    $type $parameter;
\n"; ++ } ++ } ++ print "};
\n"; ++ ++ print "

Members

\n"; ++ print "
\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ ($parameter =~ /^#/) && next; ++ ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ print "
" . $parameter . "\n"; ++ print "
"; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ } ++ print "
\n"; ++ output_section_html(@_); ++ print "
\n"; ++} ++ ++# output function in html ++sub output_function_html(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ ++ print "

" . $args{'function'} . " - " . $args{'purpose'} . "

\n"; ++ print "" . $args{'functiontype'} . "\n"; ++ print "" . $args{'function'} . "\n"; ++ print "("; ++ $count = 0; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print "$1$parameter) ($2)"; ++ } else { ++ print "" . $type . " " . $parameter . ""; ++ } ++ if ($count != $#{$args{'parameterlist'}}) { ++ $count++; ++ print ",\n"; ++ } ++ } ++ print ")\n"; ++ ++ print "

Arguments

\n"; ++ print "
\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ print "
" . $parameter . "\n"; ++ print "
"; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ } ++ print "
\n"; ++ output_section_html(@_); ++ print "
\n"; ++} ++ ++# output DOC: block header in html ++sub output_blockhead_html(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ print "

$section

\n"; ++ print "
    \n"; ++ output_highlight($args{'sections'}{$section}); ++ print "
\n"; ++ } ++ print "
\n"; ++} ++ ++# output sections in html5 ++sub output_section_html5(%) { ++ my %args = %{$_[0]}; ++ my $section; ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ print "
\n"; ++ print "

$section

\n"; ++ print "

\n"; ++ output_highlight($args{'sections'}{$section}); ++ print "

\n"; ++ print "
\n"; ++ } ++} ++ ++# output enum in html5 ++sub output_enum_html5(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $count; ++ my $html5id; ++ ++ $html5id = $args{'enum'}; ++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; ++ print "
"; ++ print "

enum " . $args{'enum'} . "

\n"; ++ print "
    \n"; ++ print "
  1. "; ++ print "enum "; ++ print "" . $args{'enum'} . " {"; ++ print "
  2. \n"; ++ $count = 0; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print "
  3. "; ++ print "" . $parameter . ""; ++ if ($count != $#{$args{'parameterlist'}}) { ++ $count++; ++ print ","; ++ } ++ print "
  4. \n"; ++ } ++ print "
  5. };
  6. \n"; ++ print "
\n"; ++ ++ print "
\n"; ++ print "

Constants

\n"; ++ print "
\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print "
" . $parameter . "
\n"; ++ print "
"; ++ output_highlight($args{'parameterdescs'}{$parameter}); ++ print "
\n"; ++ } ++ print "
\n"; ++ print "
\n"; ++ output_section_html5(@_); ++ print "
\n"; ++} ++ ++# output typedef in html5 ++sub output_typedef_html5(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $count; ++ my $html5id; ++ ++ $html5id = $args{'typedef'}; ++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; ++ print "
\n"; ++ print "

typedef " . $args{'typedef'} . "

\n"; ++ ++ print "
    \n"; ++ print "
  1. "; ++ print "typedef "; ++ print "" . $args{'typedef'} . ""; ++ print "
  2. \n"; ++ print "
\n"; ++ output_section_html5(@_); ++ print "
\n"; ++} ++ ++# output struct in html5 ++sub output_struct_html5(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $html5id; ++ ++ $html5id = $args{'struct'}; ++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; ++ print "
\n"; ++ print "
\n"; ++ print "

" . $args{'type'} . " " . $args{'struct'} . "

"; ++ print "

". $args{'purpose'} . "

\n"; ++ print "
\n"; ++ print "
    \n"; ++ print "
  1. "; ++ print "" . $args{'type'} . " "; ++ print "" . $args{'struct'} . " {"; ++ print "
  2. \n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print "
  3. "; ++ if ($parameter =~ /^#/) { ++ print "" . $parameter ."\n"; ++ print "
  4. \n"; ++ next; ++ } ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print "$1 "; ++ print "$parameter"; ++ print ") "; ++ print "($2);"; ++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) { ++ # bitfield ++ print "$1 "; ++ print "$parameter"; ++ print "$2;"; ++ } else { ++ print "$type "; ++ print "$parameter;"; ++ } ++ print "\n"; ++ } ++ print "
  5. };
  6. \n"; ++ print "
\n"; ++ ++ print "
\n"; ++ print "

Members

\n"; ++ print "
\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ ($parameter =~ /^#/) && next; ++ ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ print "
" . $parameter . "
\n"; ++ print "
"; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ print "
\n"; ++ } ++ print "
\n"; ++ print "
\n"; ++ output_section_html5(@_); ++ print "
\n"; ++} ++ ++# output function in html5 ++sub output_function_html5(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ my $html5id; ++ ++ $html5id = $args{'function'}; ++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; ++ print "
\n"; ++ print "
\n"; ++ print "

" . $args{'function'} . "

"; ++ print "

" . $args{'purpose'} . "

\n"; ++ print "
\n"; ++ print "
    \n"; ++ print "
  1. "; ++ print "" . $args{'functiontype'} . " "; ++ print "" . $args{'function'} . " ("; ++ print "
  2. "; ++ $count = 0; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print "
  3. "; ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print "$1 "; ++ print "$parameter"; ++ print ") "; ++ print "($2)"; ++ } else { ++ print "$type "; ++ print "$parameter"; ++ } ++ if ($count != $#{$args{'parameterlist'}}) { ++ $count++; ++ print ","; ++ } ++ print "
  4. \n"; ++ } ++ print "
  5. )
  6. \n"; ++ print "
\n"; ++ ++ print "
\n"; ++ print "

Arguments

\n"; ++ print "

\n"; ++ print "

\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ print "
" . $parameter . "
\n"; ++ print "
"; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ print "
\n"; ++ } ++ print "
\n"; ++ print "
\n"; ++ output_section_html5(@_); ++ print "
\n"; ++} ++ ++# output DOC: block header in html5 ++sub output_blockhead_html5(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ my $html5id; ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ $html5id = $section; ++ $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; ++ print "
\n"; ++ print "

$section

\n"; ++ print "

\n"; ++ output_highlight($args{'sections'}{$section}); ++ print "

\n"; ++ } ++ print "
\n"; ++} ++ ++sub output_section_xml(%) { ++ my %args = %{$_[0]}; ++ my $section; ++ # print out each section ++ $lineprefix=" "; ++ foreach $section (@{$args{'sectionlist'}}) { ++ print "\n"; ++ print "$section\n"; ++ if ($section =~ m/EXAMPLE/i) { ++ print "\n"; ++ $output_preformatted = 1; ++ } else { ++ print "\n"; ++ } ++ output_highlight($args{'sections'}{$section}); ++ $output_preformatted = 0; ++ if ($section =~ m/EXAMPLE/i) { ++ print "\n"; ++ } else { ++ print "\n"; ++ } ++ print "\n"; ++ } ++} ++ ++# output function in XML DocBook ++sub output_function_xml(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ my $id; ++ ++ $id = "API-" . $args{'function'}; ++ $id =~ s/[^A-Za-z0-9]/-/g; ++ ++ print "\n"; ++ print "\n"; ++ print " LINUX\n"; ++ print " Kernel Hackers Manual\n"; ++ print " $man_date\n"; ++ print "\n"; ++ print "\n"; ++ print " " . $args{'function'} . "\n"; ++ print " 9\n"; ++ print " " . $kernelversion . "\n"; ++ print "\n"; ++ print "\n"; ++ print " " . $args{'function'} . "\n"; ++ print " \n"; ++ print " "; ++ output_highlight ($args{'purpose'}); ++ print " \n"; ++ print "\n"; ++ ++ print "\n"; ++ print " Synopsis\n"; ++ print " \n"; ++ print " " . $args{'functiontype'} . " "; ++ print "" . $args{'function'} . " \n"; ++ ++ $count = 0; ++ if ($#{$args{'parameterlist'}} >= 0) { ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print " $1$parameter)\n"; ++ print " $2\n"; ++ } else { ++ print " " . $type; ++ print " $parameter\n"; ++ } ++ } ++ } else { ++ print " \n"; ++ } ++ print " \n"; ++ print "\n"; ++ ++ # print parameters ++ print "\n Arguments\n"; ++ if ($#{$args{'parameterlist'}} >= 0) { ++ print " \n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ print " \n $parameter\n"; ++ print " \n \n"; ++ $lineprefix=" "; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ print " \n \n \n"; ++ } ++ print " \n"; ++ } else { ++ print " \n None\n \n"; ++ } ++ print "\n"; ++ ++ output_section_xml(@_); ++ print "\n\n"; ++} ++ ++# output struct in XML DocBook ++sub output_struct_xml(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $id; ++ ++ $id = "API-struct-" . $args{'struct'}; ++ $id =~ s/[^A-Za-z0-9]/-/g; ++ ++ print "\n"; ++ print "\n"; ++ print " LINUX\n"; ++ print " Kernel Hackers Manual\n"; ++ print " $man_date\n"; ++ print "\n"; ++ print "\n"; ++ print " " . $args{'type'} . " " . $args{'struct'} . "\n"; ++ print " 9\n"; ++ print " " . $kernelversion . "\n"; ++ print "\n"; ++ print "\n"; ++ print " " . $args{'type'} . " " . $args{'struct'} . "\n"; ++ print " \n"; ++ print " "; ++ output_highlight ($args{'purpose'}); ++ print " \n"; ++ print "\n"; ++ ++ print "\n"; ++ print " Synopsis\n"; ++ print " \n"; ++ print $args{'type'} . " " . $args{'struct'} . " {\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ if ($parameter =~ /^#/) { ++ my $prm = $parameter; ++ # convert data read & converted thru xml_escape() into &xyz; format: ++ # This allows us to have #define macros interspersed in a struct. ++ $prm =~ s/\\\\\\/\&/g; ++ print "$prm\n"; ++ next; ++ } ++ ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ defined($args{'parameterdescs'}{$parameter_name}) || next; ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print " $1 $parameter) ($2);\n"; ++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) { ++ # bitfield ++ print " $1 $parameter$2;\n"; ++ } else { ++ print " " . $type . " " . $parameter . ";\n"; ++ } ++ } ++ print "};"; ++ print " \n"; ++ print "\n"; ++ ++ print " \n"; ++ print " Members\n"; ++ ++ if ($#{$args{'parameterlist'}} >= 0) { ++ print " \n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ ($parameter =~ /^#/) && next; ++ ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ defined($args{'parameterdescs'}{$parameter_name}) || next; ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ print " "; ++ print " $parameter\n"; ++ print " \n"; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ print " \n"; ++ print " \n"; ++ } ++ print " \n"; ++ } else { ++ print " \n None\n \n"; ++ } ++ print " \n"; ++ ++ output_section_xml(@_); ++ ++ print "\n\n"; ++} ++ ++# output enum in XML DocBook ++sub output_enum_xml(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ my $id; ++ ++ $id = "API-enum-" . $args{'enum'}; ++ $id =~ s/[^A-Za-z0-9]/-/g; ++ ++ print "\n"; ++ print "\n"; ++ print " LINUX\n"; ++ print " Kernel Hackers Manual\n"; ++ print " $man_date\n"; ++ print "\n"; ++ print "\n"; ++ print " enum " . $args{'enum'} . "\n"; ++ print " 9\n"; ++ print " " . $kernelversion . "\n"; ++ print "\n"; ++ print "\n"; ++ print " enum " . $args{'enum'} . "\n"; ++ print " \n"; ++ print " "; ++ output_highlight ($args{'purpose'}); ++ print " \n"; ++ print "\n"; ++ ++ print "\n"; ++ print " Synopsis\n"; ++ print " \n"; ++ print "enum " . $args{'enum'} . " {\n"; ++ $count = 0; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print " $parameter"; ++ if ($count != $#{$args{'parameterlist'}}) { ++ $count++; ++ print ","; ++ } ++ print "\n"; ++ } ++ print "};"; ++ print " \n"; ++ print "\n"; ++ ++ print "\n"; ++ print " Constants\n"; ++ print " \n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ print " "; ++ print " $parameter\n"; ++ print " \n"; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ print " \n"; ++ print " \n"; ++ } ++ print " \n"; ++ print "\n"; ++ ++ output_section_xml(@_); ++ ++ print "\n\n"; ++} ++ ++# output typedef in XML DocBook ++sub output_typedef_xml(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $id; ++ ++ $id = "API-typedef-" . $args{'typedef'}; ++ $id =~ s/[^A-Za-z0-9]/-/g; ++ ++ print "\n"; ++ print "\n"; ++ print " LINUX\n"; ++ print " Kernel Hackers Manual\n"; ++ print " $man_date\n"; ++ print "\n"; ++ print "\n"; ++ print " typedef " . $args{'typedef'} . "\n"; ++ print " 9\n"; ++ print "\n"; ++ print "\n"; ++ print " typedef " . $args{'typedef'} . "\n"; ++ print " \n"; ++ print " "; ++ output_highlight ($args{'purpose'}); ++ print " \n"; ++ print "\n"; ++ ++ print "\n"; ++ print " Synopsis\n"; ++ print " typedef " . $args{'typedef'} . ";\n"; ++ print "\n"; ++ ++ output_section_xml(@_); ++ ++ print "\n\n"; ++} ++ ++# output in XML DocBook ++sub output_blockhead_xml(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ ++ my $id = $args{'module'}; ++ $id =~ s/[^A-Za-z0-9]/-/g; ++ ++ # print out each section ++ $lineprefix=" "; ++ foreach $section (@{$args{'sectionlist'}}) { ++ if (!$args{'content-only'}) { ++ print "\n $section\n"; ++ } ++ if ($section =~ m/EXAMPLE/i) { ++ print "\n"; ++ $output_preformatted = 1; ++ } else { ++ print "\n"; ++ } ++ output_highlight($args{'sections'}{$section}); ++ $output_preformatted = 0; ++ if ($section =~ m/EXAMPLE/i) { ++ print "\n"; ++ } else { ++ print ""; ++ } ++ if (!$args{'content-only'}) { ++ print "\n\n"; ++ } ++ } ++ ++ print "\n\n"; ++} ++ ++# output in XML DocBook ++sub output_function_gnome { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ my $id; ++ ++ $id = $args{'module'} . "-" . $args{'function'}; ++ $id =~ s/[^A-Za-z0-9]/-/g; ++ ++ print "\n"; ++ print " " . $args{'function'} . "\n"; ++ ++ print " \n"; ++ print " " . $args{'functiontype'} . " "; ++ print "" . $args{'function'} . " "; ++ print "\n"; ++ ++ $count = 0; ++ if ($#{$args{'parameterlist'}} >= 0) { ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print " $1 $parameter)\n"; ++ print " $2\n"; ++ } else { ++ print " " . $type; ++ print " $parameter\n"; ++ } ++ } ++ } else { ++ print " \n"; ++ } ++ print " \n"; ++ if ($#{$args{'parameterlist'}} >= 0) { ++ print " \n"; ++ print "\n"; ++ print "\n"; ++ print "\n"; ++ print "\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ print " $parameter\n"; ++ print " \n"; ++ $lineprefix=" "; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ print " \n"; ++ } ++ print " \n"; ++ } else { ++ print " \n None\n \n"; ++ } ++ ++ # print out each section ++ $lineprefix=" "; ++ foreach $section (@{$args{'sectionlist'}}) { ++ print "\n $section\n"; ++ if ($section =~ m/EXAMPLE/i) { ++ print "\n"; ++ $output_preformatted = 1; ++ } else { ++ } ++ print "\n"; ++ output_highlight($args{'sections'}{$section}); ++ $output_preformatted = 0; ++ print "\n"; ++ if ($section =~ m/EXAMPLE/i) { ++ print "\n"; ++ } else { ++ } ++ print " \n"; ++ } ++ ++ print "\n\n"; ++} ++ ++## ++# output function in man ++sub output_function_man(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ ++ print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n"; ++ ++ print ".SH NAME\n"; ++ print $args{'function'} . " \\- " . $args{'purpose'} . "\n"; ++ ++ print ".SH SYNOPSIS\n"; ++ if ($args{'functiontype'} ne "") { ++ print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n"; ++ } else { ++ print ".B \"" . $args{'function'} . "\n"; ++ } ++ $count = 0; ++ my $parenth = "("; ++ my $post = ","; ++ foreach my $parameter (@{$args{'parameterlist'}}) { ++ if ($count == $#{$args{'parameterlist'}}) { ++ $post = ");"; ++ } ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print ".BI \"" . $parenth . $1 . "\" " . $parameter . " \") (" . $2 . ")" . $post . "\"\n"; ++ } else { ++ $type =~ s/([^\*])$/$1 /; ++ print ".BI \"" . $parenth . $type . "\" " . $parameter . " \"" . $post . "\"\n"; ++ } ++ $count++; ++ $parenth = ""; ++ } ++ ++ print ".SH ARGUMENTS\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ print ".IP \"" . $parameter . "\" 12\n"; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ } ++ foreach $section (@{$args{'sectionlist'}}) { ++ print ".SH \"", uc $section, "\"\n"; ++ output_highlight($args{'sections'}{$section}); ++ } ++} ++ ++## ++# output enum in man ++sub output_enum_man(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ ++ print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n"; ++ ++ print ".SH NAME\n"; ++ print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n"; ++ ++ print ".SH SYNOPSIS\n"; ++ print "enum " . $args{'enum'} . " {\n"; ++ $count = 0; ++ foreach my $parameter (@{$args{'parameterlist'}}) { ++ print ".br\n.BI \" $parameter\"\n"; ++ if ($count == $#{$args{'parameterlist'}}) { ++ print "\n};\n"; ++ last; ++ } ++ else { ++ print ", \n.br\n"; ++ } ++ $count++; ++ } ++ ++ print ".SH Constants\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ print ".IP \"" . $parameter . "\" 12\n"; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ } ++ foreach $section (@{$args{'sectionlist'}}) { ++ print ".SH \"$section\"\n"; ++ output_highlight($args{'sections'}{$section}); ++ } ++} ++ ++## ++# output struct in man ++sub output_struct_man(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ ++ print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n"; ++ ++ print ".SH NAME\n"; ++ print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n"; ++ ++ print ".SH SYNOPSIS\n"; ++ print $args{'type'} . " " . $args{'struct'} . " {\n.br\n"; ++ ++ foreach my $parameter (@{$args{'parameterlist'}}) { ++ if ($parameter =~ /^#/) { ++ print ".BI \"$parameter\"\n.br\n"; ++ next; ++ } ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print ".BI \" " . $1 . "\" " . $parameter . " \") (" . $2 . ")" . "\"\n;\n"; ++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) { ++ # bitfield ++ print ".BI \" " . $1 . "\ \" " . $parameter . $2 . " \"" . "\"\n;\n"; ++ } else { ++ $type =~ s/([^\*])$/$1 /; ++ print ".BI \" " . $type . "\" " . $parameter . " \"" . "\"\n;\n"; ++ } ++ print "\n.br\n"; ++ } ++ print "};\n.br\n"; ++ ++ print ".SH Members\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ ($parameter =~ /^#/) && next; ++ ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ print ".IP \"" . $parameter . "\" 12\n"; ++ output_highlight($args{'parameterdescs'}{$parameter_name}); ++ } ++ foreach $section (@{$args{'sectionlist'}}) { ++ print ".SH \"$section\"\n"; ++ output_highlight($args{'sections'}{$section}); ++ } ++} ++ ++## ++# output typedef in man ++sub output_typedef_man(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ ++ print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n"; ++ ++ print ".SH NAME\n"; ++ print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n"; ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ print ".SH \"$section\"\n"; ++ output_highlight($args{'sections'}{$section}); ++ } ++} ++ ++sub output_blockhead_man(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $count; ++ ++ print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n"; ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ print ".SH \"$section\"\n"; ++ output_highlight($args{'sections'}{$section}); ++ } ++} ++ ++## ++# output in text ++sub output_function_text(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $start; ++ ++ print "Name:\n\n"; ++ print $args{'function'} . " - " . $args{'purpose'} . "\n"; ++ ++ print "\nSynopsis:\n\n"; ++ if ($args{'functiontype'} ne "") { ++ $start = $args{'functiontype'} . " " . $args{'function'} . " ("; ++ } else { ++ $start = $args{'function'} . " ("; ++ } ++ print $start; ++ ++ my $count = 0; ++ foreach my $parameter (@{$args{'parameterlist'}}) { ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print $1 . $parameter . ") (" . $2; ++ } else { ++ print $type . " " . $parameter; ++ } ++ if ($count != $#{$args{'parameterlist'}}) { ++ $count++; ++ print ",\n"; ++ print " " x length($start); ++ } else { ++ print ");\n\n"; ++ } ++ } ++ ++ print "Arguments:\n\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ print $parameter . "\n\t" . $args{'parameterdescs'}{$parameter_name} . "\n"; ++ } ++ output_section_text(@_); ++} ++ ++#output sections in text ++sub output_section_text(%) { ++ my %args = %{$_[0]}; ++ my $section; ++ ++ print "\n"; ++ foreach $section (@{$args{'sectionlist'}}) { ++ print "$section:\n\n"; ++ output_highlight($args{'sections'}{$section}); ++ } ++ print "\n\n"; ++} ++ ++# output enum in text ++sub output_enum_text(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $count; ++ print "Enum:\n\n"; ++ ++ print "enum " . $args{'enum'} . " - " . $args{'purpose'} . "\n\n"; ++ print "enum " . $args{'enum'} . " {\n"; ++ $count = 0; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print "\t$parameter"; ++ if ($count != $#{$args{'parameterlist'}}) { ++ $count++; ++ print ","; ++ } ++ print "\n"; ++ } ++ print "};\n\n"; ++ ++ print "Constants:\n\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print "$parameter\n\t"; ++ print $args{'parameterdescs'}{$parameter} . "\n"; ++ } ++ ++ output_section_text(@_); ++} ++ ++# output typedef in text ++sub output_typedef_text(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $count; ++ print "Typedef:\n\n"; ++ ++ print "typedef " . $args{'typedef'} . " - " . $args{'purpose'} . "\n"; ++ output_section_text(@_); ++} ++ ++# output struct as text ++sub output_struct_text(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ ++ print $args{'type'} . " " . $args{'struct'} . " - " . $args{'purpose'} . "\n\n"; ++ print $args{'type'} . " " . $args{'struct'} . " {\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ if ($parameter =~ /^#/) { ++ print "$parameter\n"; ++ next; ++ } ++ ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print "\t$1 $parameter) ($2);\n"; ++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) { ++ # bitfield ++ print "\t$1 $parameter$2;\n"; ++ } else { ++ print "\t" . $type . " " . $parameter . ";\n"; ++ } ++ } ++ print "};\n\n"; ++ ++ print "Members:\n\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ ($parameter =~ /^#/) && next; ++ ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ print "$parameter\n\t"; ++ print $args{'parameterdescs'}{$parameter_name} . "\n"; ++ } ++ print "\n"; ++ output_section_text(@_); ++} ++ ++sub output_blockhead_text(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ print " $section:\n"; ++ print " -> "; ++ output_highlight($args{'sections'}{$section}); ++ } ++} ++ ++## ++# output in restructured text ++# ++ ++# ++# This could use some work; it's used to output the DOC: sections, and ++# starts by putting out the name of the doc section itself, but that tends ++# to duplicate a header already in the template file. ++# ++sub output_blockhead_rst(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ if ($output_selection != OUTPUT_INCLUDE) { ++ print "**$section**\n\n"; ++ } ++ print_lineno($section_start_lines{$section}); ++ output_highlight_rst($args{'sections'}{$section}); ++ print "\n"; ++ } ++} ++ ++sub output_highlight_rst { ++ my $contents = join "\n",@_; ++ my $line; ++ ++ # undo the evil effects of xml_escape() earlier ++ $contents = xml_unescape($contents); ++ ++ eval $dohighlight; ++ die $@ if $@; ++ ++ foreach $line (split "\n", $contents) { ++ print $lineprefix . $line . "\n"; ++ } ++} ++ ++sub output_function_rst(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ my $oldprefix = $lineprefix; ++ my $start = ""; ++ ++ if ($args{'typedef'}) { ++ print ".. c:type:: ". $args{'function'} . "\n\n"; ++ print_lineno($declaration_start_line); ++ print " **Typedef**: "; ++ $lineprefix = ""; ++ output_highlight_rst($args{'purpose'}); ++ $start = "\n\n**Syntax**\n\n ``"; ++ } else { ++ print ".. c:function:: "; ++ } ++ if ($args{'functiontype'} ne "") { ++ $start .= $args{'functiontype'} . " " . $args{'function'} . " ("; ++ } else { ++ $start .= $args{'function'} . " ("; ++ } ++ print $start; ++ ++ my $count = 0; ++ foreach my $parameter (@{$args{'parameterlist'}}) { ++ if ($count ne 0) { ++ print ", "; ++ } ++ $count++; ++ $type = $args{'parametertypes'}{$parameter}; ++ ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print $1 . $parameter . ") (" . $2; ++ } else { ++ print $type . " " . $parameter; ++ } ++ } ++ if ($args{'typedef'}) { ++ print ");``\n\n"; ++ } else { ++ print ")\n\n"; ++ print_lineno($declaration_start_line); ++ $lineprefix = " "; ++ output_highlight_rst($args{'purpose'}); ++ print "\n"; ++ } ++ ++ print "**Parameters**\n\n"; ++ $lineprefix = " "; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ my $parameter_name = $parameter; ++ #$parameter_name =~ s/\[.*//; ++ $type = $args{'parametertypes'}{$parameter}; ++ ++ if ($type ne "") { ++ print "``$type $parameter``\n"; ++ } else { ++ print "``$parameter``\n"; ++ } ++ ++ print_lineno($parameterdesc_start_lines{$parameter_name}); ++ ++ if (defined($args{'parameterdescs'}{$parameter_name}) && ++ $args{'parameterdescs'}{$parameter_name} ne $undescribed) { ++ output_highlight_rst($args{'parameterdescs'}{$parameter_name}); ++ } else { ++ print " *undescribed*\n"; ++ } ++ print "\n"; ++ } ++ ++ $lineprefix = $oldprefix; ++ output_section_rst(@_); ++} ++ ++sub output_section_rst(%) { ++ my %args = %{$_[0]}; ++ my $section; ++ my $oldprefix = $lineprefix; ++ $lineprefix = ""; ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ print "**$section**\n\n"; ++ print_lineno($section_start_lines{$section}); ++ output_highlight_rst($args{'sections'}{$section}); ++ print "\n"; ++ } ++ print "\n"; ++ $lineprefix = $oldprefix; ++} ++ ++sub output_enum_rst(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $oldprefix = $lineprefix; ++ my $count; ++ my $name = "enum " . $args{'enum'}; ++ ++ print "\n\n.. c:type:: " . $name . "\n\n"; ++ print_lineno($declaration_start_line); ++ $lineprefix = " "; ++ output_highlight_rst($args{'purpose'}); ++ print "\n"; ++ ++ print "**Constants**\n\n"; ++ $lineprefix = " "; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ print "``$parameter``\n"; ++ if ($args{'parameterdescs'}{$parameter} ne $undescribed) { ++ output_highlight_rst($args{'parameterdescs'}{$parameter}); ++ } else { ++ print " *undescribed*\n"; ++ } ++ print "\n"; ++ } ++ ++ $lineprefix = $oldprefix; ++ output_section_rst(@_); ++} ++ ++sub output_typedef_rst(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $oldprefix = $lineprefix; ++ my $name = "typedef " . $args{'typedef'}; ++ ++ print "\n\n.. c:type:: " . $name . "\n\n"; ++ print_lineno($declaration_start_line); ++ $lineprefix = " "; ++ output_highlight_rst($args{'purpose'}); ++ print "\n"; ++ ++ $lineprefix = $oldprefix; ++ output_section_rst(@_); ++} ++ ++sub output_struct_rst(%) { ++ my %args = %{$_[0]}; ++ my ($parameter); ++ my $oldprefix = $lineprefix; ++ my $name = $args{'type'} . " " . $args{'struct'}; ++ ++ print "\n\n.. c:type:: " . $name . "\n\n"; ++ print_lineno($declaration_start_line); ++ $lineprefix = " "; ++ output_highlight_rst($args{'purpose'}); ++ print "\n"; ++ ++ print "**Definition**\n\n"; ++ print "::\n\n"; ++ print " " . $args{'type'} . " " . $args{'struct'} . " {\n"; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ if ($parameter =~ /^#/) { ++ print " " . "$parameter\n"; ++ next; ++ } ++ ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ $type = $args{'parametertypes'}{$parameter}; ++ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { ++ # pointer-to-function ++ print " $1 $parameter) ($2);\n"; ++ } elsif ($type =~ m/^(.*?)\s*(:.*)/) { ++ # bitfield ++ print " $1 $parameter$2;\n"; ++ } else { ++ print " " . $type . " " . $parameter . ";\n"; ++ } ++ } ++ print " };\n\n"; ++ ++ print "**Members**\n\n"; ++ $lineprefix = " "; ++ foreach $parameter (@{$args{'parameterlist'}}) { ++ ($parameter =~ /^#/) && next; ++ ++ my $parameter_name = $parameter; ++ $parameter_name =~ s/\[.*//; ++ ++ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; ++ $type = $args{'parametertypes'}{$parameter}; ++ print_lineno($parameterdesc_start_lines{$parameter_name}); ++ print "``" . $parameter . "``\n"; ++ output_highlight_rst($args{'parameterdescs'}{$parameter_name}); ++ print "\n"; ++ } ++ print "\n"; ++ ++ $lineprefix = $oldprefix; ++ output_section_rst(@_); ++} ++ ++ ++## list mode output functions ++ ++sub output_function_list(%) { ++ my %args = %{$_[0]}; ++ ++ print $args{'function'} . "\n"; ++} ++ ++# output enum in list ++sub output_enum_list(%) { ++ my %args = %{$_[0]}; ++ print $args{'enum'} . "\n"; ++} ++ ++# output typedef in list ++sub output_typedef_list(%) { ++ my %args = %{$_[0]}; ++ print $args{'typedef'} . "\n"; ++} ++ ++# output struct as list ++sub output_struct_list(%) { ++ my %args = %{$_[0]}; ++ ++ print $args{'struct'} . "\n"; ++} ++ ++sub output_blockhead_list(%) { ++ my %args = %{$_[0]}; ++ my ($parameter, $section); ++ ++ foreach $section (@{$args{'sectionlist'}}) { ++ print "DOC: $section\n"; ++ } ++} ++ ++## ++# generic output function for all types (function, struct/union, typedef, enum); ++# calls the generated, variable output_ function name based on ++# functype and output_mode ++sub output_declaration { ++ no strict 'refs'; ++ my $name = shift; ++ my $functype = shift; ++ my $func = "output_${functype}_$output_mode"; ++ if (($output_selection == OUTPUT_ALL) || ++ (($output_selection == OUTPUT_INCLUDE || ++ $output_selection == OUTPUT_EXPORTED) && ++ defined($function_table{$name})) || ++ (($output_selection == OUTPUT_EXCLUDE || ++ $output_selection == OUTPUT_INTERNAL) && ++ !($functype eq "function" && defined($function_table{$name})))) ++ { ++ &$func(@_); ++ $section_counter++; ++ } ++} ++ ++## ++# generic output function - calls the right one based on current output mode. ++sub output_blockhead { ++ no strict 'refs'; ++ my $func = "output_blockhead_" . $output_mode; ++ &$func(@_); ++ $section_counter++; ++} ++ ++## ++# takes a declaration (struct, union, enum, typedef) and ++# invokes the right handler. NOT called for functions. ++sub dump_declaration($$) { ++ no strict 'refs'; ++ my ($prototype, $file) = @_; ++ my $func = "dump_" . $decl_type; ++ &$func(@_); ++} ++ ++sub dump_union($$) { ++ dump_struct(@_); ++} ++ ++sub dump_struct($$) { ++ my $x = shift; ++ my $file = shift; ++ my $nested; ++ ++ if ($x =~ /(struct|union)\s+(\w+)\s*{(.*)}/) { ++ #my $decl_type = $1; ++ $declaration_name = $2; ++ my $members = $3; ++ ++ # ignore embedded structs or unions ++ $members =~ s/({.*})//g; ++ $nested = $1; ++ ++ # ignore members marked private: ++ $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi; ++ $members =~ s/\/\*\s*private:.*//gosi; ++ # strip comments: ++ $members =~ s/\/\*.*?\*\///gos; ++ $nested =~ s/\/\*.*?\*\///gos; ++ # strip kmemcheck_bitfield_{begin,end}.*; ++ $members =~ s/kmemcheck_bitfield_.*?;//gos; ++ # strip attributes ++ $members =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; ++ $members =~ s/__aligned\s*\([^;]*\)//gos; ++ $members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos; ++ # replace DECLARE_BITMAP ++ $members =~ s/DECLARE_BITMAP\s*\(([^,)]+), ([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; ++ ++ create_parameterlist($members, ';', $file); ++ check_sections($file, $declaration_name, "struct", $sectcheck, $struct_actual, $nested); ++ ++ output_declaration($declaration_name, ++ 'struct', ++ {'struct' => $declaration_name, ++ 'module' => $modulename, ++ 'parameterlist' => \@parameterlist, ++ 'parameterdescs' => \%parameterdescs, ++ 'parametertypes' => \%parametertypes, ++ 'sectionlist' => \@sectionlist, ++ 'sections' => \%sections, ++ 'purpose' => $declaration_purpose, ++ 'type' => $decl_type ++ }); ++ } ++ else { ++ print STDERR "${file}:$.: error: Cannot parse struct or union!\n"; ++ ++$errors; ++ } ++} ++ ++sub dump_enum($$) { ++ my $x = shift; ++ my $file = shift; ++ ++ $x =~ s@/\*.*?\*/@@gos; # strip comments. ++ # strip #define macros inside enums ++ $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos; ++ ++ if ($x =~ /enum\s+(\w+)\s*{(.*)}/) { ++ $declaration_name = $1; ++ my $members = $2; ++ ++ foreach my $arg (split ',', $members) { ++ $arg =~ s/^\s*(\w+).*/$1/; ++ push @parameterlist, $arg; ++ if (!$parameterdescs{$arg}) { ++ $parameterdescs{$arg} = $undescribed; ++ print STDERR "${file}:$.: warning: Enum value '$arg' ". ++ "not described in enum '$declaration_name'\n"; ++ } ++ ++ } ++ ++ output_declaration($declaration_name, ++ 'enum', ++ {'enum' => $declaration_name, ++ 'module' => $modulename, ++ 'parameterlist' => \@parameterlist, ++ 'parameterdescs' => \%parameterdescs, ++ 'sectionlist' => \@sectionlist, ++ 'sections' => \%sections, ++ 'purpose' => $declaration_purpose ++ }); ++ } ++ else { ++ print STDERR "${file}:$.: error: Cannot parse enum!\n"; ++ ++$errors; ++ } ++} ++ ++sub dump_typedef($$) { ++ my $x = shift; ++ my $file = shift; ++ ++ $x =~ s@/\*.*?\*/@@gos; # strip comments. ++ ++ # Parse function prototypes ++ if ($x =~ /typedef\s+(\w+)\s*\(\*\s*(\w\S+)\s*\)\s*\((.*)\);/ || ++ $x =~ /typedef\s+(\w+)\s*(\w\S+)\s*\s*\((.*)\);/) { ++ ++ # Function typedefs ++ $return_type = $1; ++ $declaration_name = $2; ++ my $args = $3; ++ ++ create_parameterlist($args, ',', $file); ++ ++ output_declaration($declaration_name, ++ 'function', ++ {'function' => $declaration_name, ++ 'typedef' => 1, ++ 'module' => $modulename, ++ 'functiontype' => $return_type, ++ 'parameterlist' => \@parameterlist, ++ 'parameterdescs' => \%parameterdescs, ++ 'parametertypes' => \%parametertypes, ++ 'sectionlist' => \@sectionlist, ++ 'sections' => \%sections, ++ 'purpose' => $declaration_purpose ++ }); ++ return; ++ } ++ ++ while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) { ++ $x =~ s/\(*.\)\s*;$/;/; ++ $x =~ s/\[*.\]\s*;$/;/; ++ } ++ ++ if ($x =~ /typedef.*\s+(\w+)\s*;/) { ++ $declaration_name = $1; ++ ++ output_declaration($declaration_name, ++ 'typedef', ++ {'typedef' => $declaration_name, ++ 'module' => $modulename, ++ 'sectionlist' => \@sectionlist, ++ 'sections' => \%sections, ++ 'purpose' => $declaration_purpose ++ }); ++ } ++ else { ++ print STDERR "${file}:$.: error: Cannot parse typedef!\n"; ++ ++$errors; ++ } ++} ++ ++sub save_struct_actual($) { ++ my $actual = shift; ++ ++ # strip all spaces from the actual param so that it looks like one string item ++ $actual =~ s/\s*//g; ++ $struct_actual = $struct_actual . $actual . " "; ++} ++ ++sub create_parameterlist($$$) { ++ my $args = shift; ++ my $splitter = shift; ++ my $file = shift; ++ my $type; ++ my $param; ++ ++ # temporarily replace commas inside function pointer definition ++ while ($args =~ /(\([^\),]+),/) { ++ $args =~ s/(\([^\),]+),/$1#/g; ++ } ++ ++ foreach my $arg (split($splitter, $args)) { ++ # strip comments ++ $arg =~ s/\/\*.*\*\///; ++ # strip leading/trailing spaces ++ $arg =~ s/^\s*//; ++ $arg =~ s/\s*$//; ++ $arg =~ s/\s+/ /; ++ ++ if ($arg =~ /^#/) { ++ # Treat preprocessor directive as a typeless variable just to fill ++ # corresponding data structures "correctly". Catch it later in ++ # output_* subs. ++ push_parameter($arg, "", $file); ++ } elsif ($arg =~ m/\(.+\)\s*\(/) { ++ # pointer-to-function ++ $arg =~ tr/#/,/; ++ $arg =~ m/[^\(]+\(\*?\s*(\w*)\s*\)/; ++ $param = $1; ++ $type = $arg; ++ $type =~ s/([^\(]+\(\*?)\s*$param/$1/; ++ save_struct_actual($param); ++ push_parameter($param, $type, $file); ++ } elsif ($arg) { ++ $arg =~ s/\s*:\s*/:/g; ++ $arg =~ s/\s*\[/\[/g; ++ ++ my @args = split('\s*,\s*', $arg); ++ if ($args[0] =~ m/\*/) { ++ $args[0] =~ s/(\*+)\s*/ $1/; ++ } ++ ++ my @first_arg; ++ if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) { ++ shift @args; ++ push(@first_arg, split('\s+', $1)); ++ push(@first_arg, $2); ++ } else { ++ @first_arg = split('\s+', shift @args); ++ } ++ ++ unshift(@args, pop @first_arg); ++ $type = join " ", @first_arg; ++ ++ foreach $param (@args) { ++ if ($param =~ m/^(\*+)\s*(.*)/) { ++ save_struct_actual($2); ++ push_parameter($2, "$type $1", $file); ++ } ++ elsif ($param =~ m/(.*?):(\d+)/) { ++ if ($type ne "") { # skip unnamed bit-fields ++ save_struct_actual($1); ++ push_parameter($1, "$type:$2", $file) ++ } ++ } ++ else { ++ save_struct_actual($param); ++ push_parameter($param, $type, $file); ++ } ++ } ++ } ++ } ++} ++ ++sub push_parameter($$$) { ++ my $param = shift; ++ my $type = shift; ++ my $file = shift; ++ ++ if (($anon_struct_union == 1) && ($type eq "") && ++ ($param eq "}")) { ++ return; # ignore the ending }; from anon. struct/union ++ } ++ ++ $anon_struct_union = 0; ++ my $param_name = $param; ++ $param_name =~ s/\[.*//; ++ ++ if ($type eq "" && $param =~ /\.\.\.$/) ++ { ++ if (!$param =~ /\w\.\.\.$/) { ++ # handles unnamed variable parameters ++ $param = "..."; ++ } ++ if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") { ++ $parameterdescs{$param} = "variable arguments"; ++ } ++ } ++ elsif ($type eq "" && ($param eq "" or $param eq "void")) ++ { ++ $param="void"; ++ $parameterdescs{void} = "no arguments"; ++ } ++ elsif ($type eq "" && ($param eq "struct" or $param eq "union")) ++ # handle unnamed (anonymous) union or struct: ++ { ++ $type = $param; ++ $param = "{unnamed_" . $param . "}"; ++ $parameterdescs{$param} = "anonymous\n"; ++ $anon_struct_union = 1; ++ } ++ ++ # warn if parameter has no description ++ # (but ignore ones starting with # as these are not parameters ++ # but inline preprocessor statements); ++ # also ignore unnamed structs/unions; ++ if (!$anon_struct_union) { ++ if (!defined $parameterdescs{$param_name} && $param_name !~ /^#/) { ++ ++ $parameterdescs{$param_name} = $undescribed; ++ ++ if (($type eq 'function') || ($type eq 'enum')) { ++ print STDERR "${file}:$.: warning: Function parameter ". ++ "or member '$param' not " . ++ "described in '$declaration_name'\n"; ++ } ++ print STDERR "${file}:$.: warning:" . ++ " No description found for parameter '$param'\n"; ++ ++$warnings; ++ } ++ } ++ ++ $param = xml_escape($param); ++ ++ # strip spaces from $param so that it is one continuous string ++ # on @parameterlist; ++ # this fixes a problem where check_sections() cannot find ++ # a parameter like "addr[6 + 2]" because it actually appears ++ # as "addr[6", "+", "2]" on the parameter list; ++ # but it's better to maintain the param string unchanged for output, ++ # so just weaken the string compare in check_sections() to ignore ++ # "[blah" in a parameter string; ++ ###$param =~ s/\s*//g; ++ push @parameterlist, $param; ++ $parametertypes{$param} = $type; ++} ++ ++sub check_sections($$$$$$) { ++ my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck, $nested) = @_; ++ my @sects = split ' ', $sectcheck; ++ my @prms = split ' ', $prmscheck; ++ my $err; ++ my ($px, $sx); ++ my $prm_clean; # strip trailing "[array size]" and/or beginning "*" ++ ++ foreach $sx (0 .. $#sects) { ++ $err = 1; ++ foreach $px (0 .. $#prms) { ++ $prm_clean = $prms[$px]; ++ $prm_clean =~ s/\[.*\]//; ++ $prm_clean =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; ++ # ignore array size in a parameter string; ++ # however, the original param string may contain ++ # spaces, e.g.: addr[6 + 2] ++ # and this appears in @prms as "addr[6" since the ++ # parameter list is split at spaces; ++ # hence just ignore "[..." for the sections check; ++ $prm_clean =~ s/\[.*//; ++ ++ ##$prm_clean =~ s/^\**//; ++ if ($prm_clean eq $sects[$sx]) { ++ $err = 0; ++ last; ++ } ++ } ++ if ($err) { ++ if ($decl_type eq "function") { ++ print STDERR "${file}:$.: warning: " . ++ "Excess function parameter " . ++ "'$sects[$sx]' " . ++ "description in '$decl_name'\n"; ++ ++$warnings; ++ } else { ++ if ($nested !~ m/\Q$sects[$sx]\E/) { ++ print STDERR "${file}:$.: warning: " . ++ "Excess struct/union/enum/typedef member " . ++ "'$sects[$sx]' " . ++ "description in '$decl_name'\n"; ++ ++$warnings; ++ } ++ } ++ } ++ } ++} ++ ++## ++# Checks the section describing the return value of a function. ++sub check_return_section { ++ my $file = shift; ++ my $declaration_name = shift; ++ my $return_type = shift; ++ ++ # Ignore an empty return type (It's a macro) ++ # Ignore functions with a "void" return type. (But don't ignore "void *") ++ if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) { ++ return; ++ } ++ ++ if (!defined($sections{$section_return}) || ++ $sections{$section_return} eq "") { ++ print STDERR "${file}:$.: warning: " . ++ "No description found for return value of " . ++ "'$declaration_name'\n"; ++ ++$warnings; ++ } ++} ++ ++## ++# takes a function prototype and the name of the current file being ++# processed and spits out all the details stored in the global ++# arrays/hashes. ++sub dump_function($$) { ++ my $prototype = shift; ++ my $file = shift; ++ my $noret = 0; ++ ++ $prototype =~ s/^static +//; ++ $prototype =~ s/^extern +//; ++ $prototype =~ s/^asmlinkage +//; ++ $prototype =~ s/^inline +//; ++ $prototype =~ s/^__inline__ +//; ++ $prototype =~ s/^__inline +//; ++ $prototype =~ s/^__always_inline +//; ++ $prototype =~ s/^noinline +//; ++ $prototype =~ s/__init +//; ++ $prototype =~ s/__init_or_module +//; ++ $prototype =~ s/__meminit +//; ++ $prototype =~ s/__must_check +//; ++ $prototype =~ s/__weak +//; ++ my $define = $prototype =~ s/^#\s*define\s+//; #ak added ++ $prototype =~ s/__attribute__\s*\(\([a-z,]*\)\)//; ++ ++ # Yes, this truly is vile. We are looking for: ++ # 1. Return type (may be nothing if we're looking at a macro) ++ # 2. Function name ++ # 3. Function parameters. ++ # ++ # All the while we have to watch out for function pointer parameters ++ # (which IIRC is what the two sections are for), C types (these ++ # regexps don't even start to express all the possibilities), and ++ # so on. ++ # ++ # If you mess with these regexps, it's a good idea to check that ++ # the following functions' documentation still comes out right: ++ # - parport_register_device (function pointer parameters) ++ # - atomic_set (macro) ++ # - pci_match_device, __copy_to_user (long return type) ++ ++ if ($define && $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s+/) { ++ # This is an object-like macro, it has no return type and no parameter ++ # list. ++ # Function-like macros are not allowed to have spaces between ++ # declaration_name and opening parenthesis (notice the \s+). ++ $return_type = $1; ++ $declaration_name = $2; ++ $noret = 1; ++ } elsif ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || ++ $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || ++ $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || ++ $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || ++ $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || ++ $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || ++ $prototype =~ m/^(\w+\s+\w+\s*\*\s*\w+\s*\*\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) { ++ $return_type = $1; ++ $declaration_name = $2; ++ my $args = $3; ++ ++ create_parameterlist($args, ',', $file); ++ } else { ++ print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n"; ++ return; ++ } ++ ++ my $prms = join " ", @parameterlist; ++ check_sections($file, $declaration_name, "function", $sectcheck, $prms, ""); ++ ++ # This check emits a lot of warnings at the moment, because many ++ # functions don't have a 'Return' doc section. So until the number ++ # of warnings goes sufficiently down, the check is only performed in ++ # verbose mode. ++ # TODO: always perform the check. ++ if ($verbose && !$noret) { ++ check_return_section($file, $declaration_name, $return_type); ++ } ++ ++ output_declaration($declaration_name, ++ 'function', ++ {'function' => $declaration_name, ++ 'module' => $modulename, ++ 'functiontype' => $return_type, ++ 'parameterlist' => \@parameterlist, ++ 'parameterdescs' => \%parameterdescs, ++ 'parametertypes' => \%parametertypes, ++ 'sectionlist' => \@sectionlist, ++ 'sections' => \%sections, ++ 'purpose' => $declaration_purpose ++ }); ++} ++ ++sub reset_state { ++ $function = ""; ++ %parameterdescs = (); ++ %parametertypes = (); ++ @parameterlist = (); ++ %sections = (); ++ @sectionlist = (); ++ $sectcheck = ""; ++ $struct_actual = ""; ++ $prototype = ""; ++ ++ $state = STATE_NORMAL; ++ $inline_doc_state = STATE_INLINE_NA; ++} ++ ++sub tracepoint_munge($) { ++ my $file = shift; ++ my $tracepointname = 0; ++ my $tracepointargs = 0; ++ ++ if ($prototype =~ m/TRACE_EVENT\((.*?),/) { ++ $tracepointname = $1; ++ } ++ if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) { ++ $tracepointname = $1; ++ } ++ if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) { ++ $tracepointname = $2; ++ } ++ $tracepointname =~ s/^\s+//; #strip leading whitespace ++ if ($prototype =~ m/TP_PROTO\((.*?)\)/) { ++ $tracepointargs = $1; ++ } ++ if (($tracepointname eq 0) || ($tracepointargs eq 0)) { ++ print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n". ++ "$prototype\n"; ++ } else { ++ $prototype = "static inline void trace_$tracepointname($tracepointargs)"; ++ } ++} ++ ++sub syscall_munge() { ++ my $void = 0; ++ ++ $prototype =~ s@[\r\n\t]+@ @gos; # strip newlines/CR's/tabs ++## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) { ++ if ($prototype =~ m/SYSCALL_DEFINE0/) { ++ $void = 1; ++## $prototype = "long sys_$1(void)"; ++ } ++ ++ $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name ++ if ($prototype =~ m/long (sys_.*?),/) { ++ $prototype =~ s/,/\(/; ++ } elsif ($void) { ++ $prototype =~ s/\)/\(void\)/; ++ } ++ ++ # now delete all of the odd-number commas in $prototype ++ # so that arg types & arg names don't have a comma between them ++ my $count = 0; ++ my $len = length($prototype); ++ if ($void) { ++ $len = 0; # skip the for-loop ++ } ++ for (my $ix = 0; $ix < $len; $ix++) { ++ if (substr($prototype, $ix, 1) eq ',') { ++ $count++; ++ if ($count % 2 == 1) { ++ substr($prototype, $ix, 1) = ' '; ++ } ++ } ++ } ++} ++ ++sub process_proto_function($$) { ++ my $x = shift; ++ my $file = shift; ++ ++ $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line ++ ++ if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) { ++ # do nothing ++ } ++ elsif ($x =~ /([^\{]*)/) { ++ $prototype .= $1; ++ } ++ ++ if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) { ++ $prototype =~ s@/\*.*?\*/@@gos; # strip comments. ++ $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. ++ $prototype =~ s@^\s+@@gos; # strip leading spaces ++ if ($prototype =~ /SYSCALL_DEFINE/) { ++ syscall_munge(); ++ } ++ if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ || ++ $prototype =~ /DEFINE_SINGLE_EVENT/) ++ { ++ tracepoint_munge($file); ++ } ++ dump_function($prototype, $file); ++ reset_state(); ++ } ++} ++ ++sub process_proto_type($$) { ++ my $x = shift; ++ my $file = shift; ++ ++ $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's. ++ $x =~ s@^\s+@@gos; # strip leading spaces ++ $x =~ s@\s+$@@gos; # strip trailing spaces ++ $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line ++ ++ if ($x =~ /^#/) { ++ # To distinguish preprocessor directive from regular declaration later. ++ $x .= ";"; ++ } ++ ++ while (1) { ++ if ( $x =~ /([^{};]*)([{};])(.*)/ ) { ++ $prototype .= $1 . $2; ++ ($2 eq '{') && $brcount++; ++ ($2 eq '}') && $brcount--; ++ if (($2 eq ';') && ($brcount == 0)) { ++ dump_declaration($prototype, $file); ++ reset_state(); ++ last; ++ } ++ $x = $3; ++ } else { ++ $prototype .= $x; ++ last; ++ } ++ } ++} ++ ++# xml_escape: replace <, >, and & in the text stream; ++# ++# however, formatting controls that are generated internally/locally in the ++# kernel-doc script are not escaped here; instead, they begin life like ++# $blankline_html (4 of '\' followed by a mnemonic + ':'), then these strings ++# are converted to their mnemonic-expected output, without the 4 * '\' & ':', ++# just before actual output; (this is done by local_unescape()) ++sub xml_escape($) { ++ my $text = shift; ++ if (($output_mode eq "text") || ($output_mode eq "man")) { ++ return $text; ++ } ++ $text =~ s/\&/\\\\\\amp;/g; ++ $text =~ s/\/\\\\\\gt;/g; ++ return $text; ++} ++ ++# xml_unescape: reverse the effects of xml_escape ++sub xml_unescape($) { ++ my $text = shift; ++ if (($output_mode eq "text") || ($output_mode eq "man")) { ++ return $text; ++ } ++ $text =~ s/\\\\\\amp;/\&/g; ++ $text =~ s/\\\\\\lt;//g; ++ return $text; ++} ++ ++# convert local escape strings to html ++# local escape strings look like: '\\\\menmonic:' (that's 4 backslashes) ++sub local_unescape($) { ++ my $text = shift; ++ if (($output_mode eq "text") || ($output_mode eq "man")) { ++ return $text; ++ } ++ $text =~ s/\\\\\\\\lt://g; ++ return $text; ++} ++ ++sub map_filename($) { ++ my $file; ++ my ($orig_file) = @_; ++ ++ if (defined($ENV{'SRCTREE'})) { ++ $file = "$ENV{'SRCTREE'}" . "/" . $orig_file; ++ } else { ++ $file = $orig_file; ++ } ++ ++ if (defined($source_map{$file})) { ++ $file = $source_map{$file}; ++ } ++ ++ return $file; ++} ++ ++sub process_export_file($) { ++ my ($orig_file) = @_; ++ my $file = map_filename($orig_file); ++ ++ if (!open(IN,"<$file")) { ++ print STDERR "Error: Cannot open file $file\n"; ++ ++$errors; ++ return; ++ } ++ ++ while () { ++ if (/$export_symbol/) { ++ $function_table{$2} = 1; ++ } ++ } ++ ++ close(IN); ++} ++ ++sub process_file($) { ++ my $file; ++ my $identifier; ++ my $func; ++ my $descr; ++ my $in_purpose = 0; ++ my $initial_section_counter = $section_counter; ++ my ($orig_file) = @_; ++ my $leading_space; ++ ++ $file = map_filename($orig_file); ++ ++ if (!open(IN,"<$file")) { ++ print STDERR "Error: Cannot open file $file\n"; ++ ++$errors; ++ return; ++ } ++ ++ $. = 1; ++ ++ $section_counter = 0; ++ while () { ++ while (s/\\\s*$//) { ++ $_ .= ; ++ } ++ if ($state == STATE_NORMAL) { ++ if (/$doc_start/o) { ++ $state = STATE_NAME; # next line is always the function name ++ $in_doc_sect = 0; ++ $declaration_start_line = $. + 1; ++ } ++ } elsif ($state == STATE_NAME) {# this line is the function name (always) ++ if (/$doc_block/o) { ++ $state = STATE_DOCBLOCK; ++ $contents = ""; ++ $new_start_line = $. + 1; ++ ++ if ( $1 eq "" ) { ++ $section = $section_intro; ++ } else { ++ $section = $1; ++ } ++ } ++ elsif (/$doc_decl/o) { ++ $identifier = $1; ++ if (/\s*([\w\s]+?)\s*-/) { ++ $identifier = $1; ++ } ++ ++ $state = STATE_FIELD; ++ # if there's no @param blocks need to set up default section ++ # here ++ $contents = ""; ++ $section = $section_default; ++ $new_start_line = $. + 1; ++ if (/-(.*)/) { ++ # strip leading/trailing/multiple spaces ++ $descr= $1; ++ $descr =~ s/^\s*//; ++ $descr =~ s/\s*$//; ++ $descr =~ s/\s+/ /g; ++ $declaration_purpose = xml_escape($descr); ++ $in_purpose = 1; ++ } else { ++ $declaration_purpose = ""; ++ } ++ ++ if (($declaration_purpose eq "") && $verbose) { ++ print STDERR "${file}:$.: warning: missing initial short description on line:\n"; ++ print STDERR $_; ++ ++$warnings; ++ } ++ ++ if ($identifier =~ m/^struct/) { ++ $decl_type = 'struct'; ++ } elsif ($identifier =~ m/^union/) { ++ $decl_type = 'union'; ++ } elsif ($identifier =~ m/^enum/) { ++ $decl_type = 'enum'; ++ } elsif ($identifier =~ m/^typedef/) { ++ $decl_type = 'typedef'; ++ } else { ++ $decl_type = 'function'; ++ } ++ ++ if ($verbose) { ++ print STDERR "${file}:$.: info: Scanning doc for $identifier\n"; ++ } ++ } else { ++ print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", ++ " - I thought it was a doc line\n"; ++ ++$warnings; ++ $state = STATE_NORMAL; ++ } ++ } elsif ($state == STATE_FIELD) { # look for head: lines, and include content ++ if (/$doc_sect/i) { # case insensitive for supported section names ++ $newsection = $1; ++ $newcontents = $2; ++ ++ # map the supported section names to the canonical names ++ if ($newsection =~ m/^description$/i) { ++ $newsection = $section_default; ++ } elsif ($newsection =~ m/^context$/i) { ++ $newsection = $section_context; ++ } elsif ($newsection =~ m/^returns?$/i) { ++ $newsection = $section_return; ++ } elsif ($newsection =~ m/^\@return$/) { ++ # special: @return is a section, not a param description ++ $newsection = $section_return; ++ } ++ ++ if (($contents ne "") && ($contents ne "\n")) { ++ if (!$in_doc_sect && $verbose) { ++ print STDERR "${file}:$.: warning: contents before sections\n"; ++ ++$warnings; ++ } ++ dump_section($file, $section, xml_escape($contents)); ++ $section = $section_default; ++ } ++ ++ $in_doc_sect = 1; ++ $in_purpose = 0; ++ $contents = $newcontents; ++ $new_start_line = $.; ++ while ((substr($contents, 0, 1) eq " ") || ++ substr($contents, 0, 1) eq "\t") { ++ $contents = substr($contents, 1); ++ } ++ if ($contents ne "") { ++ $contents .= "\n"; ++ } ++ $section = $newsection; ++ $leading_space = undef; ++ } elsif (/$doc_end/) { ++ if (($contents ne "") && ($contents ne "\n")) { ++ dump_section($file, $section, xml_escape($contents)); ++ $section = $section_default; ++ $contents = ""; ++ } ++ # look for doc_com + + doc_end: ++ if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { ++ print STDERR "${file}:$.: warning: suspicious ending line: $_"; ++ ++$warnings; ++ } ++ ++ $prototype = ""; ++ $state = STATE_PROTO; ++ $brcount = 0; ++# print STDERR "end of doc comment, looking for prototype\n"; ++ } elsif (/$doc_content/) { ++ # miguel-style comment kludge, look for blank lines after ++ # @parameter line to signify start of description ++ if ($1 eq "") { ++ if ($section =~ m/^@/ || $section eq $section_context) { ++ dump_section($file, $section, xml_escape($contents)); ++ $section = $section_default; ++ $contents = ""; ++ $new_start_line = $.; ++ } else { ++ $contents .= "\n"; ++ } ++ $in_purpose = 0; ++ } elsif ($in_purpose == 1) { ++ # Continued declaration purpose ++ chomp($declaration_purpose); ++ $declaration_purpose .= " " . xml_escape($1); ++ $declaration_purpose =~ s/\s+/ /g; ++ } else { ++ my $cont = $1; ++ if ($section =~ m/^@/ || $section eq $section_context) { ++ if (!defined $leading_space) { ++ if ($cont =~ m/^(\s+)/) { ++ $leading_space = $1; ++ } else { ++ $leading_space = ""; ++ } ++ } ++ ++ $cont =~ s/^$leading_space//; ++ } ++ $contents .= $cont . "\n"; ++ } ++ } else { ++ # i dont know - bad line? ignore. ++ print STDERR "${file}:$.: warning: bad line: $_"; ++ ++$warnings; ++ } ++ } elsif ($state == STATE_INLINE) { # scanning for inline parameters ++ # First line (state 1) needs to be a @parameter ++ if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { ++ $section = $1; ++ $contents = $2; ++ $new_start_line = $.; ++ if ($contents ne "") { ++ while ((substr($contents, 0, 1) eq " ") || ++ substr($contents, 0, 1) eq "\t") { ++ $contents = substr($contents, 1); ++ } ++ $contents .= "\n"; ++ } ++ $inline_doc_state = STATE_INLINE_TEXT; ++ # Documentation block end */ ++ } elsif (/$doc_inline_end/) { ++ if (($contents ne "") && ($contents ne "\n")) { ++ dump_section($file, $section, xml_escape($contents)); ++ $section = $section_default; ++ $contents = ""; ++ } ++ $state = STATE_PROTO; ++ $inline_doc_state = STATE_INLINE_NA; ++ # Regular text ++ } elsif (/$doc_content/) { ++ if ($inline_doc_state == STATE_INLINE_TEXT) { ++ $contents .= $1 . "\n"; ++ # nuke leading blank lines ++ if ($contents =~ /^\s*$/) { ++ $contents = ""; ++ } ++ } elsif ($inline_doc_state == STATE_INLINE_NAME) { ++ $inline_doc_state = STATE_INLINE_ERROR; ++ print STDERR "${file}:$.: warning: "; ++ print STDERR "Incorrect use of kernel-doc format: $_"; ++ ++$warnings; ++ } ++ } ++ } elsif ($state == STATE_PROTO) { # scanning for function '{' (end of prototype) ++ if (/$doc_inline_oneline/) { ++ $section = $1; ++ $contents = $2; ++ if ($contents ne "") { ++ $contents .= "\n"; ++ dump_section($file, $section, xml_escape($contents)); ++ $section = $section_default; ++ $contents = ""; ++ } ++ } elsif (/$doc_inline_start/) { ++ $state = STATE_INLINE; ++ $inline_doc_state = STATE_INLINE_NAME; ++ } elsif ($decl_type eq 'function') { ++ process_proto_function($_, $file); ++ } else { ++ process_proto_type($_, $file); ++ } ++ } elsif ($state == STATE_DOCBLOCK) { ++ if (/$doc_end/) ++ { ++ dump_doc_section($file, $section, xml_escape($contents)); ++ $section = $section_default; ++ $contents = ""; ++ $function = ""; ++ %parameterdescs = (); ++ %parametertypes = (); ++ @parameterlist = (); ++ %sections = (); ++ @sectionlist = (); ++ $prototype = ""; ++ $state = STATE_NORMAL; ++ } ++ elsif (/$doc_content/) ++ { ++ if ( $1 eq "" ) ++ { ++ $contents .= $blankline; ++ } ++ else ++ { ++ $contents .= $1 . "\n"; ++ } ++ } ++ } ++ } ++ if ($initial_section_counter == $section_counter) { ++ print STDERR "${file}:1: warning: no structured comments found\n"; ++ if (($output_selection == OUTPUT_INCLUDE) && ($show_not_found == 1)) { ++ print STDERR " Was looking for '$_'.\n" for keys %function_table; ++ } ++ if ($output_mode eq "xml") { ++ # The template wants at least one RefEntry here; make one. ++ print "\n"; ++ print " \n"; ++ print " \n"; ++ print " ${orig_file}\n"; ++ print " \n"; ++ print " \n"; ++ print " Document generation inconsistency\n"; ++ print " \n"; ++ print " \n"; ++ print " \n"; ++ print " \n"; ++ print " Oops\n"; ++ print " \n"; ++ print " \n"; ++ print " \n"; ++ print " The template for this document tried to insert\n"; ++ print " the structured comment from the file\n"; ++ print " ${orig_file} at this point,\n"; ++ print " but none was found.\n"; ++ print " This dummy section is inserted to allow\n"; ++ print " generation to continue.\n"; ++ print " \n"; ++ print " \n"; ++ print " \n"; ++ print "\n"; ++ } ++ } ++} ++ ++ ++$kernelversion = get_kernel_version(); ++ ++# generate a sequence of code that will splice in highlighting information ++# using the s// operator. ++for (my $k = 0; $k < @highlights; $k++) { ++ my $pattern = $highlights[$k][0]; ++ my $result = $highlights[$k][1]; ++# print STDERR "scanning pattern:$pattern, highlight:($result)\n"; ++ $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n"; ++} ++ ++# Read the file that maps relative names to absolute names for ++# separate source and object directories and for shadow trees. ++if (open(SOURCE_MAP, "<.tmp_filelist.txt")) { ++ my ($relname, $absname); ++ while() { ++ chop(); ++ ($relname, $absname) = (split())[0..1]; ++ $relname =~ s:^/+::; ++ $source_map{$relname} = $absname; ++ } ++ close(SOURCE_MAP); ++} ++ ++if ($output_selection == OUTPUT_EXPORTED || ++ $output_selection == OUTPUT_INTERNAL) { ++ ++ push(@export_file_list, @ARGV); ++ ++ foreach (@export_file_list) { ++ chomp; ++ process_export_file($_); ++ } ++} ++ ++foreach (@ARGV) { ++ chomp; ++ process_file($_); ++} ++if ($verbose && $errors) { ++ print STDERR "$errors errors\n"; ++} ++if ($verbose && $warnings) { ++ print STDERR "$warnings warnings\n"; ++} ++ ++exit($errors); +Index: multipath-tools-130222/libdmmp/docs/libdmmp.h.3 +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/docs/libdmmp.h.3 +@@ -0,0 +1,113 @@ ++.TH "libdmmp.h" 3 "January 2016" "Device Mapper Multipath API - libdmmp Manual" ++ ++.SH NAME ++libdmmp.h \- Device Mapper Multipath API. ++ ++.SH SYNOPSIS ++#include ++ ++.SH "DESCRIPTION" ++ ++All the libdmmp public functions ships its own man pages. ++Use 'man 3 ' to check the detail usage. ++ ++.SH "USAGE" ++ ++To use libdmmp in your project, we suggest to use the 'pkg-config' way: ++ ++ * Add this line into your configure.ac: ++ ++ PKG_CHECK_MODULES([LIBDMMP], [libdmmp]) ++ ++ * Add these lines into your Makefile.am: ++ ++ foo_LDFLAGS += $(LIBDMMP_LIBS) ++ foo_CFLAGS += $(LIBDMMP_CFLAGS) ++ ++.SH LOG HANDLING ++ ++The log handler function could be set via 'dmmp_context_log_func_set()'. ++The log priority could be set via 'dmmp_context_log_priority_set()'. ++ ++By default, the log priorities is 'DMMP_LOG_PRIORITY_WARNING'. ++By default, the log handler is print log to STDERR, and its code is listed ++below in case you want to create your own log handler. ++ ++ static int _DMMP_LOG_STRERR_ALIGN_WIDTH = 80; ++ ++ static void _log_stderr(struct dmmp_context *ctx, ++ enum dmmp_log_priority priority, ++ const char *file, int line, ++ const char *func_name, ++ const char *format, va_list args) ++ { ++ int printed_bytes = 0; ++ ++ printed_bytes += fprintf(stderr, "libdmmp %s: ", ++ dmmp_log_priority_str(priority)); ++ printed_bytes += vfprintf(stderr, format, args); ++ userdata = dmmp_context_userdata_get(ctx); ++ if (userdata != NULL) ++ fprintf(stderr, "(with user data at memory address %p)", ++ userdata); ++ ++ if (printed_bytes < _DMMP_LOG_STRERR_ALIGN_WIDTH) { ++ fprintf(stderr, "%*s # %s:%s():%d\n", ++ _DMMP_LOG_STRERR_ALIGN_WIDTH - printed_bytes, "", file, ++ func_name, line); ++ } else { ++ fprintf(stderr, " # %s:%s():%d\n", file, func_name, line); ++ } ++ } ++ ++ ++.SH "SAMPLE CODE" ++ ++ #include ++ ++ int main(int argc, char *argv[]) { ++ struct dmmp_context *ctx = NULL; ++ struct dmmp_mpath **dmmp_mps = NULL; ++ struct dmmp_path_group **dmmp_pgs = NULL; ++ struct dmmp_path **dmmp_ps = NULL; ++ uint32_t dmmp_mp_count = 0; ++ uint32_t dmmp_pg_count = 0; ++ uint32_t dmmp_p_count = 0; ++ const char *name = NULL; ++ const char *wwid = NULL; ++ uint32_t i = 0; ++ int rc = DMMP_OK; ++ ++ ctx = dmmp_context_new(); ++ dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_DEBUG); ++ // By default, log will be printed to STDERR, you could ++ // change that via dmmp_context_log_func_set() ++ rc = dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count); ++ if (rc != DMMP_OK) { ++ printf("dmmp_mpath_array_get() failed with %d: %s", rc, ++ dmmp_strerror(rc)); ++ goto out; ++ } ++ for (i = 0; i < dmmp_mp_count; ++i) { ++ name = dmmp_mpath_name_get(dmmp_mps[i]); ++ wwid = dmmp_mpath_wwid_get(dmmp_mps[i]); ++ printf("dmmp_mpath_array_get(): Got mpath: %s %s\n", name, ++ wwid); ++ // You could use dmmp_path_group_array_get() to retrieve ++ // path group information and then invoke dmmp_path_array_get() ++ // for path information. ++ } ++ ++ out: ++ dmmp_context_free(ctx); ++ dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count); ++ if (rc != DMMP_OK) ++ exit(1); ++ exit(0); ++ } ++ ++.SH "LICENSE" ++GPLv2+ ++ ++.SH "BUG" ++Please report bug to +Index: multipath-tools-130222/libdmmp/docs/split-man.pl +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/docs/split-man.pl +@@ -0,0 +1,40 @@ ++#!/usr/bin/perl ++# Originally From: ++# https://www.kernel.org/doc/Documentation/kernel-doc-nano-HOWTO.txt ++# ++# Changes: ++# * Create manpage section 3 instead of 9. ++# * Replace 'Kernel Hackers Manual' to ++# 'Device Mapper Multipath API - libdmmp Manual' ++# * Remove LINUX from header. ++# * Remove DMMP_DLL_EXPORT. ++$man_sec_num = 3; ++$title = 'Device Mapper Multipath API - libdmmp Manual'; ++ ++if ( $#ARGV < 0 ) { ++ die "where do I put the results?\n"; ++} ++ ++mkdir $ARGV[0], 0777; ++$state = 0; ++while () { ++ if (/^\.TH \"[^\"]*\" 9 \"([^\"]*)\"/) { ++ if ( $state == 1 ) { close OUT } ++ $state = 1; ++ $fn = "$ARGV[0]/$1.$man_sec_num"; ++ print STDERR "Creating $fn\n"; ++ open OUT, ">$fn" or die "can't open $fn: $!\n"; ++ ++ # Change man page code from 9 to $man_sec_num; ++ s/^\.TH (\"[^\"]*\") 9 \"([^\"]*)\"/\.TH $1 $man_sec_num \"$2\"/; ++ s/Kernel Hacker's Manual/$title/g; ++ s/LINUX//g; ++ ++ print OUT $_; ++ } ++ elsif ( $state != 0 ) { ++ print OUT $_; ++ } ++} ++ ++close OUT; +Index: multipath-tools-130222/libdmmp/libdmmp.c +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/libdmmp.c +@@ -0,0 +1,285 @@ ++/* ++ * Copyright (C) 2015 - 2016 Red Hat, Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author: Gris Ge ++ * Todd Gill ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "libdmmp/libdmmp.h" ++#include "libdmmp_private.h" ++ ++#define _DEFAULT_UXSOCK_TIMEOUT 60000 ++/* ^ 60 seconds. On system with 10k sdX, dmmp_mpath_array_get() ++ * only take 3.5 seconds, so this default value should be OK for most users. ++ */ ++ ++#define _DMMP_IPC_SHOW_JSON_CMD "show maps json" ++#define _DMMP_JSON_MAJOR_KEY "major_version" ++#define _DMMP_JSON_MAJOR_VERSION 0 ++#define _DMMP_JSON_MAPS_KEY "maps" ++#define _ERRNO_STR_BUFF_SIZE 256 ++ ++struct dmmp_context { ++ void (*log_func)(struct dmmp_context *ctx, int priority, ++ const char *file, int line, const char *func_name, ++ const char *format, va_list args); ++ int log_priority; ++ void *userdata; ++ unsigned int tmo; ++}; ++ ++_dmmp_getter_func_gen(dmmp_context_log_priority_get, ++ struct dmmp_context, ctx, log_priority, ++ int); ++ ++_dmmp_getter_func_gen(dmmp_context_userdata_get, struct dmmp_context, ctx, ++ userdata, void *); ++ ++_dmmp_getter_func_gen(dmmp_context_timeout_get, struct dmmp_context, ctx, tmo, ++ unsigned int); ++ ++_dmmp_array_free_func_gen(dmmp_mpath_array_free, struct dmmp_mpath, ++ _dmmp_mpath_free); ++ ++void _dmmp_log(struct dmmp_context *ctx, int priority, const char *file, ++ int line, const char *func_name, const char *format, ...) ++{ ++ va_list args; ++ ++ va_start(args, format); ++ ctx->log_func(ctx, priority, file, line, func_name, format, args); ++ va_end(args); ++} ++ ++struct dmmp_context *dmmp_context_new(void) ++{ ++ struct dmmp_context *ctx = NULL; ++ ++ ctx = (struct dmmp_context *) malloc(sizeof(struct dmmp_context)); ++ ++ if (ctx == NULL) ++ return NULL; ++ ++ ctx->log_func = _dmmp_log_stderr; ++ ctx->log_priority = DMMP_LOG_PRIORITY_DEFAULT; ++ ctx->userdata = NULL; ++ ctx->tmo = _DEFAULT_UXSOCK_TIMEOUT; ++ ++ return ctx; ++} ++ ++void dmmp_context_free(struct dmmp_context *ctx) ++{ ++ free(ctx); ++} ++ ++void dmmp_context_log_priority_set(struct dmmp_context *ctx, int priority) ++{ ++ assert(ctx != NULL); ++ ctx->log_priority = priority; ++} ++ ++void dmmp_context_timeout_set(struct dmmp_context *ctx, unsigned int tmo) ++{ ++ assert(ctx != NULL); ++ ctx->tmo = tmo; ++} ++ ++void dmmp_context_log_func_set ++ (struct dmmp_context *ctx, ++ void (*log_func)(struct dmmp_context *ctx, int priority, ++ const char *file, int line, const char *func_name, ++ const char *format, va_list args)) ++{ ++ assert(ctx != NULL); ++ ctx->log_func = log_func; ++} ++ ++void dmmp_context_userdata_set(struct dmmp_context *ctx, void *userdata) ++{ ++ assert(ctx != NULL); ++ ctx->userdata = userdata; ++} ++ ++int dmmp_mpath_array_get(struct dmmp_context *ctx, ++ struct dmmp_mpath ***dmmp_mps, uint32_t *dmmp_mp_count) ++{ ++ struct dmmp_mpath *dmmp_mp = NULL; ++ int rc = DMMP_OK; ++ char *j_str = NULL; ++ json_object *j_obj = NULL; ++ json_object *j_obj_map = NULL; ++ enum json_tokener_error j_err = json_tokener_success; ++ json_tokener *j_token = NULL; ++ struct array_list *ar_maps = NULL; ++ uint32_t i = 0; ++ int cur_json_major_version = -1; ++ int ar_maps_len = -1; ++ int socket_fd = -1; ++ int errno_save = 0; ++ char errno_str_buff[_ERRNO_STR_BUFF_SIZE]; ++ ++ assert(ctx != NULL); ++ assert(dmmp_mps != NULL); ++ assert(dmmp_mp_count != NULL); ++ ++ *dmmp_mps = NULL; ++ *dmmp_mp_count = 0; ++ ++ socket_fd = mpath_connect(); ++ if (socket_fd == -1) { ++ errno_save = errno; ++ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE); ++ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE); ++ if (errno_save == ECONNREFUSED) { ++ rc = DMMP_ERR_NO_DAEMON; ++ _error(ctx, "Socket connection refuse. " ++ "Maybe multipathd daemon is not running"); ++ } else { ++ _error(ctx, "IPC failed with error %d(%s)", errno_save, ++ errno_str_buff); ++ rc = DMMP_ERR_IPC_ERROR; ++ } ++ goto out; ++ } ++ ++ if (mpath_process_cmd(socket_fd, _DMMP_IPC_SHOW_JSON_CMD, ++ &j_str, ctx->tmo) != 0) { ++ errno_save = errno; ++ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE); ++ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE); ++ if (errno_save == ETIMEDOUT) { ++ rc = DMMP_ERR_IPC_TIMEOUT; ++ _error(ctx, "IPC communication timeout, try to " ++ "increase it via dmmp_context_timeout_set()"); ++ goto out; ++ } ++ _error(ctx, "IPC failed when process command '%s' with " ++ "error %d(%s)", _DMMP_IPC_SHOW_JSON_CMD, errno_save, ++ errno_str_buff); ++ rc = DMMP_ERR_IPC_ERROR; ++ goto out; ++ } ++ ++ if ((j_str == NULL) || (strlen(j_str) == 0)) { ++ _error(ctx, "IPC return empty reply for command %s", ++ _DMMP_IPC_SHOW_JSON_CMD); ++ rc = DMMP_ERR_IPC_ERROR; ++ goto out; ++ } ++ ++ _debug(ctx, "Got json output from multipathd: '%s'", j_str); ++ j_token = json_tokener_new(); ++ if (j_token == NULL) { ++ rc = DMMP_ERR_BUG; ++ _error(ctx, "BUG: json_tokener_new() retuned NULL"); ++ goto out; ++ } ++ j_obj = json_tokener_parse_ex(j_token, j_str, strlen(j_str) + 1); ++ ++ if (j_obj == NULL) { ++ rc = DMMP_ERR_IPC_ERROR; ++ j_err = json_tokener_get_error(j_token); ++ _error(ctx, "Failed to parse JSON output from multipathd IPC: " ++ "%s", json_tokener_error_desc(j_err)); ++ goto out; ++ } ++ ++ _json_obj_get_value(ctx, j_obj, cur_json_major_version, ++ _DMMP_JSON_MAJOR_KEY, json_type_int, ++ json_object_get_int, rc, out); ++ ++ if (cur_json_major_version != _DMMP_JSON_MAJOR_VERSION) { ++ rc = DMMP_ERR_INCOMPATIBLE; ++ _error(ctx, "Incompatible multipathd JSON major version %d, " ++ "should be %d", cur_json_major_version, ++ _DMMP_JSON_MAJOR_VERSION); ++ goto out; ++ } ++ _debug(ctx, "multipathd JSON major version(%d) check pass", ++ _DMMP_JSON_MAJOR_VERSION); ++ ++ _json_obj_get_value(ctx, j_obj, ar_maps, _DMMP_JSON_MAPS_KEY, ++ json_type_array, json_object_get_array, rc, out); ++ ++ if (ar_maps == NULL) { ++ rc = DMMP_ERR_BUG; ++ _error(ctx, "BUG: Got NULL map array from " ++ "_json_obj_get_value()"); ++ goto out; ++ } ++ ++ ar_maps_len = array_list_length(ar_maps); ++ if (ar_maps_len < 0) { ++ rc = DMMP_ERR_BUG; ++ _error(ctx, "BUG: Got negative length for ar_maps"); ++ goto out; ++ } ++ else if (ar_maps_len == 0) ++ goto out; ++ else ++ *dmmp_mp_count = ar_maps_len & UINT32_MAX; ++ ++ *dmmp_mps = (struct dmmp_mpath **) ++ malloc(sizeof(struct dmmp_mpath *) * (*dmmp_mp_count)); ++ _dmmp_alloc_null_check(ctx, dmmp_mps, rc, out); ++ for (; i < *dmmp_mp_count; ++i) ++ (*dmmp_mps)[i] = NULL; ++ ++ for (i = 0; i < *dmmp_mp_count; ++i) { ++ j_obj_map = array_list_get_idx(ar_maps, i); ++ if (j_obj_map == NULL) { ++ rc = DMMP_ERR_BUG; ++ _error(ctx, "BUG: array_list_get_idx() return NULL"); ++ goto out; ++ } ++ ++ dmmp_mp = _dmmp_mpath_new(); ++ _dmmp_alloc_null_check(ctx, dmmp_mp, rc, out); ++ (*dmmp_mps)[i] = dmmp_mp; ++ _good(_dmmp_mpath_update(ctx, dmmp_mp, j_obj_map), rc, out); ++ } ++ ++out: ++ if (socket_fd >= 0) ++ mpath_disconnect(socket_fd); ++ free(j_str); ++ if (j_token != NULL) ++ json_tokener_free(j_token); ++ if (j_obj != NULL) ++ json_object_put(j_obj); ++ ++ if (rc != DMMP_OK) { ++ dmmp_mpath_array_free(*dmmp_mps, *dmmp_mp_count); ++ *dmmp_mps = NULL; ++ *dmmp_mp_count = 0; ++ } ++ ++ return rc; ++} +Index: multipath-tools-130222/libdmmp/libdmmp.pc.in +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/libdmmp.pc.in +@@ -0,0 +1,9 @@ ++includedir=__INCLUDEDIR__ ++libdir=__LIBDIR__ ++ ++Name: libdmmp ++Version: __VERSION__ ++Description: Device mapper multipath management library ++Requires: ++Libs: -L${libdir} -ldmmp ++Cflags: -I${includedir} +Index: multipath-tools-130222/libdmmp/libdmmp/libdmmp.h +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/libdmmp/libdmmp.h +@@ -0,0 +1,653 @@ ++/* ++ * Copyright (C) 2015 - 2017 Red Hat, Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author: Gris Ge ++ * Todd Gill ++ */ ++ ++ ++#ifndef _LIB_DMMP_H_ ++#define _LIB_DMMP_H_ ++ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define DMMP_DLL_EXPORT __attribute__ ((visibility ("default"))) ++#define DMMP_DLL_LOCAL __attribute__ ((visibility ("hidden"))) ++ ++#define DMMP_OK 0 ++#define DMMP_ERR_BUG 1 ++#define DMMP_ERR_NO_MEMORY 2 ++#define DMMP_ERR_IPC_TIMEOUT 3 ++#define DMMP_ERR_IPC_ERROR 4 ++#define DMMP_ERR_NO_DAEMON 5 ++#define DMMP_ERR_INCOMPATIBLE 6 ++ ++/* ++ * Use the syslog severity level as log priority ++ */ ++#define DMMP_LOG_PRIORITY_ERROR 3 ++#define DMMP_LOG_PRIORITY_WARNING 4 ++#define DMMP_LOG_PRIORITY_INFO 6 ++#define DMMP_LOG_PRIORITY_DEBUG 7 ++ ++#define DMMP_LOG_PRIORITY_DEFAULT DMMP_LOG_PRIORITY_WARNING ++ ++/** ++ * dmmp_log_priority_str() - Convert log priority to string. ++ * ++ * Convert log priority to string (const char *). ++ * ++ * @priority: ++ * int. Log priority. ++ * ++ * Return: ++ * const char *. Valid string are: ++ * ++ * * "ERROR" for DMMP_LOG_PRIORITY_ERROR ++ * ++ * * "WARN " for DMMP_LOG_PRIORITY_WARNING ++ * ++ * * "INFO " for DMMP_LOG_PRIORITY_INFO ++ * ++ * * "DEBUG" for DMMP_LOG_PRIORITY_DEBUG ++ * ++ * * "Invalid argument" for invalid log priority. ++ */ ++DMMP_DLL_EXPORT const char *dmmp_log_priority_str(int priority); ++ ++DMMP_DLL_EXPORT struct dmmp_context; ++ ++DMMP_DLL_EXPORT struct dmmp_mpath; ++ ++DMMP_DLL_EXPORT struct dmmp_path_group; ++ ++#define DMMP_PATH_GROUP_STATUS_UNKNOWN 0 ++#define DMMP_PATH_GROUP_STATUS_ENABLED 1 ++#define DMMP_PATH_GROUP_STATUS_DISABLED 2 ++#define DMMP_PATH_GROUP_STATUS_ACTIVE 3 ++ ++DMMP_DLL_EXPORT struct dmmp_path; ++ ++#define DMMP_PATH_STATUS_UNKNOWN 0 ++//#define DMMP_PATH_STATUS_UNCHECKED 1 ++// ^ print.h does not expose this. ++#define DMMP_PATH_STATUS_DOWN 2 ++#define DMMP_PATH_STATUS_UP 3 ++#define DMMP_PATH_STATUS_SHAKY 4 ++#define DMMP_PATH_STATUS_GHOST 5 ++#define DMMP_PATH_STATUS_PENDING 6 ++#define DMMP_PATH_STATUS_TIMEOUT 7 ++//#define DMMP_PATH_STATUS_REMOVED 8 ++// ^ print.h does not expose this. ++#define DMMP_PATH_STATUS_DELAYED 9 ++ ++/** ++ * dmmp_strerror() - Convert error code to string. ++ * ++ * Convert error code (int) to string (const char *): ++ * ++ * * DMMP_OK -- "OK" ++ * ++ * * DMMP_ERR_BUG -- "BUG of libdmmp library" ++ * ++ * * DMMP_ERR_NO_MEMORY -- "Out of memory" ++ * ++ * * DMMP_ERR_IPC_TIMEOUT -- "Timeout when communicate with multipathd, ++ * try to set bigger timeout value via dmmp_context_timeout_set ()" ++ * ++ * * DMMP_ERR_IPC_ERROR -- "Error when communicate with multipathd daemon" ++ * ++ * * DMMP_ERR_NO_DAEMON -- "The multipathd daemon not started" ++ * ++ * * DMMP_ERR_INCOMPATIBLE -- "The multipathd daemon version is not ++ * compatible with current library" ++ * ++ * * Other invalid error number -- "Invalid argument" ++ * ++ * @rc: ++ * int. Return code by libdmmp functions. When provided error code is not a ++ * valid error code, return "Invalid argument". ++ * ++ * Return: ++ * const char *. The meaning of provided error code. ++ * ++ */ ++DMMP_DLL_EXPORT const char *dmmp_strerror(int rc); ++ ++/** ++ * dmmp_context_new() - Create struct dmmp_context. ++ * ++ * The default logging level (DMMP_LOG_PRIORITY_DEFAULT) is ++ * DMMP_LOG_PRIORITY_WARNING which means only warning and error message will be ++ * forward to log handler function. The default log handler function will print ++ * log message to STDERR, to change so, please use dmmp_context_log_func_set() ++ * to set your own log handler, check manpage libdmmp.h(3) for detail. ++ * ++ * Return: ++ * Pointer of 'struct dmmp_context'. Should be freed by ++ * dmmp_context_free(). ++ */ ++DMMP_DLL_EXPORT struct dmmp_context *dmmp_context_new(void); ++ ++/** ++ * dmmp_context_free() - Release the memory of struct dmmp_context. ++ * ++ * Release the memory of struct dmmp_context, but the userdata memory defined ++ * via dmmp_context_userdata_set() will not be touched. ++ * ++ * @ctx: ++ * Pointer of 'struct dmmp_context'. ++ * Return: ++ * void ++ */ ++DMMP_DLL_EXPORT void dmmp_context_free(struct dmmp_context *ctx); ++ ++/** ++ * dmmp_context_timeout_set() - Set IPC timeout. ++ * ++ * By default, the IPC to multipathd daemon will timeout after 60 seconds. ++ * ++ * @ctx: ++ * Pointer of 'struct dmmp_context'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * @tmo: ++ * Timeout in milliseconds(1 seconds equal 1000 milliseconds). ++ * ++ * Return: ++ * void ++ */ ++DMMP_DLL_EXPORT void dmmp_context_timeout_set(struct dmmp_context *ctx, ++ unsigned int tmo); ++ ++/** ++ * dmmp_context_timeout_get() - Get IPC timeout. ++ * ++ * Retrieve timeout value of IPC connection to multipathd daemon. ++ * ++ * @ctx: ++ * Pointer of 'struct dmmp_context'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * unsigned int. Timeout in milliseconds. ++ */ ++DMMP_DLL_EXPORT unsigned int dmmp_context_timeout_get(struct dmmp_context *ctx); ++ ++/** ++ * dmmp_context_log_priority_set() - Set log priority. ++ * ++ * ++ * When library generates log message, only equal or more important(less value) ++ * message will be forwarded to log handler function. Valid log priority values ++ * are: ++ * ++ * * DMMP_LOG_PRIORITY_ERROR -- 3 ++ * ++ * * DMMP_LOG_PRIORITY_WARNING -- 4 ++ * ++ * * DMMP_LOG_PRIORITY_INFO -- 5 ++ * ++ * * DMMP_LOG_PRIORITY_DEBUG -- 7 ++ * ++ * @ctx: ++ * Pointer of 'struct dmmp_context'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * @priority: ++ * int, log priority. ++ * ++ * Return: ++ * void ++ */ ++DMMP_DLL_EXPORT void dmmp_context_log_priority_set(struct dmmp_context *ctx, ++ int priority); ++ ++/** ++ * dmmp_context_log_priority_get() - Get log priority. ++ * ++ * Retrieve current log priority. Valid log priority values are: ++ * ++ * * DMMP_LOG_PRIORITY_ERROR -- 3 ++ * ++ * * DMMP_LOG_PRIORITY_WARNING -- 4 ++ * ++ * * DMMP_LOG_PRIORITY_INFO -- 5 ++ * ++ * * DMMP_LOG_PRIORITY_DEBUG -- 7 ++ * ++ * @ctx: ++ * Pointer of 'struct dmmp_context'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * int, log priority. ++ */ ++DMMP_DLL_EXPORT int dmmp_context_log_priority_get(struct dmmp_context *ctx); ++ ++/** ++ * dmmp_context_log_func_set() - Set log handler function. ++ * ++ * Set custom log handler. The log handler will be invoked when log message ++ * is equal or more important(less value) than log priority setting. ++ * Please check manpage libdmmp.h(3) for detail usage. ++ * ++ * @ctx: ++ * Pointer of 'struct dmmp_context'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * @log_func: ++ * Pointer of log handler function. ++ * ++ * Return: ++ * void ++ */ ++DMMP_DLL_EXPORT void dmmp_context_log_func_set ++ (struct dmmp_context *ctx, ++ void (*log_func) ++ (struct dmmp_context *ctx, int priority, ++ const char *file, int line, const char *func_name, ++ const char *format, va_list args)); ++ ++/** ++ * dmmp_context_userdata_set() - Set user data pointer. ++ * ++ * Store user data pointer into 'struct dmmp_context'. ++ * ++ * @ctx: ++ * Pointer of 'struct dmmp_context'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * @userdata: ++ * Pointer of user defined data. ++ * ++ * Return: ++ * void ++ */ ++DMMP_DLL_EXPORT void dmmp_context_userdata_set(struct dmmp_context *ctx, ++ void *userdata); ++ ++/** ++ * dmmp_context_userdata_get() - Get user data pointer. ++ * ++ * Retrieve user data pointer from 'struct dmmp_context'. ++ * ++ * @ctx: ++ * Pointer of 'struct dmmp_context'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * void *. Pointer of user defined data. ++ */ ++DMMP_DLL_EXPORT void *dmmp_context_userdata_get(struct dmmp_context *ctx); ++ ++/** ++ * dmmp_mpath_array_get() - Query all existing multipath devices. ++ * ++ * Query all existing multipath devices and store them into a pointer array. ++ * The memory of 'dmmp_mps' should be freed via dmmp_mpath_array_free(). ++ * ++ * @ctx: ++ * Pointer of 'struct dmmp_context'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * @dmmp_mps: ++ * Output pointer array of 'struct dmmp_mpath'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * @dmmp_mp_count: ++ * Output pointer of uint32_t. Hold the size of 'dmmp_mps' pointer array. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * int. Valid error codes are: ++ * ++ * * DMMP_OK ++ * ++ * * DMMP_ERR_BUG ++ * ++ * * DMMP_ERR_NO_MEMORY ++ * ++ * * DMMP_ERR_NO_DAEMON ++ * ++ * * DMMP_ERR_INCONSISTENT_DATA ++ * ++ * Error number could be converted to string by dmmp_strerror(). ++ */ ++DMMP_DLL_EXPORT int dmmp_mpath_array_get(struct dmmp_context *ctx, ++ struct dmmp_mpath ***dmmp_mps, ++ uint32_t *dmmp_mp_count); ++ ++/** ++ * dmmp_mpath_array_free() - Free 'struct dmmp_mpath' pointer array. ++ * ++ * Free the 'dmmp_mps' pointer array generated by dmmp_mpath_array_get(). ++ * If provided 'dmmp_mps' pointer is NULL or dmmp_mp_count == 0, do nothing. ++ * ++ * @dmmp_mps: ++ * Pointer of 'struct dmmp_mpath' array. ++ * @dmmp_mp_count: ++ * uint32_t, the size of 'dmmp_mps' pointer array. ++ * ++ * Return: ++ * void ++ */ ++DMMP_DLL_EXPORT void dmmp_mpath_array_free(struct dmmp_mpath **dmmp_mps, ++ uint32_t dmmp_mp_count); ++ ++/** ++ * dmmp_mpath_wwid_get() - Retrieve WWID of certain mpath. ++ * ++ * @dmmp_mp: ++ * Pointer of 'struct dmmp_mpath'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * const char *. No need to free this memory, the resources will get ++ * freed when dmmp_mpath_array_free(). ++ */ ++DMMP_DLL_EXPORT const char *dmmp_mpath_wwid_get(struct dmmp_mpath *dmmp_mp); ++ ++/** ++ * dmmp_mpath_name_get() - Retrieve name(alias) of certain mpath. ++ * ++ * Retrieve the name (also known as alias) of certain mpath. ++ * When the config 'user_friendly_names' been set 'no', the name will be ++ * identical to WWID retrieved by dmmp_mpath_wwid_get(). ++ * ++ * @dmmp_mp: ++ * Pointer of 'struct dmmp_mpath'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * const char *. No need to free this memory, the resources will get ++ * freed when dmmp_mpath_array_free(). ++ */ ++DMMP_DLL_EXPORT const char *dmmp_mpath_name_get(struct dmmp_mpath *dmmp_mp); ++ ++/** ++ * dmmp_mpath_kdev_name_get() - Retrieve kernel DEVNAME of certain mpath. ++ * ++ * Retrieve DEVNAME name used by kernel uevent of specified mpath. ++ * Example: 'dm-1'. ++ * ++ * @dmmp_mp: ++ * Pointer of 'struct dmmp_mpath'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * const char *. No need to free this memory, the resources will get ++ * freed when dmmp_mpath_array_free(). ++ */ ++DMMP_DLL_EXPORT const char *dmmp_mpath_kdev_name_get ++ (struct dmmp_mpath *dmmp_mp); ++ ++/** ++ * dmmp_path_group_array_get() - Retrieve path groups pointer array. ++ * ++ * Retrieve the path groups of certain mpath. ++ * ++ * The memory of output pointer array is hold by 'struct dmmp_mpath', no ++ * need to free this memory, the resources will got freed when ++ * dmmp_mpath_array_free(). ++ * ++ * @dmmp_mp: ++ * Pointer of 'struct dmmp_mpath'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * @dmmp_pgs: ++ * Output pointer of 'struct dmmp_path_group' pointer array. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * @dmmp_pg_count: ++ * Output pointer of uint32_t. Hold the size of 'dmmp_pgs' pointer array. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * void ++ */ ++DMMP_DLL_EXPORT void dmmp_path_group_array_get ++ (struct dmmp_mpath *dmmp_mp, struct dmmp_path_group ***dmmp_pgs, ++ uint32_t *dmmp_pg_count); ++ ++/** ++ * dmmp_path_group_id_get() - Retrieve path group ID. ++ * ++ * Retrieve the path group ID which could be used to switch active path group ++ * via command: ++ * ++ * multipathd -k'switch multipath mpathb group $id' ++ * ++ * @dmmp_pg: ++ * Pointer of 'struct dmmp_path_group'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * uint32_t. ++ */ ++DMMP_DLL_EXPORT uint32_t dmmp_path_group_id_get ++ (struct dmmp_path_group *dmmp_pg); ++ ++/** ++ * dmmp_path_group_priority_get() - Retrieve path group priority. ++ * ++ * The enabled path group with highest priority will be next active path group ++ * if active path group down. ++ * ++ * @dmmp_pg: ++ * Pointer of 'struct dmmp_path_group'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * uint32_t. ++ */ ++DMMP_DLL_EXPORT uint32_t dmmp_path_group_priority_get ++ (struct dmmp_path_group *dmmp_pg); ++ ++/** ++ * dmmp_path_group_status_get() - Retrieve path group status. ++ * ++ * The valid path group statuses are: ++ * ++ * * DMMP_PATH_GROUP_STATUS_UNKNOWN ++ * ++ * * DMMP_PATH_GROUP_STATUS_ENABLED -- standby to be active ++ * ++ * * DMMP_PATH_GROUP_STATUS_DISABLED -- disabled due to all path down ++ * ++ * * DMMP_PATH_GROUP_STATUS_ACTIVE -- selected to handle I/O ++ * ++ * @dmmp_pg: ++ * Pointer of 'struct dmmp_path_group'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * uint32_t. ++ */ ++DMMP_DLL_EXPORT uint32_t dmmp_path_group_status_get ++ (struct dmmp_path_group *dmmp_pg); ++ ++/** ++ * dmmp_path_group_status_str() - Convert path group status to string. ++ * ++ * Convert path group status uint32_t to string (const char *). ++ * ++ * @pg_status: ++ * uint32_t. Path group status. ++ * When provided value is not a valid path group status, return "Invalid ++ * argument". ++ * ++ * Return: ++ * const char *. Valid string are: ++ * ++ * * "Invalid argument" ++ * ++ * * "undef" ++ * ++ * * "enabled" ++ * ++ * * "disabled" ++ * ++ * * "active" ++ */ ++DMMP_DLL_EXPORT const char *dmmp_path_group_status_str(uint32_t pg_status); ++ ++/** ++ * dmmp_path_group_selector_get() - Retrieve path group selector. ++ * ++ * Path group selector determine which path in active path group will be ++ * use to next I/O. ++ * ++ * @dmmp_pg: ++ * Pointer of 'struct dmmp_path_group'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * const char *. ++ */ ++DMMP_DLL_EXPORT const char *dmmp_path_group_selector_get ++ (struct dmmp_path_group *dmmp_pg); ++ ++/** ++ * dmmp_path_array_get() - Retrieve path pointer array. ++ * ++ * The memory of output pointer array is hold by 'struct dmmp_mpath', no ++ * need to free this memory, the resources will got freed when ++ * dmmp_mpath_array_free(). ++ * ++ * @dmmp_pg: ++ * Pointer of 'struct dmmp_path_group'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * @dmmp_ps: ++ * Output pointer of 'struct dmmp_path' pointer array. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * @dmmp_p_count: ++ * Output pointer of uint32_t. Hold the size of 'dmmp_ps' pointer array. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * void ++ */ ++DMMP_DLL_EXPORT void dmmp_path_array_get(struct dmmp_path_group *dmmp_pg, ++ struct dmmp_path ***dmmp_ps, ++ uint32_t *dmmp_p_count); ++ ++/** ++ * dmmp_path_blk_name_get() - Retrieve block name. ++ * ++ * Retrieve block name of certain path. The example of block names are 'sda', ++ * 'nvme0n1'. ++ * ++ * @dmmp_p: ++ * Pointer of 'struct dmmp_path'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * const char *. No need to free this memory, the resources will get ++ * freed when dmmp_mpath_array_free(). ++ */ ++DMMP_DLL_EXPORT const char *dmmp_path_blk_name_get(struct dmmp_path *dmmp_p); ++ ++/** ++ * dmmp_path_status_get() - Retrieve the path status. ++ * ++ * The valid path statuses are: ++ * ++ * * DMMP_PATH_STATUS_UNKNOWN ++ * ++ * * DMMP_PATH_STATUS_DOWN ++ * ++ * Path is down and you shouldn't try to send commands to it. ++ * ++ * * DMMP_PATH_STATUS_UP ++ * ++ * Path is up and I/O can be sent to it. ++ * ++ * * DMMP_PATH_STATUS_SHAKY ++ * ++ * Only emc_clariion checker when path not available for "normal" ++ * operations. ++ * ++ * * DMMP_PATH_STATUS_GHOST ++ * ++ * Only hp_sw and rdac checkers. Indicates a "passive/standby" ++ * path on active/passive HP arrays. These paths will return valid ++ * answers to certain SCSI commands (tur, read_capacity, inquiry, ++ * start_stop), but will fail I/O commands. The path needs an ++ * initialization command to be sent to it in order for I/Os to ++ * succeed. ++ * ++ * * DMMP_PATH_STATUS_PENDING ++ * ++ * Available for all async checkers when a check IO is in flight. ++ * ++ * * DMMP_PATH_STATUS_TIMEOUT ++ * ++ * Only tur checker when command timed out. ++ * ++ * * DMMP_PATH_STATUS_DELAYED ++ * ++ * If a path fails after being up for less than delay_watch_checks checks, ++ * when it comes back up again, it will not be marked as up until it has ++ * been up for delay_wait_checks checks. During this time, it is marked as ++ * "delayed". ++ * ++ * @dmmp_p: ++ * Pointer of 'struct dmmp_path'. ++ * If this pointer is NULL, your program will be terminated by assert. ++ * ++ * Return: ++ * uint32_t. ++ */ ++DMMP_DLL_EXPORT uint32_t dmmp_path_status_get(struct dmmp_path *dmmp_p); ++ ++/** ++ * dmmp_path_status_str() - Convert path status to string. ++ * ++ * Convert path status uint32_t to string (const char *): ++ * ++ * * DMMP_PATH_STATUS_UNKNOWN -- "undef" ++ * ++ * * DMMP_PATH_STATUS_DOWN -- "faulty" ++ * ++ * * DMMP_PATH_STATUS_UP -- "ready" ++ * ++ * * DMMP_PATH_STATUS_SHAKY -- "shaky" ++ * ++ * * DMMP_PATH_STATUS_GHOST -- "ghost" ++ * ++ * * DMMP_PATH_STATUS_PENDING -- "pending" ++ * ++ * * DMMP_PATH_STATUS_TIMEOUT -- "timeout" ++ * ++ * * DMMP_PATH_STATUS_REMOVED -- "removed" ++ * ++ * * DMMP_PATH_STATUS_DELAYED -- "delayed" ++ * ++ * @path_status: ++ * uint32_t. Path status. ++ * When provided value is not a valid path status, return ++ * "Invalid argument". ++ * ++ * Return: ++ * const char *. The meaning of status value. ++ */ ++DMMP_DLL_EXPORT const char *dmmp_path_status_str(uint32_t path_status); ++ ++#ifdef __cplusplus ++} /* End of extern "C" */ ++#endif ++ ++#endif /* End of _LIB_DMMP_H_ */ +Index: multipath-tools-130222/libdmmp/libdmmp_misc.c +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/libdmmp_misc.c +@@ -0,0 +1,87 @@ ++/* ++ * Copyright (C) 2015 - 2016 Red Hat, Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author: Gris Ge ++ * Todd Gill ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "libdmmp/libdmmp.h" ++#include "libdmmp_private.h" ++ ++#define _DMMP_LOG_STRERR_ALIGN_WIDTH 80 ++/* ^ Only used in _dmmp_log_stderr() for pretty log output. ++ * When provided log message is less than 80 bytes, fill it with space, then ++ * print code file name, function name, line after the 80th bytes. ++ */ ++ ++static const struct _num_str_conv _DMMP_RC_MSG_CONV[] = { ++ {DMMP_OK, "OK"}, ++ {DMMP_ERR_NO_MEMORY, "Out of memory"}, ++ {DMMP_ERR_BUG, "BUG of libdmmp library"}, ++ {DMMP_ERR_IPC_TIMEOUT, "Timeout when communicate with multipathd, " ++ "try to increase it via " ++ "dmmp_context_timeout_set()"}, ++ {DMMP_ERR_IPC_ERROR, "Error when communicate with multipathd daemon"}, ++ {DMMP_ERR_NO_DAEMON, "The multipathd daemon not started"}, ++ {DMMP_ERR_INCOMPATIBLE, "Incompatible multipathd daemon version"}, ++}; ++ ++_dmmp_str_func_gen(dmmp_strerror, int, rc, _DMMP_RC_MSG_CONV); ++ ++static const struct _num_str_conv _DMMP_PRI_CONV[] = { ++ {DMMP_LOG_PRIORITY_DEBUG, "DEBUG"}, ++ {DMMP_LOG_PRIORITY_INFO, "INFO"}, ++ {DMMP_LOG_PRIORITY_WARNING, "WARNING"}, ++ {DMMP_LOG_PRIORITY_ERROR, "ERROR"}, ++}; ++_dmmp_str_func_gen(dmmp_log_priority_str, int, priority, _DMMP_PRI_CONV); ++ ++void _dmmp_log_stderr(struct dmmp_context *ctx, int priority, ++ const char *file, int line, const char *func_name, ++ const char *format, va_list args) ++{ ++ int printed_bytes = 0; ++ void *userdata = NULL; ++ ++ printed_bytes += fprintf(stderr, "libdmmp %s: ", ++ dmmp_log_priority_str(priority)); ++ printed_bytes += vfprintf(stderr, format, args); ++ ++ userdata = dmmp_context_userdata_get(ctx); ++ if (userdata != NULL) ++ fprintf(stderr, "(userdata address: %p)", ++ userdata); ++ /* ^ Just demonstrate how userdata could be used and ++ * bypass clang static analyzer about unused ctx argument warning ++ */ ++ ++ if (printed_bytes < _DMMP_LOG_STRERR_ALIGN_WIDTH) { ++ fprintf(stderr, "%*s # %s:%s():%d\n", ++ _DMMP_LOG_STRERR_ALIGN_WIDTH - printed_bytes, "", file, ++ func_name, line); ++ } else { ++ fprintf(stderr, " # %s:%s():%d\n", file, func_name, line); ++ } ++} +Index: multipath-tools-130222/libdmmp/libdmmp_mp.c +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/libdmmp_mp.c +@@ -0,0 +1,159 @@ ++/* ++ * Copyright (C) 2015 - 2016 Red Hat, Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author: Gris Ge ++ * Todd Gill ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "libdmmp/libdmmp.h" ++#include "libdmmp_private.h" ++ ++struct dmmp_mpath { ++ char *wwid; ++ char *alias; ++ uint32_t dmmp_pg_count; ++ struct dmmp_path_group **dmmp_pgs; ++ char *kdev_name; ++}; ++ ++_dmmp_getter_func_gen(dmmp_mpath_name_get, struct dmmp_mpath, dmmp_mp, ++ alias, const char *); ++_dmmp_getter_func_gen(dmmp_mpath_wwid_get, struct dmmp_mpath, dmmp_mp, ++ wwid, const char *); ++_dmmp_getter_func_gen(dmmp_mpath_kdev_name_get, struct dmmp_mpath, dmmp_mp, ++ kdev_name, const char *); ++ ++struct dmmp_mpath *_dmmp_mpath_new(void) ++{ ++ struct dmmp_mpath *dmmp_mp = NULL; ++ ++ dmmp_mp = (struct dmmp_mpath *) malloc(sizeof(struct dmmp_mpath)); ++ ++ if (dmmp_mp != NULL) { ++ dmmp_mp->wwid = NULL; ++ dmmp_mp->alias = NULL; ++ dmmp_mp->dmmp_pg_count = 0; ++ dmmp_mp->dmmp_pgs = NULL; ++ } ++ return dmmp_mp; ++} ++ ++int _dmmp_mpath_update(struct dmmp_context *ctx, struct dmmp_mpath *dmmp_mp, ++ json_object *j_obj_map) ++{ ++ int rc = DMMP_OK; ++ const char *wwid = NULL; ++ const char *alias = NULL; ++ struct array_list *ar_pgs = NULL; ++ int ar_pgs_len = -1; ++ uint32_t i = 0; ++ struct dmmp_path_group *dmmp_pg = NULL; ++ const char *kdev_name = NULL; ++ ++ assert(ctx != NULL); ++ assert(dmmp_mp != NULL); ++ assert(j_obj_map != NULL); ++ ++ _json_obj_get_value(ctx, j_obj_map, wwid, "uuid", json_type_string, ++ json_object_get_string, rc, out); ++ _json_obj_get_value(ctx, j_obj_map, alias, "name", json_type_string, ++ json_object_get_string, rc, out); ++ _json_obj_get_value(ctx, j_obj_map, kdev_name, "sysfs", ++ json_type_string, json_object_get_string, rc, out); ++ ++ _dmmp_null_or_empty_str_check(ctx, wwid, rc, out); ++ _dmmp_null_or_empty_str_check(ctx, alias, rc, out); ++ ++ dmmp_mp->wwid = strdup(wwid); ++ _dmmp_alloc_null_check(ctx, dmmp_mp->wwid, rc, out); ++ dmmp_mp->alias = strdup(alias); ++ _dmmp_alloc_null_check(ctx, dmmp_mp->alias, rc, out); ++ dmmp_mp->kdev_name = strdup(kdev_name); ++ _dmmp_alloc_null_check(ctx, dmmp_mp->kdev_name, rc, out); ++ ++ _json_obj_get_value(ctx, j_obj_map, ar_pgs, "path_groups", ++ json_type_array, json_object_get_array, rc, out); ++ ar_pgs_len = array_list_length(ar_pgs); ++ if (ar_pgs_len < 0) { ++ rc = DMMP_ERR_BUG; ++ _error(ctx, "BUG: Got negative length for ar_pgs"); ++ goto out; ++ } ++ else if (ar_pgs_len == 0) ++ goto out; ++ else ++ dmmp_mp->dmmp_pg_count = ar_pgs_len & UINT32_MAX; ++ ++ dmmp_mp->dmmp_pgs = (struct dmmp_path_group **) ++ malloc(sizeof(struct dmmp_path_group *) * ++ dmmp_mp->dmmp_pg_count); ++ _dmmp_alloc_null_check(ctx, dmmp_mp->dmmp_pgs, rc, out); ++ for (; i < dmmp_mp->dmmp_pg_count; ++i) ++ dmmp_mp->dmmp_pgs[i] = NULL; ++ ++ for (i = 0; i < dmmp_mp->dmmp_pg_count; ++i) { ++ dmmp_pg = _dmmp_path_group_new(); ++ _dmmp_alloc_null_check(ctx, dmmp_pg, rc, out); ++ dmmp_mp->dmmp_pgs[i] = dmmp_pg; ++ _good(_dmmp_path_group_update(ctx, dmmp_pg, ++ array_list_get_idx(ar_pgs, i)), ++ rc, out); ++ } ++ ++ _debug(ctx, "Got mpath wwid: '%s', alias: '%s'", dmmp_mp->wwid, ++ dmmp_mp->alias); ++ ++out: ++ if (rc != DMMP_OK) ++ _dmmp_mpath_free(dmmp_mp); ++ return rc; ++} ++ ++void _dmmp_mpath_free(struct dmmp_mpath *dmmp_mp) ++{ ++ if (dmmp_mp == NULL) ++ return ; ++ ++ free((char *) dmmp_mp->alias); ++ free((char *) dmmp_mp->wwid); ++ free((char *) dmmp_mp->kdev_name); ++ ++ if (dmmp_mp->dmmp_pgs != NULL) ++ _dmmp_path_group_array_free(dmmp_mp->dmmp_pgs, ++ dmmp_mp->dmmp_pg_count); ++ ++ free(dmmp_mp); ++} ++ ++void dmmp_path_group_array_get(struct dmmp_mpath *dmmp_mp, ++ struct dmmp_path_group ***dmmp_pgs, ++ uint32_t *dmmp_pg_count) ++{ ++ assert(dmmp_mp != NULL); ++ assert(dmmp_pgs != NULL); ++ assert(dmmp_pg_count != NULL); ++ ++ *dmmp_pgs = dmmp_mp->dmmp_pgs; ++ *dmmp_pg_count = dmmp_mp->dmmp_pg_count; ++} +Index: multipath-tools-130222/libdmmp/libdmmp_path.c +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/libdmmp_path.c +@@ -0,0 +1,115 @@ ++/* ++ * Copyright (C) 2015 - 2016 Red Hat, Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author: Gris Ge ++ * Todd Gill ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "libdmmp/libdmmp.h" ++#include "libdmmp_private.h" ++ ++#define _DMMP_SHOW_PS_INDEX_BLK_NAME 0 ++#define _DMMP_SHOW_PS_INDEX_SATAUS 1 ++#define _DMMP_SHOW_PS_INDEX_WWID 2 ++#define _DMMP_SHOW_PS_INDEX_PGID 3 ++ ++struct dmmp_path { ++ char *blk_name; ++ uint32_t status; ++}; ++ ++static const struct _num_str_conv _DMMP_PATH_STATUS_CONV[] = { ++ {DMMP_PATH_STATUS_UNKNOWN, "undef"}, ++ {DMMP_PATH_STATUS_UP, "ready"}, ++ {DMMP_PATH_STATUS_DOWN, "faulty"}, ++ {DMMP_PATH_STATUS_SHAKY, "shaky"}, ++ {DMMP_PATH_STATUS_GHOST, "ghost"}, ++ {DMMP_PATH_STATUS_PENDING, "i/o pending"}, ++ {DMMP_PATH_STATUS_TIMEOUT, "i/o timeout"}, ++ {DMMP_PATH_STATUS_DELAYED, "delayed"}, ++}; ++ ++_dmmp_str_func_gen(dmmp_path_status_str, uint32_t, path_status, ++ _DMMP_PATH_STATUS_CONV); ++_dmmp_str_conv_func_gen(_dmmp_path_status_str_conv, ctx, path_status_str, ++ uint32_t, DMMP_PATH_STATUS_UNKNOWN, ++ _DMMP_PATH_STATUS_CONV); ++ ++_dmmp_getter_func_gen(dmmp_path_blk_name_get, struct dmmp_path, dmmp_p, ++ blk_name, const char *); ++_dmmp_getter_func_gen(dmmp_path_status_get, struct dmmp_path, dmmp_p, ++ status, uint32_t); ++ ++struct dmmp_path *_dmmp_path_new(void) ++{ ++ struct dmmp_path *dmmp_p = NULL; ++ ++ dmmp_p = (struct dmmp_path *) malloc(sizeof(struct dmmp_path)); ++ ++ if (dmmp_p != NULL) { ++ dmmp_p->blk_name = NULL; ++ dmmp_p->status = DMMP_PATH_STATUS_UNKNOWN; ++ } ++ return dmmp_p; ++} ++ ++int _dmmp_path_update(struct dmmp_context *ctx, struct dmmp_path *dmmp_p, ++ json_object *j_obj_p) ++{ ++ int rc = DMMP_OK; ++ const char *blk_name = NULL; ++ const char *status_str = NULL; ++ ++ assert(ctx != NULL); ++ assert(dmmp_p != NULL); ++ assert(j_obj_p != NULL); ++ ++ _json_obj_get_value(ctx, j_obj_p, blk_name, "dev", ++ json_type_string, json_object_get_string, rc, out); ++ _json_obj_get_value(ctx, j_obj_p, status_str, "chk_st", ++ json_type_string, json_object_get_string, rc, out); ++ ++ _dmmp_null_or_empty_str_check(ctx, blk_name, rc, out); ++ _dmmp_null_or_empty_str_check(ctx, status_str, rc, out); ++ ++ dmmp_p->blk_name = strdup(blk_name); ++ _dmmp_alloc_null_check(ctx, dmmp_p->blk_name, rc, out); ++ ++ dmmp_p->status = _dmmp_path_status_str_conv(ctx, status_str); ++ ++ _debug(ctx, "Got path blk_name: '%s'", dmmp_p->blk_name); ++ _debug(ctx, "Got path status: %s(%" PRIu32 ")", ++ dmmp_path_status_str(dmmp_p->status), dmmp_p->status); ++ ++out: ++ if (rc != DMMP_OK) ++ _dmmp_path_free(dmmp_p); ++ return rc; ++} ++ ++void _dmmp_path_free(struct dmmp_path *dmmp_p) ++{ ++ if (dmmp_p == NULL) ++ return; ++ free(dmmp_p->blk_name); ++ free(dmmp_p); ++} +Index: multipath-tools-130222/libdmmp/libdmmp_pg.c +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/libdmmp_pg.c +@@ -0,0 +1,208 @@ ++/* ++ * Copyright (C) 2015 - 2016 Red Hat, Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author: Gris Ge ++ * Todd Gill ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "libdmmp/libdmmp.h" ++#include "libdmmp_private.h" ++ ++#define _DMMP_SHOW_PGS_CMD "show groups raw format %w|%g|%p|%t|%s" ++#define _DMMP_SHOW_PG_INDEX_WWID 0 ++#define _DMMP_SHOW_PG_INDEX_PG_ID 1 ++#define _DMMP_SHOW_PG_INDEX_PRI 2 ++#define _DMMP_SHOW_PG_INDEX_STATUS 3 ++#define _DMMP_SHOW_PG_INDEX_SELECTOR 4 ++ ++struct dmmp_path_group { ++ uint32_t id; ++ /* ^ pgindex of struct path, will be used for path group switch */ ++ uint32_t status; ++ uint32_t priority; ++ char *selector; ++ uint32_t dmmp_p_count; ++ struct dmmp_path **dmmp_ps; ++}; ++ ++static const struct _num_str_conv _DMMP_PATH_GROUP_STATUS_CONV[] = { ++ {DMMP_PATH_GROUP_STATUS_UNKNOWN, "undef"}, ++ {DMMP_PATH_GROUP_STATUS_ACTIVE, "active"}, ++ {DMMP_PATH_GROUP_STATUS_DISABLED, "disabled"}, ++ {DMMP_PATH_GROUP_STATUS_ENABLED, "enabled"}, ++}; ++ ++_dmmp_str_func_gen(dmmp_path_group_status_str, uint32_t, pg_status, ++ _DMMP_PATH_GROUP_STATUS_CONV); ++_dmmp_str_conv_func_gen(_dmmp_path_group_status_str_conv, ctx, pg_status_str, ++ uint32_t, DMMP_PATH_GROUP_STATUS_UNKNOWN, ++ _DMMP_PATH_GROUP_STATUS_CONV); ++ ++_dmmp_getter_func_gen(dmmp_path_group_id_get, struct dmmp_path_group, dmmp_pg, ++ id, uint32_t); ++_dmmp_getter_func_gen(dmmp_path_group_status_get, struct dmmp_path_group, ++ dmmp_pg, status, uint32_t); ++_dmmp_getter_func_gen(dmmp_path_group_priority_get, struct dmmp_path_group, ++ dmmp_pg, priority, uint32_t); ++_dmmp_getter_func_gen(dmmp_path_group_selector_get, struct dmmp_path_group, ++ dmmp_pg, selector, const char *); ++_dmmp_array_free_func_gen(_dmmp_path_group_array_free, struct dmmp_path_group, ++ _dmmp_path_group_free); ++ ++ ++struct dmmp_path_group *_dmmp_path_group_new(void) ++{ ++ struct dmmp_path_group *dmmp_pg = NULL; ++ ++ dmmp_pg = (struct dmmp_path_group *) ++ malloc(sizeof(struct dmmp_path_group)); ++ ++ if (dmmp_pg != NULL) { ++ dmmp_pg->id = _DMMP_PATH_GROUP_ID_UNKNOWN; ++ dmmp_pg->status = DMMP_PATH_GROUP_STATUS_UNKNOWN; ++ dmmp_pg->priority = 0; ++ dmmp_pg->selector = NULL; ++ dmmp_pg->dmmp_p_count = 0; ++ dmmp_pg->dmmp_ps = NULL; ++ } ++ return dmmp_pg; ++} ++int _dmmp_path_group_update(struct dmmp_context *ctx, ++ struct dmmp_path_group *dmmp_pg, ++ json_object *j_obj_pg) ++{ ++ int rc = DMMP_OK; ++ uint32_t id = 0; ++ int priority_int = -1 ; ++ const char *status_str = NULL; ++ const char *selector = NULL; ++ struct array_list *ar_ps = NULL; ++ int ar_ps_len = -1; ++ uint32_t i = 0; ++ struct dmmp_path *dmmp_p = NULL; ++ ++ assert(ctx != NULL); ++ assert(dmmp_pg != NULL); ++ assert(j_obj_pg != NULL); ++ ++ _json_obj_get_value(ctx, j_obj_pg, status_str, "dm_st", ++ json_type_string, json_object_get_string, rc, out); ++ ++ _json_obj_get_value(ctx, j_obj_pg, selector, "selector", ++ json_type_string, json_object_get_string, rc, out); ++ ++ _json_obj_get_value(ctx, j_obj_pg, priority_int, "pri", ++ json_type_int, json_object_get_int, rc, out); ++ ++ _json_obj_get_value(ctx, j_obj_pg, id, "group", ++ json_type_int, json_object_get_int, rc, out); ++ ++ dmmp_pg->priority = (priority_int <= 0) ? 0 : priority_int & UINT32_MAX; ++ ++ _dmmp_null_or_empty_str_check(ctx, status_str, rc, out); ++ _dmmp_null_or_empty_str_check(ctx, selector, rc, out); ++ ++ dmmp_pg->selector = strdup(selector); ++ _dmmp_alloc_null_check(ctx, dmmp_pg->selector, rc, out); ++ ++ dmmp_pg->id = id; ++ ++ if (dmmp_pg->id == _DMMP_PATH_GROUP_ID_UNKNOWN) { ++ rc = DMMP_ERR_BUG; ++ _error(ctx, "BUG: Got unknown(%d) path group ID", ++ _DMMP_PATH_GROUP_ID_UNKNOWN); ++ goto out; ++ } ++ ++ dmmp_pg->status = _dmmp_path_group_status_str_conv(ctx, status_str); ++ ++ _json_obj_get_value(ctx, j_obj_pg, ar_ps, "paths", ++ json_type_array, json_object_get_array, rc, out); ++ ++ ar_ps_len = array_list_length(ar_ps); ++ if (ar_ps_len < 0) { ++ rc = DMMP_ERR_BUG; ++ _error(ctx, "BUG: Got negative length for ar_ps"); ++ goto out; ++ } ++ else if (ar_ps_len == 0) ++ goto out; ++ else ++ dmmp_pg->dmmp_p_count = ar_ps_len & UINT32_MAX; ++ ++ dmmp_pg->dmmp_ps = (struct dmmp_path **) ++ malloc(sizeof(struct dmmp_path *) * dmmp_pg->dmmp_p_count); ++ _dmmp_alloc_null_check(ctx, dmmp_pg->dmmp_ps, rc, out); ++ for (; i < dmmp_pg->dmmp_p_count; ++i) ++ dmmp_pg->dmmp_ps[i] = NULL; ++ ++ for (i = 0; i < dmmp_pg->dmmp_p_count; ++i) { ++ dmmp_p = _dmmp_path_new(); ++ _dmmp_alloc_null_check(ctx, dmmp_p, rc, out); ++ dmmp_pg->dmmp_ps[i] = dmmp_p; ++ _good(_dmmp_path_update(ctx, dmmp_p, ++ array_list_get_idx(ar_ps, i)), ++ rc, out); ++ } ++ ++ _debug(ctx, "Got path group id: %" PRIu32 "", dmmp_pg->id); ++ _debug(ctx, "Got path group priority: %" PRIu32 "", dmmp_pg->priority); ++ _debug(ctx, "Got path group status: %s(%" PRIu32 ")", ++ dmmp_path_group_status_str(dmmp_pg->status), dmmp_pg->status); ++ _debug(ctx, "Got path group selector: '%s'", dmmp_pg->selector); ++ ++out: ++ if (rc != DMMP_OK) ++ _dmmp_path_group_free(dmmp_pg); ++ return rc; ++} ++ ++void _dmmp_path_group_free(struct dmmp_path_group *dmmp_pg) ++{ ++ uint32_t i = 0; ++ ++ if (dmmp_pg == NULL) ++ return; ++ ++ free((char *) dmmp_pg->selector); ++ ++ if (dmmp_pg->dmmp_ps != NULL) { ++ for (i = 0; i < dmmp_pg->dmmp_p_count; ++i) { ++ _dmmp_path_free(dmmp_pg->dmmp_ps[i]); ++ } ++ free(dmmp_pg->dmmp_ps); ++ } ++ free(dmmp_pg); ++} ++ ++void dmmp_path_array_get(struct dmmp_path_group *mp_pg, ++ struct dmmp_path ***mp_paths, ++ uint32_t *dmmp_p_count) ++{ ++ assert(mp_pg != NULL); ++ assert(mp_paths != NULL); ++ assert(dmmp_p_count != NULL); ++ ++ *mp_paths = mp_pg->dmmp_ps; ++ *dmmp_p_count = mp_pg->dmmp_p_count; ++} +Index: multipath-tools-130222/libdmmp/libdmmp_private.h +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/libdmmp_private.h +@@ -0,0 +1,208 @@ ++/* ++ * Copyright (C) 2015 - 2016 Red Hat, Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author: Gris Ge ++ * Todd Gill ++ */ ++ ++#ifndef _LIB_DMMP_PRIVATE_H_ ++#define _LIB_DMMP_PRIVATE_H_ ++ ++/* ++ * Notes: ++ * Internal/Private functions does not check input argument but using ++ * assert() to abort if NULL pointer found in argument. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "libdmmp/libdmmp.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define _good(rc, rc_val, out) \ ++ do { \ ++ rc_val = rc; \ ++ if (rc_val != DMMP_OK) \ ++ goto out; \ ++ } while(0) ++ ++#define _DMMP_PATH_GROUP_ID_UNKNOWN 0 ++ ++DMMP_DLL_LOCAL struct _num_str_conv { ++ const uint32_t value; ++ const char *str; ++}; ++ ++#define _dmmp_str_func_gen(func_name, var_type, var, conv_array) \ ++const char *func_name(var_type var) { \ ++ size_t i = 0; \ ++ uint32_t tmp_var = var & UINT32_MAX; \ ++ /* In the whole libdmmp, we don't have negative value */ \ ++ for (; i < sizeof(conv_array)/sizeof(conv_array[0]); ++i) { \ ++ if ((conv_array[i].value) == tmp_var) \ ++ return conv_array[i].str; \ ++ } \ ++ return "Invalid argument"; \ ++} ++ ++#define _dmmp_str_conv_func_gen(func_name, ctx, var_name, out_type, \ ++ unknown_value, conv_array) \ ++static out_type func_name(struct dmmp_context *ctx, const char *var_name) { \ ++ size_t i = 0; \ ++ for (; i < sizeof(conv_array)/sizeof(conv_array[0]); ++i) { \ ++ if (strcmp(conv_array[i].str, var_name) == 0) \ ++ return conv_array[i].value; \ ++ } \ ++ _warn(ctx, "Got unknown " #var_name ": '%s'", var_name); \ ++ return unknown_value; \ ++} ++ ++#define _json_obj_get_value(ctx, j_obj, out_value, key, value_type, \ ++ value_func, rc, out) \ ++do { \ ++ json_type j_type = json_type_null; \ ++ json_object *j_obj_tmp = NULL; \ ++ if (json_object_object_get_ex(j_obj, key, &j_obj_tmp) != TRUE) { \ ++ _error(ctx, "Invalid JSON output from multipathd IPC: " \ ++ "key '%s' not found", key); \ ++ rc = DMMP_ERR_IPC_ERROR; \ ++ goto out; \ ++ } \ ++ if (j_obj_tmp == NULL) { \ ++ _error(ctx, "BUG: Got NULL j_obj_tmp from " \ ++ "json_object_object_get_ex() while it return TRUE"); \ ++ rc = DMMP_ERR_BUG; \ ++ goto out; \ ++ } \ ++ j_type = json_object_get_type(j_obj_tmp); \ ++ if (j_type != value_type) { \ ++ _error(ctx, "Invalid value type for key'%s' of JSON output " \ ++ "from multipathd IPC. Should be %s(%d), " \ ++ "but got %s(%d)", key, json_type_to_name(value_type), \ ++ value_type, json_type_to_name(j_type), j_type); \ ++ rc = DMMP_ERR_IPC_ERROR; \ ++ goto out; \ ++ } \ ++ out_value = value_func(j_obj_tmp); \ ++} while(0); ++ ++DMMP_DLL_LOCAL int _dmmp_ipc_exec(struct dmmp_context *ctx, const char *cmd, ++ char **output); ++ ++DMMP_DLL_LOCAL struct dmmp_mpath *_dmmp_mpath_new(void); ++DMMP_DLL_LOCAL struct dmmp_path_group *_dmmp_path_group_new(void); ++DMMP_DLL_LOCAL struct dmmp_path *_dmmp_path_new(void); ++ ++DMMP_DLL_LOCAL int _dmmp_mpath_update(struct dmmp_context *ctx, ++ struct dmmp_mpath *dmmp_mp, ++ json_object *j_obj_map); ++DMMP_DLL_LOCAL int _dmmp_path_group_update(struct dmmp_context *ctx, ++ struct dmmp_path_group *dmmp_pg, ++ json_object *j_obj_pg); ++DMMP_DLL_LOCAL int _dmmp_path_update(struct dmmp_context *ctx, ++ struct dmmp_path *dmmp_p, ++ json_object *j_obj_p); ++ ++DMMP_DLL_LOCAL void _dmmp_mpath_free(struct dmmp_mpath *dmmp_mp); ++DMMP_DLL_LOCAL void _dmmp_path_group_free(struct dmmp_path_group *dmmp_pg); ++DMMP_DLL_LOCAL void _dmmp_path_group_array_free ++ (struct dmmp_path_group **dmmp_pgs, uint32_t dmmp_pg_count); ++DMMP_DLL_LOCAL void _dmmp_path_free(struct dmmp_path *dmmp_p); ++DMMP_DLL_LOCAL void _dmmp_log(struct dmmp_context *ctx, int priority, ++ const char *file, int line, ++ const char *func_name, ++ const char *format, ...); ++DMMP_DLL_LOCAL void _dmmp_log_err_str(struct dmmp_context *ctx, int rc); ++ ++DMMP_DLL_LOCAL void _dmmp_log_stderr(struct dmmp_context *ctx, int priority, ++ const char *file, int line, ++ const char *func_name, const char *format, ++ va_list args); ++ ++ ++#define _dmmp_log_cond(ctx, prio, arg...) \ ++ do { \ ++ if (dmmp_context_log_priority_get(ctx) >= prio) \ ++ _dmmp_log(ctx, prio, __FILE__, __LINE__, __FUNCTION__, \ ++ ## arg); \ ++ } while (0) ++ ++#define _debug(ctx, arg...) \ ++ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_DEBUG, ## arg) ++#define _info(ctx, arg...) \ ++ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_INFO, ## arg) ++#define _warn(ctx, arg...) \ ++ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_WARNING, ## arg) ++#define _error(ctx, arg...) \ ++ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_ERROR, ## arg) ++ ++/* ++ * Check pointer returned by malloc() or strdup(), if NULL, set ++ * rc as DMMP_ERR_NO_MEMORY, report error and goto goto_out. ++ */ ++#define _dmmp_alloc_null_check(ctx, ptr, rc, goto_out) \ ++ do { \ ++ if (ptr == NULL) { \ ++ rc = DMMP_ERR_NO_MEMORY; \ ++ _error(ctx, dmmp_strerror(rc)); \ ++ goto goto_out; \ ++ } \ ++ } while(0) ++ ++#define _dmmp_null_or_empty_str_check(ctx, var, rc, goto_out) \ ++ do { \ ++ if (var == NULL) { \ ++ rc = DMMP_ERR_BUG; \ ++ _error(ctx, "BUG: Got NULL " #var); \ ++ goto goto_out; \ ++ } \ ++ if (strlen(var) == 0) { \ ++ rc = DMMP_ERR_BUG; \ ++ _error(ctx, "BUG: Got empty " #var); \ ++ goto goto_out; \ ++ } \ ++ } while(0) ++ ++#define _dmmp_getter_func_gen(func_name, struct_name, struct_data, \ ++ prop_name, prop_type) \ ++ prop_type func_name(struct_name *struct_data) \ ++ { \ ++ assert(struct_data != NULL); \ ++ return struct_data->prop_name; \ ++ } ++ ++#define _dmmp_array_free_func_gen(func_name, struct_name, struct_free_func) \ ++ void func_name(struct_name **ptr_array, uint32_t ptr_count) \ ++ { \ ++ uint32_t i = 0; \ ++ if (ptr_array == NULL) \ ++ return; \ ++ for (; i < ptr_count; ++i) \ ++ struct_free_func(ptr_array[i]); \ ++ free(ptr_array); \ ++ } ++ ++#ifdef __cplusplus ++} /* End of extern "C" */ ++#endif ++ ++#endif /* End of _LIB_DMMP_PRIVATE_H_ */ +Index: multipath-tools-130222/libdmmp/test/Makefile +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/test/Makefile +@@ -0,0 +1,30 @@ ++# Makefile ++# ++# Copyright (C) 2015-2016 Gris Ge ++# ++include ../../Makefile.inc ++ ++_libdmmpdir=../$(libdmmpdir) ++_mpathcmddir=../$(mpathcmddir) ++ ++TEST_EXEC = libdmmp_test ++SPD_TEST_EXEC = libdmmp_speed_test ++CFLAGS += -I$(_libdmmpdir) ++LDFLAGS += -L$(_libdmmpdir) -ldmmp ++ ++all: $(TEST_EXEC) $(SPD_TEST_EXEC) ++ ++check: $(TEST_EXEC) $(SPD_TEST_EXEC) ++ sudo env LD_LIBRARY_PATH=$(_libdmmpdir):$(_mpathcmddir) \ ++ valgrind --quiet --leak-check=full \ ++ --show-reachable=no --show-possibly-lost=no \ ++ --trace-children=yes --error-exitcode=1 \ ++ ./$(TEST_EXEC) ++ $(MAKE) speed_test ++ ++speed_test: $(SPD_TEST_EXEC) ++ sudo env LD_LIBRARY_PATH=$(_libdmmpdir):$(_mpathcmddir) \ ++ time -p ./$(SPD_TEST_EXEC) ++ ++clean: ++ rm -f $(TEST_EXEC) $(SPD_TEST_EXEC) +Index: multipath-tools-130222/libdmmp/test/libdmmp_speed_test.c +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/test/libdmmp_speed_test.c +@@ -0,0 +1,49 @@ ++/* ++ * Copyright (C) 2015-2016 Red Hat, Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author: Gris Ge ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(int argc, char *argv[]) ++{ ++ struct dmmp_context *ctx = NULL; ++ struct dmmp_mpath **dmmp_mps = NULL; ++ uint32_t dmmp_mp_count = 0; ++ int rc = EXIT_SUCCESS; ++ ++ ctx = dmmp_context_new(); ++ dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_WARNING); ++ ++ if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0) { ++ printf("FAILED\n"); ++ rc = EXIT_FAILURE; ++ } else { ++ printf("Got %" PRIu32 " mpath\n", dmmp_mp_count); ++ dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count); ++ } ++ dmmp_context_free(ctx); ++ exit(rc); ++} +Index: multipath-tools-130222/libdmmp/test/libdmmp_test.c +=================================================================== +--- /dev/null ++++ multipath-tools-130222/libdmmp/test/libdmmp_test.c +@@ -0,0 +1,147 @@ ++/* ++ * Copyright (C) 2015-2016 Red Hat, Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author: Gris Ge ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define FAIL(rc, out, ...) \ ++ do { \ ++ rc = EXIT_FAILURE; \ ++ fprintf(stderr, "FAIL: "__VA_ARGS__ ); \ ++ goto out; \ ++ } while(0) ++#define PASS(...) fprintf(stdout, "PASS: "__VA_ARGS__ ); ++#define FILE_NAME_SIZE 256 ++#define TMO 10000 /* Forcing timeout to 10 seconds */ ++ ++int test_paths(struct dmmp_path_group *mp_pg) ++{ ++ struct dmmp_path **mp_ps = NULL; ++ uint32_t mp_p_count = 0; ++ uint32_t i = 0; ++ const char *blk_name = NULL; ++ int rc = EXIT_SUCCESS; ++ ++ dmmp_path_array_get(mp_pg, &mp_ps, &mp_p_count); ++ if (mp_p_count == 0) ++ FAIL(rc, out, "dmmp_path_array_get(): Got no path\n"); ++ for (i = 0; i < mp_p_count; ++i) { ++ blk_name = dmmp_path_blk_name_get(mp_ps[i]); ++ if (blk_name == NULL) ++ FAIL(rc, out, "dmmp_path_blk_name_get(): Got NULL\n"); ++ PASS("dmmp_path_blk_name_get(): %s\n", blk_name); ++ PASS("dmmp_path_status_get(): %" PRIu32 " -- %s\n", ++ dmmp_path_status_get(mp_ps[i]), ++ dmmp_path_status_str(dmmp_path_status_get(mp_ps[i]))); ++ } ++out: ++ return rc; ++} ++ ++int test_path_groups(struct dmmp_mpath *dmmp_mp) ++{ ++ struct dmmp_path_group **dmmp_pgs = NULL; ++ uint32_t dmmp_pg_count = 0; ++ uint32_t i = 0; ++ int rc = EXIT_SUCCESS; ++ ++ dmmp_path_group_array_get(dmmp_mp, &dmmp_pgs, &dmmp_pg_count); ++ if ((dmmp_pg_count == 0) && (dmmp_pgs != NULL)) ++ FAIL(rc, out, "dmmp_path_group_array_get(): mp_pgs is not NULL " ++ "but mp_pg_count is 0\n"); ++ if ((dmmp_pg_count != 0) && (dmmp_pgs == NULL)) ++ FAIL(rc, out, "dmmp_path_group_array_get(): mp_pgs is NULL " ++ "but mp_pg_count is not 0\n"); ++ if (dmmp_pg_count == 0) ++ FAIL(rc, out, "dmmp_path_group_array_get(): " ++ "Got 0 path group\n"); ++ ++ PASS("dmmp_path_group_array_get(): Got %" PRIu32 " path groups\n", ++ dmmp_pg_count); ++ ++ for (i = 0; i < dmmp_pg_count; ++i) { ++ PASS("dmmp_path_group_id_get(): %" PRIu32 "\n", ++ dmmp_path_group_id_get(dmmp_pgs[i])); ++ PASS("dmmp_path_group_priority_get(): %" PRIu32 "\n", ++ dmmp_path_group_priority_get(dmmp_pgs[i])); ++ PASS("dmmp_path_group_status_get(): %" PRIu32 " -- %s\n", ++ dmmp_path_group_status_get(dmmp_pgs[i]), ++ dmmp_path_group_status_str ++ (dmmp_path_group_status_get(dmmp_pgs[i]))); ++ PASS("dmmp_path_group_selector_get(): %s\n", ++ dmmp_path_group_selector_get(dmmp_pgs[i])); ++ rc = test_paths(dmmp_pgs[i]); ++ if (rc != 0) ++ goto out; ++ } ++out: ++ return rc; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct dmmp_context *ctx = NULL; ++ struct dmmp_mpath **dmmp_mps = NULL; ++ uint32_t dmmp_mp_count = 0; ++ const char *name = NULL; ++ const char *wwid = NULL; ++ const char *kdev = NULL; ++ uint32_t i = 0; ++ int rc = EXIT_SUCCESS; ++ ++ ctx = dmmp_context_new(); ++ dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_DEBUG); ++ dmmp_context_userdata_set(ctx, ctx); ++ dmmp_context_userdata_set(ctx, NULL); ++ dmmp_context_timeout_set(ctx, TMO); ++ if (dmmp_context_timeout_get(ctx) != TMO) ++ FAIL(rc, out, "dmmp_context_timeout_set(): Failed to set " ++ "timeout to %u", TMO); ++ ++ if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0) ++ FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n"); ++ if (dmmp_mp_count == 0) ++ FAIL(rc, out, "dmmp_mpath_array_get(): " ++ "Got no multipath devices\n"); ++ PASS("dmmp_mpath_array_get(): Got %" PRIu32 " mpath\n", dmmp_mp_count); ++ for (i = 0; i < dmmp_mp_count; ++i) { ++ name = dmmp_mpath_name_get(dmmp_mps[i]); ++ wwid = dmmp_mpath_wwid_get(dmmp_mps[i]); ++ kdev = dmmp_mpath_kdev_name_get(dmmp_mps[i]); ++ if ((name == NULL) ||(wwid == NULL) || (kdev == NULL)) ++ FAIL(rc, out, ++ "dmmp_mpath_array_get(): Got NULL name or wwid"); ++ PASS("dmmp_mpath_array_get(): Got mpath(%s): %s %s\n", ++ kdev, name, wwid); ++ rc = test_path_groups(dmmp_mps[i]); ++ if (rc != 0) ++ goto out; ++ } ++ dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count); ++out: ++ dmmp_context_free(ctx); ++ exit(rc); ++} diff --git a/SOURCES/0210-RH-fix-uninstall.patch b/SOURCES/0210-RH-fix-uninstall.patch new file mode 100644 index 0000000..7083ded --- /dev/null +++ b/SOURCES/0210-RH-fix-uninstall.patch @@ -0,0 +1,25 @@ +--- + libmpathpersist/Makefile | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +Index: multipath-tools-130222/libmpathpersist/Makefile +=================================================================== +--- multipath-tools-130222.orig/libmpathpersist/Makefile ++++ multipath-tools-130222/libmpathpersist/Makefile +@@ -33,12 +33,14 @@ install: $(LIBS) + ln -sf $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB) + install -m 644 mpath_persistent_reserve_in.3.gz $(DESTDIR)$(man3dir) + install -m 644 mpath_persistent_reserve_out.3.gz $(DESTDIR)$(man3dir) ++ $(INSTALL_PROGRAM) -m 644 mpath_persist.h $(DESTDIR)$(includedir) + + uninstall: + rm -f $(DESTDIR)$(syslibdir)/$(LIBS) + rm -f $(DESTDIR)$(syslibdir)/$(DEVLIB) +- rm $(DESTDIR)$(mandir)/mpath_persistent_reserve_in.3.gz +- rm $(DESTDIR)$(mandir)/mpath_persistent_reserve_out.3.gz ++ rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3.gz ++ rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3.gz ++ rm -f $(DESTDIR)$(includedir)/mpath_persist.h + + clean: + rm -f core *.a *.o diff --git a/SOURCES/0211-RH-strlen-fix.patch b/SOURCES/0211-RH-strlen-fix.patch new file mode 100644 index 0000000..2c12fe8 --- /dev/null +++ b/SOURCES/0211-RH-strlen-fix.patch @@ -0,0 +1,35 @@ +--- + multipathd/main.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +Index: multipath-tools-130222/multipathd/main.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/main.c ++++ multipath-tools-130222/multipathd/main.c +@@ -878,7 +878,8 @@ uxsock_trigger (char * str, char ** repl + (strncmp(str, "list", strlen("list")) != 0) && + (strncmp(str, "show", strlen("show")) != 0)) { + *reply = STRDUP("permission deny: need to be root"); +- *len = strlen(*reply) + 1; ++ if (*reply) ++ *len = strlen(*reply) + 1; + r = 1; + goto out; + } +@@ -887,12 +888,14 @@ uxsock_trigger (char * str, char ** repl + + if (r > 0) { + *reply = STRDUP("fail\n"); +- *len = strlen(*reply) + 1; ++ if (*reply) ++ *len = strlen(*reply) + 1; + r = 1; + } + else if (!r && *len == 0) { + *reply = STRDUP("ok\n"); +- *len = strlen(*reply) + 1; ++ if (*reply) ++ *len = strlen(*reply) + 1; + r = 0; + } + /* else if (r < 0) leave *reply alone */ diff --git a/SOURCES/0212-RHBZ-1431562-for-read-only.patch b/SOURCES/0212-RHBZ-1431562-for-read-only.patch new file mode 100644 index 0000000..b398100 --- /dev/null +++ b/SOURCES/0212-RHBZ-1431562-for-read-only.patch @@ -0,0 +1,56 @@ +--- + libmultipath/devmapper.c | 10 ++++++---- + libmultipath/structs.h | 1 + + multipathd/main.c | 5 +++-- + 3 files changed, 10 insertions(+), 6 deletions(-) + +Index: multipath-tools-130222/libmultipath/devmapper.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/devmapper.c ++++ multipath-tools-130222/libmultipath/devmapper.c +@@ -358,10 +358,12 @@ dm_addmap_create (struct multipath *mpp, + extern int + dm_addmap_reload (struct multipath *mpp, char *params) { + sysfs_set_max_sectors_kb(mpp, 1); +- if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW, SKIP_KPARTX_OFF)) +- return 1; +- if (errno != EROFS) +- return 0; ++ if (!mpp->force_readonly) { ++ if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW, SKIP_KPARTX_OFF)) ++ return 1; ++ if (errno != EROFS) ++ return 0; ++ } + return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RO, SKIP_KPARTX_OFF); + } + +Index: multipath-tools-130222/libmultipath/structs.h +=================================================================== +--- multipath-tools-130222.orig/libmultipath/structs.h ++++ multipath-tools-130222/libmultipath/structs.h +@@ -259,6 +259,7 @@ struct multipath { + int force_udev_reload; + int skip_kpartx; + int max_sectors_kb; ++ int force_readonly; + unsigned int dev_loss; + uid_t uid; + gid_t gid; +Index: multipath-tools-130222/multipathd/main.c +=================================================================== +--- multipath-tools-130222.orig/multipathd/main.c ++++ multipath-tools-130222/multipathd/main.c +@@ -831,9 +831,10 @@ uev_update_path (struct uevent *uev, str + pp->mpp->wait_for_udev = 2; + return 0; + } +- ++ if (ro == 1) ++ pp->mpp->force_readonly = 1; + retval = reload_map(vecs, pp->mpp, 0); +- ++ pp->mpp->force_readonly = 0; + condlog(2, "%s: map %s reloaded (retval %d)", + uev->kernel, pp->mpp->alias, retval); + } diff --git a/SOURCES/0213-RHBZ-1430908-merge-dell-configs.patch b/SOURCES/0213-RHBZ-1430908-merge-dell-configs.patch new file mode 100644 index 0000000..920547e --- /dev/null +++ b/SOURCES/0213-RHBZ-1430908-merge-dell-configs.patch @@ -0,0 +1,191 @@ +--- + libmultipath/hwtable.c | 81 +----------------------------------------------- + multipath.conf.defaults | 69 +--------------------------------------- + 2 files changed, 5 insertions(+), 145 deletions(-) + +Index: multipath-tools-130222/libmultipath/hwtable.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/hwtable.c ++++ multipath-tools-130222/libmultipath/hwtable.c +@@ -731,91 +731,16 @@ static struct hwentry default_hw[] = { + .prio_args = NULL, + }, + { +- /* DELL MD3000 */ ++ /* DELL MD3xxxx family */ + .vendor = "DELL", +- .product = "MD3000", ++ .product = "^MD3", + .bl_product = "Universal Xport", + .features = "2 pg_init_retries 50", + .hwhandler = "1 rdac", + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, +- .no_path_retry = 15, +- .checker_name = RDAC, +- .prio_name = PRIO_RDAC, +- .prio_args = NULL, +- }, +- { +- /* DELL MD3000i */ +- .vendor = "DELL", +- .product = "MD3000i", +- .bl_product = "Universal Xport", +- .features = "2 pg_init_retries 50", +- .hwhandler = "1 rdac", +- .pgpolicy = GROUP_BY_PRIO, +- .pgfailback = -FAILBACK_IMMEDIATE, +- .rr_weight = RR_WEIGHT_NONE, +- .no_path_retry = 15, +- .checker_name = RDAC, +- .prio_name = PRIO_RDAC, +- .prio_args = NULL, +- }, +- { +- /* DELL MD32xx */ +- .vendor = "DELL", +- .product = "MD32xx", +- .bl_product = "Universal Xport", +- .features = "2 pg_init_retries 50", +- .hwhandler = "1 rdac", +- .pgpolicy = GROUP_BY_PRIO, +- .pgfailback = -FAILBACK_IMMEDIATE, +- .rr_weight = RR_WEIGHT_NONE, +- .no_path_retry = 15, +- .checker_name = RDAC, +- .prio_name = PRIO_RDAC, +- .prio_args = NULL, +- }, +- { +- /* DELL MD32xxi */ +- .vendor = "DELL", +- .product = "MD32xxi", +- .bl_product = "Universal Xport", +- .features = "2 pg_init_retries 50", +- .hwhandler = "1 rdac", +- .pgpolicy = GROUP_BY_PRIO, +- .pgfailback = -FAILBACK_IMMEDIATE, +- .rr_weight = RR_WEIGHT_NONE, +- .no_path_retry = 15, +- .checker_name = RDAC, +- .prio_name = PRIO_RDAC, +- .prio_args = NULL, +- }, +- { +- /* DELL MD36xxi */ +- .vendor = "DELL", +- .product = "MD36xxi", +- .bl_product = "Universal Xport", +- .features = "2 pg_init_retries 50", +- .hwhandler = "1 rdac", +- .pgpolicy = GROUP_BY_PRIO, +- .pgfailback = -FAILBACK_IMMEDIATE, +- .rr_weight = RR_WEIGHT_NONE, +- .no_path_retry = 15, +- .checker_name = RDAC, +- .prio_name = PRIO_RDAC, +- .prio_args = NULL, +- }, +- { +- /* DELL MD36xxf */ +- .vendor = "DELL", +- .product = "MD36xxf", +- .bl_product = "Universal Xport", +- .features = "2 pg_init_retries 50", +- .hwhandler = "1 rdac", +- .pgpolicy = GROUP_BY_PRIO, +- .pgfailback = -FAILBACK_IMMEDIATE, +- .rr_weight = RR_WEIGHT_NONE, +- .no_path_retry = 15, ++ .no_path_retry = 30, + .checker_name = RDAC, + .prio_name = PRIO_RDAC, + .prio_args = NULL, +Index: multipath-tools-130222/multipath.conf.defaults +=================================================================== +--- multipath-tools-130222.orig/multipath.conf.defaults ++++ multipath-tools-130222/multipath.conf.defaults +@@ -619,7 +619,7 @@ + # } + # device { + # vendor "DELL" +-# product "MD3000" ++# product "^MD3" + # product_blacklist "Universal Xport" + # path_grouping_policy "group_by_prio" + # path_checker "rdac" +@@ -628,72 +628,7 @@ + # prio "rdac" + # failback immediate + # rr_weight "uniform" +-# no_path_retry 15 +-# } +-# device { +-# vendor "DELL" +-# product "MD3000i" +-# product_blacklist "Universal Xport" +-# path_grouping_policy "group_by_prio" +-# path_checker "rdac" +-# features "2 pg_init_retries 50" +-# hardware_handler "1 rdac" +-# prio "rdac" +-# failback immediate +-# rr_weight "uniform" +-# no_path_retry 15 +-# } +-# device { +-# vendor "DELL" +-# product "MD32xx" +-# product_blacklist "Universal Xport" +-# path_grouping_policy "group_by_prio" +-# path_checker "rdac" +-# features "2 pg_init_retries 50" +-# hardware_handler "1 rdac" +-# prio "rdac" +-# failback immediate +-# rr_weight "uniform" +-# no_path_retry 15 +-# } +-# device { +-# vendor "DELL" +-# product "MD32xxi" +-# product_blacklist "Universal Xport" +-# path_grouping_policy "group_by_prio" +-# path_checker "rdac" +-# features "2 pg_init_retries 50" +-# hardware_handler "1 rdac" +-# prio "rdac" +-# failback immediate +-# rr_weight "uniform" +-# no_path_retry 15 +-# } +-# device { +-# vendor "DELL" +-# product "MD36xxi" +-# product_blacklist "Universal Xport" +-# path_grouping_policy "group_by_prio" +-# path_checker "rdac" +-# features "2 pg_init_retries 50" +-# hardware_handler "1 rdac" +-# prio "rdac" +-# failback immediate +-# rr_weight "uniform" +-# no_path_retry 15 +-# } +-# device { +-# vendor "DELL" +-# product "MD36xxf" +-# product_blacklist "Universal Xport" +-# path_grouping_policy "group_by_prio" +-# path_checker "rdac" +-# features "2 pg_init_retries 50" +-# hardware_handler "1 rdac" +-# prio "rdac" +-# failback immediate +-# rr_weight "uniform" +-# no_path_retry 15 ++# no_path_retry 30 + # } + # device { + # vendor "NETAPP" diff --git a/SOURCES/0214-RHBZ-1392115-set-paths-not-ready.patch b/SOURCES/0214-RHBZ-1392115-set-paths-not-ready.patch new file mode 100644 index 0000000..a37bc24 --- /dev/null +++ b/SOURCES/0214-RHBZ-1392115-set-paths-not-ready.patch @@ -0,0 +1,33 @@ +--- + multipath/multipath.rules | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +Index: multipath-tools-130222/multipath/multipath.rules +=================================================================== +--- multipath-tools-130222.orig/multipath/multipath.rules ++++ multipath-tools-130222/multipath/multipath.rules +@@ -4,8 +4,14 @@ SUBSYSTEM!="block", GOTO="end_mpath" + + IMPORT{cmdline}="nompath" + ENV{nompath}=="?*", GOTO="end_mpath" +-ENV{DEVTYPE}=="partition", IMPORT{parent}="DM_MULTIPATH_DEVICE_PATH", \ +- GOTO="end_mpath" ++ENV{DEVTYPE}!="partition", GOTO="test_dev" ++IMPORT{parent}="DM_MULTIPATH_DEVICE_PATH" ++ENV{DM_MULTIPATH_DEVICE_PATH}=="1", ENV{ID_FS_TYPE}="none", \ ++ ENV{SYSTEMD_READY}="0" ++GOTO="end_mpath" ++ ++LABEL="test_dev" ++ + ENV{MPATH_SBIN_PATH}="/sbin" + TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin" + TEST!="/etc/multipath.conf", GOTO="check_kpartx" +@@ -33,6 +39,7 @@ ENV{DM_MULTIPATH_DEVICE_PATH}="" + ENV{DM_MULTIPATH_WIPE_PARTS}="" + + LABEL="update_timestamp" ++ENV{DM_MULTIPATH_DEVICE_PATH}=="1", ENV{SYSTEMD_READY}="0" + ENV{DM_MULTIPATH_DEVICE_PATH}=="1", ENV{DM_MULTIPATH_WIPE_PARTS}!="1", \ + ENV{DM_MULTIPATH_WIPE_PARTS}="1", \ + RUN+="/sbin/partx -d --nr 1-1024 $env{DEVNAME}" diff --git a/SOURCES/0215-RHBZ-1444194-fix-check-partitions.patch b/SOURCES/0215-RHBZ-1444194-fix-check-partitions.patch new file mode 100644 index 0000000..10d511e --- /dev/null +++ b/SOURCES/0215-RHBZ-1444194-fix-check-partitions.patch @@ -0,0 +1,101 @@ +--- + libmultipath/devmapper.c | 53 +++++++++++++++++++++-------------------------- + 1 file changed, 24 insertions(+), 29 deletions(-) + +Index: multipath-tools-130222/libmultipath/devmapper.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/devmapper.c ++++ multipath-tools-130222/libmultipath/devmapper.c +@@ -466,42 +466,35 @@ uuidout: + extern int + dm_get_uuid(char *name, char *uuid) + { +- char uuidtmp[WWID_SIZE]; +- +- if (dm_get_prefixed_uuid(name, uuidtmp)) ++ if (dm_get_prefixed_uuid(name, uuid)) + return 1; + +- if (!strncmp(uuidtmp, UUID_PREFIX, UUID_PREFIX_LEN)) +- strcpy(uuid, uuidtmp + UUID_PREFIX_LEN); +- else +- strcpy(uuid, uuidtmp); +- ++ if (!strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN)) ++ memmove(uuid, uuid + UUID_PREFIX_LEN, ++ strlen(uuid + UUID_PREFIX_LEN) + 1); + return 0; + } + +-/* +- * returns: +- * 0 : if both uuids end with same suffix which starts with UUID_PREFIX +- * 1 : otherwise +- */ +-int +-dm_compare_uuid(const char* mapname1, const char* mapname2) ++static int ++is_mpath_part(const char *part_name, const char *map_name) + { +- char *p1, *p2; +- char uuid1[WWID_SIZE], uuid2[WWID_SIZE]; ++ char *p; ++ char part_uuid[WWID_SIZE], map_uuid[WWID_SIZE]; + +- if (dm_get_prefixed_uuid(mapname1, uuid1)) +- return 1; ++ if (dm_get_prefixed_uuid(part_name, part_uuid)) ++ return 0; + +- if (dm_get_prefixed_uuid(mapname2, uuid2)) +- return 1; ++ if (dm_get_prefixed_uuid(map_name, map_uuid)) ++ return 0; + +- p1 = strstr(uuid1, UUID_PREFIX); +- p2 = strstr(uuid2, UUID_PREFIX); +- if (p1 && p2 && !strcmp(p1, p2)) ++ if (strncmp(part_uuid, "part", 4) != 0) + return 0; + +- return 1; ++ p = strstr(part_uuid, UUID_PREFIX); ++ if (p && !strcmp(p, map_uuid)) ++ return 1; ++ ++ return 0; + } + + extern int +@@ -1143,6 +1136,7 @@ do_foreach_partmaps (const char * mapnam + unsigned long long size; + char dev_t[32]; + int r = 1; ++ char *p; + + if (!(dmt = dm_task_create(DM_DEVICE_LIST))) + return 1; +@@ -1171,10 +1165,10 @@ do_foreach_partmaps (const char * mapnam + (dm_type(names->name, TGT_PART) > 0) && + + /* +- * and both uuid end with same suffix starting +- * at UUID_PREFIX ++ * and the uuid of the target is a partition of the ++ * uuid of the multipath device + */ +- (!dm_compare_uuid(names->name, mapname)) && ++ is_mpath_part(names->name, mapname) && + + /* + * and we can fetch the map table from the kernel +@@ -1184,7 +1178,8 @@ do_foreach_partmaps (const char * mapnam + /* + * and the table maps over the multipath map + */ +- strstr(params, dev_t) ++ (p = strstr(params, dev_t)) && ++ !isdigit(*(p + strlen(dev_t))) + ) { + if (partmap_func(names->name, data) != 0) + goto out; diff --git a/SOURCES/0216-RHBZ-1448562-fix-reserve.patch b/SOURCES/0216-RHBZ-1448562-fix-reserve.patch new file mode 100644 index 0000000..b68376b --- /dev/null +++ b/SOURCES/0216-RHBZ-1448562-fix-reserve.patch @@ -0,0 +1,122 @@ +--- + libmpathpersist/mpath_persist.c | 43 +++++++++++++++++++++++++--------------- + libmpathpersist/mpath_persist.h | 4 ++- + 2 files changed, 30 insertions(+), 17 deletions(-) + +Index: multipath-tools-130222/libmpathpersist/mpath_persist.c +=================================================================== +--- multipath-tools-130222.orig/libmpathpersist/mpath_persist.c ++++ multipath-tools-130222/libmpathpersist/mpath_persist.c +@@ -440,7 +440,7 @@ int mpath_prout_reg(struct multipath *mp + thread[i].param.rq_type = rq_type; + thread[i].param.paramp = paramp; + thread[i].param.noisy = noisy; +- thread[i].param.status = -1; ++ thread[i].param.status = MPATH_PR_SKIP; + + condlog (3, "THRED ID [%d] INFO]", i); + condlog (3, "rq_servact=%d ", thread[i].param.rq_servact); +@@ -476,14 +476,17 @@ int mpath_prout_reg(struct multipath *mp + rc = pthread_create(&thread[count].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[count].param)); + if (rc){ + condlog (0, "%s: failed to create thread %d", mpp->wwid, rc); ++ thread[count].param.status = MPATH_PR_THREAD_ERROR; + } + count = count +1; + } + } + for( i=0; i < active_pathcount ; i++){ +- rc = pthread_join(thread[i].id, NULL); +- if (rc){ +- condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc); ++ if (thread[i].param.status != MPATH_PR_THREAD_ERROR) { ++ rc = pthread_join(thread[i].id, NULL); ++ if (rc){ ++ condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc); ++ } + } + if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){ + rollback = 1; +@@ -502,23 +505,27 @@ int mpath_prout_reg(struct multipath *mp + if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){ + condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid); + for( i=0 ; i < active_pathcount ; i++){ +- if((thread[i].param.status == MPATH_PR_SUCCESS) && +- ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ ++ if (thread[i].param.status == MPATH_PR_SUCCESS) { + memcpy(&thread[i].param.paramp->key, &thread[i].param.paramp->sa_key, 8); + memset(&thread[i].param.paramp->sa_key, 0, 8); + thread[i].param.status = MPATH_PR_SUCCESS; + rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn, +- (void *)(&thread[count].param)); ++ (void *)(&thread[i].param)); + if (rc){ + condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc); ++ thread[i].param.status = MPATH_PR_THREAD_ERROR; + } +- } ++ } else ++ thread[i].param.status = MPATH_PR_SKIP; + } + for(i=0; i < active_pathcount ; i++){ +- rc = pthread_join(thread[i].id, NULL); +- if (rc){ +- condlog (3, "%s: failed to join thread while rolling back %d", +- mpp->wwid, i); ++ if (thread[i].param.status != MPATH_PR_SKIP && ++ thread[i].param.status != MPATH_PR_THREAD_ERROR) { ++ rc = pthread_join(thread[i].id, NULL); ++ if (rc){ ++ condlog (3, "%s: failed to join thread while rolling back %d", ++ mpp->wwid, i); ++ } + } + } + } +@@ -649,16 +656,20 @@ int mpath_prout_rel(struct multipath *mp + condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); + rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn, + (void *) (&thread[count].param)); +- if (rc) ++ if (rc) { + condlog (0, "%s: failed to create thread. %d", mpp->wwid, rc); ++ thread[count].param.status = MPATH_PR_THREAD_ERROR; ++ } + count = count + 1; + } + } + pthread_attr_destroy (&attr); + for (i = 0; i < active_pathcount; i++){ +- rc = pthread_join (thread[i].id, NULL); +- if (rc){ +- condlog (1, "%s: failed to join thread. %d", mpp->wwid, rc); ++ if (thread[i].param.status != MPATH_PR_THREAD_ERROR) { ++ rc = pthread_join (thread[i].id, NULL); ++ if (rc){ ++ condlog (1, "%s: failed to join thread. %d", mpp->wwid, rc); ++ } + } + } + +Index: multipath-tools-130222/libmpathpersist/mpath_persist.h +=================================================================== +--- multipath-tools-130222.orig/libmpathpersist/mpath_persist.h ++++ multipath-tools-130222/libmpathpersist/mpath_persist.h +@@ -43,6 +43,7 @@ extern "C" { + + + /* PR RETURN_STATUS */ ++#define MPATH_PR_SKIP -1 /* skipping this path */ + #define MPATH_PR_SUCCESS 0 + #define MPATH_PR_SYNTAX_ERROR 1 /* syntax error or invalid parameter */ + /* status for check condition */ +@@ -59,7 +60,8 @@ extern "C" { + #define MPATH_PR_RESERV_CONFLICT 11 /* Reservation conflict on the device */ + #define MPATH_PR_FILE_ERROR 12 /* file (device node) problems(e.g. not found)*/ + #define MPATH_PR_DMMP_ERROR 13 /* DMMP related error.(e.g Error in getting dm info */ +-#define MPATH_PR_OTHER 14 /*other error/warning has occurred(transport ++#define MPATH_PR_THREAD_ERROR 14 /* pthreads error (e.g. unable to create new thread) */ ++#define MPATH_PR_OTHER 15 /*other error/warning has occurred(transport + or driver error) */ + + /* PR MASK */ diff --git a/SOURCES/0217-RHBZ-1448576-3PAR-config.patch b/SOURCES/0217-RHBZ-1448576-3PAR-config.patch new file mode 100644 index 0000000..a1c982f --- /dev/null +++ b/SOURCES/0217-RHBZ-1448576-3PAR-config.patch @@ -0,0 +1,32 @@ +--- + libmultipath/hwtable.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +Index: multipath-tools-130222/libmultipath/hwtable.c +=================================================================== +--- multipath-tools-130222.orig/libmultipath/hwtable.c ++++ multipath-tools-130222/libmultipath/hwtable.c +@@ -65,14 +65,17 @@ static struct hwentry default_hw[] = { + .vendor = "3PARdata", + .product = "VV", + .features = DEFAULT_FEATURES, +- .hwhandler = DEFAULT_HWHANDLER, +- .pgpolicy = MULTIBUS, +- .pgfailback = FAILBACK_UNDEF, ++ .hwhandler = "1 alua", ++ .pgpolicy = GROUP_BY_PRIO, ++ .pgfailback = -FAILBACK_IMMEDIATE, + .rr_weight = RR_WEIGHT_NONE, +- .no_path_retry = 12, +- .checker_name = DEFAULT_CHECKER, +- .prio_name = DEFAULT_PRIO, ++ .no_path_retry = 18, ++ .checker_name = TUR, ++ .prio_name = PRIO_ALUA, + .prio_args = NULL, ++ .selector = "service-time 0", ++ .fast_io_fail = 10, ++ .dev_loss = MAX_DEV_LOSS_TMO, + }, + { + .vendor = "DEC", diff --git a/SPECS/device-mapper-multipath.spec b/SPECS/device-mapper-multipath.spec index be3de72..d5e21a2 100644 --- a/SPECS/device-mapper-multipath.spec +++ b/SPECS/device-mapper-multipath.spec @@ -1,7 +1,7 @@ Summary: Tools to manage multipath devices using device-mapper Name: device-mapper-multipath Version: 0.4.9 -Release: 99%{?dist}.3 +Release: 111%{?dist} License: GPL+ Group: System Environment/Base URL: http://christophe.varoqui.free.fr/ @@ -196,8 +196,34 @@ Patch0185: 0185-rbd-check-for-nonshared-clients.patch Patch0186: 0186-rbd-check-for-exclusive-lock-enabled.patch Patch0187: 0187-rbd-fixup-log-messages.patch Patch0188: 0188-RHBZ-1368501-dont-exit.patch -Patch0189: 0189-RHBZ-1395298-rbd-lock-on-read.patch -Patch0190: 0190-RHBZ-1429885-max-sectors-kb.patch +Patch0189: 0189-RHBZ-1368211-remove-retries.patch +Patch0190: 0190-RHBZ-1380602-rbd-lock-on-read.patch +Patch0191: 0191-RHBZ-1169168-disable-changed-paths.patch +Patch0192: 0192-RHBZ-1362409-infinibox-config.patch +Patch0194: 0194-RHBZ-1351964-kpartx-recurse.patch +Patch0195: 0195-RHBZ-1359510-no-daemon-msg.patch +Patch0196: 0196-RHBZ-1239173-dont-set-flag.patch +Patch0197: 0197-RHBZ-1394059-max-sectors-kb.patch +Patch0198: 0198-RHBZ-1372032-detect-path-checker.patch +Patch0199: 0199-RHBZ-1279355-3pardata-config.patch +Patch0200: 0200-RHBZ-1402092-orphan-status.patch +Patch0201: 0201-RHBZ-1403552-silence-warning.patch +Patch0202: 0202-RHBZ-1362120-skip-prio.patch +Patch0203: 0203-RHBZ-1363718-add-msgs.patch +Patch0204: 0204-RHBZ-1406226-nimble-config.patch +Patch0205: 0205-RHBZ-1416569-reset-stats.patch +Patch0206: 0206-RHBZ-1239173-pt2-no-paths.patch +Patch0207: 0207-UP-add-libmpathcmd.patch +Patch0208: 0208-UPBZ-1430097-multipathd-IPC-changes.patch +Patch0209: 0209-UPBZ-1430097-multipath-C-API.patch +Patch0210: 0210-RH-fix-uninstall.patch +Patch0211: 0211-RH-strlen-fix.patch +Patch0212: 0212-RHBZ-1431562-for-read-only.patch +Patch0213: 0213-RHBZ-1430908-merge-dell-configs.patch +Patch0214: 0214-RHBZ-1392115-set-paths-not-ready.patch +Patch0215: 0215-RHBZ-1444194-fix-check-partitions.patch +Patch0216: 0216-RHBZ-1448562-fix-reserve.patch +Patch0217: 0217-RHBZ-1448576-3PAR-config.patch # runtime Requires: %{name}-libs = %{version}-%{release} @@ -213,6 +239,7 @@ BuildRequires: libaio-devel, device-mapper-devel >= 1.02.89 BuildRequires: libselinux-devel, libsepol-devel BuildRequires: readline-devel, ncurses-devel BuildRequires: systemd-units, systemd-devel +BuildRequires: json-c-devel, perl, pkgconfig %ifarch x86_64 BuildRequires: librados2-devel %endif @@ -233,9 +260,20 @@ Group: System Environment/Libraries %description libs The %{name}-libs provides the path checker -and prioritizer modules. It also contains the multipath shared library, +and prioritizer modules. It also contains the libmpathpersist and +libmpathcmd shared libraries, as well as multipath's internal library, libmultipath. +%package devel +Summary: Development libraries and headers for %{name} +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Requires: %{name}-libs = %{version}-%{release} + +%description devel +This package contains the files need to develop applications that use +device-mapper-multipath's lbmpathpersist and libmpathcmd libraries. + %package sysvinit Summary: SysV init script for device-mapper-multipath Group: System Environment/Libraries @@ -251,6 +289,27 @@ Group: System Environment/Base %description -n kpartx kpartx manages partition creation and removal for device-mapper devices. +%package -n libdmmp +Summary: device-mapper-multipath C API library +Group: System Environment/Libraries +Requires: json-c +Requires: %{name} = %{version}-%{release} +Requires: %{name}-libs = %{version}-%{release} + +%description -n libdmmp +This package contains the shared library for the device-mapper-multipath +C API library. + +%package -n libdmmp-devel +Summary: device-mapper-multipath C API library headers +Group: Development/Libraries +Requires: pkgconfig +Requires: libdmmp = %{version}-%{release} + +%description -n libdmmp-devel +This package contains the files needed to develop applications that use +device-mapper-multipath's libdmmp C API library + %prep %setup -q -n multipath-tools-130222 %patch0001 -p1 @@ -443,12 +502,39 @@ kpartx manages partition creation and removal for device-mapper devices. %patch0188 -p1 %patch0189 -p1 %patch0190 -p1 +%patch0191 -p1 +%patch0192 -p1 +%patch0194 -p1 +%patch0195 -p1 +%patch0196 -p1 +%patch0197 -p1 +%patch0198 -p1 +%patch0199 -p1 +%patch0200 -p1 +%patch0201 -p1 +%patch0202 -p1 +%patch0203 -p1 +%patch0204 -p1 +%patch0205 -p1 +%patch0206 -p1 +%patch0207 -p1 +%patch0208 -p1 +%patch0209 -p1 +%patch0210 -p1 +%patch0211 -p1 +%patch0212 -p1 +%patch0213 -p1 +%patch0214 -p1 +%patch0215 -p1 +%patch0216 -p1 +%patch0217 -p1 cp %{SOURCE1} . %build %define _sbindir /usr/sbin %define _libdir /usr/%{_lib} %define _libmpathdir %{_libdir}/multipath +%define _pkgconfdir %{_libdir}/pkgconfig make %{?_smp_mflags} LIB=%{_lib} %install @@ -460,7 +546,9 @@ make install \ syslibdir=%{_libdir} \ libdir=%{_libmpathdir} \ rcdir=%{_initrddir} \ - unitdir=%{_unitdir} + unitdir=%{_unitdir} \ + includedir=%{_includedir} \ + pkgconfdir=%{_pkgconfdir} # tree fix up install -d %{buildroot}/etc/multipath @@ -501,8 +589,6 @@ bin/systemctl --no-reload enable multipathd.service >/dev/null 2>&1 ||: %{_sbindir}/mpathconf %{_sbindir}/mpathpersist %{_unitdir}/multipathd.service -%{_mandir}/man3/mpath_persistent_reserve_in.3.gz -%{_mandir}/man3/mpath_persistent_reserve_out.3.gz %{_mandir}/man5/multipath.conf.5.gz %{_mandir}/man8/multipath.8.gz %{_mandir}/man8/multipathd.8.gz @@ -519,8 +605,8 @@ bin/systemctl --no-reload enable multipathd.service >/dev/null 2>&1 ||: %doc AUTHOR COPYING %{_libdir}/libmultipath.so %{_libdir}/libmultipath.so.* -%{_libdir}/libmpathpersist.so %{_libdir}/libmpathpersist.so.* +%{_libdir}/libmpathcmd.so.* %dir %{_libmpathdir} %{_libmpathdir}/* @@ -528,6 +614,16 @@ bin/systemctl --no-reload enable multipathd.service >/dev/null 2>&1 ||: %postun libs -p /sbin/ldconfig +%files devel +%defattr(-,root,root,-) +%doc AUTHOR COPYING +%{_libdir}/libmpathpersist.so +%{_libdir}/libmpathcmd.so +%{_includedir}/mpath_cmd.h +%{_includedir}/mpath_persist.h +%{_mandir}/man3/mpath_persistent_reserve_in.3.gz +%{_mandir}/man3/mpath_persistent_reserve_out.3.gz + %files sysvinit %{_initrddir}/multipathd @@ -536,21 +632,147 @@ bin/systemctl --no-reload enable multipathd.service >/dev/null 2>&1 ||: %{_sbindir}/kpartx %{_mandir}/man8/kpartx.8.gz +%files -n libdmmp +%defattr(-,root,root,-) +%doc AUTHOR COPYING +%{_libdir}/libdmmp.so.* + +%post -n libdmmp -p /sbin/ldconfig + +%postun -n libdmmp -p /sbin/ldconfig + +%files -n libdmmp-devel +%defattr(-,root,root,-) +%doc AUTHOR COPYING +%{_libdir}/libdmmp.so +%dir %{_includedir}/libdmmp +%{_includedir}/libdmmp/* +%{_mandir}/man3/dmmp_* +%{_mandir}/man3/libdmmp.h.3.gz +%{_pkgconfdir}/libdmmp.pc + %changelog -* Mon Apr 3 2017 Benjamin Marzinski 0.4.9-99.3 -- Modify 0190-RHBZ-1429885-max-sectors-kb.patch - * make max_sectors_kb only update the device sysfs paramter on creates. -- Resolves: bz #1429885 - -* Wed Mar 8 2017 Benjamin Marzinski 0.4.9-99.2 -- Add 0190-RHBZ-1429885-max-sectors-kb.patch - * add new max_sectors_kb multipath.conf parameter -- Resolves: bz #1429885 - -* Wed Sep 7 2016 Benjamin Marzinski 0.4.9-99.1 -- Add 0189-RHBZ-1395298-rbd-lock-on-read.patch +* Mon May 15 2017 Benjamin Marzinski 0.4.9-111 +- Remove 0217-RHBZ-1437329-blacklist-oracle-devs.patch + * Incorrect change, and the bug is already fixed. +- Move 0218-RHBZ-1448576-3PAR-config.patch to + 0217-RHBZ-1448576-3PAR-config.patch +- Resolves: bz #1448576 + +* Fri May 12 2017 Benjamin Marzinski 0.4.9-110 +- Add 0215-RHBZ-1444194-fix-check-partitions.patch + * make sure kpartx partions match the correct device +- Add 0216-RHBZ-1448562-fix-reserve.patch + * don't join threads that haven't been created +- Add 0217-RHBZ-1437329-blacklist-oracle-devs.patch + * blacklist db2.* devices +- Add 0218-RHBZ-1448576-3PAR-config.patch +- Resolves: bz #1444194, #1448562, #1437329, #1448576 + +* Tue Apr 25 2017 Benjamin Marzinski 0.4.9-109 +- Add 0214-RHBZ-1392115-set-paths-not-ready.patch + * Set ENV{SYSTEMD_READY}="0" on multipath path devices +- Resolves: bz #1392115 + +* Tue Apr 25 2017 Benjamin Marzinski 0.4.9-108 +- Add 0213-RHBZ-1430908-merge-dell-configs.patch +- Resolves: bz #1430908 + +* Mon Apr 3 2017 Benjamin Marzinski 0.4.9-107 +- Modify 0197-RHBZ-1394059-max-sectors-kb.patch + * Make multipath only change max_sectors_kb on creates. On reloads, it + just makes sure the new path matches the multipath device. +- Refresh 0198-RHBZ-1372032-detect-path-checker.patch +- Refresh 0201-RHBZ-1403552-silence-warning.patch +- Refresh 0206-RHBZ-1239173-pt2-no-paths.patch +- Refresh 0207-UP-add-libmpathcmd.patch +- Refresh 0212-RHBZ-1431562-for-read-only.patch +- Resolves: bz #1394059 + + +* Fri Mar 24 2017 Benjamin Marzinski 0.4.9-106 +- Add 0212-RHBZ-1431562-for-read-only.patch +- Resolves: bz #1431562 + +* Fri Mar 10 2017 Benjamin Marzinski 0.4.9-105 +- fix specfile issue +- Related: bz #1430097 + +* Thu Mar 9 2017 Benjamin Marzinski 0.4.9-104 +- Change _pkgconfdir from /usr/share/pkgconfig to /usr/lib/pkgconfig +- Modify 0209-UPBZ-1430097-multipath-C-API.patch + * change _pkgconfdir and fixed double-closing fd +- Add 0211-RH-strlen-fix.patch + * checks that variables are not NULL before passing them to strlen +- Related: bz #1430097 + +* Thu Mar 9 2017 Benjamin Marzinski 0.4.9-103 +- Add more explicit Requires to subpackages to make rpmdiff happy +- Related: bz #1430097 + +* Tue Mar 7 2017 Benjamin Marzinski 0.4.9-102 +- Add 0207-UP-add-libmpathcmd.patch + * New shared library, libmpathcmd, that sends and receives messages from + multipathd. device-mapper-multipath now uses this library internally. +- Add 0208-UPBZ-1430097-multipathd-IPC-changes.patch + * validation that modifying commands are coming from root. +- Add 0209-UPBZ-1430097-multipath-C-API.patch + * New shared library. libdmmp, that presents the information from multipathd + in a structured manner to make it easier for callers to use +- Add 0210-RH-fix-uninstall.patch + * Minor compilation fixes +- Make 3 new subpackages + * device-mapper-multipath-devel, libdmmp, and libdmmp-devel. libmpathcmd + and libmpathprio are in device-mapper-multipath-libs and + device-mapper-multipath-devel. libdmmp is in its own subpackages +- Move libmpathprio devel files to device-mapper-multipath-devel +- Resolves: bz #1430097 + +* Wed Feb 15 2017 Benjamin Marzinski 0.4.9-101 +- Modify 0166-RHBZ-1323429-dont-allow-new-wwid.patch + * change print message +- Add 0191-RHBZ-1169168-disable-changed-paths.patch + * add "disabled_changed_wwids" multipath.conf parameter to disable + paths whose wwid changes +- Add 0192-RHBZ-1362409-infinibox-config.patch +- Add 0194-RHBZ-1351964-kpartx-recurse.patch + * fix recursion on corrupt dos partitions +- Add 0195-RHBZ-1359510-no-daemon-msg.patch + * print a messages when multipathd isn't running +- Add 0196-RHBZ-1239173-dont-set-flag.patch + * don't set reload flag on reloads when you gain your first + valid path +- Add 0197-RHBZ-1394059-max-sectors-kb.patch + * add "max_sectors_kb" multipath.conf parameter to set max_sectors_kb + on a multipath device and all its path devices +- Add 0198-RHBZ-1372032-detect-path-checker.patch + * add "detect_checker" multipath.conf parameter to detect ALUA arrays + and set the path checker to TUR +- Add 0199-RHBZ-1279355-3pardata-config.patch +- Add 0200-RHBZ-1402092-orphan-status.patch + * clear status on orphan paths +- Add 0201-RHBZ-1403552-silence-warning.patch +- Add 0202-RHBZ-1362120-skip-prio.patch + * don't run prio on failed paths +- Add 0203-RHBZ-1363718-add-msgs.patch +- Add 0204-RHBZ-1406226-nimble-config.patch +- Add 0205-RHBZ-1416569-reset-stats.patch + * add "reset maps stats" and "reset map stats" multipathd + interactive commands to reset the stats tracked by multipathd +- Add 0206-RHBZ-1239173-pt2-no-paths.patch + * make multipath correctly disable scanning and rules running when + it gets a uevent and there are not valid paths. +- Resolves: bz #1169168, #1239173, #1279355, #1359510, #1362120, #1362409 +- Resolves: bz #1363718, #1394059, #1351964, #1372032, #1402092, #1403552 +- Resolves: bz #1406226, #1416569 + +* Wed Sep 7 2016 Benjamin Marzinski 0.4.9-100 +- Add 0189-RHBZ-1368211-remove-retries.patch + * add "remove_retries" multipath.conf parameter to make multiple attempts + to remove a multipath device if it is busy. +- Add 0190-RHBZ-1380602-rbd-lock-on-read.patch * pass lock_on_read when remapping image -- Resolves: bz #1395298 +- Resolves: bz #1368211, #1380602 * Wed Sep 7 2016 Benjamin Marzinski 0.4.9-99 - Add 0188-RHBZ-1368501-dont-exit.patch