Imported Upstream version 0.8.5 upstream/0.8.5
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:19 +0000 (13:50 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:19 +0000 (13:50 +0900)
91 files changed:
Makefile
Makefile.inc
README [deleted file]
README.md [new file with mode: 0644]
kpartx/bsd.c
kpartx/dasd.c
kpartx/devmapper.c
kpartx/gpt.c
kpartx/kpartx.c
kpartx/kpartx.h
kpartx/kpartx.rules
libdmmp/libdmmp_private.h
libmpathpersist/mpath_persist.c
libmpathpersist/mpath_pr_ioctl.c
libmultipath/Makefile
libmultipath/alias.c
libmultipath/alias.h
libmultipath/blacklist.c
libmultipath/blacklist.h
libmultipath/checkers.c
libmultipath/checkers.h
libmultipath/checkers/Makefile
libmultipath/checkers/directio.c
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/configure.h
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/devmapper.h
libmultipath/dict.c
libmultipath/discovery.c
libmultipath/dmparser.c
libmultipath/dmparser.h
libmultipath/foreign.c
libmultipath/foreign/nvme.c
libmultipath/hwtable.c
libmultipath/io_err_stat.c
libmultipath/libsg.c [moved from libmultipath/checkers/libsg.c with 100% similarity]
libmultipath/libsg.h [moved from libmultipath/checkers/libsg.h with 100% similarity]
libmultipath/parser.c
libmultipath/pgpolicies.c
libmultipath/print.c
libmultipath/print.h
libmultipath/prioritizers/Makefile
libmultipath/prioritizers/alua_rtpg.c
libmultipath/prioritizers/alua_spc3.h
libmultipath/propsel.c
libmultipath/structs.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/structs_vec.h
libmultipath/sysfs.c
libmultipath/sysfs.h
libmultipath/uevent.c
libmultipath/uevent.h
libmultipath/util.c
libmultipath/util.h
libmultipath/valid.c [new file with mode: 0644]
libmultipath/valid.h [new file with mode: 0644]
libmultipath/vector.c
libmultipath/vector.h
libmultipath/version.h
libmultipath/wwids.c
mpathpersist/main.c
multipath/11-dm-mpath.rules
multipath/main.c
multipath/multipath.8
multipath/multipath.conf.5
multipathd/Makefile
multipathd/cli.c
multipathd/cli_handlers.c
multipathd/cli_handlers.h
multipathd/dmevents.c
multipathd/main.c
multipathd/main.h
multipathd/waiter.c
tests/Makefile
tests/README.md
tests/alias.c
tests/blacklist.c
tests/devt.c [new file with mode: 0644]
tests/directio.c
tests/hwtable.c
tests/parser.c
tests/test-lib.c
tests/test-log.c
tests/uevent.c
tests/util.c
tests/valid.c [new file with mode: 0644]
tests/vpd.c

index 1dee368..4a3491d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -28,8 +28,13 @@ all: $(BUILDDIRS)
 $(BUILDDIRS):
        $(MAKE) -C $@
 
-multipath multipathd mpathpersist: libmultipath
-mpathpersist:  libmpathpersist
+libmultipath libdmmp: libmpathcmd
+libmpathpersist multipath multipathd: libmultipath
+mpathpersist multipathd:  libmpathpersist
+
+libmultipath/checkers.install \
+       libmultipath/prioritizers.install \
+       libmultipath/foreign.install: libmultipath.install
 
 $(BUILDDIRS.clean):
        $(MAKE) -C ${@:.clean=} clean
@@ -47,6 +52,9 @@ uninstall: $(BUILDDIRS:=.uninstall)
 test:  all
        $(MAKE) -C tests
 
+valgrind-test: all
+       $(MAKE) -C tests valgrind
+
 .PHONY:        TAGS
 TAGS:
        etags -a libmultipath/*.c
index d4d1e0d..e05f3a9 100644 (file)
@@ -37,7 +37,7 @@ endif
 
 ifndef SYSTEMD
        ifeq ($(shell pkg-config --modversion libsystemd >/dev/null 2>&1 && echo 1), 1)
-               SYSTEMD = $(shell pkg-config --modversion libsystemd)
+               SYSTEMD = $(shell pkg-config --modversion libsystemd | awk '{print $$1}')
        else
                ifeq ($(shell systemctl --version >/dev/null 2>&1 && echo 1), 1)
                        SYSTEMD = $(shell systemctl --version 2> /dev/null | \
@@ -91,20 +91,20 @@ TEST_CC_OPTION = $(shell \
 
 STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
 ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
-WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered,)
+WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
 
-OPTFLAGS       = -O2 -g -pipe -Werror -Wall -Wextra -Wformat=2 -Werror=implicit-int \
+OPTFLAGS       := -O2 -g $(STACKPROT) --param=ssp-buffer-size=4
+WARNFLAGS      := -Werror -Wall -Wextra -Wformat=2 -Werror=implicit-int \
                  -Werror=implicit-function-declaration -Werror=format-security \
-                 $(WNOCLOBBERED) \
-                 -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) \
-                 $(STACKPROT) --param=ssp-buffer-size=4
+                 $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS)
 CPPFLAGS       := -Wp,-D_FORTIFY_SOURCE=2 
-CFLAGS         := $(OPTFLAGS) -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
-                  -MMD -MP $(CFLAGS)
+CFLAGS         := --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \
+                  -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
+                  -MMD -MP
 BIN_CFLAGS     = -fPIE -DPIE
 LIB_CFLAGS     = -fPIC
 SHARED_FLAGS   = -shared
-LDFLAGS                = -Wl,-z,relro -Wl,-z,now
+LDFLAGS                := $(LDFLAGS) -Wl,-z,relro -Wl,-z,now
 BIN_LDFLAGS    = -pie
 
 # Check whether a function with name $1 has been declared in header file $2.
diff --git a/README b/README
deleted file mode 100644 (file)
index 2fc4a81..0000000
--- a/README
+++ /dev/null
@@ -1,60 +0,0 @@
-       multipath-tools for Linux <http://christophe.varoqui.free.fr/>
-
-
-This package provides the following binaries to drive the Device Mapper
-multipathing driver:
-
-multipath - Device mapper target autoconfig.
-multipathd - Multipath daemon.
-mpathpersist - Manages SCSI persistent reservations on dm multipath devices.
-kpartx - Create device maps from partition tables.
-
-
-Releases
-========
-Tarballs are not generated anymore, to get a specific release do:
-git clone https://git.opensvc.com/multipath-tools/.git
-cd multipath-tools
-git tag
-git archive --format=tar.gz --prefix=multipath-tools-X.Y.Z/ X.Y.Z > ../multipath-tools-X.Y.Z.tar.gz
-
-Alternatively it may be obtained from gitweb, go to:
-https://git.opensvc.com/?p=multipath-tools/.git;a=tags
-select a release-tag and then click on "snapshot". Or get it with
-wget "https://git.opensvc.com/?p=multipath-tools/.git;a=snapshot;sf=tgz;h=refs/tags/X.Y.Z" -O multipath-tools-X.Y.Z.tar.gz
-
-
-Source code
-===========
-To get latest devel code: git clone https://git.opensvc.com/multipath-tools/.git
-Gitweb: https://git.opensvc.com/?p=multipath-tools/.git
-
-
-Add storage devices
-===================
-Follow the instructions in the libmultipath/hwtable.c header.
-
-
-Mailing list (subscribers-only)
-============
-To subscribe and archives: https://www.redhat.com/mailman/listinfo/dm-devel
-Searchable: https://marc.info/?l=dm-devel
-
-
-Changelog
-=========
-pre-0.4.5: https://web.archive.org/web/20070309224034/http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog
-post-0.4.5: https://git.opensvc.com/?p=multipath-tools/.git;a=log
-
-
-Maintainer
-==========
-Christophe Varoqui <christophe.varoqui@opensvc.com>
-Device-mapper development mailing list <dm-devel@redhat.com>
-
-Licence
-=======
-The multipath-tools source code is covered by several different
-licences. Refer to the individual source files for details.
-Source files which do not specify a licence are shipped under
-LGPL-2.0 (see LICENSES/LGPL-2.0).
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..3e3ef67
--- /dev/null
+++ b/README.md
@@ -0,0 +1,87 @@
+multipath-tools for Linux
+*************************
+
+https://github.com/opensvc/multipath-tools
+
+This package provides the following binaries to drive the Device Mapper multipathing driver:
+
+* multipath - Device mapper target autoconfig.
+* multipathd - Multipath daemon.
+* mpathpersist - Manages SCSI persistent reservations on dm multipath devices.
+* kpartx - Create device maps from partition tables.
+
+
+Releases
+========
+
+To get a specific X.Y.Z release, use one of the following method:
+
+
+Git
+---
+
+    git clone https://github.com/opensvc/multipath-tools.git
+    cd multipath-tools
+    git tag
+    git archive --format=tar.gz --prefix=multipath-tools-X.Y.Z/ X.Y.Z > ../multipath-tools-X.Y.Z.tar.gz
+
+
+Direct download
+---------------
+
+    wget "https://github.com/opensvc/multipath-tools/archive/X.Y.Z.tar.gz" -O multipath-tools-X.Y.Z.tar.gz
+
+
+Browser
+-------
+
+Go to: https://github.com/opensvc/multipath-tools/tags
+Select a release-tag and then click on "zip" or "tar.gz".
+
+
+Source code
+===========
+
+To get latest devel code:
+
+    git clone https://github.com/opensvc/multipath-tools.git
+
+Github page: https://github.com/opensvc/multipath-tools
+
+
+Add storage devices
+===================
+
+Follow the instructions in the `libmultipath/hwtable.c` header.
+
+
+Mailing list
+============
+
+(subscribers-only)
+To subscribe and archives: https://www.redhat.com/mailman/listinfo/dm-devel
+Searchable: https://marc.info/?l=dm-devel
+
+
+Changelog
+=========
+
+pre-0.4.5: https://web.archive.org/web/20070309224034/http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog
+post-0.4.5: https://github.com/opensvc/multipath-tools/commits/master
+
+
+Maintainer
+==========
+
+Christophe Varoqui <christophe.varoqui@opensvc.com>
+Device-mapper development mailing list <dm-devel@redhat.com>
+
+
+Licence
+=======
+
+The multipath-tools source code is covered by several different licences.
+Refer to the individual source files for details.
+Source files which do not specify a licence are shipped under LGPL-2.0
+(see `LICENSES/LGPL-2.0`).
+
index 0e661fb..950b0f9 100644 (file)
@@ -1,6 +1,7 @@
 #include "kpartx.h"
 #include <stdio.h>
 
+#define BSD_LABEL_OFFSET       64
 #define BSD_DISKMAGIC  (0x82564557UL)  /* The disk magic number */
 #define XBSD_MAXPARTITIONS     16
 #define BSD_FS_UNUSED          0
@@ -60,8 +61,19 @@ read_bsd_pt(int fd, struct slice all, struct slice *sp, unsigned int ns) {
                return -1;
 
        l = (struct bsd_disklabel *) bp;
-       if (l->d_magic != BSD_DISKMAGIC)
-               return -1;
+       if (l->d_magic != BSD_DISKMAGIC) {
+               /*
+                * BSD disklabels can also start 64 bytes offset from the
+                * start of the first sector
+                */
+               bp = getblock(fd, offset);
+               if (bp == NULL)
+                       return -1;
+
+               l = (struct bsd_disklabel *)(bp + 64);
+               if (l->d_magic != BSD_DISKMAGIC)
+                       return -1;
+       }
 
        max_partitions = 16;
        if (l->d_npartitions < max_partitions)
index 14b9d3a..f039864 100644 (file)
@@ -22,6 +22,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -117,13 +118,13 @@ read_dasd_pt(int fd, __attribute__((unused)) struct slice all,
 
                sprintf(pathname, "/dev/.kpartx-node-%u-%u",
                        (unsigned int)major(dev), (unsigned int)minor(dev));
-               if ((fd_dasd = open(pathname, O_RDONLY)) == -1) {
+               if ((fd_dasd = open(pathname, O_RDONLY | O_DIRECT)) == -1) {
                        /* Devicenode does not exist. Try to create one */
                        if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) {
                                /* Couldn't create a device node */
                                return -1;
                        }
-                       fd_dasd = open(pathname, O_RDONLY);
+                       fd_dasd = open(pathname, O_RDONLY | O_DIRECT);
                        /*
                         * The file will vanish when the last process (we)
                         * has ceased to access it.
@@ -175,7 +176,7 @@ read_dasd_pt(int fd, __attribute__((unused)) struct slice all,
         * Get volume label, extract name and type.
         */
 
-       if (!(data = (unsigned char *)malloc(blocksize)))
+       if (aligned_malloc((void **)&data, blocksize, NULL))
                goto out;
 
 
index 86731ea..3efd6df 100644 (file)
@@ -618,7 +618,7 @@ remove_partmap(const char *name, void *data)
 
        if (dm_get_opencount(name)) {
                if (rd->verbose)
-                       printf("%s is in use. Not removing", name);
+                       printf("%s is in use. Not removing\n", name);
                return 1;
        }
        if (!dm_simplecmd(DM_DEVICE_REMOVE, name, 0, 0)) {
index 785b34e..f7fefb7 100644 (file)
@@ -243,8 +243,7 @@ alloc_read_gpt_entries(int fd, gpt_header * gpt)
 
        if (!count) return NULL;
 
-       pte = (gpt_entry *)malloc(count);
-       if (!pte)
+       if (aligned_malloc((void **)&pte, get_sector_size(fd), &count))
                return NULL;
        memset(pte, 0, count);
 
@@ -269,12 +268,11 @@ static gpt_header *
 alloc_read_gpt_header(int fd, uint64_t lba)
 {
        gpt_header *gpt;
-       gpt = (gpt_header *)
-           malloc(sizeof (gpt_header));
-       if (!gpt)
+       size_t size = sizeof (gpt_header);
+       if (aligned_malloc((void **)&gpt, get_sector_size(fd), &size))
                return NULL;
-       memset(gpt, 0, sizeof (*gpt));
-       if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) {
+       memset(gpt, 0, size);
+       if (!read_lba(fd, lba, gpt, size)) {
                free(gpt);
                return NULL;
        }
@@ -498,6 +496,7 @@ find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes)
        gpt_header *pgpt = NULL, *agpt = NULL;
        gpt_entry *pptes = NULL, *aptes = NULL;
        legacy_mbr *legacymbr = NULL;
+       size_t size = sizeof(legacy_mbr);
        uint64_t lastlba;
        if (!gpt || !ptes)
                return 0;
@@ -526,11 +525,10 @@ find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes)
        }
 
        /* This will be added to the EFI Spec. per Intel after v1.02. */
-       legacymbr = malloc(sizeof (*legacymbr));
-       if (legacymbr) {
-               memset(legacymbr, 0, sizeof (*legacymbr));
-               read_lba(fd, 0, (uint8_t *) legacymbr,
-                        sizeof (*legacymbr));
+       if (aligned_malloc((void **)&legacymbr, get_sector_size(fd),
+                          &size) == 0) {
+               memset(legacymbr, 0, size);
+               read_lba(fd, 0, (uint8_t *) legacymbr, size);
                good_pmbr = is_pmbr_valid(legacymbr);
                free(legacymbr);
                legacymbr=NULL;
index d3620c5..4a0aae9 100644 (file)
@@ -19,6 +19,7 @@
  * cva, 2002-10-26
  */
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -41,7 +42,6 @@
 
 #define SIZE(a) (sizeof(a)/sizeof((a)[0]))
 
-#define READ_SIZE      1024
 #define MAXTYPES       64
 #define MAXSLICES      256
 #define DM_TARGET      "linear"
@@ -209,6 +209,23 @@ check_uuid(char *uuid, char *part_uuid, char **err_msg) {
        return 0;
 }
 
+static void *
+xmalloc (size_t size) {
+       void *t;
+
+       if (size == 0)
+               return NULL;
+
+       t = malloc (size);
+
+       if (t == NULL) {
+               fprintf(stderr, "Out of memory\n");
+               exit(1);
+       }
+
+       return t;
+}
+
 int
 main(int argc, char **argv){
        int i, j, m, n, op, off, arg, c, d, ro=0;
@@ -383,12 +400,12 @@ main(int argc, char **argv){
                mapname = device + off;
 
        if (delim == NULL) {
-               delim = malloc(DELIM_SIZE);
+               delim = xmalloc(DELIM_SIZE);
                memset(delim, 0, DELIM_SIZE);
                set_delimiter(mapname, delim);
        }
 
-       fd = open(device, O_RDONLY);
+       fd = open(device, O_RDONLY | O_DIRECT);
 
        if (fd == -1) {
                perror(device);
@@ -635,6 +652,8 @@ main(int argc, char **argv){
 
                                if (!dm_simplecmd(DM_DEVICE_REMOVE,
                                                  partname, 1, 0)) {
+                                       fprintf(stderr, "failed to remove %s",
+                                               partname);
                                        r++;
                                        continue;
                                }
@@ -668,31 +687,14 @@ end:
        return r;
 }
 
-void *
-xmalloc (size_t size) {
-       void *t;
-
-       if (size == 0)
-               return NULL;
-
-       t = malloc (size);
-
-       if (t == NULL) {
-               fprintf(stderr, "Out of memory\n");
-               exit(1);
-       }
-
-       return t;
-}
-
 /*
  * sseek: seek to specified sector
  */
 
 static int
-sseek(int fd, unsigned int secnr) {
+sseek(int fd, unsigned int secnr, int secsz) {
        off64_t in, out;
-       in = ((off64_t) secnr << 9);
+       in = ((off64_t) secnr * secsz);
        out = 1;
 
        if ((out = lseek64(fd, in, SEEK_SET)) != in)
@@ -703,6 +705,31 @@ sseek(int fd, unsigned int secnr) {
        return 0;
 }
 
+int
+aligned_malloc(void **mem_p, size_t align, size_t *size_p)
+{
+       static size_t pgsize = 0;
+       size_t size;
+       int err;
+
+       if (!mem_p || !align || (size_p && !*size_p))
+               return EINVAL;
+
+       if (!pgsize)
+               pgsize = getpagesize();
+
+       if (size_p)
+               size = ((*size_p + align - 1) / align) * align;
+       else
+               size = pgsize;
+
+       err = posix_memalign(mem_p, pgsize, size);
+       if (!err && size_p)
+               *size_p = size;
+       return err;
+}
+
+/* always in sector size blocks */
 static
 struct block {
        unsigned int secnr;
@@ -710,30 +737,39 @@ struct block {
        struct block *next;
 } *blockhead;
 
+/* blknr is always in 512 byte blocks */
 char *
-getblock (int fd, unsigned int secnr) {
+getblock (int fd, unsigned int blknr) {
+       int secsz = get_sector_size(fd);
+       unsigned int blks_per_sec = secsz / 512;
+       unsigned int secnr = blknr / blks_per_sec;
+       unsigned int blk_off = (blknr % blks_per_sec) * 512;
        struct block *bp;
 
        for (bp = blockhead; bp; bp = bp->next)
 
                if (bp->secnr == secnr)
-                       return bp->block;
+                       return bp->block + blk_off;
 
-       if (sseek(fd, secnr))
+       if (sseek(fd, secnr, secsz))
                return NULL;
 
        bp = xmalloc(sizeof(struct block));
        bp->secnr = secnr;
        bp->next = blockhead;
        blockhead = bp;
-       bp->block = (char *) xmalloc(READ_SIZE);
+       if (aligned_malloc((void **)&bp->block, secsz, NULL)) {
+               fprintf(stderr, "aligned_malloc failed\n");
+               exit(1);
+       }
 
-       if (read(fd, bp->block, READ_SIZE) != READ_SIZE) {
+       if (read(fd, bp->block, secsz) != secsz) {
                fprintf(stderr, "read error, sector %d\n", secnr);
-               bp->block = NULL;
+               blockhead = bp->next;
+               return NULL;
        }
 
-       return bp->block;
+       return bp->block + blk_off;
 }
 
 int
index 67edeb8..727632c 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _KPARTX_H
 #define _KPARTX_H
 
+#include <stddef.h>
 #include <stdint.h>
 #include <sys/ioctl.h>
 
@@ -61,6 +62,7 @@ extern ptreader read_mac_pt;
 extern ptreader read_sun_pt;
 extern ptreader read_ps3_pt;
 
+int aligned_malloc(void **mem_p, size_t align, size_t *size_p);
 char *getblock(int fd, unsigned int secnr);
 
 static inline unsigned int
index 8f99049..d7527d7 100644 (file)
@@ -7,13 +7,17 @@
 KERNEL!="dm-*", GOTO="kpartx_end"
 ACTION!="add|change", GOTO="kpartx_end"
 ENV{DM_UUID}!="?*", GOTO="kpartx_end"
+ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", GOTO="kpartx_end"
 
 # Create dm tables for partitions on multipath devices.
 ENV{DM_UUID}!="mpath-?*", GOTO="mpath_kpartx_end"
 
 # DM_SUBSYSTEM_UDEV_FLAG1 is the "skip_kpartx" flag.
-# For events not generated by libdevmapper, we need to fetch it from db.
-ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
+# For events not generated by libdevmapper, we need to fetch it from db:
+# - "change" events with DM_ACTIVATION!="1" (e.g. partition table changes)
+# - "add" events for which rules are not disabled ("coldplug" case)
+ENV{DM_ACTIVATION}!="1", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
+ACTION=="add", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
 ENV{DM_SUBSYSTEM_UDEV_FLAG1}=="1", GOTO="mpath_kpartx_end"
 
 # 11-dm-mpath.rules sets MPATH_UNCHANGED for events that can be ignored.
index ac85b63..b1a6dde 100644 (file)
@@ -30,6 +30,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <assert.h>
+#include <stdbool.h>
 #include <json.h>
 
 #include "libdmmp/libdmmp.h"
@@ -82,7 +83,7 @@ static out_type func_name(struct dmmp_context *ctx, const char *var_name) { \
 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) { \
+       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; \
index 3da7a6c..1f9817e 100644 (file)
@@ -65,52 +65,6 @@ mpath_lib_exit (struct config *conf)
        return 0;
 }
 
-static int
-updatepaths (struct multipath * mpp)
-{
-       int i, j;
-       struct pathgroup * pgp;
-       struct path * pp;
-       struct config *conf;
-
-       if (!mpp->pg)
-               return 0;
-
-       vector_foreach_slot (mpp->pg, pgp, i){
-               if (!pgp->paths)
-                       continue;
-
-               vector_foreach_slot (pgp->paths, pp, j){
-                       if (!strlen(pp->dev)){
-                               /*
-                                * path is not in sysfs anymore
-                                */
-                               pp->state = PATH_DOWN;
-                               continue;
-                       }
-                       pp->mpp = mpp;
-                       if (pp->udev == NULL) {
-                               pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
-                               if (pp->udev == NULL) {
-                                       pp->state = PATH_DOWN;
-                                       continue;
-                               }
-                               conf = get_multipath_config();
-                               pathinfo(pp, conf, DI_SYSFS|DI_CHECKER);
-                               put_multipath_config(conf);
-                               continue;
-                       }
-                       if (pp->state == PATH_UNCHECKED ||
-                                       pp->state == PATH_WILD) {
-                               conf = get_multipath_config();
-                               pathinfo(pp, conf, DI_CHECKER);
-                               put_multipath_config(conf);
-                       }
-               }
-       }
-       return 0;
-}
-
 int
 mpath_prin_activepath (struct multipath *mpp, int rq_servact,
        struct prin_resp * resp, int noisy)
@@ -369,7 +323,6 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid)
 {
        int i;
        struct multipath *mpp;
-       char params[PARAMS_SIZE], status[PARAMS_SIZE];
 
        vector_foreach_slot (curmp, mpp, i){
                /*
@@ -387,20 +340,12 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid)
                if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1))
                        continue;
 
-               dm_get_map(mpp->alias, &mpp->size, params);
-               condlog(3, "params = %s", params);
-               dm_get_status(mpp->alias, status);
-               condlog(3, "status = %s", status);
-               disassemble_map (pathvec, params, mpp, 0);
-
-               /*
-                * disassemble_map() can add new paths to pathvec.
-                * If not in "fast list mode", we need to fetch information
-                * about them
-                */
-               updatepaths(mpp);
-               disassemble_status (status, mpp);
-
+               if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
+                   update_multipath_status(mpp) != DMP_OK) {
+                       condlog(1, "error parsing map %s", mpp->wwid);
+                       remove_map(mpp, pathvec, curmp, PURGE_VEC);
+                       i--;
+               }
        }
        return MPATH_PR_SUCCESS ;
 }
@@ -436,7 +381,7 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
 
        all_tg_pt = (mpp->all_tg_pt == ALL_TG_PT_ON ||
                     paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK);
-       active_pathcount = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
+       active_pathcount = count_active_paths(mpp);
 
        if (active_pathcount == 0) {
                condlog (0, "%s: no path available", mpp->wwid);
@@ -648,7 +593,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
        if (!mpp)
                return MPATH_PR_DMMP_ERROR;
 
-       active_pathcount = pathcount (mpp, PATH_UP) + pathcount (mpp, PATH_GHOST);
+       active_pathcount = count_active_paths(mpp);
 
        struct threadinfo thread[active_pathcount];
        memset(thread, 0, sizeof(thread));
index 74b26b0..126601c 100644 (file)
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <stddef.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -138,38 +139,64 @@ retry :
        return status;
 }
 
+/*
+ * Helper macro to avoid overflow of prout_param_descriptor in
+ * format_transportids(). Data must not be written past
+ * MPATH_MAX_PARAM_LEN bytes from struct prout_param_descriptor.
+ */
+#define check_overflow(ofs, n, start, label)                           \
+       do {                                                            \
+               if ((ofs) + (n) +                                       \
+                   offsetof(struct prout_param_descriptor, private_buffer) \
+                   > MPATH_MAX_PARAM_LEN)                              \
+               {                                                       \
+                       (ofs) = (start);                                \
+                       goto label;                                     \
+               }                                                       \
+       } while(0)
+
 uint32_t  format_transportids(struct prout_param_descriptor *paramp)
 {
        unsigned int i = 0, len;
        uint32_t buff_offset = 4;
-       memset(paramp->private_buffer, 0, MPATH_MAX_PARAM_LEN);
+       memset(paramp->private_buffer, 0, sizeof(paramp->private_buffer));
        for (i=0; i < paramp->num_transportid; i++ )
        {
+               uint32_t start_offset = buff_offset;
+
+               check_overflow(buff_offset, 1, start_offset, end_loop);
                paramp->private_buffer[buff_offset] = (uint8_t)((paramp->trnptid_list[i]->format_code & 0xff)|
                                                        (paramp->trnptid_list[i]->protocol_id & 0xff));
                buff_offset += 1;
                switch(paramp->trnptid_list[i]->protocol_id)
                {
                        case MPATH_PROTOCOL_ID_FC:
+                               check_overflow(buff_offset, 7 + 8 + 8,
+                                              start_offset, end_loop);
                                buff_offset += 7;
                                memcpy(&paramp->private_buffer[buff_offset], &paramp->trnptid_list[i]->n_port_name, 8);
                                buff_offset +=8 ;
                                buff_offset +=8 ;
                                break;
                        case MPATH_PROTOCOL_ID_SAS:
+                               check_overflow(buff_offset, 3 + 12,
+                                              start_offset, end_loop);
                                buff_offset += 3;
                                memcpy(&paramp->private_buffer[buff_offset], &paramp->trnptid_list[i]->sas_address, 8);
                                buff_offset += 12;
                                break;
                        case MPATH_PROTOCOL_ID_ISCSI:
-                               buff_offset += 1;
                                len = (paramp->trnptid_list[i]->iscsi_name[1] & 0xff)+2;
+                               check_overflow(buff_offset, 1 + len,
+                                              start_offset, end_loop);
+                               buff_offset += 1;
                                memcpy(&paramp->private_buffer[buff_offset], &paramp->trnptid_list[i]->iscsi_name,len);
                                buff_offset += len ;
                                break;
                }
 
        }
+end_loop:
        buff_offset -= 4;
        paramp->private_buffer[0] = (unsigned char)((buff_offset >> 24) & 0xff);
        paramp->private_buffer[1] = (unsigned char)((buff_offset >> 16) & 0xff);
@@ -211,6 +238,8 @@ static void mpath_format_readfullstatus(struct prin_resp *pr_buff)
        uint32_t additional_length, k, tid_len_len = 0;
        char tempbuff[MPATH_MAX_PARAM_LEN];
        struct prin_fulldescr fdesc;
+       static const unsigned int pbuf_size =
+               sizeof(pr_buff->prin_descriptor.prin_readfd.private_buffer);
 
        convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readfd.prgeneration);
        convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readfd.number_of_descriptor);
@@ -222,16 +251,18 @@ static void mpath_format_readfullstatus(struct prin_resp *pr_buff)
        }
 
        additional_length = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor;
-       if (additional_length > MPATH_MAX_PARAM_LEN) {
+       if (additional_length > pbuf_size) {
                condlog(3, "PRIN length %u exceeds max length %d", additional_length,
-                       MPATH_MAX_PARAM_LEN);
+                       pbuf_size);
                return;
        }
 
        memset(&fdesc, 0, sizeof(struct prin_fulldescr));
 
-       memcpy( tempbuff, pr_buff->prin_descriptor.prin_readfd.private_buffer,MPATH_MAX_PARAM_LEN );
-       memset(&pr_buff->prin_descriptor.prin_readfd.private_buffer, 0, MPATH_MAX_PARAM_LEN);
+       memcpy( tempbuff, pr_buff->prin_descriptor.prin_readfd.private_buffer,
+               pbuf_size);
+       memset(&pr_buff->prin_descriptor.prin_readfd.private_buffer, 0,
+              pbuf_size);
 
        p =(unsigned char *)tempbuff;
        ppbuff = (char *)pr_buff->prin_descriptor.prin_readfd.private_buffer;
@@ -543,5 +574,7 @@ int get_prin_length(int rq_servact)
                        mx_resp_len = 0;
                        break;
        }
+       if (mx_resp_len > MPATH_MAX_PARAM_LEN)
+               mx_resp_len = MPATH_MAX_PARAM_LEN;
        return mx_resp_len;
 }
index e5651e4..62ba16e 100644 (file)
@@ -24,6 +24,10 @@ ifneq ($(call check_func,dm_task_no_flush,/usr/include/libdevmapper.h),0)
        CFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE
 endif
 
+ifneq ($(call check_func,dm_task_get_errno,/usr/include/libdevmapper.h),0)
+       CFLAGS += -DLIBDM_API_GET_ERRNO
+endif
+
 ifneq ($(call check_func,dm_task_set_cookie,/usr/include/libdevmapper.h),0)
        CFLAGS += -DLIBDM_API_COOKIE
 endif
@@ -36,6 +40,10 @@ ifneq ($(call check_func,dm_task_deferred_remove,/usr/include/libdevmapper.h),0)
        CFLAGS += -DLIBDM_API_DEFERRED
 endif
 
+ifneq ($(call check_func,dm_hold_control_dev,/usr/include/libdevmapper.h),0)
+       CFLAGS += -DLIBDM_API_HOLD_CONTROL
+endif
+
 OBJS = memory.o parser.o vector.o devmapper.o callout.o \
        hwtable.o blacklist.o util.o dmparser.o config.o \
        structs.o discovery.o propsel.o dict.o \
@@ -43,7 +51,8 @@ OBJS = memory.o parser.o vector.o devmapper.o callout.o \
        switchgroup.o uxsock.o print.o alias.o log_pthread.o \
        log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \
        lock.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \
-       io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o
+       io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o \
+       libsg.o valid.o
 
 all: $(LIBS)
 
index 14401ca..a7ba485 100644 (file)
@@ -4,6 +4,7 @@
  */
 #include <stdlib.h>
 #include <errno.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <limits.h>
@@ -17,6 +18,9 @@
 #include "vector.h"
 #include "checkers.h"
 #include "structs.h"
+#include "config.h"
+#include "util.h"
+#include "errno.h"
 
 
 /*
  * See the file COPYING included with this distribution for more details.
  */
 
+#define BINDINGS_FILE_HEADER           \
+"# Multipath bindings, Version : 1.0\n" \
+"# NOTE: this file is automatically maintained by the multipath program.\n" \
+"# You should not need to edit this file in normal circumstances.\n" \
+"#\n" \
+"# Format:\n" \
+"# alias wwid\n" \
+"#\n"
+
+static const char bindings_file_header[] = BINDINGS_FILE_HEADER;
+
 int
 valid_alias(const char *alias)
 {
@@ -126,14 +141,14 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
        rewind(f);
        while (fgets(buf, LINE_MAX, f)) {
                const char *alias, *wwid;
-               char *c;
+               char *c, *saveptr;
                int curr_id;
 
                line_nr++;
                c = strpbrk(buf, "#\n\r");
                if (c)
                        *c = '\0';
-               alias = strtok(buf, " \t");
+               alias = strtok_r(buf, " \t", &saveptr);
                if (!alias) /* blank line */
                        continue;
                curr_id = scan_devname(alias, prefix);
@@ -149,7 +164,7 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
                        biggest_id = curr_id;
                if (curr_id > id && curr_id < smallest_bigger_id)
                        smallest_bigger_id = curr_id;
-               wwid = strtok(NULL, " \t");
+               wwid = strtok_r(NULL, " \t", &saveptr);
                if (!wwid){
                        condlog(3,
                                "Ignoring malformed line %u in bindings file",
@@ -191,17 +206,17 @@ rlookup_binding(FILE *f, char *buff, const char *map_alias)
        buff[0] = '\0';
 
        while (fgets(line, LINE_MAX, f)) {
-               char *c;
+               char *c, *saveptr;
                const char *alias, *wwid;
 
                line_nr++;
                c = strpbrk(line, "#\n\r");
                if (c)
                        *c = '\0';
-               alias = strtok(line, " \t");
+               alias = strtok_r(line, " \t", &saveptr);
                if (!alias) /* blank line */
                        continue;
-               wwid = strtok(NULL, " \t");
+               wwid = strtok_r(NULL, " \t", &saveptr);
                if (!wwid){
                        condlog(3,
                                "Ignoring malformed line %u in bindings file",
@@ -215,7 +230,7 @@ rlookup_binding(FILE *f, char *buff, const char *map_alias)
                }
                if (strcmp(alias, map_alias) == 0){
                        condlog(3, "Found matching alias [%s] in bindings file."
-                               "\nSetting wwid to %s", alias, wwid);
+                               " Setting wwid to %s", alias, wwid);
                        strlcpy(buff, wwid, WWID_SIZE);
                        return 0;
                }
@@ -287,7 +302,7 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
        char buff[WWID_SIZE];
        FILE *f;
 
-       fd = open_file(file, &can_write, BINDINGS_FILE_HEADER);
+       fd = open_file(file, &can_write, bindings_file_header);
        if (fd < 0)
                return NULL;
 
@@ -361,7 +376,7 @@ get_user_friendly_alias(const char *wwid, const char *file, const char *prefix,
                return NULL;
        }
 
-       fd = open_file(file, &can_write, BINDINGS_FILE_HEADER);
+       fd = open_file(file, &can_write, bindings_file_header);
        if (fd < 0)
                return NULL;
 
@@ -406,7 +421,7 @@ get_user_friendly_wwid(const char *alias, char *buff, const char *file)
                return -1;
        }
 
-       fd = open_file(file, &unused, BINDINGS_FILE_HEADER);
+       fd = open_file(file, &unused, bindings_file_header);
        if (fd < 0)
                return -1;
 
@@ -427,3 +442,264 @@ get_user_friendly_wwid(const char *alias, char *buff, const char *file)
        fclose(f);
        return 0;
 }
+
+struct binding {
+       char *alias;
+       char *wwid;
+};
+
+static void _free_binding(struct binding *bdg)
+{
+       free(bdg->wwid);
+       free(bdg->alias);
+       free(bdg);
+}
+
+/*
+ * Perhaps one day we'll implement this more efficiently, thus use
+ * an abstract type.
+ */
+typedef struct _vector Bindings;
+
+static void free_bindings(Bindings *bindings)
+{
+       struct binding *bdg;
+       int i;
+
+       vector_foreach_slot(bindings, bdg, i)
+               _free_binding(bdg);
+       vector_reset(bindings);
+}
+
+enum {
+       BINDING_EXISTS,
+       BINDING_CONFLICT,
+       BINDING_ADDED,
+       BINDING_DELETED,
+       BINDING_NOTFOUND,
+       BINDING_ERROR,
+};
+
+static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
+{
+       struct binding *bdg;
+       int i, cmp = 0;
+
+       /*
+        * Keep the bindings array sorted by alias.
+        * Optimization: Search backwards, assuming that the bindings file is
+        * sorted already.
+        */
+       vector_foreach_slot_backwards(bindings, bdg, i) {
+               if ((cmp = strcmp(bdg->alias, alias)) <= 0)
+                       break;
+       }
+
+       /* Check for exact match */
+       if (i >= 0 && cmp == 0)
+               return strcmp(bdg->wwid, wwid) ?
+                       BINDING_CONFLICT : BINDING_EXISTS;
+
+       i++;
+       bdg = calloc(1, sizeof(*bdg));
+       if (bdg) {
+               bdg->wwid = strdup(wwid);
+               bdg->alias = strdup(alias);
+               if (bdg->wwid && bdg->alias &&
+                   vector_insert_slot(bindings, i, bdg))
+                       return BINDING_ADDED;
+               else
+                       _free_binding(bdg);
+       }
+
+       return BINDING_ERROR;
+}
+
+static int write_bindings_file(const Bindings *bindings, int fd)
+{
+       struct binding *bnd;
+       char line[LINE_MAX];
+       int i;
+
+       if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
+           != sizeof(BINDINGS_FILE_HEADER) - 1)
+               return -1;
+
+       vector_foreach_slot(bindings, bnd, i) {
+               int len;
+
+               len = snprintf(line, sizeof(line), "%s %s\n",
+                              bnd->alias, bnd->wwid);
+
+               if (len < 0 || (size_t)len >= sizeof(line)) {
+                       condlog(1, "%s: line overflow", __func__);
+                       return -1;
+               }
+
+               if (write(fd, line, len) != len)
+                       return -1;
+       }
+       return 0;
+}
+
+static int fix_bindings_file(const struct config *conf,
+                            const Bindings *bindings)
+{
+       int rc;
+       long fd;
+       char tempname[PATH_MAX];
+
+       if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file))
+               return -1;
+       if ((fd = mkstemp(tempname)) == -1) {
+               condlog(1, "%s: mkstemp: %m", __func__);
+               return -1;
+       }
+       pthread_cleanup_push(close_fd, (void*)fd);
+       rc = write_bindings_file(bindings, fd);
+       pthread_cleanup_pop(1);
+       if (rc == -1) {
+               condlog(1, "failed to write new bindings file %s",
+                       tempname);
+               unlink(tempname);
+               return rc;
+       }
+       if ((rc = rename(tempname, conf->bindings_file)) == -1)
+               condlog(0, "%s: rename: %m", __func__);
+       else
+               condlog(1, "updated bindings file %s", conf->bindings_file);
+       return rc;
+}
+
+static int _check_bindings_file(const struct config *conf, FILE *file,
+                                Bindings *bindings)
+{
+       int rc = 0;
+       unsigned int linenr = 0;
+       char *line = NULL;
+       size_t line_len = 0;
+       ssize_t n;
+
+       pthread_cleanup_push(cleanup_free_ptr, &line);
+       while ((n = getline(&line, &line_len, file)) >= 0) {
+               char *c, *alias, *wwid, *saveptr;
+               const char *mpe_wwid;
+
+               linenr++;
+               c = strpbrk(line, "#\n\r");
+               if (c)
+                       *c = '\0';
+               alias = strtok_r(line, " \t", &saveptr);
+               if (!alias) /* blank line */
+                       continue;
+               wwid = strtok_r(NULL, " \t", &saveptr);
+               if (!wwid) {
+                       condlog(1, "invalid line %d in bindings file, missing WWID",
+                               linenr);
+                       continue;
+               }
+               c = strtok_r(NULL, " \t", &saveptr);
+               if (c)
+                       /* This is non-fatal */
+                       condlog(1, "invalid line %d in bindings file, extra args \"%s\"",
+                               linenr, c);
+
+               mpe_wwid = get_mpe_wwid(conf->mptable, alias);
+               if (mpe_wwid && strcmp(mpe_wwid, wwid)) {
+                       condlog(0, "ERROR: alias \"%s\" for WWID %s in bindings file "
+                               "on line %u conflicts with multipath.conf entry for %s",
+                               alias, wwid, linenr, mpe_wwid);
+                       rc = -1;
+                       continue;
+               }
+
+               switch (add_binding(bindings, alias, wwid)) {
+               case BINDING_CONFLICT:
+                       condlog(0, "ERROR: multiple bindings for alias \"%s\" in "
+                               "bindings file on line %u, discarding binding to WWID %s",
+                               alias, linenr, wwid);
+                       rc = -1;
+                       break;
+               case BINDING_EXISTS:
+                       condlog(2, "duplicate line for alias %s in bindings file on line %u",
+                               alias, linenr);
+                       break;
+               case BINDING_ERROR:
+                       condlog(2, "error adding binding %s -> %s",
+                               alias, wwid);
+                       break;
+               default:
+                       break;
+               }
+       }
+       pthread_cleanup_pop(1);
+       return rc;
+}
+
+static void cleanup_fclose(void *p)
+{
+       fclose(p);
+}
+
+/*
+ * check_alias_settings(): test for inconsistent alias configuration
+ *
+ * It's a fatal configuration error if the same alias is assigned to
+ * multiple WWIDs. In the worst case, it can cause data corruption
+ * by mangling devices with different WWIDs into the same multipath map.
+ * This function tests the configuration from multipath.conf and the
+ * bindings file for consistency, drops inconsistent multipath.conf
+ * alias settings, and rewrites the bindings file if necessary, dropping
+ * conflicting lines (if user_friendly_names is on, multipathd will
+ * fill in the deleted lines with a newly generated alias later).
+ * Note that multipath.conf is not rewritten. Use "multipath -T" for that.
+ *
+ * Returns: 0 in case of success, -1 if the configuration was bad
+ * and couldn't be fixed.
+ */
+int check_alias_settings(const struct config *conf)
+{
+       int can_write;
+       int rc = 0, i, fd;
+       Bindings bindings = {.allocated = 0, };
+       struct mpentry *mpe;
+
+       pthread_cleanup_push_cast(free_bindings, &bindings);
+       vector_foreach_slot(conf->mptable, mpe, i) {
+               if (!mpe->wwid || !mpe->alias)
+                       continue;
+               if (add_binding(&bindings, mpe->alias, mpe->wwid) ==
+                   BINDING_CONFLICT) {
+                       condlog(0, "ERROR: alias \"%s\" bound to multiple wwids in multipath.conf, "
+                               "discarding binding to %s",
+                               mpe->alias, mpe->wwid);
+                       free(mpe->alias);
+                       mpe->alias = NULL;
+               }
+       }
+       /* This clears the bindings */
+       pthread_cleanup_pop(1);
+
+       pthread_cleanup_push_cast(free_bindings, &bindings);
+       fd = open_file(conf->bindings_file, &can_write, BINDINGS_FILE_HEADER);
+       if (fd != -1) {
+               FILE *file = fdopen(fd, "r");
+
+               if (file != NULL) {
+                       pthread_cleanup_push(cleanup_fclose, file);
+                       rc = _check_bindings_file(conf, file, &bindings);
+                       pthread_cleanup_pop(1);
+                       if (rc == -1 && can_write && !conf->bindings_read_only)
+                               rc = fix_bindings_file(conf, &bindings);
+                       else if (rc == -1)
+                               condlog(0, "ERROR: bad settings in read-only bindings file %s",
+                                       conf->bindings_file);
+               } else {
+                       condlog(1, "failed to fdopen %s: %m",
+                               conf->bindings_file);
+                       close(fd);
+               }
+       }
+       pthread_cleanup_pop(1);
+       return rc;
+}
index 7c4b302..dbc950c 100644 (file)
@@ -1,11 +1,5 @@
-#define BINDINGS_FILE_HEADER \
-"# Multipath bindings, Version : 1.0\n" \
-"# NOTE: this file is automatically maintained by the multipath program.\n" \
-"# You should not need to edit this file in normal circumstances.\n" \
-"#\n" \
-"# Format:\n" \
-"# alias wwid\n" \
-"#\n"
+#ifndef _ALIAS_H
+#define _ALIAS_H
 
 int valid_alias(const char *alias);
 char *get_user_friendly_alias(const char *wwid, const char *file,
@@ -15,3 +9,8 @@ int get_user_friendly_wwid(const char *alias, char *buff, const char *file);
 char *use_existing_alias (const char *wwid, const char *file,
                          const char *alias_old,
                          const char *prefix, int bindings_read_only);
+
+struct config;
+int check_alias_settings(const struct config *);
+
+#endif /* _ALIAS_H */
index 00e8dbd..6c6a597 100644 (file)
 #include "structs_vec.h"
 #include "print.h"
 
-int store_ble(vector blist, char * str, int origin)
+char *check_invert(char *str, bool *invert)
+{
+       if (str[0] == '!') {
+               *invert = true;
+               return str + 1;
+       }
+       if (str[0] == '\\' && str[1] == '!') {
+               *invert = false;
+               return str + 1;
+       }
+       *invert = false;
+       return str;
+}
+
+int store_ble(vector blist, const char *str, int origin)
 {
        struct blentry * ble;
+       char *regex_str;
+       char *strdup_str = NULL;
 
        if (!str)
                return 0;
 
+       strdup_str = strdup(str);
+       if (!strdup_str)
+               return 1;
+
        if (!blist)
                goto out;
 
@@ -30,32 +50,37 @@ int store_ble(vector blist, char * str, int origin)
        if (!ble)
                goto out;
 
-       if (regcomp(&ble->regex, str, REG_EXTENDED|REG_NOSUB))
+       regex_str = check_invert(strdup_str, &ble->invert);
+       if (regcomp(&ble->regex, regex_str, REG_EXTENDED|REG_NOSUB))
                goto out1;
 
        if (!vector_alloc_slot(blist))
                goto out1;
 
-       ble->str = str;
+       ble->str = strdup_str;
        ble->origin = origin;
        vector_set_slot(blist, ble);
        return 0;
 out1:
        FREE(ble);
 out:
-       FREE(str);
+       FREE(strdup_str);
        return 1;
 }
 
 
 int alloc_ble_device(vector blist)
 {
-       struct blentry_device * ble = MALLOC(sizeof(struct blentry_device));
+       struct blentry_device *ble;
 
+       if (!blist)
+               return 1;
+
+       ble = MALLOC(sizeof(struct blentry_device));
        if (!ble)
                return 1;
 
-       if (!blist || !vector_alloc_slot(blist)) {
+       if (!vector_alloc_slot(blist)) {
                FREE(ble);
                return 1;
        }
@@ -63,9 +88,12 @@ int alloc_ble_device(vector blist)
        return 0;
 }
 
-int set_ble_device(vector blist, char * vendor, char * product, int origin)
+int set_ble_device(vector blist, const char *vendor, const char *product, int origin)
 {
        struct blentry_device * ble;
+       char *regex_str;
+       char *vendor_str = NULL;
+       char *product_str = NULL;
 
        if (!blist)
                return 1;
@@ -76,79 +104,56 @@ int set_ble_device(vector blist, char * vendor, char * product, int origin)
                return 1;
 
        if (vendor) {
-               if (regcomp(&ble->vendor_reg, vendor,
-                           REG_EXTENDED|REG_NOSUB)) {
-                       FREE(vendor);
-                       if (product)
-                               FREE(product);
-                       return 1;
-               }
-               ble->vendor = vendor;
+               vendor_str = STRDUP(vendor);
+               if (!vendor_str)
+                       goto out;
+
+               regex_str = check_invert(vendor_str, &ble->vendor_invert);
+               if (regcomp(&ble->vendor_reg, regex_str, REG_EXTENDED|REG_NOSUB))
+                       goto out;
+
+               ble->vendor = vendor_str;
        }
        if (product) {
-               if (regcomp(&ble->product_reg, product,
-                           REG_EXTENDED|REG_NOSUB)) {
-                       FREE(product);
-                       if (vendor) {
-                               ble->vendor = NULL;
-                               FREE(vendor);
-                       }
-                       return 1;
-               }
-               ble->product = product;
-       }
-       ble->origin = origin;
-       return 0;
-}
+               product_str = STRDUP(product);
+               if (!product_str)
+                       goto out1;
 
-int
-_blacklist_exceptions (vector elist, const char * str)
-{
-       int i;
-       struct blentry * ele;
+               regex_str = check_invert(product_str, &ble->product_invert);
+               if (regcomp(&ble->product_reg, regex_str, REG_EXTENDED|REG_NOSUB))
+                       goto out1;
 
-       vector_foreach_slot (elist, ele, i) {
-               if (!regexec(&ele->regex, str, 0, NULL, 0))
-                       return 1;
+               ble->product = product_str;
        }
+       ble->origin = origin;
        return 0;
+out1:
+       if (vendor) {
+               regfree(&ble->vendor_reg);
+               ble->vendor = NULL;
+       }
+out:
+       free(vendor_str);
+       free(product_str);
+       return 1;
 }
 
-int
-_blacklist (vector blist, const char * str)
+static int
+match_reglist (const struct _vector *blist, const char *str)
 {
        int i;
        struct blentry * ble;
 
        vector_foreach_slot (blist, ble, i) {
-               if (!regexec(&ble->regex, str, 0, NULL, 0))
-                       return 1;
-       }
-       return 0;
-}
-
-int
-_blacklist_exceptions_device(const struct _vector *elist, const char * vendor,
-                            const char * product)
-{
-       int i;
-       struct blentry_device * ble;
-
-       vector_foreach_slot (elist, ble, i) {
-               if (!ble->vendor && !ble->product)
-                       continue;
-               if ((!ble->vendor ||
-                    !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
-                   (!ble->product ||
-                    !regexec(&ble->product_reg, product, 0, NULL, 0)))
+               if (!!regexec(&ble->regex, str, 0, NULL, 0) == ble->invert)
                        return 1;
        }
        return 0;
 }
 
-int
-_blacklist_device (const struct _vector *blist, const char * vendor,
-                  const char * product)
+static int
+match_reglist_device (const struct _vector *blist, const char *vendor,
+                     const char * product)
 {
        int i;
        struct blentry_device * ble;
@@ -157,17 +162,19 @@ _blacklist_device (const struct _vector *blist, const char * vendor,
                if (!ble->vendor && !ble->product)
                        continue;
                if ((!ble->vendor ||
-                    !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
+                    !!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) ==
+                    ble->vendor_invert) &&
                    (!ble->product ||
-                    !regexec(&ble->product_reg, product, 0, NULL, 0)))
+                    !!regexec(&ble->product_reg, product, 0, NULL, 0) ==
+                    ble->product_invert))
                        return 1;
        }
        return 0;
 }
 
 static int
-find_blacklist_device (const struct _vector *blist, const char * vendor,
-                      const char * product)
+find_blacklist_device (const struct _vector *blist, const char *vendor,
+                      const char *product)
 {
        int i;
        struct blentry_device * ble;
@@ -189,25 +196,12 @@ setup_default_blist (struct config * conf)
 {
        struct blentry * ble;
        struct hwentry *hwe;
-       char * str;
        int i;
 
-       str = STRDUP("^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]");
-       if (!str)
-               return 1;
-       if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
-               return 1;
-
-       str = STRDUP("^(td|hd|vd)[a-z]");
-       if (!str)
-               return 1;
-       if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
+       if (store_ble(conf->blist_devnode, "!^(sd[a-z]|dasd[a-z]|nvme[0-9])", ORIGIN_DEFAULT))
                return 1;
 
-       str = STRDUP("(SCSI_IDENT_|ID_WWN)");
-       if (!str)
-               return 1;
-       if (store_ble(conf->elist_property, str, ORIGIN_DEFAULT))
+       if (store_ble(conf->elist_property, "(SCSI_IDENT_|ID_WWN)", ORIGIN_DEFAULT))
                return 1;
 
        vector_foreach_slot (conf->hwtable, hwe, i) {
@@ -219,9 +213,7 @@ setup_default_blist (struct config * conf)
                                return 1;
                        ble = VECTOR_SLOT(conf->blist_device,
                                          VECTOR_SIZE(conf->blist_device) - 1);
-                       if (set_ble_device(conf->blist_device,
-                                          STRDUP(hwe->vendor),
-                                          STRDUP(hwe->bl_product),
+                       if (set_ble_device(conf->blist_device, hwe->vendor, hwe->bl_product,
                                           ORIGIN_DEFAULT)) {
                                FREE(ble);
                                vector_del_slot(conf->blist_device, VECTOR_SIZE(conf->blist_device) - 1);
@@ -248,8 +240,9 @@ setup_default_blist (struct config * conf)
                condlog(lvl, "%s: %s %s", dev, (M), (S))
 
 static void
-log_filter (const char *dev, char *vendor, char *product, char *wwid,
-           const char *env, const char *protocol, int r, int lvl)
+log_filter (const char *dev, const char *vendor, const char *product,
+           const char *wwid, const char *env, const char *protocol,
+           int r, int lvl)
 {
        /*
         * Try to sort from most likely to least.
@@ -294,15 +287,15 @@ log_filter (const char *dev, char *vendor, char *product, char *wwid,
 }
 
 int
-filter_device (vector blist, vector elist, char * vendor, char * product,
-              char * dev)
+filter_device (const struct _vector *blist, const struct _vector *elist,
+              const char *vendor, const char * product, const char *dev)
 {
        int r = MATCH_NOTHING;
 
        if (vendor && product) {
-               if (_blacklist_exceptions_device(elist, vendor, product))
+               if (match_reglist_device(elist, vendor, product))
                        r = MATCH_DEVICE_BLIST_EXCEPT;
-               else if (_blacklist_device(blist, vendor, product))
+               else if (match_reglist_device(blist, vendor, product))
                        r = MATCH_DEVICE_BLIST;
        }
 
@@ -311,14 +304,15 @@ filter_device (vector blist, vector elist, char * vendor, char * product,
 }
 
 int
-filter_devnode (vector blist, vector elist, char * dev)
+filter_devnode (const struct _vector *blist, const struct _vector *elist,
+               const char *dev)
 {
        int r = MATCH_NOTHING;
 
        if (dev) {
-               if (_blacklist_exceptions(elist, dev))
+               if (match_reglist(elist, dev))
                        r = MATCH_DEVNODE_BLIST_EXCEPT;
-               else if (_blacklist(blist, dev))
+               else if (match_reglist(blist, dev))
                        r = MATCH_DEVNODE_BLIST;
        }
 
@@ -327,14 +321,15 @@ filter_devnode (vector blist, vector elist, char * dev)
 }
 
 int
-filter_wwid (vector blist, vector elist, char * wwid, char * dev)
+filter_wwid (const struct _vector *blist, const struct _vector *elist,
+            const char *wwid, const char *dev)
 {
        int r = MATCH_NOTHING;
 
        if (wwid) {
-               if (_blacklist_exceptions(elist, wwid))
+               if (match_reglist(elist, wwid))
                        r = MATCH_WWID_BLIST_EXCEPT;
-               else if (_blacklist(blist, wwid))
+               else if (match_reglist(blist, wwid))
                        r = MATCH_WWID_BLIST;
        }
 
@@ -343,7 +338,8 @@ filter_wwid (vector blist, vector elist, char * wwid, char * dev)
 }
 
 int
-filter_protocol(vector blist, vector elist, struct path * pp)
+filter_protocol(const struct _vector *blist, const struct _vector *elist,
+               const struct path *pp)
 {
        char buf[PROTOCOL_BUF_SIZE];
        int r = MATCH_NOTHING;
@@ -351,9 +347,9 @@ filter_protocol(vector blist, vector elist, struct path * pp)
        if (pp) {
                snprint_path_protocol(buf, sizeof(buf), pp);
 
-               if (_blacklist_exceptions(elist, buf))
+               if (match_reglist(elist, buf))
                        r = MATCH_PROTOCOL_BLIST_EXCEPT;
-               else if (_blacklist(blist, buf))
+               else if (match_reglist(blist, buf))
                        r = MATCH_PROTOCOL_BLIST;
        }
 
@@ -362,7 +358,7 @@ filter_protocol(vector blist, vector elist, struct path * pp)
 }
 
 int
-filter_path (struct config * conf, struct path * pp)
+filter_path (const struct config *conf, const struct path *pp)
 {
        int r;
 
@@ -384,8 +380,8 @@ filter_path (struct config * conf, struct path * pp)
 }
 
 int
-filter_property(struct config *conf, struct udev_device *udev, int lvl,
-               const char *uid_attribute)
+filter_property(const struct config *conf, struct udev_device *udev,
+               int lvl, const char *uid_attribute)
 {
        const char *devname = udev_device_get_sysname(udev);
        struct udev_list_entry *list_entry;
@@ -422,11 +418,11 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl,
                        if (check_missing_prop && !strcmp(env, uid_attribute))
                                uid_attr_seen = true;
 
-                       if (_blacklist_exceptions(conf->elist_property, env)) {
+                       if (match_reglist(conf->elist_property, env)) {
                                r = MATCH_PROPERTY_BLIST_EXCEPT;
                                break;
                        }
-                       if (_blacklist(conf->blist_property, env)) {
+                       if (match_reglist(conf->blist_property, env)) {
                                r = MATCH_PROPERTY_BLIST;
                                break;
                        }
index 2d721f6..dde5cea 100644 (file)
@@ -20,6 +20,7 @@
 struct blentry {
        char * str;
        regex_t regex;
+       bool invert;
        int origin;
 };
 
@@ -28,19 +29,26 @@ struct blentry_device {
        char * product;
        regex_t vendor_reg;
        regex_t product_reg;
+       bool vendor_invert;
+       bool product_invert;
        int origin;
 };
 
 int setup_default_blist (struct config *);
 int alloc_ble_device (vector);
-int filter_devnode (vector, vector, char *);
-int filter_wwid (vector, vector, char *, char *);
-int filter_device (vector, vector, char *, char *, char *);
-int filter_path (struct config *, struct path *);
-int filter_property(struct config *, struct udev_device *, int, const char*);
-int filter_protocol(vector, vector, struct path *);
-int store_ble (vector, char *, int);
-int set_ble_device (vector, char *, char *, int);
+int filter_devnode (const struct _vector *, const struct _vector *,
+                   const char *);
+int filter_wwid (const struct _vector *, const struct _vector *,
+                const char *, const char *);
+int filter_device (const struct _vector *, const struct _vector *,
+                  const char *, const char *, const char *);
+int filter_path (const struct config *, const struct path *);
+int filter_property(const struct config *, struct udev_device *,
+                   int, const char*);
+int filter_protocol(const struct _vector *, const struct _vector *,
+                   const struct path *);
+int store_ble (vector, const char *, int);
+int set_ble_device (vector, const char *, const char *, int);
 void free_blacklist (vector);
 void free_blacklist_device (vector);
 void merge_blacklist(vector);
index 8d2be8a..f7ddd53 100644 (file)
@@ -23,23 +23,27 @@ struct checker_class {
        short msgtable_size;
 };
 
-char *checker_state_names[] = {
-       "wild",
-       "unchecked",
-       "down",
-       "up",
-       "shaky",
-       "ghost",
-       "pending",
-       "timeout",
-       "removed",
-       "delayed",
+static const char *checker_state_names[PATH_MAX_STATE] = {
+       [PATH_WILD] = "wild",
+       [PATH_UNCHECKED] = "unchecked",
+       [PATH_DOWN] = "down",
+       [PATH_UP] = "up",
+       [PATH_SHAKY] = "shaky",
+       [PATH_GHOST] = "ghost",
+       [PATH_PENDING] = "pending",
+       [PATH_TIMEOUT] = "timeout",
+       [PATH_REMOVED] = "removed",
+       [PATH_DELAYED] = "delayed",
 };
 
 static LIST_HEAD(checkers);
 
 const char *checker_state_name(int i)
 {
+       if (i < 0 || i >= PATH_MAX_STATE) {
+               condlog (2, "invalid state index = %d", i);
+               return INVALID;
+       }
        return checker_state_names[i];
 }
 
index b458118..9d5f90b 100644 (file)
@@ -67,7 +67,7 @@
  *   During this time, it is marked as "delayed"
  */
 enum path_check_state {
-       PATH_WILD,
+       PATH_WILD = 0,
        PATH_UNCHECKED,
        PATH_DOWN,
        PATH_UP,
@@ -88,6 +88,7 @@ enum path_check_state {
 #define READSECTOR0  "readsector0"
 #define CCISS_TUR    "cciss_tur"
 #define NONE         "none"
+#define INVALID      "invalid"
 
 #define ASYNC_TIMEOUT_SEC      30
 
index 02caea6..01c0451 100644 (file)
@@ -17,10 +17,10 @@ LIBS= \
 
 all: $(LIBS)
 
-libcheckdirectio.so: libsg.o directio.o
+libcheckdirectio.so: directio.o
        $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -laio
 
-libcheck%.so: libsg.o %.o
+libcheck%.so: %.o
        $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^
 
 install:
@@ -32,7 +32,7 @@ uninstall:
 clean: dep_clean
        $(RM) core *.a *.o *.gz *.so
 
-OBJS := $(LIBS:libcheck%.so=%.o) libsg.o directio.o
+OBJS := $(LIBS:libcheck%.so=%.o)
 .SECONDARY: $(OBJS)
 
 include $(wildcard $(OBJS:.o=.d))
index 503519e..f73cbe3 100644 (file)
@@ -256,7 +256,7 @@ get_events(struct aio_group *aio_grp, struct timespec *timeout)
 {
        struct io_event events[128];
        int i, nr, got_events = 0;
-       struct timespec zero_timeout = {0};
+       struct timespec zero_timeout = { .tv_sec = 0, };
        struct timespec *timep = timeout;
 
        do {
index b4d8768..b9bdbdb 100644 (file)
@@ -131,7 +131,7 @@ find_hwe (const struct _vector *hwtable,
        vector_foreach_slot_backwards (hwtable, tmp, i) {
                if (hwe_regmatch(tmp, vendor, product, revision))
                        continue;
-               if (vector_alloc_slot(result) != NULL) {
+               if (vector_alloc_slot(result)) {
                        vector_set_slot(result, tmp);
                        n++;
                }
@@ -157,7 +157,7 @@ struct mpentry *find_mpe(vector mptable, char *wwid)
        return NULL;
 }
 
-char *get_mpe_wwid(vector mptable, char *alias)
+const char *get_mpe_wwid(const struct _vector *mptable, const char *alias)
 {
        int i;
        struct mpentry * mpe;
@@ -631,6 +631,8 @@ free_config (struct config * conf)
 
        if (conf->config_dir)
                FREE(conf->config_dir);
+       if (conf->enable_foreign)
+               FREE(conf->enable_foreign);
 
        free_blacklist(conf->blist_devnode);
        free_blacklist(conf->blist_wwid);
@@ -696,9 +698,9 @@ process_config_dir(struct config *conf, char *dir)
        pthread_cleanup_pop(1);
 }
 
+#ifdef USE_SYSTEMD
 static void set_max_checkint_from_watchdog(struct config *conf)
 {
-#ifdef USE_SYSTEMD
        char *envp = getenv("WATCHDOG_USEC");
        unsigned long checkint;
 
@@ -714,8 +716,8 @@ static void set_max_checkint_from_watchdog(struct config *conf)
                condlog(3, "enabling watchdog, interval %ld", checkint);
                conf->use_watchdog = true;
        }
-#endif
 }
+#endif
 
 struct config *
 load_config (char * file)
@@ -789,7 +791,9 @@ load_config (char * file)
        /*
         * fill the voids left in the config file
         */
+#ifdef USE_SYSTEMD
        set_max_checkint_from_watchdog(conf);
+#endif
        if (conf->max_checkint == 0) {
                if (conf->checkint == CHECKINT_UNDEF)
                        conf->checkint = DEFAULT_CHECKINT;
index ceecff2..290aea5 100644 (file)
@@ -38,6 +38,8 @@ enum mpath_cmds {
        CMD_ADD_WWID,
        CMD_USABLE_PATHS,
        CMD_DUMP_CONFIG,
+       CMD_FLUSH_ONE,
+       CMD_FLUSH_ALL,
 };
 
 enum force_reload_types {
@@ -142,7 +144,6 @@ struct config {
        unsigned int max_checkint;
        bool use_watchdog;
        int pgfailback;
-       int remove;
        int rr_weight;
        int no_path_retry;
        int user_friendly_names;
@@ -157,6 +158,7 @@ struct config {
        unsigned int dev_loss;
        int log_checker_err;
        int allow_queueing;
+       int allow_usb_devices;
        int find_multipaths;
        uid_t uid;
        gid_t gid;
@@ -189,6 +191,7 @@ struct config {
        int ghost_delay;
        int find_multipaths_timeout;
        int marginal_pathgroups;
+       int skip_delegate;
        unsigned int version[3];
        unsigned int sequence_nr;
 
@@ -237,7 +240,7 @@ int find_hwe (const struct _vector *hwtable,
              const char * vendor, const char * product, const char *revision,
              vector result);
 struct mpentry * find_mpe (vector mptable, char * wwid);
-char * get_mpe_wwid (vector mptable, char * alias);
+const char *get_mpe_wwid (const struct _vector *mptable, const char *alias);
 
 struct hwentry * alloc_hwe (void);
 struct mpentry * alloc_mpe (void);
index c95848a..6fb477f 100644 (file)
@@ -298,6 +298,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
        struct pathgroup * pgp;
        struct config *conf;
        int i, n_paths, marginal_pathgroups;
+       char *save_attr;
 
        /*
         * don't bother if devmap size is unknown
@@ -307,10 +308,6 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
                return 1;
        }
 
-       /*
-        * free features, selector, and hwhandler properties if they are being reused
-        */
-       free_multipath_attributes(mpp);
        if (mpp->disable_queueing && VECTOR_SIZE(mpp->paths) != 0)
                mpp->disable_queueing = 0;
 
@@ -328,11 +325,35 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
 
        select_pgfailback(conf, mpp);
        select_pgpolicy(conf, mpp);
+
+       /*
+        * If setup_map() is called from e.g. from reload_map() or resize_map(),
+        * make sure that we don't corrupt attributes.
+        */
+       save_attr = steal_ptr(mpp->selector);
        select_selector(conf, mpp);
+       if (!mpp->selector)
+               mpp->selector = save_attr;
+       else
+               free(save_attr);
+
        select_no_path_retry(conf, mpp);
        select_retain_hwhandler(conf, mpp);
+
+       save_attr = steal_ptr(mpp->features);
        select_features(conf, mpp);
+       if (!mpp->features)
+               mpp->features = save_attr;
+       else
+               free(save_attr);
+
+       save_attr = steal_ptr(mpp->hwhandler);
        select_hwhandler(conf, mpp);
+       if (!mpp->hwhandler)
+               mpp->hwhandler = save_attr;
+       else
+               free(save_attr);
+
        select_rr_weight(conf, mpp);
        select_minio(conf, mpp);
        select_mode(conf, mpp);
@@ -359,6 +380,11 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
        marginal_pathgroups = conf->marginal_pathgroups;
        pthread_cleanup_pop(1);
 
+       if (!mpp->features || !mpp->hwhandler || !mpp->selector) {
+               condlog(0, "%s: map select failed", mpp->alias);
+               return 1;
+       }
+
        if (marginal_path_check_enabled(mpp))
                start_io_err_stat_thread(vecs);
 
@@ -510,6 +536,7 @@ static void trigger_partitions_udev_change(struct udev_device *dev,
 {
        struct udev_enumerate *part_enum;
        struct udev_list_entry *item;
+       const char *devtype;
 
        part_enum = udev_enumerate_new(udev);
        if (!part_enum)
@@ -530,7 +557,8 @@ static void trigger_partitions_udev_change(struct udev_device *dev,
                if (!part)
                        continue;
 
-               if (!strcmp("partition", udev_device_get_devtype(part))) {
+               devtype = udev_device_get_devtype(part);
+               if (devtype && !strcmp("partition", devtype)) {
                        condlog(4, "%s: triggering %s event for %s", __func__,
                                action, syspath);
                        sysfs_attr_set_value(part, "uevent", action, len);
@@ -661,7 +689,34 @@ sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload)
 }
 
 static void
-select_action (struct multipath * mpp, vector curmp, int force_reload)
+select_reload_action(struct multipath *mpp, const struct multipath *cmpp,
+                    const char *reason)
+{
+       struct udev_device *mpp_ud;
+       const char *env;
+
+       /*
+        * MPATH_DEVICE_READY != 1 can mean two things:
+        *  (a) no usable paths
+        *  (b) device was never fully processed (e.g. udev killed)
+        * If we are in this code path (startup or forced reconfigure),
+        * (b) can mean that upper layers like kpartx have never been
+        * run for this map. Thus force udev reload.
+        */
+
+       mpp_ud = get_udev_for_mpp(cmpp);
+       env = udev_device_get_property_value(mpp_ud, "MPATH_DEVICE_READY");
+       if ((!env || strcmp(env, "1")) && count_active_paths(mpp) > 0)
+               mpp->force_udev_reload = 1;
+       udev_device_unref(mpp_ud);
+       mpp->action = ACT_RELOAD;
+       condlog(3, "%s: set ACT_RELOAD (%s%s)", mpp->alias,
+               mpp->force_udev_reload ? "forced, " : "",
+               reason);
+}
+
+void select_action (struct multipath *mpp, const struct _vector *curmp,
+                   int force_reload)
 {
        struct multipath * cmpp;
        struct multipath * cmpp_by_name;
@@ -689,12 +744,13 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
        }
 
        if (!cmpp) {
-               condlog(2, "%s: remove (wwid changed)", mpp->alias);
-               dm_flush_map(mpp->alias);
-               strlcpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE);
-               drop_multipath(curmp, cmpp_by_name->wwid, KEEP_PATHS);
+               condlog(1, "%s: can't use alias \"%s\" used by %s, falling back to WWID",
+                       mpp->wwid, mpp->alias, cmpp_by_name->wwid);
+               /* We can do this because wwid wasn't found */
+               free(mpp->alias);
+               mpp->alias = strdup(mpp->wwid);
                mpp->action = ACT_CREATE;
-               condlog(3, "%s: set ACT_CREATE (map wwid change)",
+               condlog(3, "%s: set ACT_CREATE (map does not exist, name changed)",
                        mpp->alias);
                return;
        }
@@ -710,12 +766,6 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
                return;
        }
 
-       if (pathcount(mpp, PATH_UP) == 0) {
-               mpp->action = ACT_IMPOSSIBLE;
-               condlog(3, "%s: set ACT_IMPOSSIBLE (no usable path)",
-                       mpp->alias);
-               return;
-       }
        if (force_reload) {
                mpp->force_udev_reload = 1;
                mpp->action = ACT_RELOAD;
@@ -734,9 +784,7 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
        if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
            !!strstr(mpp->features, "queue_if_no_path") !=
            !!strstr(cmpp->features, "queue_if_no_path")) {
-               mpp->action =  ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (no_path_retry change)",
-                       mpp->alias);
+               select_reload_action(mpp, cmpp, "no_path_retry change");
                return;
        }
        if ((mpp->retain_hwhandler != RETAIN_HWHANDLER_ON ||
@@ -744,9 +792,7 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
            (strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) ||
             strncmp(cmpp->hwhandler, mpp->hwhandler,
                    strlen(mpp->hwhandler)))) {
-               mpp->action = ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (hwhandler change)",
-                       mpp->alias);
+               select_reload_action(mpp, cmpp, "hwhandler change");
                return;
        }
 
@@ -754,9 +800,7 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
            !!strstr(mpp->features, "retain_attached_hw_handler") !=
            !!strstr(cmpp->features, "retain_attached_hw_handler") &&
            get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) {
-               mpp->action = ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (retain_hwhandler change)",
-                       mpp->alias);
+               select_reload_action(mpp, cmpp, "retain_hwhandler change");
                return;
        }
 
@@ -768,9 +812,10 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
                remove_feature(&cmpp_feat, "queue_if_no_path");
                remove_feature(&cmpp_feat, "retain_attached_hw_handler");
                if (strncmp(mpp_feat, cmpp_feat, PARAMS_SIZE)) {
-                       mpp->action =  ACT_RELOAD;
-                       condlog(3, "%s: set ACT_RELOAD (features change)",
-                               mpp->alias);
+                       select_reload_action(mpp, cmpp, "features change");
+                       FREE(cmpp_feat);
+                       FREE(mpp_feat);
+                       return;
                }
        }
        FREE(cmpp_feat);
@@ -778,27 +823,19 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
 
        if (!cmpp->selector || strncmp(cmpp->selector, mpp->selector,
                    strlen(mpp->selector))) {
-               mpp->action = ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (selector change)",
-                       mpp->alias);
+               select_reload_action(mpp, cmpp, "selector change");
                return;
        }
        if (cmpp->minio != mpp->minio) {
-               mpp->action = ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (minio change, %u->%u)",
-                       mpp->alias, cmpp->minio, mpp->minio);
+               select_reload_action(mpp, cmpp, "minio change");
                return;
        }
        if (!cmpp->pg || VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) {
-               mpp->action = ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (path group number change)",
-                       mpp->alias);
+               select_reload_action(mpp, cmpp, "path group number change");
                return;
        }
        if (pgcmp(mpp, cmpp)) {
-               mpp->action = ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (path group topology change)",
-                       mpp->alias);
+               select_reload_action(mpp, cmpp, "path group topology change");
                return;
        }
        if (cmpp->nextpg != mpp->bestpg) {
@@ -903,10 +940,21 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
                return DOMAP_DRY;
        }
 
-       if (mpp->action == ACT_CREATE &&
-           dm_map_present(mpp->alias)) {
-               condlog(3, "%s: map already present", mpp->alias);
-               mpp->action = ACT_RELOAD;
+       if (mpp->action == ACT_CREATE && dm_map_present(mpp->alias)) {
+               char wwid[WWID_SIZE];
+
+               if (dm_get_uuid(mpp->alias, wwid, sizeof(wwid)) == 0) {
+                       if (!strncmp(mpp->wwid, wwid, sizeof(wwid))) {
+                               condlog(3, "%s: map already present",
+                                       mpp->alias);
+                               mpp->action = ACT_RELOAD;
+                       } else {
+                               condlog(0, "%s: map \"%s\" already present with WWID %s, skipping",
+                                       mpp->wwid, mpp->alias, wwid);
+                               condlog(0, "please check alias settings in config and bindings file");
+                               mpp->action = ACT_REJECT;
+                       }
+               }
        }
 
        switch (mpp->action) {
@@ -998,7 +1046,7 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
                } else  {
                        /* multipath daemon mode */
                        mpp->stat_map_loads++;
-                       condlog(2, "%s: load table [0 %llu %s %s]", mpp->alias,
+                       condlog(4, "%s: load table [0 %llu %s %s]", mpp->alias,
                                mpp->size, TGT_MPATH, params);
                        /*
                         * Required action is over, reset for the stateful daemon.
@@ -1098,7 +1146,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
        vector pathvec = vecs->pathvec;
        struct config *conf;
        int allow_queueing;
-       uint64_t *size_mismatch_seen;
+       struct bitfield *size_mismatch_seen;
 
        /* ignore refwwid if it's empty */
        if (refwwid && !strlen(refwwid))
@@ -1112,8 +1160,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
 
        if (VECTOR_SIZE(pathvec) == 0)
                return CP_OK;
-       size_mismatch_seen = calloc((VECTOR_SIZE(pathvec) - 1) / 64 + 1,
-                                   sizeof(uint64_t));
+       size_mismatch_seen = alloc_bitfield(VECTOR_SIZE(pathvec));
        if (size_mismatch_seen == NULL)
                return CP_FAIL;
 
@@ -1137,7 +1184,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
                }
 
                /* 2. if path already coalesced, or seen and discarded */
-               if (pp1->mpp || is_bit_set_in_array(k, size_mismatch_seen))
+               if (pp1->mpp || is_bit_set_in_bitfield(k, size_mismatch_seen))
                        continue;
 
                /* 3. if path has disappeared */
@@ -1167,7 +1214,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
 
                if (!mpp->paths) {
                        condlog(0, "%s: skip coalesce (no paths)", mpp->alias);
-                       remove_map(mpp, vecs, 0);
+                       remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
                        continue;
                }
 
@@ -1189,14 +1236,14 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
                                        "Discard", pp2->dev, pp2->size,
                                        mpp->size);
                                mpp->action = ACT_REJECT;
-                               set_bit_in_array(i, size_mismatch_seen);
+                               set_bit_in_bitfield(i, size_mismatch_seen);
                        }
                }
-               verify_paths(mpp, vecs);
+               verify_paths(mpp);
 
                params[0] = '\0';
                if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
-                       remove_map(mpp, vecs, 0);
+                       remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
                        continue;
                }
 
@@ -1216,7 +1263,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
                                condlog(2, "%s: %s map",
                                        mpp->alias, (mpp->action == ACT_CREATE)?
                                        "ignoring" : "removing");
-                               remove_map(mpp, vecs, 0);
+                               remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
                                continue;
                        } else /* if (r == DOMAP_RETRY && !is_daemon) */ {
                                ret = CP_RETRY;
@@ -1267,7 +1314,8 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
                                vector_set_slot(newmp, mpp);
                        }
                        else
-                               remove_map(mpp, vecs, 0);
+                               remove_map(mpp, vecs->pathvec, vecs->mpvec,
+                                          KEEP_VEC);
                }
        }
        /*
@@ -1285,7 +1333,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
 
                        vector_del_slot(newmp, i);
                        i--;
-                       remove_map(mpp, vecs, 0);
+                       remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
 
                        if (dm_flush_map(alias))
                                condlog(2, "%s: remove failed (dead)",
@@ -1335,223 +1383,122 @@ struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type)
        return ud;
 }
 
-/*
- * returns:
- * 0 - success
- * 1 - failure
- * 2 - blacklist
- */
-int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
-               vector pathvec, char **wwid)
+static int _get_refwwid(enum mpath_cmds cmd, const char *dev,
+                       enum devtypes dev_type,
+                       vector pathvec, struct config *conf, char **wwid)
 {
        int ret = 1;
        struct path * pp;
        char buff[FILE_NAME_SIZE];
-       char * refwwid = NULL, tmpwwid[WWID_SIZE];
+       const char *refwwid = NULL;
+       char tmpwwid[WWID_SIZE];
+       struct udev_device *udevice;
        int flags = DI_SYSFS | DI_WWID;
-       struct config *conf;
-       int invalid = 0;
 
        if (!wwid)
-               return 1;
+               return PATHINFO_FAILED;
        *wwid = NULL;
 
        if (dev_type == DEV_NONE)
-               return 1;
+               return PATHINFO_FAILED;
 
        if (cmd != CMD_REMOVE_WWID)
                flags |= DI_BLACKLIST;
 
-       if (dev_type == DEV_DEVNODE) {
+       switch (dev_type) {
+       case DEV_DEVNODE:
                if (basenamecpy(dev, buff, FILE_NAME_SIZE) == 0) {
                        condlog(1, "basename failed for '%s' (%s)",
                                dev, buff);
-                       return 1;
+                       return PATHINFO_FAILED;
                }
 
-               pp = find_path_by_dev(pathvec, buff);
-               if (!pp) {
-                       struct udev_device *udevice =
-                               get_udev_device(buff, dev_type);
-
-                       if (!udevice)
-                               return 1;
+               /* dev is used in common code below */
+               dev = buff;
+               pp = find_path_by_dev(pathvec, dev);
+               goto common;
 
-                       conf = get_multipath_config();
-                       pthread_cleanup_push(put_multipath_config, conf);
-                       ret = store_pathinfo(pathvec, conf, udevice,
-                                            flags, &pp);
-                       pthread_cleanup_pop(1);
-                       udev_device_unref(udevice);
-                       if (!pp) {
-                               if (ret == 1)
-                                       condlog(0, "%s: can't store path info",
-                                               dev);
-                               return ret;
-                       }
-               }
-               conf = get_multipath_config();
-               pthread_cleanup_push(put_multipath_config, conf);
-               if (pp->udev && pp->uid_attribute &&
-                   filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
-                       invalid = 1;
-               pthread_cleanup_pop(1);
-               if (invalid)
-                       return 2;
-
-               refwwid = pp->wwid;
-               goto out;
-       }
+       case DEV_DEVT:
+               pp = find_path_by_devt(pathvec, dev);
+               goto common;
 
-       if (dev_type == DEV_DEVT) {
-               strchop(dev);
-               if (devt2devname(buff, FILE_NAME_SIZE, dev)) {
-                       condlog(0, "%s: cannot find block device\n", dev);
-                       return 1;
-               }
-               pp = find_path_by_dev(pathvec, buff);
+       case DEV_UEVENT:
+               pp = NULL;
+               /* For condlog below, dev is unused in get_udev_device() */
+               dev = "environment";
+       common:
                if (!pp) {
-                       struct udev_device *udevice =
-                               get_udev_device(dev, dev_type);
+                       udevice = get_udev_device(dev, dev_type);
 
-                       if (!udevice)
-                               return 1;
+                       if (!udevice) {
+                               condlog(0, "%s: cannot find block device", dev);
+                               return PATHINFO_FAILED;
+                       }
 
-                       conf = get_multipath_config();
-                       pthread_cleanup_push(put_multipath_config, conf);
                        ret = store_pathinfo(pathvec, conf, udevice,
                                             flags, &pp);
-                       pthread_cleanup_pop(1);
                        udev_device_unref(udevice);
                        if (!pp) {
-                               if (ret == 1)
-                                       condlog(0, "%s can't store path info",
-                                               buff);
+                               if (ret == PATHINFO_FAILED)
+                                       condlog(0, "%s: can't store path info",
+                                               dev);
                                return ret;
                        }
                }
-               conf = get_multipath_config();
-               pthread_cleanup_push(put_multipath_config, conf);
                if (pp->udev && pp->uid_attribute &&
                    filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
-                       invalid = 1;
-               pthread_cleanup_pop(1);
-               if (invalid)
-                       return 2;
+                       return PATHINFO_SKIPPED;
                refwwid = pp->wwid;
-               goto out;
-       }
-
-       if (dev_type == DEV_UEVENT) {
-               struct udev_device *udevice = get_udev_device(dev, dev_type);
-
-               if (!udevice)
-                       return 1;
-
-               conf = get_multipath_config();
-               pthread_cleanup_push(put_multipath_config, conf);
-               ret = store_pathinfo(pathvec, conf, udevice,
-                                    flags, &pp);
-               pthread_cleanup_pop(1);
-               udev_device_unref(udevice);
-               if (!pp) {
-                       if (ret == 1)
-                               condlog(0, "%s: can't store path info", dev);
-                       return ret;
-               }
-               conf = get_multipath_config();
-               pthread_cleanup_push(put_multipath_config, conf);
-               if (pp->udev && pp->uid_attribute &&
-                   filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
-                       invalid = 1;
-               pthread_cleanup_pop(1);
-               if (invalid)
-                       return 2;
-               refwwid = pp->wwid;
-               goto out;
-       }
-
-       if (dev_type == DEV_DEVMAP) {
+               break;
 
-               conf = get_multipath_config();
-               pthread_cleanup_push(put_multipath_config, conf);
+       case DEV_DEVMAP:
                if (((dm_get_uuid(dev, tmpwwid, WWID_SIZE)) == 0)
-                   && (strlen(tmpwwid))) {
+                   && (strlen(tmpwwid)))
                        refwwid = tmpwwid;
-                       goto check;
-               }
 
-               /*
-                * may be a binding
-                */
-               if (get_user_friendly_wwid(dev, tmpwwid,
-                                          conf->bindings_file) == 0) {
+               /* or may be a binding */
+               else if (get_user_friendly_wwid(dev, tmpwwid,
+                                               conf->bindings_file) == 0)
                        refwwid = tmpwwid;
-                       goto check;
-               }
 
-               /*
-                * or may be an alias
-                */
-               refwwid = get_mpe_wwid(conf->mptable, dev);
+               /* or may be an alias */
+               else {
+                       refwwid = get_mpe_wwid(conf->mptable, dev);
 
-               /*
-                * or directly a wwid
-                */
-               if (!refwwid)
-                       refwwid = dev;
+                       /* or directly a wwid */
+                       if (!refwwid)
+                               refwwid = dev;
+               }
 
-check:
                if (refwwid && strlen(refwwid) &&
                    filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid,
                                NULL) > 0)
-                       invalid = 1;
-               pthread_cleanup_pop(1);
-               if (invalid)
-                       return 2;
+                       return PATHINFO_SKIPPED;
+               break;
+       default:
+               break;
        }
-out:
+
        if (refwwid && strlen(refwwid)) {
                *wwid = STRDUP(refwwid);
-               return 0;
+               return PATHINFO_OK;
        }
 
-       return 1;
+       return PATHINFO_FAILED;
 }
 
-int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
-              int is_daemon)
-{
-       char params[PARAMS_SIZE] = {0};
-       struct path *pp;
-       int i, r;
-
-       update_mpp_paths(mpp, vecs->pathvec);
-       if (refresh) {
-               vector_foreach_slot (mpp->paths, pp, i) {
-                       struct config *conf = get_multipath_config();
-                       pthread_cleanup_push(put_multipath_config, conf);
-                       r = pathinfo(pp, conf, DI_PRIO);
-                       pthread_cleanup_pop(1);
-                       if (r) {
-                               condlog(2, "%s: failed to refresh pathinfo",
-                                       mpp->alias);
-                               return 1;
-                       }
-               }
-       }
-       if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
-               condlog(0, "%s: failed to setup map", mpp->alias);
-               return 1;
-       }
-       select_action(mpp, vecs->mpvec, 1);
+/*
+ * Returns: PATHINFO_OK, PATHINFO_FAILED, or PATHINFO_SKIPPED (see pathinfo())
+ */
+int get_refwwid(enum mpath_cmds cmd, const char *dev, enum devtypes dev_type,
+               vector pathvec, char **wwid)
 
-       r = domap(mpp, params, is_daemon);
-       if (r == DOMAP_FAIL || r == DOMAP_RETRY) {
-               condlog(3, "%s: domap (%u) failure "
-                       "for reload map", mpp->alias, r);
-               return 1;
-       }
+{
+       int ret;
+       struct config *conf = get_multipath_config();
 
-       return 0;
+       pthread_cleanup_push(put_multipath_config, conf);
+       ret = _get_refwwid(cmd, dev, dev_type, pathvec, conf, wwid);
+       pthread_cleanup_pop(1);
+       return ret;
 }
index d750900..6b23ccb 100644 (file)
@@ -45,18 +45,16 @@ enum {
        CP_RETRY,
 };
 
-#define FLUSH_ONE 1
-#define FLUSH_ALL 2
-
 struct vectors;
 
 int setup_map (struct multipath * mpp, char * params, int params_size,
               struct vectors *vecs );
+void select_action (struct multipath *mpp, const struct _vector *curmp,
+                   int force_reload);
 int domap (struct multipath * mpp, char * params, int is_daemon);
 int reinstate_paths (struct multipath *mpp);
 int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload, enum mpath_cmds cmd);
-int get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type,
+int get_refwwid (enum mpath_cmds cmd, const char *dev, enum devtypes dev_type,
                 vector pathvec, char **wwid);
-int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon);
 struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type);
 void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath);
index e5ee6af..39a5e41 100644 (file)
@@ -31,6 +31,8 @@
 #define DEFAULT_DEFERRED_REMOVE        DEFERRED_REMOVE_OFF
 #define DEFAULT_DELAY_CHECKS   NU_NO
 #define DEFAULT_ERR_CHECKS     NU_NO
+/* half of minimum value for marginal_path_err_sample_time */
+#define IOTIMEOUT_SEC          60
 #define DEFAULT_UEVENT_STACKSIZE 256
 #define DEFAULT_RETRIGGER_DELAY        10
 #define DEFAULT_RETRIGGER_TRIES        3
 #define DEFAULT_FIND_MULTIPATHS_TIMEOUT -10
 #define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1
 #define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF
-/* Enable all foreign libraries by default */
-#define DEFAULT_ENABLE_FOREIGN ""
+/* Enable no foreign libraries by default */
+#define DEFAULT_ENABLE_FOREIGN "NONE"
 
 #define CHECKINT_UNDEF         UINT_MAX
 #define DEFAULT_CHECKINT       5
 
+#define DEV_LOSS_TMO_UNSET     0U
 #define MAX_DEV_LOSS_TMO       UINT_MAX
 #define DEFAULT_PIDFILE                "/" RUN_DIR "/multipathd.pid"
 #define DEFAULT_SOCKET         "/org/kernel/linux/storage/multipathd"
index bed8ddc..7f09361 100644 (file)
@@ -12,6 +12,7 @@
 #include <ctype.h>
 #include <unistd.h>
 #include <errno.h>
+#include <syslog.h>
 #include <sys/sysmacros.h>
 #include <linux/dm-ioctl.h>
 
@@ -33,6 +34,8 @@
 #define MAX_WAIT 5
 #define LOOPS_PER_SEC 5
 
+static pthread_once_t dm_initialized = PTHREAD_ONCE_INIT;
+
 static int dm_conf_verbosity;
 
 #ifdef LIBDM_API_DEFERRED
@@ -63,13 +66,15 @@ __attribute__((format(printf, 4, 5))) static void
 dm_write_log (int level, const char *file, int line, const char *f, ...)
 {
        va_list ap;
-       int thres;
 
-       if (level > 6)
-               level = 6;
+       /*
+        * libdm uses the same log levels as syslog,
+        * except that EMERG/ALERT are not used
+        */
+       if (level > LOG_DEBUG)
+               level = LOG_DEBUG;
 
-       thres = dm_conf_verbosity;
-       if (thres <= 3 || level > thres)
+       if (level > dm_conf_verbosity)
                return;
 
        va_start(ap, f);
@@ -88,8 +93,9 @@ dm_write_log (int level, const char *file, int line, const char *f, ...)
                vfprintf(stderr, f, ap);
                fprintf(stderr, "\n");
        } else {
-               condlog(level, "libdevmapper: %s(%i): ", file, line);
-               log_safe(level + 3, f, ap);
+               condlog(level >= LOG_ERR ? level - LOG_ERR : 0,
+                       "libdevmapper: %s(%i): ", file, line);
+               log_safe(level, f, ap);
        }
        va_end(ap);
 
@@ -98,9 +104,12 @@ dm_write_log (int level, const char *file, int line, const char *f, ...)
 
 void dm_init(int v)
 {
-       dm_conf_verbosity = v;
+       /*
+        * This maps libdm's standard loglevel _LOG_WARN (= 4), which is rather
+        * quiet in practice, to multipathd's default verbosity 2
+        */
+       dm_conf_verbosity = v + 2;
        dm_log_init(&dm_write_log);
-       dm_log_init_verbose(v + 3);
 }
 
 static int
@@ -108,7 +117,11 @@ dm_lib_prereq (void)
 {
        char version[64];
        int v[3];
-#if defined(LIBDM_API_DEFERRED)
+#if defined(LIBDM_API_HOLD_CONTROL)
+       int minv[3] = {1, 2, 111};
+#elif defined(LIBDM_API_GET_ERRNO)
+       int minv[3] = {1, 2, 99};
+#elif defined(LIBDM_API_DEFERRED)
        int minv[3] = {1, 2, 89};
 #elif defined(DM_SUBSYSTEM_UDEV_FLAG0)
        int minv[3] = {1, 2, 82};
@@ -171,6 +184,7 @@ dm_tgt_version (unsigned int * version, char * str)
        dm_task_no_open_count(dmt);
 
        if (!dm_task_run(dmt)) {
+               dm_log_error(2, DM_DEVICE_LIST_VERSIONS, dmt);
                condlog(0, "Can not communicate with kernel DM");
                goto out;
        }
@@ -227,7 +241,7 @@ dm_tgt_prereq (unsigned int *ver)
        return 1;
 }
 
-static int dm_prereq(unsigned int *v)
+int dm_prereq(unsigned int *v)
 {
        if (dm_lib_prereq())
                return 1;
@@ -241,7 +255,7 @@ void libmp_udev_set_sync_support(int on)
        libmp_dm_udev_sync = !!on;
 }
 
-void libmp_dm_init(void)
+static void libmp_dm_init(void)
 {
        struct config *conf;
        int verbosity;
@@ -254,14 +268,24 @@ void libmp_dm_init(void)
        memcpy(conf->version, version, sizeof(version));
        put_multipath_config(conf);
        dm_init(verbosity);
+#ifdef LIBDM_API_HOLD_CONTROL
+       dm_hold_control_dev(1);
+#endif
        dm_udev_set_sync_support(libmp_dm_udev_sync);
 }
 
+static void _do_skip_libmp_dm_init(void)
+{
+}
+
+void skip_libmp_dm_init(void)
+{
+       pthread_once(&dm_initialized, _do_skip_libmp_dm_init);
+}
+
 struct dm_task*
 libmp_dm_task_create(int task)
 {
-       static pthread_once_t dm_initialized = PTHREAD_ONCE_INIT;
-
        pthread_once(&dm_initialized, libmp_dm_init);
        return dm_task_create(task);
 }
@@ -299,6 +323,8 @@ dm_simplecmd (int task, const char *name, int no_flush, int need_sync, uint16_t
                goto out;
 
        r = dm_task_run (dmt);
+       if (!r)
+               dm_log_error(2, task, dmt);
 
        if (udev_wait_flag)
                        dm_udev_wait(cookie);
@@ -331,6 +357,12 @@ dm_addmap (int task, const char *target, struct multipath *mpp,
        char *prefixed_uuid = NULL;
        uint32_t cookie = 0;
 
+       if (task == DM_DEVICE_CREATE && strlen(mpp->wwid) == 0) {
+               condlog(1, "%s: refusing to create map with empty WWID",
+                       mpp->alias);
+               return 0;
+       }
+
        /* Need to add this here to allow 0 to be passed in udev_flags */
        udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
 
@@ -347,18 +379,16 @@ dm_addmap (int task, const char *target, struct multipath *mpp,
                dm_task_set_ro(dmt);
 
        if (task == DM_DEVICE_CREATE) {
-               if (strlen(mpp->wwid) > 0) {
-                       prefixed_uuid = MALLOC(UUID_PREFIX_LEN +
-                                              strlen(mpp->wwid) + 1);
-                       if (!prefixed_uuid) {
-                               condlog(0, "cannot create prefixed uuid : %s",
-                                       strerror(errno));
-                               goto addout;
-                       }
-                       sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid);
-                       if (!dm_task_set_uuid(dmt, prefixed_uuid))
-                               goto freeout;
+               prefixed_uuid = MALLOC(UUID_PREFIX_LEN +
+                                      strlen(mpp->wwid) + 1);
+               if (!prefixed_uuid) {
+                       condlog(0, "cannot create prefixed uuid : %s",
+                               strerror(errno));
+                       goto addout;
                }
+               sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid);
+               if (!dm_task_set_uuid(dmt, prefixed_uuid))
+                       goto freeout;
                dm_task_skip_lockfs(dmt);
 #ifdef LIBDM_API_FLUSH
                dm_task_no_flush(dmt);
@@ -374,7 +404,7 @@ dm_addmap (int task, const char *target, struct multipath *mpp,
        if (mpp->attribute_flags & (1 << ATTR_GID) &&
            !dm_task_set_gid(dmt, mpp->gid))
                goto freeout;
-       condlog(4, "%s: %s [0 %llu %s %s]", mpp->alias,
+       condlog(2, "%s: %s [0 %llu %s %s]", mpp->alias,
                task == DM_DEVICE_RELOAD ? "reload" : "addmap", mpp->size,
                target, params);
 
@@ -385,6 +415,8 @@ dm_addmap (int task, const char *target, struct multipath *mpp,
                goto freeout;
 
        r = dm_task_run (dmt);
+       if (!r)
+               dm_log_error(2, task, dmt);
 
        if (task == DM_DEVICE_CREATE)
                        dm_udev_wait(cookie);
@@ -403,7 +435,8 @@ static uint16_t build_udev_flags(const struct multipath *mpp, int reload)
        /* DM_UDEV_DISABLE_LIBRARY_FALLBACK is added in dm_addmap */
        return  (mpp->skip_kpartx == SKIP_KPARTX_ON ?
                 MPATH_UDEV_NO_KPARTX_FLAG : 0) |
-               ((count_active_paths(mpp) == 0 || mpp->ghost_delay_tick > 0) ?
+               ((count_active_pending_paths(mpp) == 0 ||
+                 mpp->ghost_delay_tick > 0) ?
                 MPATH_UDEV_NO_PATHS_FLAG : 0) |
                (reload && !mpp->force_udev_reload ?
                 MPATH_UDEV_RELOAD_FLAG : 0);
@@ -496,8 +529,10 @@ do_get_info(const char *name, struct dm_info *info)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_INFO, dmt);
                goto out;
+       }
 
        if (!dm_task_get_info(dmt, info))
                goto out;
@@ -520,36 +555,44 @@ int dm_map_present(const char * str)
 
 int dm_get_map(const char *name, unsigned long long *size, char *outparams)
 {
-       int r = 1;
+       int r = DMP_ERR;
        struct dm_task *dmt;
        uint64_t start, length;
        char *target_type = NULL;
        char *params = NULL;
 
        if (!(dmt = libmp_dm_task_create(DM_DEVICE_TABLE)))
-               return 1;
+               return r;
 
        if (!dm_task_set_name(dmt, name))
                goto out;
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       errno = 0;
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_TABLE, dmt);
+               if (dm_task_get_errno(dmt) == ENXIO)
+                       r = DMP_NOT_FOUND;
                goto out;
+       }
 
+       r = DMP_NOT_FOUND;
        /* Fetch 1st target */
-       dm_get_next_target(dmt, NULL, &start, &length,
-                          &target_type, &params);
+       if (dm_get_next_target(dmt, NULL, &start, &length,
+                              &target_type, &params) != NULL)
+               /* more than one target */
+               goto out;
 
        if (size)
                *size = length;
 
        if (!outparams) {
-               r = 0;
+               r = DMP_OK;
                goto out;
        }
        if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE)
-               r = 0;
+               r = DMP_OK;
 out:
        dm_task_destroy(dmt);
        return r;
@@ -569,8 +612,10 @@ dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
        if (!dm_task_set_name (dmt, name))
                goto uuidout;
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_INFO, dmt);
                goto uuidout;
+       }
 
        uuidtmp = dm_task_get_uuid(dmt);
        if (uuidtmp)
@@ -623,35 +668,46 @@ is_mpath_part(const char *part_name, const char *map_name)
 
 int dm_get_status(const char *name, char *outstatus)
 {
-       int r = 1;
+       int r = DMP_ERR;
        struct dm_task *dmt;
        uint64_t start, length;
        char *target_type = NULL;
        char *status = NULL;
 
        if (!(dmt = libmp_dm_task_create(DM_DEVICE_STATUS)))
-               return 1;
+               return r;
 
        if (!dm_task_set_name(dmt, name))
                goto out;
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       errno = 0;
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_STATUS, dmt);
+               if (dm_task_get_errno(dmt) == ENXIO)
+                       r = DMP_NOT_FOUND;
                goto out;
+       }
 
+       r = DMP_NOT_FOUND;
        /* Fetch 1st target */
-       dm_get_next_target(dmt, NULL, &start, &length,
-                          &target_type, &status);
+       if (dm_get_next_target(dmt, NULL, &start, &length,
+                              &target_type, &status) != NULL)
+               goto out;
+
+       if (!target_type || strcmp(target_type, TGT_MPATH) != 0)
+               goto out;
+
        if (!status) {
                condlog(2, "get null status.");
                goto out;
        }
 
        if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE)
-               r = 0;
+               r = DMP_OK;
 out:
-       if (r)
+       if (r != DMP_OK)
                condlog(0, "%s: error getting map status string", name);
 
        dm_task_destroy(dmt);
@@ -680,8 +736,10 @@ int dm_type(const char *name, char *type)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_TABLE, dmt);
                goto out;
+       }
 
        /* Fetch 1st target */
        if (dm_get_next_target(dmt, NULL, &start, &length,
@@ -722,8 +780,10 @@ int dm_is_mpath(const char *name)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_TABLE, dmt);
                goto out_task;
+       }
 
        if (!dm_task_get_info(dmt, &info))
                goto out_task;
@@ -756,6 +816,53 @@ out:
        return r;
 }
 
+/*
+ * Return
+ *   1 : map with uuid exists
+ *   0 : map with uuid doesn't exist
+ *  -1 : error
+ */
+int
+dm_map_present_by_uuid(const char *uuid)
+{
+       struct dm_task *dmt;
+       struct dm_info info;
+       char prefixed_uuid[WWID_SIZE + UUID_PREFIX_LEN];
+       int r = -1;
+
+       if (!uuid || uuid[0] == '\0')
+               return 0;
+
+       if (safe_sprintf(prefixed_uuid, UUID_PREFIX "%s", uuid))
+               goto out;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+               goto out;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_set_uuid(dmt, prefixed_uuid))
+               goto out_task;
+
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_INFO, dmt);
+               goto out_task;
+       }
+
+       if (!dm_task_get_info(dmt, &info))
+               goto out_task;
+
+       r = !!info.exists;
+
+out_task:
+       dm_task_destroy(dmt);
+out:
+       if (r < 0)
+               condlog(3, "%s: dm command failed in %s: %s", uuid,
+                       __FUNCTION__, strerror(errno));
+       return r;
+}
+
 static int
 dm_dev_t (const char * mapname, char * dev_t, int len)
 {
@@ -783,8 +890,10 @@ dm_get_opencount (const char * mapname)
        if (!dm_task_set_name(dmt, mapname))
                goto out;
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_INFO, dmt);
                goto out;
+       }
 
        if (!dm_task_get_info(dmt, &info))
                goto out;
@@ -861,7 +970,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove,
                        return 1;
 
        if (need_suspend &&
-           !dm_get_map(mapname, &mapsize, params) &&
+           dm_get_map(mapname, &mapsize, params) == DMP_OK &&
            strstr(params, "queue_if_no_path")) {
                if (!dm_queue_if_no_path(mapname, 0))
                        queue_if_no_path = 1;
@@ -929,29 +1038,35 @@ dm_flush_map_nopaths(const char * mapname, int deferred_remove)
 
 #endif
 
-int dm_flush_maps (int retries)
+int dm_flush_maps (int need_suspend, int retries)
 {
-       int r = 0;
+       int r = 1;
        struct dm_task *dmt;
        struct dm_names *names;
        unsigned next = 0;
 
        if (!(dmt = libmp_dm_task_create (DM_DEVICE_LIST)))
-               return 0;
+               return r;
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run (dmt))
+       if (!dm_task_run (dmt)) {
+               dm_log_error(3, DM_DEVICE_LIST, dmt);
                goto out;
+       }
 
        if (!(names = dm_task_get_names (dmt)))
                goto out;
 
+       r = 0;
        if (!names->dev)
                goto out;
 
        do {
-               r |= dm_suspend_and_flush_map(names->name, retries);
+               if (need_suspend)
+                       r |= dm_suspend_and_flush_map(names->name, retries);
+               else
+                       r |= dm_flush_map(names->name);
                next = names->next;
                names = (void *) names + next;
        } while (next);
@@ -981,8 +1096,10 @@ dm_message(const char * mapname, char * message)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(2, DM_DEVICE_TARGET_MSG, dmt);
                goto out;
+       }
 
        r = 0;
 out:
@@ -1070,7 +1187,7 @@ struct multipath *dm_get_multipath(const char *name)
        if (!mpp->alias)
                goto out;
 
-       if (dm_get_map(name, &mpp->size, NULL))
+       if (dm_get_map(name, &mpp->size, NULL) != DMP_OK)
                goto out;
 
        dm_get_uuid(name, mpp->wwid, WWID_SIZE);
@@ -1099,8 +1216,10 @@ dm_get_maps (vector mp)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_LIST, dmt);
                goto out;
+       }
 
        if (!(names = dm_task_get_names(dmt)))
                goto out;
@@ -1118,8 +1237,10 @@ dm_get_maps (vector mp)
                if (!mpp)
                        goto out;
 
-               if (!vector_alloc_slot(mp))
+               if (!vector_alloc_slot(mp)) {
+                       free_multipath(mpp, KEEP_PATHS);
                        goto out;
+               }
 
                vector_set_slot(mp, mpp);
                mpp = NULL;
@@ -1189,13 +1310,14 @@ dm_mapname(int major, int minor)
        }
 
        if (!r) {
+               dm_log_error(2, DM_DEVICE_STATUS, dmt);
                condlog(0, "%i:%i: timeout fetching map name", major, minor);
                goto bad;
        }
 
        map = dm_task_get_name(dmt);
        if (map && strlen(map))
-               response = STRDUP((const char *)dm_task_get_name(dmt));
+               response = STRDUP((const char *)map);
 
        dm_task_destroy(dmt);
        return response;
@@ -1224,8 +1346,10 @@ do_foreach_partmaps (const char * mapname,
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_LIST, dmt);
                goto out;
+       }
 
        if (!(names = dm_task_get_names(dmt)))
                goto out;
@@ -1254,7 +1378,7 @@ do_foreach_partmaps (const char * mapname,
                    /*
                     * and we can fetch the map table from the kernel
                     */
-                   !dm_get_map(names->name, &size, &params[0]) &&
+                   dm_get_map(names->name, &size, &params[0]) == DMP_OK &&
 
                    /*
                     * and the table maps over the multipath map
@@ -1384,7 +1508,6 @@ dm_get_info (const char * mapname, struct dm_info ** dmi)
                return 1;
 
        if (do_get_info(mapname, *dmi) != 0) {
-               memset(*dmi, 0, sizeof(struct dm_info));
                FREE(*dmi);
                *dmi = NULL;
                return 1;
@@ -1459,6 +1582,8 @@ dm_rename (const char * old, char * new, char *delim, int skip_kpartx)
        if (!dm_task_set_cookie(dmt, &cookie, udev_flags))
                goto out;
        r = dm_task_run(dmt);
+       if (!r)
+               dm_log_error(2, DM_DEVICE_RENAME, dmt);
 
        dm_udev_wait(cookie);
 
@@ -1502,8 +1627,10 @@ int dm_reassign_table(const char *name, char *old, char *new)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_TABLE, dmt);
                goto out;
+       }
        if (!(reload_dmt = libmp_dm_task_create(DM_DEVICE_RELOAD)))
                goto out;
        if (!dm_task_set_name(reload_dmt, name))
@@ -1534,6 +1661,7 @@ int dm_reassign_table(const char *name, char *old, char *new)
                dm_task_no_open_count(reload_dmt);
 
                if (!dm_task_run(reload_dmt)) {
+                       dm_log_error(3, DM_DEVICE_RELOAD, reload_dmt);
                        condlog(3, "%s: failed to reassign targets", name);
                        goto out_reload;
                }
@@ -1579,8 +1707,10 @@ int dm_reassign(const char *mapname)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_DEPS, dmt);
                goto out;
+       }
 
        if (!dm_task_get_info(dmt, &info))
                goto out;
@@ -1646,6 +1776,8 @@ int dm_setgeometry(struct multipath *mpp)
        }
 
        r = dm_task_run(dmt);
+       if (!r)
+               dm_log_error(3, DM_DEVICE_SET_GEOMETRY, dmt);
 out:
        dm_task_destroy(dmt);
 
index 7557a86..f469c98 100644 (file)
 #define UUID_PREFIX "mpath-"
 #define UUID_PREFIX_LEN (sizeof(UUID_PREFIX) - 1)
 
+enum {
+       DMP_ERR,
+       DMP_OK,
+       DMP_NOT_FOUND,
+};
+
 void dm_init(int verbosity);
-void libmp_dm_init(void);
+int dm_prereq(unsigned int *v);
+void skip_libmp_dm_init(void);
 void libmp_udev_set_sync_support(int on);
 struct dm_task *libmp_dm_task_create(int task);
 int dm_drv_version (unsigned int * version);
@@ -38,6 +45,7 @@ int dm_simplecmd_noflush (int, const char *, uint16_t);
 int dm_addmap_create (struct multipath *mpp, char *params);
 int dm_addmap_reload (struct multipath *mpp, char *params, int flush);
 int dm_map_present (const char *);
+int dm_map_present_by_uuid(const char *uuid);
 int dm_get_map(const char *, unsigned long long *, char *);
 int dm_get_status(const char *, char *);
 int dm_type(const char *, char *);
@@ -49,7 +57,7 @@ int dm_flush_map_nopaths(const char * mapname, int deferred_remove);
 #define dm_suspend_and_flush_map(mapname, retries) \
        _dm_flush_map(mapname, 1, 0, 1, retries)
 int dm_cancel_deferred_remove(struct multipath *mpp);
-int dm_flush_maps (int retries);
+int dm_flush_maps (int need_suspend, int retries);
 int dm_fail_path(const char * mapname, char * path);
 int dm_reinstate_path(const char * mapname, char * path);
 int dm_queue_if_no_path(const char *mapname, int enable);
@@ -77,4 +85,13 @@ struct multipath *dm_get_multipath(const char *name);
        ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2])) \
 )
 
+#ifndef LIBDM_API_GET_ERRNO
+#include <errno.h>
+#define dm_task_get_errno(x) errno
+#endif
+
+#define dm_log_error(lvl, cmd, dmt)                          \
+       condlog(lvl, "%s: libdm task=%d error: %s", __func__, \
+               cmd, strerror(dm_task_get_errno(dmt)))        \
+
 #endif /* _DEVMAPPER_H */
index 3e25e74..f12c2e5 100644 (file)
@@ -60,19 +60,22 @@ static int
 set_uint(vector strvec, void *ptr)
 {
        unsigned int *uint_ptr = (unsigned int *)ptr;
-       char *buff, *eptr;
-       long res;
+       char *buff, *eptr, *p;
+       unsigned long res;
        int rc;
 
        buff = set_value(strvec);
        if (!buff)
                return 1;
 
-       res = strtol(buff, &eptr, 10);
+       p = buff;
+       while (isspace(*p))
+               p++;
+       res = strtoul(p, &eptr, 10);
        if (eptr > buff)
                while (isspace(*eptr))
                        eptr++;
-       if (*buff == '\0' || *eptr != '\0' || res < 0 || res > UINT_MAX) {
+       if (*buff == '\0' || *eptr != '\0' || !isdigit(*p) || res > UINT_MAX) {
                condlog(1, "%s: invalid value for %s: \"%s\"",
                        __func__, (char*)VECTOR_SLOT(strvec, 0), buff);
                rc = 1;
@@ -540,6 +543,9 @@ snprint_def_queue_without_daemon (struct config *conf,
 declare_def_handler(checker_timeout, set_int)
 declare_def_snprint(checker_timeout, print_nonzero)
 
+declare_def_handler(allow_usb_devices, set_yes_no)
+declare_def_snprint(allow_usb_devices, print_yes_no)
+
 declare_def_handler(flush_on_last_del, set_yes_no_undef)
 declare_def_snprint_defint(flush_on_last_del, print_yes_no_undef, DEFAULT_FLUSH)
 declare_ovr_handler(flush_on_last_del, set_yes_no_undef)
@@ -870,7 +876,7 @@ set_dev_loss(vector strvec, void *ptr)
        if (!strcmp(buff, "infinity"))
                *uint_ptr = MAX_DEV_LOSS_TMO;
        else if (sscanf(buff, "%u", uint_ptr) != 1)
-               *uint_ptr = 0;
+               *uint_ptr = DEV_LOSS_TMO_UNSET;
 
        FREE(buff);
        return 0;
@@ -879,7 +885,7 @@ set_dev_loss(vector strvec, void *ptr)
 int
 print_dev_loss(char * buff, int len, unsigned long v)
 {
-       if (!v)
+       if (v == DEV_LOSS_TMO_UNSET)
                return 0;
        if (v >= MAX_DEV_LOSS_TMO)
                return snprintf(buff, len, "\"infinity\"");
@@ -1496,7 +1502,8 @@ blacklist_exceptions_handler(struct config *conf, vector strvec)
 static int                                                             \
 ble_ ## option ## _handler (struct config *conf, vector strvec)                \
 {                                                                      \
-       char * buff;                                                    \
+       char *buff;                                                     \
+       int rc;                                                         \
                                                                        \
        if (!conf->option)                                              \
                return 1;                                               \
@@ -1505,7 +1512,9 @@ ble_ ## option ## _handler (struct config *conf, vector strvec)           \
        if (!buff)                                                      \
                return 1;                                               \
                                                                        \
-       return store_ble(conf->option, buff, ORIGIN_CONFIG);            \
+       rc = store_ble(conf->option, buff, ORIGIN_CONFIG);              \
+       free(buff);                                                     \
+       return rc;                                                      \
 }
 
 #define declare_ble_device_handler(name, option, vend, prod)           \
@@ -1513,6 +1522,7 @@ static int                                                                \
 ble_ ## option ## _ ## name ## _handler (struct config *conf, vector strvec) \
 {                                                                      \
        char * buff;                                                    \
+       int rc;                                                         \
                                                                        \
        if (!conf->option)                                              \
                return 1;                                               \
@@ -1521,7 +1531,9 @@ ble_ ## option ## _ ## name ## _handler (struct config *conf, vector strvec) \
        if (!buff)                                                      \
                return 1;                                               \
                                                                        \
-       return set_ble_device(conf->option, vend, prod, ORIGIN_CONFIG); \
+       rc = set_ble_device(conf->option, vend, prod, ORIGIN_CONFIG);   \
+       free(buff);                                                     \
+       return rc;                                                      \
 }
 
 declare_ble_handler(blist_devnode)
@@ -1750,6 +1762,7 @@ init_keywords(vector keywords)
        install_keyword("no_path_retry", &def_no_path_retry_handler, &snprint_def_no_path_retry);
        install_keyword("queue_without_daemon", &def_queue_without_daemon_handler, &snprint_def_queue_without_daemon);
        install_keyword("checker_timeout", &def_checker_timeout_handler, &snprint_def_checker_timeout);
+       install_keyword("allow_usb_devices", &def_allow_usb_devices_handler, &snprint_def_allow_usb_devices);
        install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated);
        install_keyword("flush_on_last_del", &def_flush_on_last_del_handler, &snprint_def_flush_on_last_del);
        install_keyword("user_friendly_names", &def_user_friendly_names_handler, &snprint_def_user_friendly_names);
index ee3290c..c2e1754 100644 (file)
@@ -33,6 +33,8 @@
 #include "unaligned.h"
 #include "prioritizers/alua_rtpg.h"
 #include "foreign.h"
+#include "configure.h"
+#include "print.h"
 
 struct vpd_vendor_page vpd_vendor_pages[VPD_VP_ARRAY_SIZE] = {
        [VPD_VP_UNDEF]  = { 0x00, "undef" },
@@ -64,6 +66,7 @@ alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,
 
        if (safe_sprintf(pp->dev, "%s", devname)) {
                condlog(0, "pp->dev too small");
+               err = 1;
        } else {
                pp->udev = udev_device_ref(udevice);
                err = pathinfo(pp, conf, flag | DI_BLACKLIST);
@@ -122,27 +125,24 @@ static int
 path_discover (vector pathvec, struct config * conf,
               struct udev_device *udevice, int flag)
 {
-       struct path * pp;
-       const char * devname;
-
-       devname = udev_device_get_sysname(udevice);
-       if (!devname)
-               return PATHINFO_FAILED;
-
-       pp = find_path_by_dev(pathvec, devname);
-       if (!pp) {
-               char devt[BLK_DEV_SIZE];
-               dev_t devnum = udev_device_get_devnum(udevice);
+       struct path *pp;
+       char devt[BLK_DEV_SIZE];
+       dev_t devnum = udev_device_get_devnum(udevice);
 
-               snprintf(devt, BLK_DEV_SIZE, "%d:%d",
-                        major(devnum), minor(devnum));
-               pp = find_path_by_devt(pathvec, devt);
-               if (!pp)
-                       return store_pathinfo(pathvec, conf,
-                                             udevice, flag | DI_BLACKLIST,
-                                             NULL);
-       }
-       return pathinfo(pp, conf, flag);
+       snprintf(devt, BLK_DEV_SIZE, "%d:%d",
+                major(devnum), minor(devnum));
+       pp = find_path_by_devt(pathvec, devt);
+       if (!pp)
+               return store_pathinfo(pathvec, conf,
+                                     udevice, flag | DI_BLACKLIST,
+                                     NULL);
+       else
+               /*
+                * Don't use DI_BLACKLIST on paths already in pathvec. We rely
+                * on the caller to pre-populate the pathvec with valid paths
+                * only.
+                */
+               return pathinfo(pp, conf, flag);
 }
 
 static void cleanup_udev_enumerate_ptr(void *arg)
@@ -344,7 +344,10 @@ sysfs_get_tgt_nodename(struct path *pp, char *node)
        struct udev_device *parent, *tgtdev;
        int host, channel, tgtid = -1;
 
-       parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_device");
+       if (!pp->udev)
+               return 1;
+       parent = udev_device_get_parent_with_subsystem_devtype(pp->udev,
+                                                        "scsi", "scsi_device");
        if (!parent)
                return 1;
        /* Check for SAS */
@@ -353,7 +356,7 @@ sysfs_get_tgt_nodename(struct path *pp, char *node)
                tgtdev = udev_device_get_parent(parent);
                while (tgtdev) {
                        tgtname = udev_device_get_sysname(tgtdev);
-                       if (sscanf(tgtname, "end_device-%d:%d",
+                       if (tgtname && sscanf(tgtname, "end_device-%d:%d",
                                   &host, &tgtid) == 2)
                                break;
                        tgtdev = udev_device_get_parent(tgtdev);
@@ -372,11 +375,10 @@ sysfs_get_tgt_nodename(struct path *pp, char *node)
        while (tgtdev) {
                value = udev_device_get_subsystem(tgtdev);
                if (value && !strcmp(value, "usb")) {
-                       pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
+                       pp->sg_id.proto_id = SCSI_PROTOCOL_USB;
                        tgtname = udev_device_get_sysname(tgtdev);
                        strlcpy(node, tgtname, NODE_NAME_SIZE);
-                       condlog(3, "%s: skip USB device %s", pp->dev, node);
-                       return 1;
+                       return 0;
                }
                tgtdev = udev_device_get_parent(tgtdev);
        }
@@ -386,12 +388,12 @@ sysfs_get_tgt_nodename(struct path *pp, char *node)
        /* Check for FibreChannel */
        tgtdev = udev_device_get_parent(parent);
        value = udev_device_get_sysname(tgtdev);
-       if (sscanf(value, "rport-%d:%d-%d",
+       if (value && sscanf(value, "rport-%d:%d-%d",
                   &host, &channel, &tgtid) == 3) {
                tgtdev = udev_device_new_from_subsystem_sysname(udev,
                                "fc_remote_ports", value);
                if (tgtdev) {
-                       condlog(3, "SCSI target %d:%d:%d -> "
+                       condlog(4, "SCSI target %d:%d:%d -> "
                                "FC rport %d:%d-%d",
                                pp->sg_id.host_no, pp->sg_id.channel,
                                pp->sg_id.scsi_id, host, channel,
@@ -516,6 +518,11 @@ int sysfs_get_host_pci_name(const struct path *pp, char *pci_name)
                 */
                value = udev_device_get_sysname(parent);
 
+               if (!value) {
+                       udev_device_unref(hostdev);
+                       return 1;
+               }
+
                strncpy(pci_name, value, SLOT_NAME_SIZE);
                udev_device_unref(hostdev);
                return 0;
@@ -583,7 +590,7 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
        struct udev_device *rport_dev = NULL;
        char value[16], *eptr;
        char rport_id[32];
-       unsigned long long tmo = 0;
+       unsigned int tmo;
        int ret;
 
        sprintf(rport_id, "rport-%d:%d-%d",
@@ -607,8 +614,8 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
                        "error %d", rport_id, -ret);
                goto out;
        }
-       tmo = strtoull(value, &eptr, 0);
-       if (value == eptr || tmo == ULLONG_MAX) {
+       tmo = strtoul(value, &eptr, 0);
+       if (value == eptr) {
                condlog(0, "%s: Cannot parse dev_loss_tmo "
                        "attribute '%s'", rport_id, value);
                goto out;
@@ -648,7 +655,7 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
                }
        } else if (mpp->dev_loss > DEFAULT_DEV_LOSS_TMO &&
                mpp->no_path_retry != NO_PATH_RETRY_QUEUE) {
-               condlog(3, "%s: limiting dev_loss_tmo to %d, since "
+               condlog(2, "%s: limiting dev_loss_tmo to %d, since "
                        "fast_io_fail is not set",
                        rport_id, DEFAULT_DEV_LOSS_TMO);
                mpp->dev_loss = DEFAULT_DEV_LOSS_TMO;
@@ -670,7 +677,7 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
                                        rport_id, value, -ret);
                }
        }
-       if (mpp->dev_loss > 0) {
+       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
                snprintf(value, 16, "%u", mpp->dev_loss);
                ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
                                           value, strlen(value));
@@ -704,7 +711,7 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
        condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
                pp->sg_id.channel, pp->sg_id.scsi_id, session_id);
 
-       if (mpp->dev_loss) {
+       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
                condlog(3, "%s: ignoring dev_loss_tmo on iSCSI", pp->dev);
        }
        if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
@@ -746,7 +753,7 @@ sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp)
        condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
                pp->sg_id.channel, pp->sg_id.scsi_id, end_dev_id);
 
-       if (mpp->dev_loss) {
+       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
                snprintf(value, 11, "%u", mpp->dev_loss);
                if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout",
                                         value, strlen(value)) <= 0)
@@ -764,6 +771,7 @@ sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint)
        struct path *pp;
        int i;
        unsigned int dev_loss_tmo = mpp->dev_loss;
+       struct path *err_path = NULL;
 
        if (mpp->no_path_retry > 0) {
                uint64_t no_path_retry_tmo =
@@ -773,30 +781,55 @@ sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint)
                        no_path_retry_tmo = MAX_DEV_LOSS_TMO;
                if (no_path_retry_tmo > dev_loss_tmo)
                        dev_loss_tmo = no_path_retry_tmo;
-               condlog(3, "%s: update dev_loss_tmo to %u",
-                       mpp->alias, dev_loss_tmo);
        } else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) {
                dev_loss_tmo = MAX_DEV_LOSS_TMO;
-               condlog(3, "%s: update dev_loss_tmo to %u",
-                       mpp->alias, dev_loss_tmo);
        }
-       mpp->dev_loss = dev_loss_tmo;
-       if (mpp->dev_loss && mpp->fast_io_fail > 0 &&
+       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET &&
+           mpp->dev_loss != dev_loss_tmo) {
+               condlog(2, "%s: Using dev_loss_tmo=%u instead of %u because of no_path_retry setting",
+                       mpp->alias, dev_loss_tmo, mpp->dev_loss);
+               mpp->dev_loss = dev_loss_tmo;
+       }
+       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET &&
+           mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
            (unsigned int)mpp->fast_io_fail >= mpp->dev_loss) {
                condlog(3, "%s: turning off fast_io_fail (%d is not smaller than dev_loss_tmo)",
                        mpp->alias, mpp->fast_io_fail);
                mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF;
        }
-       if (!mpp->dev_loss && mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
+       if (mpp->dev_loss == DEV_LOSS_TMO_UNSET &&
+           mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
                return 0;
 
        vector_foreach_slot(mpp->paths, pp, i) {
-               if (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP)
+               if (pp->bus != SYSFS_BUS_SCSI) {
+                       if (!err_path)
+                               err_path = pp;
+                       continue;
+               }
+
+               switch (pp->sg_id.proto_id) {
+               case SCSI_PROTOCOL_FCP:
                        sysfs_set_rport_tmo(mpp, pp);
-               if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI)
+                       continue;
+               case SCSI_PROTOCOL_ISCSI:
                        sysfs_set_session_tmo(mpp, pp);
-               if (pp->sg_id.proto_id == SCSI_PROTOCOL_SAS)
+                       continue;
+               case SCSI_PROTOCOL_SAS:
                        sysfs_set_nexus_loss_tmo(mpp, pp);
+                       continue;
+               default:
+                       if (!err_path)
+                               err_path = pp;
+               }
+       }
+
+       if (err_path) {
+               char proto_buf[32];
+
+               snprint_path_protocol(proto_buf, sizeof(proto_buf), err_path);
+               condlog(2, "%s: setting dev_loss_tmo is unsupported for protocol %s",
+                       mpp->alias, proto_buf);
        }
        return 0;
 }
@@ -887,6 +920,12 @@ detect_alua(struct path * pp)
        int tpgs;
        unsigned int timeout;
 
+
+       if (pp->bus != SYSFS_BUS_SCSI) {
+               pp->tpgs = TPGS_NONE;
+               return;
+       }
+
        if (sysfs_get_timeout(pp, &timeout) <= 0)
                timeout = DEF_TIMEOUT;
 
@@ -986,7 +1025,7 @@ parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len)
        }
 
        if (len >= out_len) {
-               condlog(2, "vpd pg80 overflow, %lu/%lu bytes required",
+               condlog(2, "vpd pg80 overflow, %zu/%zu bytes required",
                        len + 1, out_len);
                len = out_len - 1;
        }
@@ -1087,7 +1126,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
 
                len = sprintf(out, "%d", vpd_type);
                if (2 * vpd_len >= out_len - len) {
-                       condlog(1, "%s: WWID overflow, type %d, %lu/%lu bytes required",
+                       condlog(1, "%s: WWID overflow, type %d, %zu/%zu bytes required",
                                __func__, vpd_type,
                                2 * vpd_len + len + 1, out_len);
                        vpd_len = (out_len - len - 1) / 2;
@@ -1096,7 +1135,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                        len += sprintf(out + len,
                                       "%02x", vpd[i]);
        } else if (vpd_type == 0x8 && vpd_len < 4) {
-               condlog(1, "%s: VPD length %lu too small for designator type 8",
+               condlog(1, "%s: VPD length %zu too small for designator type 8",
                        __func__, vpd_len);
                return -EINVAL;
        } else if (vpd_type == 0x8) {
@@ -1112,7 +1151,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                while (len > 2 && vpd[len - 2] == '\0')
                        --len;
                if (len > out_len - 1) {
-                       condlog(1, "%s: WWID overflow, type 8/%c, %lu/%lu bytes required",
+                       condlog(1, "%s: WWID overflow, type 8/%c, %zu/%zu bytes required",
                                __func__, out[0], len + 1, out_len);
                        len = out_len - 1;
                }
@@ -1136,7 +1175,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                while ((p = memchr(vpd, ' ', vpd_len))) {
                        p_len = p - vpd;
                        if (len + p_len > out_len - 1) {
-                               condlog(1, "%s: WWID overflow, type 1, %lu/%lu bytes required",
+                               condlog(1, "%s: WWID overflow, type 1, %zu/%zu bytes required",
                                        __func__, len + p_len, out_len);
                                p_len = out_len - len - 1;
                        }
@@ -1162,7 +1201,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                p_len = vpd_len;
                if (p_len > 0 && len < out_len - 1) {
                        if (len + p_len > out_len - 1) {
-                               condlog(1, "%s: WWID overflow, type 1, %lu/%lu bytes required",
+                               condlog(1, "%s: WWID overflow, type 1, %zu/%zu bytes required",
                                        __func__, len + p_len + 1, out_len);
                                p_len = out_len - len - 1;
                        }
@@ -1186,14 +1225,14 @@ parse_vpd_c0_hp3par(const unsigned char *in, size_t in_len,
 
        memset(out, 0x0, out_len);
        if (in_len <= 4 || (in[4] > 3 && in_len < 44)) {
-               condlog(3, "HP/3PAR vendor specific VPD page length too short: %lu", in_len);
+               condlog(3, "HP/3PAR vendor specific VPD page length too short: %zu", in_len);
                return -EINVAL;
        }
        if (in[4] <= 3) /* revision must be > 3 to have Vomlume Name */
                return -ENODATA;
        len = get_unaligned_be32(&in[40]);
        if (len > out_len || len + 44 > in_len) {
-               condlog(3, "HP/3PAR vendor specific Volume name too long: %lu",
+               condlog(3, "HP/3PAR vendor specific Volume name too long: %zu",
                        len);
                return -EINVAL;
        }
@@ -1278,7 +1317,7 @@ get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen)
 }
 
 static int
-scsi_sysfs_pathinfo (struct path * pp, vector hwtable)
+scsi_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
 {
        struct udev_device *parent;
        const char *attr_path = NULL;
@@ -1345,13 +1384,14 @@ scsi_sysfs_pathinfo (struct path * pp, vector hwtable)
 }
 
 static int
-nvme_sysfs_pathinfo (struct path * pp, vector hwtable)
+nvme_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
 {
        struct udev_device *parent;
        const char *attr_path = NULL;
        const char *attr;
 
-       attr_path = udev_device_get_sysname(pp->udev);
+       if (pp->udev)
+               attr_path = udev_device_get_sysname(pp->udev);
        if (!attr_path)
                return PATHINFO_FAILED;
 
@@ -1390,7 +1430,7 @@ nvme_sysfs_pathinfo (struct path * pp, vector hwtable)
 }
 
 static int
-ccw_sysfs_pathinfo (struct path * pp, vector hwtable)
+ccw_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
 {
        struct udev_device *parent;
        char attr_buff[NAME_SIZE];
@@ -1432,6 +1472,8 @@ ccw_sysfs_pathinfo (struct path * pp, vector hwtable)
         * host / bus / target / lun
         */
        attr_path = udev_device_get_sysname(parent);
+       if (!attr_path)
+               return PATHINFO_FAILED;
        pp->sg_id.lun = 0;
        if (sscanf(attr_path, "%i.%i.%x",
                   &pp->sg_id.host_no,
@@ -1449,7 +1491,7 @@ ccw_sysfs_pathinfo (struct path * pp, vector hwtable)
 }
 
 static int
-cciss_sysfs_pathinfo (struct path * pp, vector hwtable)
+cciss_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
 {
        const char * attr_path = NULL;
        struct udev_device *parent;
@@ -1603,8 +1645,8 @@ path_offline (struct path * pp)
        return PATH_DOWN;
 }
 
-int
-sysfs_pathinfo(struct path * pp, vector hwtable)
+static int
+sysfs_pathinfo(struct path *pp, const struct _vector *hwtable)
 {
        int r = common_sysfs_pathinfo(pp);
 
@@ -1891,7 +1933,7 @@ get_udev_uid(struct path * pp, char *uid_attribute, struct udev_device *udev)
        } else {
                condlog(3, "%s: no %s attribute", pp->dev,
                        uid_attribute);
-               len = -EINVAL;
+               len = -ENODATA;
        }
        return len;
 }
@@ -1931,6 +1973,9 @@ static ssize_t uid_fallback(struct path *pp, int path_state,
                }
        } else if (pp->bus == SYSFS_BUS_NVME) {
                char value[256];
+
+               if (!pp->udev)
+                       return -1;
                len = sysfs_attr_get_value(pp->udev, "wwid", value,
                                           sizeof(value));
                if (len <= 0)
@@ -2008,12 +2053,9 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev,
 
                if (udev_available) {
                        len = get_udev_uid(pp, pp->uid_attribute, udev);
-                       if (len <= 0)
-                               condlog(1,
-                                       "%s: failed to get udev uid: %s",
-                                       pp->dev, strerror(-len));
-                       else
-                               origin = "udev";
+                       origin = "udev";
+                       if (len == 0)
+                               condlog(1, "%s: empty udev uid", pp->dev);
                }
                if ((!udev_available || (len <= 0 && allow_fallback))
                    && has_uid_fallback(pp)) {
@@ -2047,6 +2089,10 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
        if (!pp || !conf)
                return PATHINFO_FAILED;
 
+       /* Treat removed paths as if they didn't exist */
+       if (pp->initialized == INIT_REMOVED)
+               return PATHINFO_FAILED;
+
        /*
         * For behavior backward-compatibility with multipathd,
         * the blacklisting by filter_property|devnode() is not
@@ -2066,7 +2112,7 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
                        return PATHINFO_SKIPPED;
        }
 
-       if (filter_devnode(conf->blist_devnode,
+       if (strlen(pp->dev) != 0 && filter_devnode(conf->blist_devnode,
                           conf->elist_devnode,
                           pp->dev) > 0)
                return PATHINFO_SKIPPED;
@@ -2091,6 +2137,14 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
 
                if (rc != PATHINFO_OK)
                        return rc;
+
+               if (pp->bus == SYSFS_BUS_SCSI &&
+                   pp->sg_id.proto_id == SCSI_PROTOCOL_USB &&
+                   !conf->allow_usb_devices) {
+                       condlog(3, "%s: skip USB device %s", pp->dev,
+                               pp->tgt_node_name);
+                       return PATHINFO_SKIPPED;
+               }
        }
 
        if (mask & DI_BLACKLIST && mask & DI_SYSFS) {
@@ -2121,7 +2175,7 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
                pp->fd = open(udev_device_get_devnode(pp->udev), O_RDONLY);
 
        if (pp->fd < 0) {
-               condlog(4, "Couldn't open node for %s: %s",
+               condlog(4, "Couldn't open device node for %s: %s",
                        pp->dev, strerror(errno));
                goto blank;
        }
index b856a07..b306c46 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include "checkers.h"
 #include "vector.h"
@@ -18,7 +19,7 @@
 #define WORD_SIZE 64
 
 static int
-merge_words(char **dst, char *word)
+merge_words(char **dst, const char *word)
 {
        char * p = *dst;
        int len, dstlen;
@@ -65,7 +66,7 @@ assemble_map (struct multipath * mp, char * params, int len)
        int i, j;
        int minio;
        int nr_priority_groups, initial_pg_nr;
-       char * p, * f;
+       char * p;
        const char *const end = params + len;
        char no_path_retry[] = "queue_if_no_path";
        char retain_hwhandler[] = "retain_attached_hw_handler";
@@ -86,10 +87,9 @@ assemble_map (struct multipath * mp, char * params, int len)
            get_linux_version_code() < KERNEL_VERSION(4, 3, 0))
                add_feature(&mp->features, retain_hwhandler);
 
-       f = STRDUP(mp->features);
-
-       APPEND(p, end, "%s %s %i %i", f, mp->hwhandler, nr_priority_groups,
-              initial_pg_nr);
+       /* mp->features must not be NULL */
+       APPEND(p, end, "%s %s %i %i", mp->features, mp->hwhandler,
+               nr_priority_groups, initial_pg_nr);
 
        vector_foreach_slot (mp->pg, pgp, i) {
                pgp = VECTOR_SLOT(mp->pg, i);
@@ -110,22 +110,26 @@ assemble_map (struct multipath * mp, char * params, int len)
                }
        }
 
-       FREE(f);
        condlog(4, "%s: assembled map [%s]", mp->alias, params);
        return 0;
 
 err:
-       FREE(f);
        return 1;
 }
 
 #undef APPEND
 
-int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
-                   int is_daemon)
+/*
+ * Caution callers: If this function encounters yet unkown path devices, it
+ * adds them uninitialized to the mpp.
+ * Call update_pathvec_from_dm() after this function to make sure
+ * all data structures are in a sane state.
+ */
+int disassemble_map(const struct _vector *pathvec,
+                   const char *params, struct multipath *mpp)
 {
        char * word;
-       char * p;
+       const char *p;
        int i, j, k;
        int num_features = 0;
        int num_hwhandler = 0;
@@ -137,6 +141,7 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
        struct path * pp;
        struct pathgroup * pgp;
 
+       assert(pathvec != NULL);
        p = params;
 
        condlog(4, "%s: disassemble map [%s]", mpp->alias, params);
@@ -261,8 +266,10 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
                if (!pgp)
                        goto out;
 
-               if (add_pathgroup(mpp, pgp))
+               if (add_pathgroup(mpp, pgp)) {
+                       free_pathgroup(pgp, KEEP_PATHS);
                        goto out;
+               }
 
                p += get_word(p, &word);
 
@@ -281,26 +288,13 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
                FREE(word);
 
                for (j = 0; j < num_paths; j++) {
-                       char devname[FILE_NAME_SIZE];
-
                        pp = NULL;
                        p += get_word(p, &word);
 
                        if (!word)
                                goto out;
 
-                       if (devt2devname(devname, FILE_NAME_SIZE, word)) {
-                               condlog(2, "%s: cannot find block device",
-                                       word);
-                               devname[0] = '\0';
-                       }
-
-                       if (pathvec) {
-                               if (strlen(devname))
-                                       pp = find_path_by_dev(pathvec, devname);
-                               else
-                                       pp = find_path_by_devt(pathvec, word);
-                       }
+                       pp = find_path_by_devt(pathvec, word);
 
                        if (!pp) {
                                pp = alloc_path();
@@ -309,46 +303,15 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
                                        goto out1;
 
                                strlcpy(pp->dev_t, word, BLK_DEV_SIZE);
-                               strlcpy(pp->dev, devname, FILE_NAME_SIZE);
-                               if (strlen(mpp->wwid)) {
-                                       strlcpy(pp->wwid, mpp->wwid,
-                                               WWID_SIZE);
-                               }
-                               /* Only call this in multipath client mode */
-                               if (!is_daemon && store_path(pathvec, pp))
-                                       goto out1;
-                       } else {
-                               if (!strlen(pp->wwid) &&
-                                   strlen(mpp->wwid))
-                                       strlcpy(pp->wwid, mpp->wwid,
-                                               WWID_SIZE);
-                       }
-                       FREE(word);
-
-                       if (store_path(pgp->paths, pp))
-                               goto out;
-
-                       /*
-                        * Update wwid for multipaths which are not setup
-                        * in the get_dm_mpvec() code path
-                        */
-                       if (!strlen(mpp->wwid))
-                               strlcpy(mpp->wwid, pp->wwid, WWID_SIZE);
 
-                       /*
-                        * Update wwid for paths which may not have been
-                        * active at the time the getuid callout was run
-                        */
-                       else if (!strlen(pp->wwid))
-                               strlcpy(pp->wwid, mpp->wwid, WWID_SIZE);
+                               if (store_path(pgp->paths, pp)) {
+                                       free_path(pp);
+                                       goto out1;
+                               }
+                       } else if (store_path(pgp->paths, pp))
+                               goto out1;
 
-                       /*
-                        * Do not allow in-use patch to change wwid
-                        */
-                       else if (strcmp(pp->wwid, mpp->wwid) != 0) {
-                               condlog(0, "%s: path wwid appears to have changed. Using map wwid.\n", pp->dev_t);
-                               strlcpy(pp->wwid, mpp->wwid, WWID_SIZE);
-                       }
+                       FREE(word);
 
                        pgp->id ^= (long)pp;
                        pp->pgindex = i + 1;
@@ -385,10 +348,10 @@ out:
        return 1;
 }
 
-int disassemble_status(char *params, struct multipath *mpp)
+int disassemble_status(const char *params, struct multipath *mpp)
 {
-       char * word;
-       char * p;
+       char *word;
+       const char *p;
        int i, j, k;
        int num_feature_args;
        int num_hwhandler_args;
@@ -562,6 +525,7 @@ int disassemble_status(char *params, struct multipath *mpp)
                                                   &def_minio) == 1 &&
                                            def_minio != mpp->minio)
                                                        mpp->minio = def_minio;
+                                       FREE(word);
                                } else
                                        p += get_word(p, NULL);
                        }
index e1badb0..212fee5 100644 (file)
@@ -1,3 +1,3 @@
 int assemble_map (struct multipath *, char *, int);
-int disassemble_map (vector, char *, struct multipath *, int);
-int disassemble_status (char *, struct multipath *);
+int disassemble_map (const struct _vector *, const char *, struct multipath *);
+int disassemble_status (const char *, struct multipath *);
index 0159a83..fce1934 100644 (file)
@@ -236,7 +236,7 @@ static int _init_foreign(const char *multipath_dir, const char *enable)
                        goto dl_err;
                }
 
-               if (vector_alloc_slot(foreigns) == NULL) {
+               if (!vector_alloc_slot(foreigns)) {
                        goto dl_err;
                }
 
@@ -544,8 +544,8 @@ void print_foreign_topology(int verbosity)
        int buflen = MAX_LINE_LEN * MAX_LINES;
        char *buf = NULL, *tmp = NULL;
 
-       buf = malloc(buflen);
-       buf[0] = '\0';
+       buf = calloc(1, buflen);
+
        while (buf != NULL) {
                char *c = buf;
 
index 09cdddf..b726be2 100644 (file)
@@ -482,6 +482,7 @@ _find_path_by_syspath(struct nvme_map *map, const char *syspath)
        struct nvme_pathgroup *pg;
        char real[PATH_MAX];
        const char *ppath;
+       const char *psyspath;
        int i;
 
        ppath = realpath(syspath, real);
@@ -493,8 +494,8 @@ _find_path_by_syspath(struct nvme_map *map, const char *syspath)
        vector_foreach_slot(&map->pgvec, pg, i) {
                struct nvme_path *path = nvme_pg_to_path(pg);
 
-               if (!strcmp(ppath,
-                           udev_device_get_syspath(path->udev)))
+               psyspath = udev_device_get_syspath(path->udev);
+               if (psyspath && !strcmp(ppath, psyspath))
                        return path;
        }
        condlog(4, "%s: %s: %s not found", __func__, THIS, ppath);
@@ -538,6 +539,7 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx,
        struct udev_list_entry *item;
        struct udev_device *blkdev = NULL;
        struct udev_enumerate *enm = udev_enumerate_new(ctx->udev);
+       const char *devtype;
 
        if (enm == NULL)
                return NULL;
@@ -562,7 +564,9 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx,
                                           udev_list_entry_get_name(item));
                if (tmp == NULL)
                        continue;
-               if (!strcmp(udev_device_get_devtype(tmp), "disk")) {
+
+               devtype = udev_device_get_devtype(tmp);
+               if (devtype && !strcmp(devtype, "disk")) {
                        blkdev = tmp;
                        break;
                } else
@@ -718,12 +722,12 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map)
                test_ana_support(map, path->ctl);
 
                path->pg.gen.ops = &nvme_pg_ops;
-               if (vector_alloc_slot(&path->pg.pathvec) == NULL) {
+               if (!vector_alloc_slot(&path->pg.pathvec)) {
                        cleanup_nvme_path(path);
                        continue;
                }
                vector_set_slot(&path->pg.pathvec, path);
-               if (vector_alloc_slot(&map->pgvec) == NULL) {
+               if (!vector_alloc_slot(&map->pgvec)) {
                        cleanup_nvme_path(path);
                        continue;
                }
@@ -779,7 +783,7 @@ static int _add_map(struct context *ctx, struct udev_device *ud,
        map->subsys = subsys;
        map->gen.ops = &nvme_map_ops;
 
-       if (vector_alloc_slot(ctx->mpvec) == NULL) {
+       if (!vector_alloc_slot(ctx->mpvec)) {
                cleanup_nvme_map(map);
                return FOREIGN_ERR;
        }
@@ -793,12 +797,14 @@ int add(struct context *ctx, struct udev_device *ud)
 {
        struct udev_device *subsys;
        int rc;
+       const char *devtype;
 
        condlog(5, "%s called for \"%s\"", __func__, THIS);
 
        if (ud == NULL)
                return FOREIGN_ERR;
-       if (strcmp("disk", udev_device_get_devtype(ud)))
+       if ((devtype = udev_device_get_devtype(ud)) == NULL ||
+                                               strcmp("disk", devtype))
                return FOREIGN_IGNORED;
 
        subsys = udev_device_get_parent_with_subsystem_devtype(ud,
index d1fcfdb..cd65afc 100644 (file)
@@ -181,9 +181,9 @@ static struct hwentry default_hw[] = {
                .prio_name     = PRIO_ALUA,
        },
        {
-               /* MSA 1040, 1050, 2040 and 2050 families */
+               /* MSA 1040, 1050, 1060, 2040, 2050 and 2060 families */
                .vendor        = "HP",
-               .product       = "MSA [12]0[45]0 SA[NS]",
+               .product       = "MSA [12]0[456]0 SA[NS]",
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .no_path_retry = 18,
@@ -428,6 +428,22 @@ static struct hwentry default_hw[] = {
                .pgpolicy      = MULTIBUS,
                .no_path_retry = 10,
        },
+       {
+               /*
+                * ETERNUS AB/HB
+                * Maintainer: NetApp RDAC team <ng-eseries-upstream-maintainers@netapp.com>
+                */
+               .vendor        = "FUJITSU",
+               .product       = "ETERNUS_AHB",
+               .bl_product    = "Universal Xport",
+               .pgpolicy      = GROUP_BY_PRIO,
+               .checker_name  = RDAC,
+               .features      = "2 pg_init_retries 50",
+               .hwhandler     = "1 rdac",
+               .prio_name     = PRIO_RDAC,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .no_path_retry = 30,
+       },
        /*
         * Hitachi Vantara
         *
@@ -729,26 +745,26 @@ static struct hwentry default_hw[] = {
                .no_path_retry = (300 / DEFAULT_CHECKINT),
                .prio_name     = PRIO_ALUA,
        },
-        /*
-         * Lenovo
-         */
-        {
-                /*
+       /*
+        * Lenovo
+        */
+       {
+               /*
                 * DE Series
                 *
                 * Maintainer: NetApp RDAC team <ng-eseries-upstream-maintainers@netapp.com>
                 */
-                .vendor        = "LENOVO",
-                .product       = "DE_Series",
-                .bl_product    = "Universal Xport",
-                .pgpolicy      = GROUP_BY_PRIO,
-                .checker_name  = RDAC,
-                .features      = "2 pg_init_retries 50",
-                .hwhandler     = "1 rdac",
-                .prio_name     = PRIO_RDAC,
-                .pgfailback    = -FAILBACK_IMMEDIATE,
-                .no_path_retry = 30,
-        },
+               .vendor        = "LENOVO",
+               .product       = "DE_Series",
+               .bl_product    = "Universal Xport",
+               .pgpolicy      = GROUP_BY_PRIO,
+               .checker_name  = RDAC,
+               .features      = "2 pg_init_retries 50",
+               .hwhandler     = "1 rdac",
+               .prio_name     = PRIO_RDAC,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .no_path_retry = 30,
+       },
        /*
         * NetApp
         */
@@ -1263,6 +1279,18 @@ static struct hwentry default_hw[] = {
                .prio_name     = PRIO_ALUA,
        },
        /*
+        * MacroSAN Technologies
+        */
+       {
+               /* MS family */
+               .vendor        = "MacroSAN",
+               .product       = "LU",
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .prio_name     = PRIO_ALUA,
+               .no_path_retry = 30,
+       },
+       /*
         * EOL
         */
        {
index 1b9cd6c..58bc1dd 100644 (file)
@@ -35,7 +35,6 @@
 #include "time-util.h"
 #include "io_err_stat.h"
 
-#define IOTIMEOUT_SEC                  60
 #define TIMEOUT_NO_IO_NSEC             10000000 /*10ms = 10000000ns*/
 #define FLAKY_PATHFAIL_THRESHOLD       2
 #define CONCUR_NR_EVENT                        32
@@ -301,30 +300,22 @@ int io_err_stat_handle_pathfail(struct path *path)
        struct timespec curr_time;
 
        if (uatomic_read(&io_err_thread_running) == 0)
-               return 1;
+               return 0;
 
        if (path->io_err_disable_reinstate) {
                io_err_stat_log(3, "%s: reinstate is already disabled",
                                path->dev);
-               return 1;
+               return 0;
        }
        if (path->io_err_pathfail_cnt < 0)
-               return 1;
+               return 0;
 
        if (!path->mpp)
-               return 1;
-       if (path->mpp->marginal_path_double_failed_time <= 0 ||
-               path->mpp->marginal_path_err_sample_time <= 0 ||
-               path->mpp->marginal_path_err_recheck_gap_time <= 0 ||
-               path->mpp->marginal_path_err_rate_threshold < 0) {
-               io_err_stat_log(4, "%s: parameter not set", path->mpp->alias);
-               return 1;
-       }
-       if (path->mpp->marginal_path_err_sample_time < (2 * IOTIMEOUT_SEC)) {
-               io_err_stat_log(2, "%s: marginal_path_err_sample_time should not less than %d",
-                               path->mpp->alias, 2 * IOTIMEOUT_SEC);
-               return 1;
-       }
+               return 0;
+
+       if (!marginal_path_check_enabled(path->mpp))
+               return 0;
+
        /*
         * The test should only be started for paths that have failed
         * repeatedly in a certain time frame, so that we have reason
index d478b17..ed6d5d6 100644 (file)
@@ -194,7 +194,9 @@ snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
 static const char quote_marker[] = { '\0', '"', '\0' };
 bool is_quote(const char* token)
 {
-       return !memcmp(token, quote_marker, sizeof(quote_marker));
+       return token[0] == quote_marker[0] &&
+               token[1] == quote_marker[1] &&
+               token[2] == quote_marker[2];
 }
 
 vector
@@ -235,6 +237,7 @@ alloc_strvec(char *string)
                if (!vector_alloc_slot(strvec))
                        goto out;
 
+               vector_set_slot(strvec, NULL);
                start = cp;
                if (*cp == '"' && !(in_string && *(cp + 1) == '"')) {
                        cp++;
@@ -300,8 +303,10 @@ alloc_strvec(char *string)
                        (isspace((int) *cp) || !isascii((int) *cp)))
                       && *cp != '\0')
                        cp++;
-               if (*cp == '\0' || *cp == '!' || *cp == '#')
+               if (*cp == '\0' ||
+                   (!in_string && (*cp == '!' || *cp == '#'))) {
                        return strvec;
+               }
        }
 out:
        vector_free(strvec);
@@ -372,7 +377,7 @@ set_value(vector strvec)
                        goto oom;
                }
                if (*alloc != '\0')
-                       strncat(alloc, " ", 1);
+                       strncat(alloc, " ", len - strlen(alloc));
                strncat(alloc, str, len - strlen(alloc) - 1);
        }
        return alloc;
@@ -431,14 +436,16 @@ is_sublevel_keyword(char *str)
 int
 validate_config_strvec(vector strvec, char *file)
 {
-       char *str;
+       char *str = NULL;
        int i;
 
-       str = VECTOR_SLOT(strvec, 0);
+       if (strvec && VECTOR_SIZE(strvec) > 0)
+               str = VECTOR_SLOT(strvec, 0);
+
        if (str == NULL) {
                condlog(0, "can't parse option on line %d of %s",
                        line_nr, file);
-       return -1;
+               return -1;
        }
        if (*str == '}') {
                if (VECTOR_SIZE(strvec) > 1)
@@ -451,7 +458,7 @@ validate_config_strvec(vector strvec, char *file)
                return -1;
        }
        if (is_sublevel_keyword(str)) {
-               str = VECTOR_SLOT(strvec, 1);
+               str = VECTOR_SIZE(strvec) > 1 ? VECTOR_SLOT(strvec, 1) : NULL;
                if (str == NULL)
                        condlog(0, "missing '{' on line %d of %s",
                                line_nr, file);
@@ -462,7 +469,7 @@ validate_config_strvec(vector strvec, char *file)
                        condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
                return 0;
        }
-       str = VECTOR_SLOT(strvec, 1);
+       str = VECTOR_SIZE(strvec) > 1 ? VECTOR_SLOT(strvec, 1) : NULL;
        if (str == NULL) {
                condlog(0, "missing value for option '%s' on line %d of %s",
                        (char *)VECTOR_SLOT(strvec, 0), line_nr, file);
index 02cafdc..0e55109 100644 (file)
@@ -196,20 +196,20 @@ int group_by_match(struct multipath * mp, vector paths,
                   bool (*path_match_fn)(struct path *, struct path *))
 {
        int i, j;
-       int * bitmap;
+       struct bitfield *bitmap;
        struct path * pp;
        struct pathgroup * pgp;
        struct path * pp2;
 
        /* init the bitmap */
-       bitmap = (int *)MALLOC(VECTOR_SIZE(paths) * sizeof (int));
+       bitmap = alloc_bitfield(VECTOR_SIZE(paths));
 
        if (!bitmap)
                goto out;
 
        for (i = 0; i < VECTOR_SIZE(paths); i++) {
 
-               if (bitmap[i])
+               if (is_bit_set_in_bitfield(i, bitmap))
                        continue;
 
                pp = VECTOR_SLOT(paths, i);
@@ -227,11 +227,11 @@ int group_by_match(struct multipath * mp, vector paths,
                if (store_path(pgp->paths, pp))
                        goto out1;
 
-               bitmap[i] = 1;
+               set_bit_in_bitfield(i, bitmap);
 
                for (j = i + 1; j < VECTOR_SIZE(paths); j++) {
 
-                       if (bitmap[j])
+                       if (is_bit_set_in_bitfield(j, bitmap))
                                continue;
 
                        pp2 = VECTOR_SLOT(paths, j);
@@ -240,7 +240,7 @@ int group_by_match(struct multipath * mp, vector paths,
                                if (store_path(pgp->paths, pp2))
                                        goto out1;
 
-                               bitmap[j] = 1;
+                               set_bit_in_bitfield(j, bitmap);
                        }
                }
        }
index b944ef3..19de2c7 100644 (file)
@@ -30,6 +30,7 @@
 #include "debug.h"
 #include "discovery.h"
 #include "util.h"
+#include "foreign.h"
 
 #define MAX(x,y) (((x) > (y)) ? (x) : (y))
 #define MIN(x,y) (((x) > (y)) ? (y) : (x))
@@ -683,6 +684,8 @@ snprint_path_protocol(char * buff, size_t len, const struct path * pp)
                        return snprintf(buff, len, "scsi:adt");
                case SCSI_PROTOCOL_ATA:
                        return snprintf(buff, len, "scsi:ata");
+               case SCSI_PROTOCOL_USB:
+                       return snprintf(buff, len, "scsi:usb");
                case SCSI_PROTOCOL_UNSPEC:
                default:
                        return snprintf(buff, len, "scsi:unspec");
@@ -1958,25 +1961,25 @@ char *snprint_config(const struct config *conf, int *len,
                }
 
                c = reply + snprint_defaults(conf, reply, maxlen);
-               if ((c - reply) == maxlen)
+               if (c == reply + maxlen)
                        continue;
 
                c += snprint_blacklist(conf, c, reply + maxlen - c);
-               if ((c - reply) == maxlen)
+               if (c == reply + maxlen)
                        continue;
 
                c += snprint_blacklist_except(conf, c, reply + maxlen - c);
-               if ((c - reply) == maxlen)
+               if (c == reply + maxlen)
                        continue;
 
                c += snprint_hwtable(conf, c, reply + maxlen - c,
                                     hwtable ? hwtable : conf->hwtable);
-               if ((c - reply) == maxlen)
+               if (c == reply + maxlen)
                        continue;
 
                c += snprint_overrides(conf, c, reply + maxlen - c,
                                       conf->overrides);
-               if ((c - reply) == maxlen)
+               if (c == reply + maxlen)
                        continue;
 
                if (VECTOR_SIZE(conf->mptable) > 0 ||
@@ -1984,7 +1987,7 @@ char *snprint_config(const struct config *conf, int *len,
                        c += snprint_mptable(conf, c, reply + maxlen - c,
                                             mpvec);
 
-               if ((c - reply) < maxlen) {
+               if (c < reply + maxlen) {
                        if (len)
                                *len = c - reply;
                        return reply;
@@ -2026,65 +2029,68 @@ int snprint_status(char *buff, int len, const struct vectors *vecs)
        return fwd;
 }
 
-int snprint_devices(struct config *conf, char * buff, int len,
+int snprint_devices(struct config *conf, char *buff, size_t len,
                    const struct vectors *vecs)
 {
-       DIR *blkdir;
-       struct dirent *blkdev;
-       struct stat statbuf;
-       char devpath[PATH_MAX];
-       int threshold = MAX_LINE_LEN;
-       int fwd = 0;
+       size_t fwd = 0;
        int r;
+       struct udev_enumerate *enm;
+       struct udev_list_entry *item, *first;
 
        struct path * pp;
 
-       if (!(blkdir = opendir("/sys/block")))
+       enm = udev_enumerate_new(udev);
+       if (!enm)
                return 1;
+       udev_enumerate_add_match_subsystem(enm, "block");
 
-       if ((len - fwd - threshold) <= 0) {
-               closedir(blkdir);
-               return len;
-       }
        fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n");
+       r = udev_enumerate_scan_devices(enm);
+       if (r < 0)
+               goto out;
 
-       while ((blkdev = readdir(blkdir)) != NULL) {
-               if ((strcmp(blkdev->d_name,".") == 0) ||
-                   (strcmp(blkdev->d_name,"..") == 0))
-                       continue;
-
-               if (safe_sprintf(devpath, "/sys/block/%s", blkdev->d_name))
-                       continue;
-
-               if (stat(devpath, &statbuf) < 0)
-                       continue;
+       first = udev_enumerate_get_list_entry(enm);
+       udev_list_entry_foreach(item, first) {
+               const char *path, *devname, *status;
+               struct udev_device *u_dev;
 
-               if (S_ISDIR(statbuf.st_mode) == 0)
-                       continue;
+               path = udev_list_entry_get_name(item);
+               u_dev = udev_device_new_from_syspath(udev, path);
+               devname = udev_device_get_sysname(u_dev);
 
-               if ((len - fwd - threshold)  <= 0) {
-                       closedir(blkdir);
-                       return len;
-               }
+               fwd += snprintf(buff + fwd, len - fwd, "    %s", devname);
+               if (fwd >= len)
+                       break;
 
-               fwd += snprintf(buff + fwd, len - fwd, "    %s",
-                               blkdev->d_name);
-               pp = find_path_by_dev(vecs->pathvec, blkdev->d_name);
+               pp = find_path_by_dev(vecs->pathvec, devname);
                if (!pp) {
-                       r = filter_devnode(conf->blist_devnode,
-                                          conf->elist_devnode, blkdev->d_name);
-                       if (r > 0)
-                               fwd += snprintf(buff + fwd, len - fwd,
-                                               " devnode blacklisted, unmonitored");
-                       else if (r <= 0)
-                               fwd += snprintf(buff + fwd, len - fwd,
-                                               " devnode whitelisted, unmonitored");
+                       const char *hidden;
+
+                       hidden = udev_device_get_sysattr_value(u_dev,
+                                                              "hidden");
+                       if (hidden && !strcmp(hidden, "1"))
+                               status = "hidden, unmonitored";
+                       else if (is_claimed_by_foreign(u_dev))
+                               status = "foreign, monitored";
+                       else {
+                               r = filter_devnode(conf->blist_devnode,
+                                                  conf->elist_devnode,
+                                                  devname);
+                               if (r > 0)
+                                       status = "devnode blacklisted, unmonitored";
+                               else
+                                       status = "devnode whitelisted, unmonitored";
+                       }
                } else
-                       fwd += snprintf(buff + fwd, len - fwd,
-                                       " devnode whitelisted, monitored");
-               fwd += snprintf(buff + fwd, len - fwd, "\n");
+                       status = " devnode whitelisted, monitored";
+
+               fwd += snprintf(buff + fwd, len - fwd, " %s\n", status);
+               udev_device_unref(u_dev);
+               if (fwd >= len)
+                       break;
        }
-       closedir(blkdir);
+out:
+       udev_enumerate_unref(enm);
 
        if (fwd >= len)
                return len;
index e8260d0..0042cef 100644 (file)
@@ -129,7 +129,7 @@ int snprint_multipath_map_json (char * buff, int len,
 int snprint_blacklist_report (struct config *, char *, int);
 int snprint_wildcards (char *, int);
 int snprint_status (char *, int, const struct vectors *);
-int snprint_devices (struct config *, char *, int, const struct vectors *);
+int snprint_devices (struct config *, char *, size_t, const struct vectors *);
 int snprint_path_serial (char *, size_t, const struct path *);
 int snprint_host_wwnn (char *, size_t, const struct path *);
 int snprint_host_wwpn (char *, size_t, const struct path *);
index 9d0fe03..fc6e0e0 100644 (file)
@@ -28,7 +28,7 @@ endif
 
 all: $(LIBS)
 
-libpriopath_latency.so: path_latency.o  ../checkers/libsg.o
+libpriopath_latency.so: path_latency.o
        $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -lm
 
 libprio%.so: %.o
index bbf5aac..420a2e3 100644 (file)
@@ -188,9 +188,11 @@ retry:
 int do_inquiry(const struct path *pp, int evpd, unsigned int codepage,
               void *resp, int resplen, unsigned int timeout)
 {
-       struct udev_device *ud;
+       struct udev_device *ud = NULL;
 
-       ud = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi",
+       if (pp->udev)
+               ud = udev_device_get_parent_with_subsystem_devtype(pp->udev,
+                                                                  "scsi",
                                                           "scsi_device");
        if (ud != NULL) {
                int rc;
index 18b495e..7ba2cf4 100644 (file)
@@ -284,7 +284,7 @@ struct rtpg_data {
 #define RTPG_FOR_EACH_PORT_GROUP(p, g) \
                for( \
                        g = &(p->data[0]); \
-                       (((char *) g) - ((char *) p)) < get_unaligned_be32(p->length); \
+                       ((char *) g) < ((char *) p) + get_unaligned_be32(p->length); \
                        g = (struct rtpg_tpg_dscr *) ( \
                                ((char *) g) + \
                                sizeof(struct rtpg_tpg_dscr) + \
index 897e48c..7e6e0d6 100644 (file)
@@ -65,7 +65,9 @@ do {                                                                  \
        __do_set_from_vec(struct hwentry, var, (src)->hwe, dest)
 
 #define do_set_from_hwe(var, src, dest, msg)                           \
-       if (__do_set_from_hwe(var, src, dest)) {                        \
+       if (!src->hwe) {                                                \
+               condlog(0, "BUG: do_set_from_hwe called with hwe == NULL"); \
+       } else if (__do_set_from_hwe(var, src, dest)) {                 \
                origin = msg;                                           \
                goto out;                                               \
        }
@@ -521,7 +523,9 @@ int select_checker(struct config *conf, struct path *pp)
                if (check_rdac(pp)) {
                        ckr_name = RDAC;
                        goto out;
-               } else if (path_get_tpgs(pp) != TPGS_NONE) {
+               }
+               path_get_tpgs(pp);
+               if (pp->tpgs != TPGS_NONE && pp->tpgs != TPGS_UNDEF) {
                        ckr_name = TUR;
                        goto out;
                }
@@ -764,7 +768,7 @@ int select_dev_loss(struct config *conf, struct multipath *mp)
        mp_set_ovr(dev_loss);
        mp_set_hwe(dev_loss);
        mp_set_conf(dev_loss);
-       mp->dev_loss = 0;
+       mp->dev_loss = DEV_LOSS_TMO_UNSET;
        return 0;
 out:
        print_dev_loss(buff, 12, mp->dev_loss);
@@ -1064,6 +1068,12 @@ int select_marginal_path_err_sample_time(struct config *conf, struct multipath *
        mp_set_conf(marginal_path_err_sample_time);
        mp_set_default(marginal_path_err_sample_time, DEFAULT_ERR_CHECKS);
 out:
+       if (mp->marginal_path_err_sample_time > 0 &&
+           mp->marginal_path_err_sample_time < 2 * IOTIMEOUT_SEC) {
+               condlog(2, "%s: configuration error: marginal_path_err_sample_time must be >= %d",
+                       mp->alias, 2 * IOTIMEOUT_SEC);
+                       mp->marginal_path_err_sample_time = 2 * IOTIMEOUT_SEC;
+       }
        if (print_off_int_undef(buff, 12, mp->marginal_path_err_sample_time)
            != 0)
                condlog(3, "%s: marginal_path_err_sample_time = %s %s",
index 2dd378c..464596f 100644 (file)
@@ -92,6 +92,7 @@ alloc_path (void)
        pp = (struct path *)MALLOC(sizeof(struct path));
 
        if (pp) {
+               pp->initialized = INIT_NEW;
                pp->sg_id.host_no = -1;
                pp->sg_id.channel = -1;
                pp->sg_id.scsi_id = -1;
@@ -113,19 +114,34 @@ alloc_path (void)
 }
 
 void
-free_path (struct path * pp)
+uninitialize_path(struct path *pp)
 {
        if (!pp)
                return;
 
+       pp->dmstate = PSTATE_UNDEF;
+       pp->uid_attribute = NULL;
+       pp->getuid = NULL;
+
        if (checker_selected(&pp->checker))
                checker_put(&pp->checker);
 
        if (prio_selected(&pp->prio))
                prio_put(&pp->prio);
 
-       if (pp->fd >= 0)
+       if (pp->fd >= 0) {
                close(pp->fd);
+               pp->fd = -1;
+       }
+}
+
+void
+free_path (struct path * pp)
+{
+       if (!pp)
+               return;
+
+       uninitialize_path(pp);
 
        if (pp->udev) {
                udev_device_unref(pp->udev);
@@ -257,6 +273,21 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
                mpp->dmi = NULL;
        }
 
+       if (!free_paths && mpp->pg) {
+               struct pathgroup *pgp;
+               struct path *pp;
+               int i, j;
+
+               /*
+                * Make sure paths carry no reference to this mpp any more
+                */
+               vector_foreach_slot(mpp->pg, pgp, i) {
+                       vector_foreach_slot(pgp->paths, pp, j)
+                               if (pp->mpp == mpp)
+                                       pp->mpp = NULL;
+               }
+       }
+
        free_pathvec(mpp->paths, free_paths);
        free_pgvec(mpp->pg, free_paths);
        FREE_PTR(mpp->mpcontext);
@@ -306,7 +337,7 @@ store_path (vector pathvec, struct path * pp)
                err++;
        }
        if (!strlen(pp->dev)) {
-               condlog(2, "%s: Empty device name", pp->dev_t);
+               condlog(3, "%s: Empty device name", pp->dev_t);
                err++;
        }
 
@@ -455,30 +486,33 @@ find_path_by_devt (const struct _vector *pathvec, const char * dev_t)
        return NULL;
 }
 
-int pathcountgr(const struct pathgroup *pgp, int state)
+static int do_pathcount(const struct multipath *mpp, const int *states,
+                       unsigned int nr_states)
 {
+       struct pathgroup *pgp;
        struct path *pp;
        int count = 0;
-       int i;
+       unsigned int i, j, k;
 
-       vector_foreach_slot (pgp->paths, pp, i)
-               if ((pp->state == state) || (state == PATH_WILD))
-                       count++;
+       if (!mpp->pg || !nr_states)
+               return count;
 
+       vector_foreach_slot (mpp->pg, pgp, i) {
+               vector_foreach_slot (pgp->paths, pp, j) {
+                       for (k = 0; k < nr_states; k++) {
+                               if (pp->state == states[k]) {
+                                       count++;
+                                       break;
+                               }
+                       }
+               }
+       }
        return count;
 }
 
 int pathcount(const struct multipath *mpp, int state)
 {
-       struct pathgroup *pgp;
-       int count = 0;
-       int i;
-
-       if (mpp->pg) {
-               vector_foreach_slot (mpp->pg, pgp, i)
-                       count += pathcountgr(pgp, state);
-       }
-       return count;
+       return do_pathcount(mpp, &state, 1);
 }
 
 int count_active_paths(const struct multipath *mpp)
@@ -500,6 +534,13 @@ int count_active_paths(const struct multipath *mpp)
        return count;
 }
 
+int count_active_pending_paths(const struct multipath *mpp)
+{
+       int states[] = {PATH_UP, PATH_GHOST, PATH_PENDING};
+
+       return do_pathcount(mpp, states, 3);
+}
+
 int pathcmp(const struct pathgroup *pgp, const struct pathgroup *cpgp)
 {
        int i, j;
index 9bd39eb..7de93d6 100644 (file)
@@ -101,29 +101,13 @@ enum yes_no_undef_states {
        YNU_YES,
 };
 
-#define _FIND_MULTIPATHS_F (1 << 1)
-#define _FIND_MULTIPATHS_I (1 << 2)
-#define _FIND_MULTIPATHS_N (1 << 3)
-/*
- * _FIND_MULTIPATHS_F must have the same value as YNU_YES.
- * Generate a compile time error if that isn't the case.
- */
-extern char ___error1___[-(_FIND_MULTIPATHS_F != YNU_YES)];
-
-#define find_multipaths_on(conf) \
-       (!!((conf)->find_multipaths & _FIND_MULTIPATHS_F))
-#define ignore_wwids_on(conf) \
-       (!!((conf)->find_multipaths & _FIND_MULTIPATHS_I))
-#define ignore_new_devs_on(conf) \
-       (!!((conf)->find_multipaths & _FIND_MULTIPATHS_N))
-
 enum find_multipaths_states {
        FIND_MULTIPATHS_UNDEF = YNU_UNDEF,
        FIND_MULTIPATHS_OFF = YNU_NO,
-       FIND_MULTIPATHS_ON = _FIND_MULTIPATHS_F,
-       FIND_MULTIPATHS_GREEDY = _FIND_MULTIPATHS_I,
-       FIND_MULTIPATHS_SMART = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_I,
-       FIND_MULTIPATHS_STRICT = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_N,
+       FIND_MULTIPATHS_ON = YNU_YES,
+       FIND_MULTIPATHS_GREEDY,
+       FIND_MULTIPATHS_SMART,
+       FIND_MULTIPATHS_STRICT,
        __FIND_MULTIPATHS_LAST,
 };
 
@@ -190,6 +174,7 @@ enum scsi_protocol {
        SCSI_PROTOCOL_SAS = 6,
        SCSI_PROTOCOL_ADT = 7,  /* Media Changers */
        SCSI_PROTOCOL_ATA = 8,
+       SCSI_PROTOCOL_USB = 9,  /* USB Attached SCSI (UAS), and others */
        SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */
 };
 
@@ -209,6 +194,11 @@ enum initialized_states {
        INIT_MISSING_UDEV,
        INIT_REQUESTED_UDEV,
        INIT_OK,
+       /*
+        * INIT_REMOVED: supposed to be removed from pathvec, but still
+        * mapped by some multipath map because of map reload failure.
+        */
+       INIT_REMOVED,
 };
 
 enum prkey_sources {
@@ -432,6 +422,7 @@ struct host_group {
 struct path * alloc_path (void);
 struct pathgroup * alloc_pathgroup (void);
 struct multipath * alloc_multipath (void);
+void uninitialize_path(struct path *pp);
 void free_path (struct path *);
 void free_pathvec (vector vec, enum free_path_mode free_paths);
 void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths);
@@ -462,9 +453,9 @@ struct path * find_path_by_devt (const struct _vector *pathvec, const char *devt
 struct path * find_path_by_dev (const struct _vector *pathvec, const char *dev);
 struct path * first_path (const struct multipath *mpp);
 
-int pathcountgr (const struct pathgroup *, int);
 int pathcount (const struct multipath *, int);
 int count_active_paths(const struct multipath *);
+int count_active_pending_paths(const struct multipath *);
 int pathcmp (const struct pathgroup *, const struct pathgroup *);
 int add_feature (char **, const char *);
 int remove_feature (char **, const char *);
index 3dbbaa0..8895fa7 100644 (file)
@@ -29,6 +29,7 @@ int update_mpp_paths(struct multipath *mpp, vector pathvec)
        struct pathgroup * pgp;
        struct path * pp;
        int i,j;
+       bool store_failure = false;
 
        if (!mpp || !mpp->pg)
                return 0;
@@ -39,13 +40,204 @@ int update_mpp_paths(struct multipath *mpp, vector pathvec)
 
        vector_foreach_slot (mpp->pg, pgp, i) {
                vector_foreach_slot (pgp->paths, pp, j) {
-                       if (!find_path_by_devt(mpp->paths, pp->dev_t) &&
-                           (find_path_by_devt(pathvec, pp->dev_t)) &&
-                           store_path(mpp->paths, pp))
-                               return 1;
+                       if (!find_path_by_devt(mpp->paths, pp->dev_t)) {
+                               struct path *pp1;
+
+                               /*
+                                * Avoid adding removed paths to the map again
+                                * when we reload it. Such paths may exist if
+                                * domap fails in ev_remove_path().
+                                */
+                               pp1 = find_path_by_devt(pathvec, pp->dev_t);
+                               if (pp1 && pp->initialized != INIT_REMOVED &&
+                                   store_path(mpp->paths, pp))
+                                       store_failure = true;
+                       }
                }
        }
-       return 0;
+
+       return store_failure;
+}
+
+static bool guess_mpp_wwid(struct multipath *mpp)
+{
+       int i, j;
+       struct pathgroup *pgp;
+       struct path *pp;
+
+       if (strlen(mpp->wwid) || !mpp->pg)
+               return true;
+
+       vector_foreach_slot(mpp->pg, pgp, i) {
+               if (!pgp->paths)
+                       continue;
+               vector_foreach_slot(pgp->paths, pp, j) {
+                       if (pp->initialized == INIT_OK && strlen(pp->wwid)) {
+                               strlcpy(mpp->wwid, pp->wwid, sizeof(mpp->wwid));
+                               condlog(2, "%s: guessed WWID %s from path %s",
+                                       mpp->alias, mpp->wwid, pp->dev);
+                               return true;
+                       }
+               }
+       }
+       condlog(1, "%s: unable to guess WWID", mpp->alias);
+       return false;
+}
+
+/*
+ * update_pathvec_from_dm() - update pathvec after disassemble_map()
+ *
+ * disassemble_map() may return block devices that are members in
+ * multipath maps but haven't been discovered. Check whether they
+ * need to be added to pathvec or discarded.
+ *
+ * Returns: true if immediate map reload is desirable
+ *
+ * Side effects:
+ * - may delete non-existing paths and empty pathgroups from mpp
+ * - may set pp->wwid and / or mpp->wwid
+ * - calls pathinfo() on existing paths is pathinfo_flags is not 0
+ */
+bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
+       int pathinfo_flags)
+{
+       int i, j;
+       struct pathgroup *pgp;
+       struct path *pp;
+       struct config *conf;
+       bool mpp_has_wwid;
+       bool must_reload = false;
+
+       if (!mpp->pg)
+               return false;
+
+       /*
+        * This will initialize mpp->wwid with an educated guess,
+        * either from the dm uuid or from a member path with properly
+        * determined WWID.
+        */
+       mpp_has_wwid = guess_mpp_wwid(mpp);
+
+       vector_foreach_slot(mpp->pg, pgp, i) {
+               if (!pgp->paths)
+                       goto delete_pg;
+
+               vector_foreach_slot(pgp->paths, pp, j) {
+
+                       if (pp->mpp && pp->mpp != mpp) {
+                               condlog(0, "BUG: %s: found path %s which is already in %s",
+                                       mpp->alias, pp->dev, pp->mpp->alias);
+
+                               /*
+                                * Either we added this path to the other mpp
+                                * explicitly, or we came by here earlier and
+                                * decided it belonged there. In both cases,
+                                * the path should remain in the other map,
+                                * and be deleted here.
+                                */
+                               must_reload = true;
+                               dm_fail_path(mpp->alias, pp->dev_t);
+                               vector_del_slot(pgp->paths, j--);
+                               continue;
+                       }
+                       pp->mpp = mpp;
+
+                       /*
+                        * The way disassemble_map() works: If it encounters a
+                        * path device which isn't found in pathvec, it adds an
+                        * uninitialized struct path to pgp->paths, with only
+                        * pp->dev_t filled in. Thus if pp->udev is set here,
+                        * we know that the path is in pathvec already.
+                        * However, it's possible that the path in pathvec is
+                        * different from the one the kernel still had in its
+                        * map.
+                        */
+                       if (pp->udev) {
+                               if (pathinfo_flags & ~DI_NOIO) {
+                                       conf = get_multipath_config();
+                                       pthread_cleanup_push(put_multipath_config,
+                                                            conf);
+                                       pathinfo(pp, conf, pathinfo_flags|DI_WWID);
+                                       pthread_cleanup_pop(1);
+                               }
+                       } else {
+                               /* If this fails, the device is not in sysfs */
+                               pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
+
+                               if (!pp->udev) {
+                                       condlog(2, "%s: discarding non-existing path %s",
+                                               mpp->alias, pp->dev_t);
+                                       vector_del_slot(pgp->paths, j--);
+                                       free_path(pp);
+                                       must_reload = true;
+                                       continue;
+                               } else {
+                                       int rc;
+
+                                       devt2devname(pp->dev, sizeof(pp->dev),
+                                                    pp->dev_t);
+                                       conf = get_multipath_config();
+                                       pthread_cleanup_push(put_multipath_config,
+                                                            conf);
+                                       pp->checkint = conf->checkint;
+                                       rc = pathinfo(pp, conf,
+                                                     DI_SYSFS|DI_WWID|DI_BLACKLIST|
+                                                     pathinfo_flags);
+                                       pthread_cleanup_pop(1);
+                                       if (rc != PATHINFO_OK) {
+                                               condlog(1, "%s: error %d in pathinfo, discarding path",
+                                                       pp->dev, rc);
+                                               vector_del_slot(pgp->paths, j--);
+                                               free_path(pp);
+                                               must_reload = true;
+                                               continue;
+                                       }
+                                       condlog(2, "%s: adding new path %s",
+                                               mpp->alias, pp->dev);
+                                       store_path(pathvec, pp);
+                                       pp->tick = 1;
+                               }
+                       }
+
+                       /* We don't set the map WWID from paths here */
+                       if (!mpp_has_wwid)
+                               continue;
+
+                       /*
+                        * At this point, pp->udev is valid and and pp->wwid
+                        * is the best we could get
+                        */
+                       if (*pp->wwid && strcmp(mpp->wwid, pp->wwid)) {
+                               condlog(0, "%s: path %s WWID %s doesn't match, removing from map",
+                                       mpp->wwid, pp->dev_t, pp->wwid);
+                               /*
+                                * This path exists, but in the wrong map.
+                                * We can't reload the map from here.
+                                * Make sure it isn't used in this map
+                                * any more, and let the checker re-add
+                                * it as it sees fit.
+                                */
+                               dm_fail_path(mpp->alias, pp->dev_t);
+                               vector_del_slot(pgp->paths, j--);
+                               orphan_path(pp, "WWID mismatch");
+                               pp->tick = 1;
+                               must_reload = true;
+                       } else if (!*pp->wwid) {
+                               condlog(3, "%s: setting wwid from map: %s",
+                                       pp->dev, mpp->wwid);
+                               strlcpy(pp->wwid, mpp->wwid,
+                                       sizeof(pp->wwid));
+                       }
+               }
+               if (VECTOR_SIZE(pgp->paths) != 0)
+                       continue;
+       delete_pg:
+               condlog(2, "%s: removing empty pathgroup %d", mpp->alias, i);
+               vector_del_slot(mpp->pg, i--);
+               free_pathgroup(pgp, KEEP_PATHS);
+               must_reload = true;
+       }
+       return must_reload;
 }
 
 int adopt_paths(vector pathvec, struct multipath *mpp)
@@ -68,40 +260,47 @@ int adopt_paths(vector pathvec, struct multipath *mpp)
                                        pp->dev, mpp->alias);
                                continue;
                        }
-                       condlog(3, "%s: ownership set to %s",
-                               pp->dev, mpp->alias);
-                       pp->mpp = mpp;
-
+                       if (pp->initialized == INIT_REMOVED)
+                               continue;
                        if (!mpp->paths && !(mpp->paths = vector_alloc()))
-                               return 1;
+                               goto err;
 
-                       if (!find_path_by_dev(mpp->paths, pp->dev) &&
-                           store_path(mpp->paths, pp))
-                                       return 1;
                        conf = get_multipath_config();
                        pthread_cleanup_push(put_multipath_config, conf);
                        ret = pathinfo(pp, conf,
                                       DI_PRIO | DI_CHECKER);
                        pthread_cleanup_pop(1);
-                       if (ret)
-                               return 1;
+                       if (ret) {
+                               condlog(3, "%s: pathinfo failed for %s",
+                                       __func__, pp->dev);
+                               continue;
+                       }
+
+                       if (!find_path_by_devt(mpp->paths, pp->dev_t) &&
+                           store_path(mpp->paths, pp))
+                               goto err;
+
+                       pp->mpp = mpp;
+                       condlog(3, "%s: ownership set to %s",
+                               pp->dev, mpp->alias);
                }
        }
        return 0;
+err:
+       condlog(1, "error setting ownership of %s to %s", pp->dev, mpp->alias);
+       return 1;
 }
 
 void orphan_path(struct path *pp, const char *reason)
 {
        condlog(3, "%s: orphan path, %s", pp->dev, reason);
+       if (pp->mpp && pp->hwe && pp->mpp->hwe == pp->hwe) {
+               condlog(0, "BUG: orphaning path %s that holds hwe of %s",
+                       pp->dev, pp->mpp->alias);
+               pp->mpp->hwe = NULL;
+       }
        pp->mpp = NULL;
-       pp->dmstate = PSTATE_UNDEF;
-       pp->uid_attribute = NULL;
-       pp->getuid = NULL;
-       prio_put(&pp->prio);
-       checker_put(&pp->checker);
-       if (pp->fd >= 0)
-               close(pp->fd);
-       pp->fd = -1;
+       uninitialize_path(pp);
 }
 
 void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
@@ -109,26 +308,51 @@ void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
        int i;
        struct path * pp;
 
+       /* Avoid BUG message from orphan_path() */
+       mpp->hwe = NULL;
        vector_foreach_slot (pathvec, pp, i) {
                if (pp->mpp == mpp) {
-                       orphan_path(pp, reason);
+                       if (pp->initialized == INIT_REMOVED) {
+                               condlog(3, "%s: freeing path in removed state",
+                                       pp->dev);
+                               vector_del_slot(pathvec, i--);
+                               free_path(pp);
+                       } else
+                               orphan_path(pp, reason);
                }
        }
 }
 
+void set_path_removed(struct path *pp)
+{
+       struct multipath *mpp = pp->mpp;
+
+       orphan_path(pp, "removed");
+       /*
+        * Keep link to mpp. It will be removed when the path
+        * is successfully removed from the map.
+        */
+       if (!mpp) {
+               condlog(0, "%s: internal error: mpp == NULL", pp->dev);
+               return;
+       }
+       pp->mpp = mpp;
+       pp->initialized = INIT_REMOVED;
+}
+
 void
-remove_map(struct multipath * mpp, struct vectors * vecs, int purge_vec)
+remove_map(struct multipath *mpp, vector pathvec, vector mpvec, int purge_vec)
 {
        int i;
 
        /*
         * clear references to this map
         */
-       orphan_paths(vecs->pathvec, mpp, "map removed internally");
+       orphan_paths(pathvec, mpp, "map removed internally");
 
        if (purge_vec &&
-           (i = find_slot(vecs->mpvec, (void *)mpp)) != -1)
-               vector_del_slot(vecs->mpvec, i);
+           (i = find_slot(mpvec, (void *)mpp)) != -1)
+               vector_del_slot(mpvec, i);
 
        /*
         * final free
@@ -142,7 +366,7 @@ remove_map_by_alias(const char *alias, struct vectors * vecs, int purge_vec)
        struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias);
        if (mpp) {
                condlog(2, "%s: removing map by alias", alias);
-               remove_map(mpp, vecs, purge_vec);
+               remove_map(mpp, vecs->pathvec, vecs->mpvec, purge_vec);
        }
 }
 
@@ -156,7 +380,7 @@ remove_maps(struct vectors * vecs)
                return;
 
        vector_foreach_slot (vecs->mpvec, mpp, i) {
-               remove_map(mpp, vecs, 1);
+               remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
                i--;
        }
 
@@ -194,45 +418,84 @@ extract_hwe_from_path(struct multipath * mpp)
 }
 
 int
-update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon)
+update_multipath_table (struct multipath *mpp, vector pathvec, int flags)
 {
+       int r = DMP_ERR;
        char params[PARAMS_SIZE] = {0};
 
        if (!mpp)
-               return 1;
+               return r;
 
-       if (dm_get_map(mpp->alias, &mpp->size, params)) {
-               condlog(3, "%s: cannot get map", mpp->alias);
-               return 1;
+       r = dm_get_map(mpp->alias, &mpp->size, params);
+       if (r != DMP_OK) {
+               condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting table" : "map not present");
+               return r;
        }
 
-       if (disassemble_map(pathvec, params, mpp, is_daemon)) {
+       if (disassemble_map(pathvec, params, mpp)) {
                condlog(3, "%s: cannot disassemble map", mpp->alias);
-               return 1;
+               return DMP_ERR;
        }
 
-       return 0;
+       /* FIXME: we should deal with the return value here */
+       update_pathvec_from_dm(pathvec, mpp, flags);
+
+       return DMP_OK;
 }
 
 int
 update_multipath_status (struct multipath *mpp)
 {
+       int r = DMP_ERR;
        char status[PARAMS_SIZE] = {0};
 
        if (!mpp)
-               return 1;
+               return r;
 
-       if (dm_get_status(mpp->alias, status)) {
-               condlog(3, "%s: cannot get status", mpp->alias);
-               return 1;
+       r = dm_get_status(mpp->alias, status);
+       if (r != DMP_OK) {
+               condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting status" : "map not present");
+               return r;
        }
 
        if (disassemble_status(status, mpp)) {
                condlog(3, "%s: cannot disassemble status", mpp->alias);
-               return 1;
+               return DMP_ERR;
        }
 
-       return 0;
+       return DMP_OK;
+}
+
+static struct path *find_devt_in_pathgroups(const struct multipath *mpp,
+                                           const char *dev_t)
+{
+       struct pathgroup  *pgp;
+       struct path *pp;
+       int j;
+
+       vector_foreach_slot(mpp->pg, pgp, j) {
+               pp = find_path_by_devt(pgp->paths, dev_t);
+               if (pp)
+                       return pp;
+       }
+       return NULL;
+}
+
+static void check_removed_paths(const struct multipath *mpp, vector pathvec)
+{
+       struct path *pp;
+       int i;
+
+       vector_foreach_slot(pathvec, pp, i) {
+               if (pp->initialized != INIT_REMOVED || pp->mpp != mpp)
+                       continue;
+               if (!find_devt_in_pathgroups(mpp, pp->dev_t)) {
+                       condlog(2, "%s: %s: freeing path in removed state",
+                               __func__, pp->dev);
+                       vector_del_slot(pathvec, i--);
+                       free_path(pp);
+               }
+       }
 }
 
 void sync_paths(struct multipath *mpp, vector pathvec)
@@ -251,23 +514,28 @@ void sync_paths(struct multipath *mpp, vector pathvec)
                }
                if (!found) {
                        condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
+                       if (mpp->hwe == pp->hwe)
+                               mpp->hwe = NULL;
                        vector_del_slot(mpp->paths, i--);
                        orphan_path(pp, "path removed externally");
                }
        }
+       check_removed_paths(mpp, pathvec);
        update_mpp_paths(mpp, pathvec);
        vector_foreach_slot (mpp->paths, pp, i)
                pp->mpp = mpp;
+       if (mpp->hwe == NULL)
+               extract_hwe_from_path(mpp);
 }
 
 int
-update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
+update_multipath_strings(struct multipath *mpp, vector pathvec)
 {
        struct pathgroup *pgp;
-       int i;
+       int i, r = DMP_ERR;
 
        if (!mpp)
-               return 1;
+               return r;
 
        update_mpp_paths(mpp, pathvec);
        condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
@@ -276,18 +544,20 @@ update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
        free_pgvec(mpp->pg, KEEP_PATHS);
        mpp->pg = NULL;
 
-       if (update_multipath_table(mpp, pathvec, is_daemon))
-               return 1;
+       r = update_multipath_table(mpp, pathvec, 0);
+       if (r != DMP_OK)
+               return r;
        sync_paths(mpp, pathvec);
 
-       if (update_multipath_status(mpp))
-               return 1;
+       r = update_multipath_status(mpp);
+       if (r != DMP_OK)
+               return r;
 
        vector_foreach_slot(mpp->pg, pgp, i)
                if (pgp->paths)
                        path_group_prio_update(pgp);
 
-       return 0;
+       return DMP_OK;
 }
 
 static void enter_recovery_mode(struct multipath *mpp)
@@ -336,7 +606,7 @@ static void leave_recovery_mode(struct multipath *mpp)
 
 void __set_no_path_retry(struct multipath *mpp, bool check_features)
 {
-       bool is_queueing;
+       bool is_queueing = false; /* assign a value to make gcc happy */
 
        check_features = check_features && mpp->features != NULL;
        if (check_features)
@@ -440,7 +710,8 @@ struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
                goto out;
        mpp->size = pp->size;
 
-       if (adopt_paths(vecs->pathvec, mpp))
+       if (adopt_paths(vecs->pathvec, mpp) ||
+           find_slot(vecs->pathvec, pp) == -1)
                goto out;
 
        if (add_vec) {
@@ -453,15 +724,15 @@ struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
        return mpp;
 
 out:
-       remove_map(mpp, vecs, PURGE_VEC);
+       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
        return NULL;
 }
 
-int verify_paths(struct multipath *mpp, struct vectors *vecs)
+int verify_paths(struct multipath *mpp)
 {
        struct path * pp;
        int count = 0;
-       int i, j;
+       int i;
 
        if (!mpp)
                return 0;
@@ -470,13 +741,13 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs)
                /*
                 * see if path is in sysfs
                 */
-               if (sysfs_attr_get_value(pp->udev, "dev",
+               if (!pp->udev || sysfs_attr_get_value(pp->udev, "dev",
                                         pp->dev_t, BLK_DEV_SIZE) < 0) {
                        if (pp->state != PATH_DOWN) {
                                condlog(1, "%s: removing valid path %s in state %d",
                                        mpp->alias, pp->dev, pp->state);
                        } else {
-                               condlog(3, "%s: failed to access path %s",
+                               condlog(2, "%s: failed to access path %s",
                                        mpp->alias, pp->dev);
                        }
                        count++;
@@ -489,10 +760,12 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs)
                         */
                        if (mpp->hwe == pp->hwe)
                                mpp->hwe = NULL;
-                       if ((j = find_slot(vecs->pathvec,
-                                          (void *)pp)) != -1)
-                               vector_del_slot(vecs->pathvec, j);
-                       free_path(pp);
+                       /*
+                        * Don't delete path from pathvec yet. We'll do this
+                        * after the path has been removed from the map, in
+                        * sync_paths().
+                        */
+                       set_path_removed(pp);
                } else {
                        condlog(4, "%s: verified path %s dev_t %s",
                                mpp->alias, pp->dev, pp->dev_t);
index 2a5e3d6..ee2b723 100644 (file)
@@ -18,16 +18,22 @@ int adopt_paths (vector pathvec, struct multipath * mpp);
 void orphan_paths(vector pathvec, struct multipath *mpp,
                  const char *reason);
 void orphan_path (struct path * pp, const char *reason);
+void set_path_removed(struct path *pp);
 
-int verify_paths(struct multipath * mpp, struct vectors * vecs);
+int verify_paths(struct multipath *mpp);
+bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
+                           int pathinfo_flags);
 int update_mpp_paths(struct multipath * mpp, vector pathvec);
-int update_multipath_strings (struct multipath *mpp, vector pathvec,
-                             int is_daemon);
+int update_multipath_strings (struct multipath *mpp, vector pathvec);
 void extract_hwe_from_path(struct multipath * mpp);
 
-#define PURGE_VEC 1
+enum {
+       KEEP_VEC,
+       PURGE_VEC,
+};
 
-void remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec);
+void remove_map (struct multipath *mpp, vector pathvec, vector mpvec,
+                int purge_vec);
 void remove_map_by_alias(const char *alias, struct vectors * vecs,
                         int purge_vec);
 void remove_maps (struct vectors * vecs);
@@ -37,8 +43,7 @@ struct multipath * add_map_with_path (struct vectors * vecs,
                                struct path * pp, int add_vec);
 void update_queue_mode_del_path(struct multipath *mpp);
 void update_queue_mode_add_path(struct multipath *mpp);
-int update_multipath_table (struct multipath *mpp, vector pathvec,
-                           int is_daemon);
+int update_multipath_table (struct multipath *mpp, vector pathvec, int flags);
 int update_multipath_status (struct multipath *mpp);
 vector get_used_hwes(const struct _vector *pathvec);
 
index 62ec2ed..5390de6 100644 (file)
@@ -278,7 +278,11 @@ int sysfs_check_holders(char * check_devt, char * new_devt)
                        continue;
                }
                table_name = dm_mapname(major, table_minor);
-
+               if (!table_name) {
+                       condlog(2, "%s: mapname not found for %d:%d", check_dev,
+                               major, table_minor);
+                       continue;
+               }
                condlog(0, "%s: reassign table %s old %s new %s", check_dev,
                        table_name, check_devt, new_devt);
 
@@ -295,7 +299,7 @@ static int select_dm_devs(const struct dirent *di)
        return fnmatch("dm-*", di->d_name, FNM_FILE_NAME) == 0;
 }
 
-bool sysfs_is_multipathed(const struct path *pp)
+bool sysfs_is_multipathed(struct path *pp, bool set_wwid)
 {
        char pathbuf[PATH_MAX];
        struct scandir_result sr;
@@ -325,7 +329,7 @@ bool sysfs_is_multipathed(const struct path *pp)
        for (i = 0; i < r && !found; i++) {
                long fd;
                int nr;
-               char uuid[6];
+               char uuid[WWID_SIZE + UUID_PREFIX_LEN];
 
                if (safe_snprintf(pathbuf + n, sizeof(pathbuf) - n,
                                  "/%s/dm/uuid", di[i]->d_name))
@@ -339,12 +343,26 @@ bool sysfs_is_multipathed(const struct path *pp)
 
                pthread_cleanup_push(close_fd, (void *)fd);
                nr = read(fd, uuid, sizeof(uuid));
-               if (nr == sizeof(uuid) && !memcmp(uuid, "mpath-", sizeof(uuid)))
+               if (nr > (int)UUID_PREFIX_LEN &&
+                   !memcmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN))
                        found = true;
                else if (nr < 0) {
-                       condlog(1, "%s: error reading from %s: %s",
-                               __func__, pathbuf, strerror(errno));
+                       condlog(1, "%s: error reading from %s: %m",
+                               __func__, pathbuf);
                }
+               if (found && set_wwid) {
+                       nr -= UUID_PREFIX_LEN;
+                       memcpy(pp->wwid, uuid + UUID_PREFIX_LEN, nr);
+                       if (nr == WWID_SIZE) {
+                               condlog(4, "%s: overflow while reading from %s",
+                                       __func__, pathbuf);
+                               pp->wwid[0] = '\0';
+                       } else {
+                               pp->wwid[nr] = '\0';
+                               strchop(pp->wwid);
+                       }
+                }
+
                pthread_cleanup_pop(1);
        }
        pthread_cleanup_pop(1);
index 9ae30b3..72b39ab 100644 (file)
@@ -14,5 +14,5 @@ ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
                                 unsigned char * value, size_t value_len);
 int sysfs_get_size (struct path *pp, unsigned long long * size);
 int sysfs_check_holders(char * check_devt, char * new_devt);
-bool sysfs_is_multipathed(const struct path *pp);
+bool sysfs_is_multipathed(struct path *pp, bool set_wwid);
 #endif
index d38e8a7..d3061bf 100644 (file)
 
 typedef int (uev_trigger)(struct uevent *, void * trigger_data);
 
-LIST_HEAD(uevq);
-pthread_mutex_t uevq_lock = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t *uevq_lockp = &uevq_lock;
-pthread_cond_t uev_cond = PTHREAD_COND_INITIALIZER;
-pthread_cond_t *uev_condp = &uev_cond;
-uev_trigger *my_uev_trigger;
-void * my_trigger_data;
-int servicing_uev;
+static LIST_HEAD(uevq);
+static pthread_mutex_t uevq_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t *uevq_lockp = &uevq_lock;
+static pthread_cond_t uev_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t *uev_condp = &uev_cond;
+static uev_trigger *my_uev_trigger;
+static void *my_trigger_data;
+static int servicing_uev;
 
 int is_uevent_busy(void)
 {
@@ -91,8 +91,7 @@ struct uevent * alloc_uevent (void)
        return uev;
 }
 
-void
-uevq_cleanup(struct list_head *tmpq)
+static void uevq_cleanup(struct list_head *tmpq)
 {
        struct uevent *uev, *tmp;
 
@@ -129,7 +128,7 @@ static const char* uevent_get_env_var(const struct uevent *uev,
                }
        }
 
-       condlog(4, "%s: %s -> '%s'", __func__, attr, p);
+       condlog(4, "%s: %s -> '%s'", __func__, attr, p ?: "(null)");
        return p;
 
 invalid:
@@ -137,7 +136,7 @@ invalid:
        return NULL;
 }
 
-static int uevent_get_env_positive_int(const struct uevent *uev,
+int uevent_get_env_positive_int(const struct uevent *uev,
                                       const char *attr)
 {
        const char *p = uevent_get_env_var(uev, attr);
@@ -172,8 +171,7 @@ uevent_get_wwid(struct uevent *uev)
                uev->wwid = val;
 }
 
-bool
-uevent_need_merge(void)
+static bool uevent_need_merge(void)
 {
        struct config * conf;
        bool need_merge = false;
@@ -186,8 +184,7 @@ uevent_need_merge(void)
        return need_merge;
 }
 
-bool
-uevent_can_discard(struct uevent *uev)
+static bool uevent_can_discard(struct uevent *uev)
 {
        int invalid = 0;
        struct config * conf;
@@ -212,7 +209,7 @@ uevent_can_discard(struct uevent *uev)
        return false;
 }
 
-bool
+static bool
 uevent_can_filter(struct uevent *earlier, struct uevent *later)
 {
 
@@ -246,7 +243,7 @@ uevent_can_filter(struct uevent *earlier, struct uevent *later)
        return false;
 }
 
-bool
+static bool
 merge_need_stop(struct uevent *earlier, struct uevent *later)
 {
        /*
@@ -283,7 +280,7 @@ merge_need_stop(struct uevent *earlier, struct uevent *later)
        return false;
 }
 
-bool
+static bool
 uevent_can_merge(struct uevent *earlier, struct uevent *later)
 {
        /* merge paths uevents
@@ -302,7 +299,7 @@ uevent_can_merge(struct uevent *earlier, struct uevent *later)
        return false;
 }
 
-void
+static void
 uevent_prepare(struct list_head *tmpq)
 {
        struct uevent *uev, *tmp;
@@ -322,7 +319,7 @@ uevent_prepare(struct list_head *tmpq)
        }
 }
 
-void
+static void
 uevent_filter(struct uevent *later, struct list_head *tmpq)
 {
        struct uevent *earlier, *tmp;
@@ -345,7 +342,7 @@ uevent_filter(struct uevent *later, struct list_head *tmpq)
        }
 }
 
-void
+static void
 uevent_merge(struct uevent *later, struct list_head *tmpq)
 {
        struct uevent *earlier, *tmp;
@@ -366,7 +363,7 @@ uevent_merge(struct uevent *later, struct list_head *tmpq)
        }
 }
 
-void
+static void
 merge_uevq(struct list_head *tmpq)
 {
        struct uevent *later;
@@ -379,7 +376,7 @@ merge_uevq(struct list_head *tmpq)
        }
 }
 
-void
+static void
 service_uevq(struct list_head *tmpq)
 {
        struct uevent *uev, *tmp;
@@ -450,244 +447,7 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
        return 0;
 }
 
-struct uevent *uevent_from_buffer(char *buf, ssize_t buflen)
-{
-       struct uevent *uev;
-       char *buffer;
-       size_t bufpos;
-       int i;
-       char *pos;
-
-       uev = alloc_uevent();
-       if (!uev) {
-               condlog(1, "lost uevent, oom");
-               return NULL;
-       }
-
-       if ((size_t)buflen > sizeof(buf)-1)
-               buflen = sizeof(buf)-1;
-
-       /*
-        * Copy the shared receive buffer contents to buffer private
-        * to this uevent so we can immediately reuse the shared buffer.
-        */
-       memcpy(uev->buffer, buf, HOTPLUG_BUFFER_SIZE + OBJECT_SIZE);
-       buffer = uev->buffer;
-       buffer[buflen] = '\0';
-
-       /* save start of payload */
-       bufpos = strlen(buffer) + 1;
-
-       /* action string */
-       uev->action = buffer;
-       pos = strchr(buffer, '@');
-       if (!pos) {
-               condlog(3, "bad action string '%s'", buffer);
-               FREE(uev);
-               return NULL;
-       }
-       pos[0] = '\0';
-
-       /* sysfs path */
-       uev->devpath = &pos[1];
-
-       /* hotplug events have the environment attached - reconstruct envp[] */
-       for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) {
-               int keylen;
-               char *key;
-
-               key = &buffer[bufpos];
-               keylen = strlen(key);
-               uev->envp[i] = key;
-               /* Filter out sequence number */
-               if (strncmp(key, "SEQNUM=", 7) == 0) {
-                       char *eptr;
-
-                       uev->seqnum = strtoul(key + 7, &eptr, 10);
-                       if (eptr == key + 7)
-                               uev->seqnum = -1;
-               }
-               bufpos += keylen + 1;
-       }
-       uev->envp[i] = NULL;
-
-       condlog(3, "uevent %ld '%s' from '%s'", uev->seqnum,
-               uev->action, uev->devpath);
-       uev->kernel = strrchr(uev->devpath, '/');
-       if (uev->kernel)
-               uev->kernel++;
-
-       /* print payload environment */
-       for (i = 0; uev->envp[i] != NULL; i++)
-               condlog(5, "%s", uev->envp[i]);
-
-       return uev;
-}
-
-int failback_listen(void)
-{
-       int sock;
-       struct sockaddr_nl snl;
-       struct sockaddr_un sun;
-       socklen_t addrlen;
-       int retval;
-       int rcvbufsz = 128*1024;
-       int rcvsz = 0;
-       int rcvszsz = sizeof(rcvsz);
-       unsigned int *prcvszsz = (unsigned int *)&rcvszsz;
-       const int feature_on = 1;
-       /*
-        * First check whether we have a udev socket
-        */
-       memset(&sun, 0x00, sizeof(struct sockaddr_un));
-       sun.sun_family = AF_LOCAL;
-       strcpy(&sun.sun_path[1], "/org/kernel/dm/multipath_event");
-       addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun.sun_path+1) + 1;
-
-       sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
-       if (sock >= 0) {
-
-               condlog(3, "reading events from udev socket.");
-
-               /* the bind takes care of ensuring only one copy running */
-               retval = bind(sock, (struct sockaddr *) &sun, addrlen);
-               if (retval < 0) {
-                       condlog(0, "bind failed, exit");
-                       goto exit;
-               }
-
-               /* enable receiving of the sender credentials */
-               retval = setsockopt(sock, SOL_SOCKET, SO_PASSCRED,
-                                   &feature_on, sizeof(feature_on));
-               if (retval < 0) {
-                       condlog(0, "failed to enable credential passing, exit");
-                       goto exit;
-               }
-
-       } else {
-               /* Fallback to read kernel netlink events */
-               memset(&snl, 0x00, sizeof(struct sockaddr_nl));
-               snl.nl_family = AF_NETLINK;
-               snl.nl_pid = getpid();
-               snl.nl_groups = 0x01;
-
-               sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
-               if (sock == -1) {
-                       condlog(0, "error getting socket, exit");
-                       return 1;
-               }
-
-               condlog(3, "reading events from kernel.");
-
-               /*
-                * try to avoid dropping uevents, even so, this is not a guarantee,
-                * but it does help to change the netlink uevent socket's
-                * receive buffer threshold from the default value of 106,496 to
-                * the maximum value of 262,142.
-                */
-               retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbufsz,
-                                   sizeof(rcvbufsz));
-
-               if (retval < 0) {
-                       condlog(0, "error setting receive buffer size for socket, exit");
-                       exit(1);
-               }
-               retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsz, prcvszsz);
-               if (retval < 0) {
-                       condlog(0, "error setting receive buffer size for socket, exit");
-                       exit(1);
-               }
-               condlog(3, "receive buffer size for socket is %u.", rcvsz);
-
-               /* enable receiving of the sender credentials */
-               if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED,
-                              &feature_on, sizeof(feature_on)) < 0) {
-                       condlog(0, "error on enabling credential passing for socket");
-                       exit(1);
-               }
-
-               retval = bind(sock, (struct sockaddr *) &snl,
-                             sizeof(struct sockaddr_nl));
-               if (retval < 0) {
-                       condlog(0, "bind failed, exit");
-                       goto exit;
-               }
-       }
-
-       while (1) {
-               size_t bufpos;
-               ssize_t buflen;
-               struct uevent *uev;
-               struct msghdr smsg;
-               struct iovec iov;
-               char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
-               struct cmsghdr *cmsg;
-               struct ucred *cred;
-               static char buf[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE];
-
-               memset(buf, 0x00, sizeof(buf));
-               iov.iov_base = &buf;
-               iov.iov_len = sizeof(buf);
-               memset (&smsg, 0x00, sizeof(struct msghdr));
-               smsg.msg_iov = &iov;
-               smsg.msg_iovlen = 1;
-               smsg.msg_control = cred_msg;
-               smsg.msg_controllen = sizeof(cred_msg);
-
-               buflen = recvmsg(sock, &smsg, 0);
-               if (buflen < 0) {
-                       if (errno != EINTR)
-                               condlog(0, "error receiving message, errno %d", errno);
-                       continue;
-               }
-
-               cmsg = CMSG_FIRSTHDR(&smsg);
-               if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
-                       condlog(3, "no sender credentials received, message ignored");
-                       continue;
-               }
-
-               cred = (struct ucred *)CMSG_DATA(cmsg);
-               if (cred->uid != 0) {
-                       condlog(3, "sender uid=%d, message ignored", cred->uid);
-                       continue;
-               }
-
-               /* skip header */
-               bufpos = strlen(buf) + 1;
-               if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) {
-                       condlog(3, "invalid message length");
-                       continue;
-               }
-
-               /* check message header */
-               if (strstr(buf, "@/") == NULL) {
-                       condlog(3, "unrecognized message header");
-                       continue;
-               }
-               if ((size_t)buflen > sizeof(buf)-1) {
-                       condlog(2, "buffer overflow for received uevent");
-                       buflen = sizeof(buf)-1;
-               }
-
-               uev = uevent_from_buffer(buf, buflen);
-               if (!uev)
-                       continue;
-               /*
-                * Queue uevent and poke service pthread.
-                */
-               pthread_mutex_lock(uevq_lockp);
-               list_add_tail(&uev->node, &uevq);
-               pthread_cond_signal(uev_condp);
-               pthread_mutex_unlock(uevq_lockp);
-       }
-
-exit:
-       close(sock);
-       return 1;
-}
-
-struct uevent *uevent_from_udev_device(struct udev_device *dev)
+static struct uevent *uevent_from_udev_device(struct udev_device *dev)
 {
        struct uevent *uev;
        int i = 0;
@@ -749,7 +509,7 @@ struct uevent *uevent_from_udev_device(struct udev_device *dev)
        return uev;
 }
 
-bool uevent_burst(struct timeval *start_time, int events)
+static bool uevent_burst(struct timeval *start_time, int events)
 {
        struct timeval diff_time, end_time;
        unsigned long speed;
@@ -786,7 +546,6 @@ int uevent_listen(struct udev *udev)
        struct udev_monitor *monitor = NULL;
        int fd, socket_flags, events;
        struct timeval start_time;
-       int need_failback = 1;
        int timeout = 30;
        LIST_HEAD(uevlisten_tmp);
 
@@ -806,7 +565,7 @@ int uevent_listen(struct udev *udev)
        monitor = udev_monitor_new_from_netlink(udev, "udev");
        if (!monitor) {
                condlog(2, "failed to create udev monitor");
-               goto failback;
+               goto out_udev;
        }
        pthread_cleanup_push(monitor_cleanup, monitor);
 #ifdef LIBUDEV_API_RECVBUF
@@ -891,32 +650,14 @@ int uevent_listen(struct udev *udev)
                gettimeofday(&start_time, NULL);
                timeout = 30;
        }
-       need_failback = 0;
 out:
        pthread_cleanup_pop(1);
-failback:
-       if (need_failback)
-               err = failback_listen();
+out_udev:
        pthread_cleanup_pop(1);
        return err;
 }
 
-int uevent_get_major(const struct uevent *uev)
-{
-       return uevent_get_env_positive_int(uev, "MAJOR");
-}
-
-int uevent_get_minor(const struct uevent *uev)
-{
-       return uevent_get_env_positive_int(uev, "MINOR");
-}
-
-int uevent_get_disk_ro(const struct uevent *uev)
-{
-       return uevent_get_env_positive_int(uev, "DISK_RO");
-}
-
-static char *uevent_get_dm_str(const struct uevent *uev, char *attr)
+char *uevent_get_dm_str(const struct uevent *uev, char *attr)
 {
        const char *tmp = uevent_get_env_var(uev, attr);
 
@@ -925,21 +666,6 @@ static char *uevent_get_dm_str(const struct uevent *uev, char *attr)
        return strdup(tmp);
 }
 
-char *uevent_get_dm_name(const struct uevent *uev)
-{
-       return uevent_get_dm_str(uev, "DM_NAME");
-}
-
-char *uevent_get_dm_path(const struct uevent *uev)
-{
-       return uevent_get_dm_str(uev, "DM_PATH");
-}
-
-char *uevent_get_dm_action(const struct uevent *uev)
-{
-       return uevent_get_dm_str(uev, "DM_ACTION");
-}
-
 bool uevent_is_mpath(const struct uevent *uev)
 {
        const char *uuid = uevent_get_env_var(uev, "DM_UUID");
index 0aa8675..61ca1b5 100644 (file)
@@ -9,10 +9,6 @@
 #define HOTPLUG_NUM_ENVP               32
 #define OBJECT_SIZE                    512
 
-#ifndef NETLINK_KOBJECT_UEVENT
-#define NETLINK_KOBJECT_UEVENT         15
-#endif
-
 struct udev;
 
 struct uevent {
@@ -28,17 +24,48 @@ struct uevent {
        char *envp[HOTPLUG_NUM_ENVP];
 };
 
+struct uevent *alloc_uevent(void);
 int is_uevent_busy(void);
 
 int uevent_listen(struct udev *udev);
 int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data),
                    void * trigger_data);
-int uevent_get_major(const struct uevent *uev);
-int uevent_get_minor(const struct uevent *uev);
-int uevent_get_disk_ro(const struct uevent *uev);
-char *uevent_get_dm_name(const struct uevent *uev);
-char *uevent_get_dm_path(const struct uevent *uev);
-char *uevent_get_dm_action(const struct uevent *uev);
 bool uevent_is_mpath(const struct uevent *uev);
+void uevent_get_wwid(struct uevent *uev);
+
+int uevent_get_env_positive_int(const struct uevent *uev,
+                               const char *attr);
+
+static inline int uevent_get_major(const struct uevent *uev)
+{
+       return uevent_get_env_positive_int(uev, "MAJOR");
+}
+
+static inline int uevent_get_minor(const struct uevent *uev)
+{
+       return uevent_get_env_positive_int(uev, "MINOR");
+}
+
+static inline int uevent_get_disk_ro(const struct uevent *uev)
+{
+       return uevent_get_env_positive_int(uev, "DISK_RO");
+}
+
+char *uevent_get_dm_str(const struct uevent *uev, char *attr);
+
+static inline char *uevent_get_dm_name(const struct uevent *uev)
+{
+       return uevent_get_dm_str(uev, "DM_NAME");
+}
+
+static inline char *uevent_get_dm_path(const struct uevent *uev)
+{
+       return uevent_get_dm_str(uev, "DM_PATH");
+}
+
+static inline char *uevent_get_dm_action(const struct uevent *uev)
+{
+       return uevent_get_dm_str(uev, "DM_ACTION");
+}
 
 #endif /* _UEVENT_H */
index 51c38c8..1748eaf 100644 (file)
 #include "checkers.h"
 #include "vector.h"
 #include "structs.h"
+#include "config.h"
 #include "log.h"
 
 size_t
 strchop(char *str)
 {
-       int i;
+       size_t i;
 
-       for (i=strlen(str)-1; i >=0 && isspace(str[i]); --i) ;
+       for (i = strlen(str) - 1; i != (size_t) -1 && isspace(str[i]); i--) ;
        str[++i] = '\0';
-       return strlen(str);
+       return i;
+}
+
+#ifndef __GLIBC__
+/*
+ * glibc's non-destructive version of basename()
+ * License: LGPL-2.1-or-later
+ */
+static const char *__basename(const char *filename)
+{
+       char *p = strrchr(filename, '/');
+       return p ? p + 1 : filename;
 }
+#define basename(x) __basename(x)
+#endif
 
 int
 basenamecpy (const char *src, char *dst, size_t size)
@@ -52,7 +66,7 @@ basenamecpy (const char *src, char *dst, size_t size)
 }
 
 int
-filepresent (char * run) {
+filepresent (const char *run) {
        struct stat buf;
 
        if(!stat(run, &buf))
@@ -60,7 +74,7 @@ filepresent (char * run) {
        return 0;
 }
 
-char *get_next_string(char **temp, char *split_char)
+char *get_next_string(char **temp, const char *split_char)
 {
        char *token = NULL;
        token = strsep(temp, split_char);
@@ -70,9 +84,9 @@ char *get_next_string(char **temp, char *split_char)
 }
 
 int
-get_word (char * sentence, char ** word)
+get_word (const char *sentence, char **word)
 {
-       char * p;
+       const char *p;
        int len;
        int skip = 0;
 
@@ -112,132 +126,62 @@ get_word (char * sentence, char ** word)
        return skip + len;
 }
 
-size_t strlcpy(char *dst, const char *src, size_t size)
+size_t strlcpy(char * restrict dst, const char * restrict src, size_t size)
 {
        size_t bytes = 0;
-       char *q = dst;
-       const char *p = src;
        char ch;
 
-       while ((ch = *p++)) {
-               if (bytes+1 < size)
-                       *q++ = ch;
+       while ((ch = *src++)) {
+               if (bytes + 1 < size)
+                       *dst++ = ch;
                bytes++;
        }
 
        /* If size == 0 there is no space for a final null... */
        if (size)
-               *q = '\0';
+               *dst = '\0';
        return bytes;
 }
 
-size_t strlcat(char *dst, const char *src, size_t size)
+size_t strlcat(char * restrict dst, const char * restrict src, size_t size)
 {
        size_t bytes = 0;
-       char *q = dst;
-       const char *p = src;
        char ch;
 
-       while (bytes < size && *q) {
-               q++;
+       while (bytes < size && *dst) {
+               dst++;
                bytes++;
        }
        if (bytes == size)
                return (bytes + strlen(src));
 
-       while ((ch = *p++)) {
-               if (bytes+1 < size)
-               *q++ = ch;
+       while ((ch = *src++)) {
+               if (bytes + 1 < size)
+                       *dst++ = ch;
                bytes++;
        }
 
-       *q = '\0';
+       *dst = '\0';
        return bytes;
 }
 
-int devt2devname(char *devname, int devname_len, char *devt)
+int devt2devname(char *devname, int devname_len, const char *devt)
 {
-       FILE *fd;
-       unsigned int tmpmaj, tmpmin, major, minor;
-       char dev[FILE_NAME_SIZE];
-       char block_path[PATH_SIZE];
-       struct stat statbuf;
-
-       memset(block_path, 0, sizeof(block_path));
-       memset(dev, 0, sizeof(dev));
-       if (sscanf(devt, "%u:%u", &major, &minor) != 2) {
-               condlog(0, "Invalid device number %s", devt);
-               return 1;
-       }
-
-       if (devname_len > FILE_NAME_SIZE)
-               devname_len = FILE_NAME_SIZE;
-
-       if (stat("/sys/dev/block", &statbuf) == 0) {
-               /* Newer kernels have /sys/dev/block */
-               sprintf(block_path,"/sys/dev/block/%u:%u", major, minor);
-               dev[FILE_NAME_SIZE - 1] = '\0';
-               if (lstat(block_path, &statbuf) == 0) {
-                       if (S_ISLNK(statbuf.st_mode) &&
-                           readlink(block_path, dev, FILE_NAME_SIZE-1) > 0) {
-                               char *p = strrchr(dev, '/');
-
-                               if (!p) {
-                                       condlog(0, "No sysfs entry for %s",
-                                               block_path);
-                                       return 1;
-                               }
-                               p++;
-                               strlcpy(devname, p, devname_len);
-                               return 0;
-                       }
-               }
-               condlog(4, "%s is invalid", block_path);
-               return 1;
-       }
-       memset(block_path, 0, sizeof(block_path));
-
-       if (!(fd = fopen("/proc/partitions", "r"))) {
-               condlog(0, "Cannot open /proc/partitions");
-               return 1;
-       }
-
-       while (!feof(fd)) {
-               int r = fscanf(fd,"%u %u %*d %s",&tmpmaj, &tmpmin, dev);
-               if (!r) {
-                       r = fscanf(fd,"%*s\n");
-                       continue;
-               }
-               if (r != 3)
-                       continue;
-
-               if ((major == tmpmaj) && (minor == tmpmin)) {
-                       if (safe_sprintf(block_path, "/sys/block/%s", dev)) {
-                               condlog(0, "device name %s is too long", dev);
-                               fclose(fd);
-                               return 1;
-                       }
-                       break;
-               }
-       }
-       fclose(fd);
+       struct udev_device *u_dev;
+       int r;
 
-       if (strncmp(block_path,"/sys/block", 10)) {
-               condlog(3, "No device found for %u:%u", major, minor);
+       if (!devname || !devname_len || !devt)
                return 1;
-       }
 
-       if (stat(block_path, &statbuf) < 0) {
-               condlog(0, "No sysfs entry for %s", block_path);
+       u_dev = udev_device_new_from_devnum(udev, 'b', parse_devt(devt));
+       if (!u_dev) {
+               condlog(0, "\"%s\": invalid major/minor numbers, not found in sysfs", devt);
                return 1;
        }
+       r = strlcpy(devname, udev_device_get_sysname(u_dev), devname_len);
+       udev_device_unref(u_dev);
 
-       if (S_ISDIR(statbuf.st_mode) == 0) {
-               condlog(0, "sysfs entry %s is not a directory", block_path);
-               return 1;
-       }
-       basenamecpy((const char *)block_path, devname, devname_len);
-       return 0;
+       return !(r < devname_len);
 }
 
 /* This function returns a pointer inside of the supplied pathname string.
@@ -319,9 +263,9 @@ int systemd_service_enabled_in(const char *dev, const char *prefix)
                p = d->d_name + strlen(d->d_name) - 6;
                if (strcmp(p, ".wants"))
                        continue;
-               snprintf(file, sizeof(file), "%s/%s/%s",
-                        path, d->d_name, service);
-               if (stat(file, &stbuf) == 0) {
+               if (!safe_sprintf(file, "%s/%s/%s",
+                                 path, d->d_name, service)
+                   && stat(file, &stbuf) == 0) {
                        condlog(3, "%s: found %s", dev, file);
                        found++;
                        break;
@@ -381,7 +325,7 @@ int get_linux_version_code(void)
        return _linux_version_code;
 }
 
-int parse_prkey(char *ptr, uint64_t *prkey)
+int parse_prkey(const char *ptr, uint64_t *prkey)
 {
        if (!ptr)
                return 1;
@@ -398,7 +342,7 @@ int parse_prkey(char *ptr, uint64_t *prkey)
        return 0;
 }
 
-int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags)
+int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags)
 {
        char *flagstr;
 
@@ -447,11 +391,13 @@ void set_max_fds(rlim_t max_fds)
                if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
                        condlog(0, "can't set open fds limit to "
                                "%lu/%lu : %s",
-                               fd_limit.rlim_cur, fd_limit.rlim_max,
+                               (unsigned long)fd_limit.rlim_cur,
+                               (unsigned long)fd_limit.rlim_max,
                                strerror(errno));
                } else {
                        condlog(3, "set open fds limit to %lu/%lu",
-                               fd_limit.rlim_cur, fd_limit.rlim_max);
+                               (unsigned long)fd_limit.rlim_cur,
+                               (unsigned long)fd_limit.rlim_max);
                }
        }
 }
@@ -469,3 +415,33 @@ void close_fd(void *arg)
 {
        close((long)arg);
 }
+
+void cleanup_free_ptr(void *arg)
+{
+       void **p = arg;
+
+       if (p && *p)
+               free(*p);
+}
+
+struct bitfield *alloc_bitfield(unsigned int maxbit)
+{
+       unsigned int n;
+       struct bitfield *bf;
+
+       if (maxbit == 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       n = (maxbit - 1) / bits_per_slot + 1;
+       bf = calloc(1, sizeof(struct bitfield) + n * sizeof(bitfield_t));
+       if (bf)
+               bf->len = maxbit;
+       return bf;
+}
+
+void _log_bitfield_overflow(const char *f, unsigned int bit, unsigned int len)
+{
+       condlog(0, "%s: bitfield overflow: %u >= %u", f, bit, len);
+}
index 56bd78c..2b9703a 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef _UTIL_H
 #define _UTIL_H
 
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
 #include <sys/types.h>
 /* for rlim_t */
 #include <sys/resource.h>
 
 size_t strchop(char *);
 int basenamecpy (const char *src, char *dst, size_t size);
-int filepresent (char * run);
-char *get_next_string(char **temp, char *split_char);
-int get_word (char * sentence, char ** word);
-size_t strlcpy(char *dst, const char *src, size_t size);
-size_t strlcat(char *dst, const char *src, size_t size);
-int devt2devname (char *, int, char *);
+int filepresent (const char *run);
+char *get_next_string(char **temp, const char *split_char);
+int get_word (const char * sentence, char ** word);
+size_t strlcpy(char * restrict dst, const char * restrict src, size_t size);
+size_t strlcat(char * restrict dst, const char * restrict src, size_t size);
+int devt2devname (char *, int, const char *);
 dev_t parse_devt(const char *dev_t);
 char *convert_dev(char *dev, int is_path_device);
 void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
 int systemd_service_enabled(const char *dev);
 int get_linux_version_code(void);
-int parse_prkey(char *ptr, uint64_t *prkey);
-int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags);
+int parse_prkey(const char *ptr, uint64_t *prkey);
+int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags);
 int safe_write(int fd, const void *buf, size_t count);
 void set_max_fds(rlim_t max_fds);
 
@@ -44,6 +47,7 @@ void set_max_fds(rlim_t max_fds);
        pthread_cleanup_push(((void (*)(void *))&f), (arg))
 
 void close_fd(void *arg);
+void cleanup_free_ptr(void *arg);
 
 struct scandir_result {
        struct dirent **di;
@@ -51,19 +55,70 @@ struct scandir_result {
 };
 void free_scandir_result(struct scandir_result *);
 
-static inline bool is_bit_set_in_array(unsigned int bit, const uint64_t *arr)
+#ifndef __GLIBC_PREREQ
+#define __GLIBC_PREREQ(x, y) 0
+#endif
+/*
+ * ffsll() is also available on glibc < 2.27 if _GNU_SOURCE is defined.
+ * But relying on that would require that every program using this header file
+ * set _GNU_SOURCE during compilation, because otherwise the library and the
+ * program would use different types for bitfield_t, causing errors.
+ * That's too error prone, so if in doubt, use ffs().
+ */
+#if __GLIBC_PREREQ(2, 27)
+typedef unsigned long long int bitfield_t;
+#define _ffs(x) ffsll(x)
+#else
+typedef unsigned int bitfield_t;
+#define _ffs(x) ffs(x)
+#endif
+#define bits_per_slot (sizeof(bitfield_t) * CHAR_BIT)
+
+struct bitfield {
+       unsigned int len;
+       bitfield_t bits[];
+};
+
+struct bitfield *alloc_bitfield(unsigned int maxbit);
+
+void _log_bitfield_overflow(const char *f, unsigned int bit, unsigned int len);
+#define log_bitfield_overflow(bit, len) \
+       _log_bitfield_overflow(__func__, bit, len)
+
+static inline bool is_bit_set_in_bitfield(unsigned int bit,
+                                      const struct bitfield *bf)
 {
-       return arr[bit / 64] & (1ULL << (bit % 64)) ? 1 : 0;
+       if (bit >= bf->len) {
+               log_bitfield_overflow(bit, bf->len);
+               return false;
+       }
+       return !!(bf->bits[bit / bits_per_slot] &
+                 (1ULL << (bit % bits_per_slot)));
 }
 
-static inline void set_bit_in_array(unsigned int bit, uint64_t *arr)
+static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
 {
-       arr[bit / 64] |= (1ULL << (bit % 64));
+       if (bit >= bf->len) {
+               log_bitfield_overflow(bit, bf->len);
+               return;
+       }
+       bf->bits[bit / bits_per_slot] |= (1ULL << (bit % bits_per_slot));
 }
 
-static inline void clear_bit_in_array(unsigned int bit, uint64_t *arr)
+static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
 {
-       arr[bit / 64] &= ~(1ULL << (bit % 64));
+       if (bit >= bf->len) {
+               log_bitfield_overflow(bit, bf->len);
+               return;
+       }
+       bf->bits[bit / bits_per_slot] &= ~(1ULL << (bit % bits_per_slot));
 }
 
+#define steal_ptr(x)                  \
+       ({                             \
+               void *___p = x;        \
+               x = NULL;              \
+               ___p;                  \
+       })
+
 #endif /* _UTIL_H */
diff --git a/libmultipath/valid.c b/libmultipath/valid.c
new file mode 100644 (file)
index 0000000..456b1f6
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+  Copyright (c) 2020 Benjamin Marzinski, IBM
+
+  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 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 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 <https://www.gnu.org/licenses/>.
+ */
+#include <stddef.h>
+#include <errno.h>
+#include <libudev.h>
+
+#include "vector.h"
+#include "config.h"
+#include "debug.h"
+#include "util.h"
+#include "devmapper.h"
+#include "discovery.h"
+#include "wwids.h"
+#include "sysfs.h"
+#include "blacklist.h"
+#include "mpath_cmd.h"
+#include "valid.h"
+
+int
+is_path_valid(const char *name, struct config *conf, struct path *pp,
+             bool check_multipathd)
+{
+       int r;
+       int fd;
+
+       if (!pp || !name || !conf)
+               return PATH_IS_ERROR;
+
+       if (conf->find_multipaths <= FIND_MULTIPATHS_UNDEF ||
+           conf->find_multipaths >= __FIND_MULTIPATHS_LAST)
+               return PATH_IS_ERROR;
+
+       if (safe_sprintf(pp->dev, "%s", name))
+               return PATH_IS_ERROR;
+
+       if (sysfs_is_multipathed(pp, true)) {
+               if (pp->wwid[0] == '\0')
+                       return PATH_IS_ERROR;
+               return PATH_IS_VALID_NO_CHECK;
+       }
+
+       /*
+        * "multipath -u" may be run before the daemon is started. In this
+        * case, systemd might own the socket but might delay multipathd
+        * startup until some other unit (udev settle!)  has finished
+        * starting. With many LUNs, the listen backlog may be exceeded, which
+        * would cause connect() to block. This causes udev workers calling
+        * "multipath -u" to hang, and thus creates a deadlock, until "udev
+        * settle" times out.  To avoid this, call connect() in non-blocking
+        * mode here, and take EAGAIN as indication for a filled-up systemd
+        * backlog.
+        */
+
+       if (check_multipathd) {
+               fd = __mpath_connect(1);
+               if (fd < 0) {
+                       if (errno != EAGAIN && !systemd_service_enabled(name)) {
+                               condlog(3, "multipathd not running or enabled");
+                               return PATH_IS_NOT_VALID;
+                       }
+               } else
+                       mpath_disconnect(fd);
+       }
+
+       pp->udev = udev_device_new_from_subsystem_sysname(udev, "block", name);
+       if (!pp->udev)
+               return PATH_IS_ERROR;
+
+       r = pathinfo(pp, conf, DI_SYSFS | DI_WWID | DI_BLACKLIST);
+       if (r == PATHINFO_SKIPPED)
+               return PATH_IS_NOT_VALID;
+       else if (r)
+               return PATH_IS_ERROR;
+
+       if (pp->wwid[0] == '\0')
+               return PATH_IS_NOT_VALID;
+
+       if (pp->udev && pp->uid_attribute &&
+           filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
+               return PATH_IS_NOT_VALID;
+
+       r = is_failed_wwid(pp->wwid);
+       if (r != WWID_IS_NOT_FAILED) {
+               if (r == WWID_IS_FAILED)
+                       return PATH_IS_NOT_VALID;
+               return PATH_IS_ERROR;
+       }
+
+       if (conf->find_multipaths == FIND_MULTIPATHS_GREEDY)
+               return PATH_IS_VALID;
+
+       if (check_wwids_file(pp->wwid, 0) == 0)
+               return PATH_IS_VALID_NO_CHECK;
+
+       if (dm_map_present_by_uuid(pp->wwid) == 1)
+               return PATH_IS_VALID;
+
+       /* all these act like FIND_MULTIPATHS_STRICT for finding if a
+        * path is valid */
+       if (conf->find_multipaths != FIND_MULTIPATHS_SMART)
+               return PATH_IS_NOT_VALID;
+
+       return PATH_IS_MAYBE_VALID;
+}
diff --git a/libmultipath/valid.h b/libmultipath/valid.h
new file mode 100644 (file)
index 0000000..ce1c7cb
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+  Copyright (c) 2020 Benjamin Marzinski, IBM
+
+  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 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 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 <https://www.gnu.org/licenses/>.
+ */
+#ifndef _VALID_H
+#define _VALID_H
+
+/*
+ * PATH_IS_VALID_NO_CHECK is returned when multipath should claim
+ * the path, regardless of whether is has been released to systemd
+ * already.
+ * PATH_IS_VALID is returned by is_path_valid, when the path is
+ * valid only if it hasn't been released to systemd already.
+ * PATH_IS_MAYBE_VALID is returned when the the path would be valid
+ * if other paths with the same wwid existed. It is up to the caller
+ * to check for these other paths.
+ */
+enum is_path_valid_result {
+       PATH_IS_ERROR = -1,
+       PATH_IS_NOT_VALID,
+       PATH_IS_VALID,
+       PATH_IS_VALID_NO_CHECK,
+       PATH_IS_MAYBE_VALID,
+       PATH_MAX_VALID_RESULT, /* only for bounds checking */
+};
+
+int is_path_valid(const char *name, struct config *conf, struct path *pp,
+                 bool check_multipathd);
+
+#endif /* _VALID_D */
index 501cf4c..6605eb2 100644 (file)
@@ -35,26 +35,27 @@ vector_alloc(void)
 }
 
 /* allocated one slot */
-void *
+bool
 vector_alloc_slot(vector v)
 {
        void *new_slot = NULL;
+       int new_allocated;
+       int i;
 
        if (!v)
-               return NULL;
-
-       v->allocated += VECTOR_DEFAULT_SIZE;
-       if (v->slot)
-               new_slot = REALLOC(v->slot, sizeof (void *) * v->allocated);
-       else
-               new_slot = (void *) MALLOC(sizeof (void *) * v->allocated);
+               return false;
 
+       new_allocated = v->allocated + VECTOR_DEFAULT_SIZE;
+       new_slot = REALLOC(v->slot, sizeof (void *) * new_allocated);
        if (!new_slot)
-               v->allocated -= VECTOR_DEFAULT_SIZE;
-       else
-               v->slot = new_slot;
+               return false;
 
-       return v->slot;
+       v->slot = new_slot;
+       for (i = v->allocated; i < new_allocated; i++)
+               v->slot[i] = NULL;
+
+       v->allocated = new_allocated;
+       return true;
 }
 
 int
@@ -109,7 +110,7 @@ vector_del_slot(vector v, int slot)
 {
        int i;
 
-       if (!v || !v->allocated || slot < 0 || slot > VECTOR_SIZE(v))
+       if (!v || !v->allocated || slot < 0 || slot >= VECTOR_SIZE(v))
                return;
 
        for (i = slot + 1; i < VECTOR_SIZE(v); i++)
@@ -203,7 +204,7 @@ int vector_find_or_add_slot(vector v, void *value)
 
        if (n >= 0)
                return n;
-       if (vector_alloc_slot(v) == NULL)
+       if (!vector_alloc_slot(v))
                return -1;
        vector_set_slot(v, value);
        return VECTOR_SIZE(v) - 1;
index e16ec46..2862dc2 100644 (file)
@@ -23,6 +23,8 @@
 #ifndef _VECTOR_H
 #define _VECTOR_H
 
+#include <stdbool.h>
+
 /* vector definition */
 struct _vector {
        int allocated;
@@ -32,7 +34,7 @@ typedef struct _vector *vector;
 
 #define VECTOR_DEFAULT_SIZE 1
 #define VECTOR_SIZE(V)   ((V) ? ((V)->allocated) / VECTOR_DEFAULT_SIZE : 0)
-#define VECTOR_SLOT(V,E) (((V) && (E) < VECTOR_SIZE(V)) ? (V)->slot[(E)] : NULL)
+#define VECTOR_SLOT(V,E) (((V) && (E) < VECTOR_SIZE(V) && (E) >= 0) ? (V)->slot[(E)] : NULL)
 #define VECTOR_LAST_SLOT(V)   (((V) && VECTOR_SIZE(V) > 0) ? (V)->slot[(VECTOR_SIZE(V) - 1)] : NULL)
 
 #define vector_foreach_slot(v,p,i) \
@@ -60,7 +62,7 @@ typedef struct _vector *vector;
                        __t = vector_alloc();                           \
                if (__t != NULL) {                                      \
                        vector_foreach_slot(__v, __j, __i) {            \
-                               if (vector_alloc_slot(__t) == NULL) {   \
+                               if (!vector_alloc_slot(__t)) {  \
                                        vector_free(__t);               \
                                        __t = NULL;                     \
                                        break;                          \
@@ -73,7 +75,7 @@ typedef struct _vector *vector;
 
 /* Prototypes */
 extern vector vector_alloc(void);
-extern void *vector_alloc_slot(vector v);
+extern bool vector_alloc_slot(vector v);
 vector vector_reset(vector v);
 extern void vector_free(vector v);
 #define vector_free_const(x) vector_free((vector)(long)(x))
index 7ddb4e8..6ceed53 100644 (file)
@@ -20,8 +20,8 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000804
-#define DATE_CODE    0x050414
+#define VERSION_CODE 0x000805
+#define DATE_CODE    0x0b0914
 
 #define PROG    "multipath-tools"
 
index 28a2150..61d9c39 100644 (file)
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 
 #include "util.h"
 #include "checkers.h"
@@ -289,19 +290,19 @@ out:
 int
 should_multipath(struct path *pp1, vector pathvec, vector mpvec)
 {
-       int i, ignore_new_devs, find_multipaths;
+       int i, find_multipaths;
        struct path *pp2;
        struct config *conf;
 
        conf = get_multipath_config();
-       ignore_new_devs = ignore_new_devs_on(conf);
-       find_multipaths = find_multipaths_on(conf);
+       find_multipaths = conf->find_multipaths;
        put_multipath_config(conf);
-       if (!find_multipaths && !ignore_new_devs)
+       if (find_multipaths == FIND_MULTIPATHS_OFF ||
+           find_multipaths == FIND_MULTIPATHS_GREEDY)
                return 1;
 
        condlog(4, "checking if %s should be multipathed", pp1->dev);
-       if (!ignore_new_devs) {
+       if (find_multipaths != FIND_MULTIPATHS_STRICT) {
                char tmp_wwid[WWID_SIZE];
                struct multipath *mp = find_mp_by_wwid(mpvec, pp1->wwid);
 
@@ -348,109 +349,105 @@ remember_wwid(char *wwid)
 }
 
 static const char shm_dir[] = MULTIPATH_SHM_BASE "failed_wwids";
-static const char shm_lock[] = ".lock";
-static const char shm_header[] = "multipath shm lock file, don't edit";
-static char _shm_lock_path[sizeof(shm_dir)+sizeof(shm_lock)];
-static const char *shm_lock_path = &_shm_lock_path[0];
 
-static void init_shm_paths(void)
+static void print_failed_wwid_result(const char * msg, const char *wwid, int r)
 {
-       snprintf(_shm_lock_path, sizeof(_shm_lock_path),
-                "%s/%s", shm_dir, shm_lock);
+       switch(r) {
+       case WWID_FAILED_ERROR:
+               condlog(1, "%s: %s: %m", msg, wwid);
+               return;
+       case WWID_IS_FAILED:
+       case WWID_IS_NOT_FAILED:
+               condlog(4, "%s: %s is %s", msg, wwid,
+                       r == WWID_IS_FAILED ? "failed" : "good");
+               return;
+       case WWID_FAILED_CHANGED:
+               condlog(3, "%s: %s", msg, wwid);
+       }
 }
 
-static pthread_once_t shm_path_once = PTHREAD_ONCE_INIT;
-
-static int multipath_shm_open(bool rw)
+int is_failed_wwid(const char *wwid)
 {
-       int fd;
-       int can_write;
-
-       pthread_once(&shm_path_once, init_shm_paths);
-       fd = open_file(shm_lock_path, &can_write, shm_header);
+       struct stat st;
+       char path[PATH_MAX];
+       int r;
 
-       if (fd >= 0 && rw && !can_write) {
-               close(fd);
-               condlog(1, "failed to open %s for writing", shm_dir);
-               return -1;
+       if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
+               condlog(1, "%s: path name overflow", __func__);
+               return WWID_FAILED_ERROR;
        }
 
-       return fd;
-}
-
-static void multipath_shm_close(void *arg)
-{
-       long fd = (long)arg;
+       if (lstat(path, &st) == 0)
+               r = WWID_IS_FAILED;
+       else if (errno == ENOENT)
+               r = WWID_IS_NOT_FAILED;
+       else
+               r = WWID_FAILED_ERROR;
 
-       close(fd);
-       unlink(shm_lock_path);
+       print_failed_wwid_result("is_failed", wwid, r);
+       return r;
 }
 
-static int _failed_wwid_op(const char *wwid, bool rw,
-                          int (*func)(const char *), const char *msg)
+int mark_failed_wwid(const char *wwid)
 {
-       char path[PATH_MAX];
-       long lockfd;
-       int r = -1;
+       char tmpfile[WWID_SIZE + 2 * sizeof(long) + 1];
+       int r = WWID_FAILED_ERROR, fd, dfd;
 
-       if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
-               condlog(1, "%s: path name overflow", __func__);
-               return -1;
+       dfd = open(shm_dir, O_RDONLY|O_DIRECTORY);
+       if (dfd == -1 && errno == ENOENT) {
+               char path[sizeof(shm_dir) + 2];
+
+               /* arg for ensure_directories_exist() must not end with "/" */
+               safe_sprintf(path, "%s/_", shm_dir);
+               ensure_directories_exist(path, 0700);
+               dfd = open(shm_dir, O_RDONLY|O_DIRECTORY);
+       }
+       if (dfd == -1) {
+               condlog(1, "%s: can't setup %s: %m", __func__, shm_dir);
+               return WWID_FAILED_ERROR;
        }
 
-       lockfd = multipath_shm_open(rw);
-       if (lockfd == -1)
-               return -1;
+       safe_sprintf(tmpfile, "%s.%lx", wwid, (long)getpid());
+       fd = openat(dfd, tmpfile, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR);
+       if (fd >= 0)
+               close(fd);
+       else
+               goto out_closedir;
 
-       pthread_cleanup_push(multipath_shm_close, (void *)lockfd);
-       r = func(path);
-       pthread_cleanup_pop(1);
+       if (linkat(dfd, tmpfile, dfd, wwid, 0) == 0)
+               r = WWID_FAILED_CHANGED;
+       else if (errno == EEXIST)
+               r = WWID_FAILED_UNCHANGED;
+       else
+               r = WWID_FAILED_ERROR;
 
-       if (r == WWID_FAILED_ERROR)
-               condlog(1, "%s: %s: %s", msg, wwid, strerror(errno));
-       else if (r == WWID_FAILED_CHANGED)
-               condlog(3, "%s: %s", msg, wwid);
-       else if (!rw)
-               condlog(4, "%s: %s is %s", msg, wwid,
-                       r == WWID_IS_FAILED ? "failed" : "good");
+       if (unlinkat(dfd, tmpfile, 0) == -1)
+               condlog(2, "%s: failed to unlink %s/%s: %m",
+                       __func__, shm_dir, tmpfile);
 
+out_closedir:
+       close(dfd);
+       print_failed_wwid_result("mark_failed", wwid, r);
        return r;
 }
 
-static int _is_failed(const char *path)
+int unmark_failed_wwid(const char *wwid)
 {
-       struct stat st;
+       char path[PATH_MAX];
+       int r;
 
-       if (lstat(path, &st) == 0)
-               return WWID_IS_FAILED;
-       else if (errno == ENOENT)
-               return WWID_IS_NOT_FAILED;
-       else
+       if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
+               condlog(1, "%s: path name overflow", __func__);
                return WWID_FAILED_ERROR;
-}
-
-static int _mark_failed(const char *path)
-{
-       /* Called from _failed_wwid_op: we know that shm_lock_path exists */
-       if (_is_failed(path) == WWID_IS_FAILED)
-               return WWID_FAILED_UNCHANGED;
-       return (link(shm_lock_path, path) == 0 ? WWID_FAILED_CHANGED :
-               WWID_FAILED_ERROR);
-}
+       }
 
-static int _unmark_failed(const char *path)
-{
-       if (_is_failed(path) == WWID_IS_NOT_FAILED)
-               return WWID_FAILED_UNCHANGED;
-       return (unlink(path) == 0 ? WWID_FAILED_CHANGED : WWID_FAILED_ERROR);
-}
+       if (unlink(path) == 0)
+               r = WWID_FAILED_CHANGED;
+       else if (errno == ENOENT)
+               r = WWID_FAILED_UNCHANGED;
+       else
+               r = WWID_FAILED_ERROR;
 
-#define declare_failed_wwid_op(op, rw) \
-int op ## _wwid(const char *wwid) \
-{ \
-       return _failed_wwid_op(wwid, (rw), _ ## op, #op); \
+       print_failed_wwid_result("unmark_failed", wwid, r);
+       return r;
 }
-
-declare_failed_wwid_op(is_failed, false)
-declare_failed_wwid_op(mark_failed, true)
-declare_failed_wwid_op(unmark_failed, true)
index 28bfe41..a6a3bcf 100644 (file)
@@ -153,6 +153,37 @@ static int do_batch_file(const char *batch_fn)
        return ret;
 }
 
+static struct prout_param_descriptor *
+alloc_prout_param_descriptor(int num_transportid)
+{
+       struct prout_param_descriptor *paramp;
+
+       if (num_transportid < 0 || num_transportid > MPATH_MX_TIDS)
+               return NULL;
+
+       paramp= malloc(sizeof(struct prout_param_descriptor) +
+                               (sizeof(struct transportid *) * num_transportid));
+
+       if (!paramp)
+               return NULL;
+
+       memset(paramp, 0, sizeof(struct prout_param_descriptor) +
+                       (sizeof(struct transportid *) * num_transportid));
+       return paramp;
+}
+
+static void free_prout_param_descriptor(struct prout_param_descriptor *paramp)
+{
+       unsigned int i;
+       if (!paramp)
+               return;
+
+       for (i = 0; i < paramp->num_transportid; i++)
+               free(paramp->trnptid_list[i]);
+
+       free(paramp);
+}
+
 static int handle_args(int argc, char * argv[], int nline)
 {
        int c;
@@ -177,10 +208,8 @@ static int handle_args(int argc, char * argv[], int nline)
        int prin = 1;
        int prin_sa = -1;
        int prout_sa = -1;
-       int num_transport =0;
        char *batch_fn = NULL;
        void *resp = NULL;
-       struct transportid * tmp;
 
        memset(transportids, 0, MPATH_MX_TIDS * sizeof(struct transportid));
 
@@ -334,13 +363,13 @@ static int handle_args(int argc, char * argv[], int nline)
                                break;
 
                        case 'X':
-                               if (0 != construct_transportid(optarg, transportids, num_transport)) {
+                               if (0 != construct_transportid(optarg, transportids, num_transportids)) {
                                        fprintf(stderr, "bad argument to '--transport-id'\n");
                                        ret = MPATH_PR_SYNTAX_ERROR;
                                        goto out;
                                }
 
-                               ++num_transport;
+                               ++num_transportids;
                                break;
 
                        case 'l':
@@ -525,9 +554,12 @@ static int handle_args(int argc, char * argv[], int nline)
                int j;
                struct prout_param_descriptor *paramp;
 
-               paramp= malloc(sizeof(struct prout_param_descriptor) + (sizeof(struct transportid *)*(MPATH_MX_TIDS )));
-
-               memset(paramp, 0, sizeof(struct prout_param_descriptor) + (sizeof(struct transportid *)*(MPATH_MX_TIDS)));
+               paramp = alloc_prout_param_descriptor(num_transportids);
+               if (!paramp) {
+                       fprintf(stderr, "malloc paramp failed\n");
+                       ret = MPATH_PR_OTHER;
+                       goto out_fd;
+               }
 
                for (j = 7; j >= 0; --j) {
                        paramp->key[j] = (param_rk & 0xff);
@@ -544,13 +576,19 @@ static int handle_args(int argc, char * argv[], int nline)
                if (param_aptpl)
                        paramp->sa_flags |= MPATH_F_APTPL_MASK;
 
-               if (num_transport)
+               if (num_transportids)
                {
                        paramp->sa_flags |= MPATH_F_SPEC_I_PT_MASK;
-                       paramp->num_transportid = num_transport;
-                       for (j = 0 ; j < num_transport; j++)
+                       paramp->num_transportid = num_transportids;
+                       for (j = 0 ; j < num_transportids; j++)
                        {
                                paramp->trnptid_list[j] = (struct transportid *)malloc(sizeof(struct transportid));
+                               if (!paramp->trnptid_list[j]) {
+                                       fprintf(stderr, "malloc paramp->trnptid_list[%d] failed.\n", j);
+                                       ret = MPATH_PR_OTHER;
+                                       free_prout_param_descriptor(paramp);
+                                       goto out_fd;
+                               }
                                memcpy(paramp->trnptid_list[j], &transportids[j],sizeof(struct transportid));
                        }
                }
@@ -558,12 +596,7 @@ static int handle_args(int argc, char * argv[], int nline)
                /* PROUT commands other than 'register and move' */
                ret = __mpath_persistent_reserve_out (fd, prout_sa, 0, prout_type,
                                paramp, noisy);
-               for (j = 0 ; j < num_transport; j++)
-               {
-                       tmp = paramp->trnptid_list[j];
-                       free(tmp);
-               }
-               free(paramp);
+               free_prout_param_descriptor(paramp);
        }
 
        if (ret != MPATH_PR_SUCCESS)
index 07320a1..cd522e8 100644 (file)
@@ -75,7 +75,7 @@ ENV{MPATH_DEVICE_READY}=="0", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="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"
+       ENV{DM_ACTIVATION}="1", ENV{MPATH_UNCHANGED}="0"
 
 # The code to check multipath state ends here. We need to set
 # properties and symlinks regardless whether the map is usable or
index cf9d2a2..9e920d8 100644 (file)
 #include "propsel.h"
 #include "time-util.h"
 #include "file.h"
+#include "valid.h"
+#include "alias.h"
 
 int logsink;
 struct udev *udev;
 struct config *multipath_conf;
 
 /*
- * Return values of configure(), print_cmd_valid(), and main().
- * RTVL_{YES,NO} are synonyms for RTVL_{OK,FAIL} for the CMD_VALID_PATH case.
+ * Return values of configure(), check_path_valid(), and main().
  */
 enum {
        RTVL_OK = 0,
-       RTVL_YES = RTVL_OK,
        RTVL_FAIL = 1,
-       RTVL_NO = RTVL_FAIL,
-       RTVL_MAYBE, /* only used internally, never returned */
        RTVL_RETRY, /* returned by configure(), not by main() */
 };
 
@@ -148,6 +146,7 @@ usage (char * progname)
                "  -h      print this usage text\n"
                "  -l      show multipath topology (sysfs and DM info)\n"
                "  -ll     show multipath topology (maximum info)\n"
+               "  -e      enable foreign libraries with -l/-ll\n"
                "  -f      flush a multipath device map\n"
                "  -F      flush all multipath device maps\n"
                "  -a      add a device wwid to the wwids file\n"
@@ -190,68 +189,11 @@ usage (char * progname)
 }
 
 static int
-update_paths (struct multipath * mpp, int quick)
-{
-       int i, j;
-       struct pathgroup * pgp;
-       struct path * pp;
-       struct config *conf;
-
-       if (!mpp->pg)
-               return 0;
-
-       vector_foreach_slot (mpp->pg, pgp, i) {
-               if (!pgp->paths)
-                       continue;
-
-               vector_foreach_slot (pgp->paths, pp, j) {
-                       if (!strlen(pp->dev)) {
-                               if (devt2devname(pp->dev, FILE_NAME_SIZE,
-                                                pp->dev_t)) {
-                                       /*
-                                        * path is not in sysfs anymore
-                                        */
-                                       pp->chkrstate = pp->state = PATH_DOWN;
-                                       pp->offline = 1;
-                                       continue;
-                               }
-                               pp->mpp = mpp;
-                               if (quick)
-                                       continue;
-                               conf = get_multipath_config();
-                               if (pathinfo(pp, conf, DI_ALL))
-                                       pp->state = PATH_UNCHECKED;
-                               put_multipath_config(conf);
-                               continue;
-                       }
-                       pp->mpp = mpp;
-                       if (quick)
-                               continue;
-                       if (pp->state == PATH_UNCHECKED ||
-                           pp->state == PATH_WILD) {
-                               conf = get_multipath_config();
-                               if (pathinfo(pp, conf, DI_CHECKER))
-                                       pp->state = PATH_UNCHECKED;
-                               put_multipath_config(conf);
-                       }
-
-                       if (pp->priority == PRIO_UNDEF) {
-                               conf = get_multipath_config();
-                               if (pathinfo(pp, conf, DI_PRIO))
-                                       pp->priority = PRIO_UNDEF;
-                               put_multipath_config(conf);
-                       }
-               }
-       }
-       return 0;
-}
-
-static int
 get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
 {
        int i;
        struct multipath * mpp;
-       char params[PARAMS_SIZE], status[PARAMS_SIZE];
+       int flags = (cmd == CMD_LIST_SHORT ? DI_NOIO : DI_ALL);
 
        if (dm_get_maps(curmp))
                return 1;
@@ -263,34 +205,22 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
                if (refwwid && strlen(refwwid) &&
                    strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
                        condlog(3, "skip map %s: out of scope", mpp->alias);
-                       free_multipath(mpp, KEEP_PATHS);
-                       vector_del_slot(curmp, i);
+                       remove_map(mpp, pathvec, curmp, PURGE_VEC);
                        i--;
                        continue;
                }
 
-               if (cmd == CMD_VALID_PATH)
+               if (update_multipath_table(mpp, pathvec, flags) != DMP_OK ||
+                   update_multipath_status(mpp) != DMP_OK) {
+                       condlog(1, "error parsing map %s", mpp->wwid);
+                       remove_map(mpp, pathvec, curmp, PURGE_VEC);
+                       i--;
                        continue;
-
-               dm_get_map(mpp->alias, &mpp->size, params);
-               condlog(3, "params = %s", params);
-               dm_get_status(mpp->alias, status);
-               condlog(3, "status = %s", status);
-
-               disassemble_map(pathvec, params, mpp, 0);
-
-               /*
-                * disassemble_map() can add new paths to pathvec.
-                * If not in "fast list mode", we need to fetch information
-                * about them
-                */
-               update_paths(mpp, (cmd == CMD_LIST_SHORT));
+               }
 
                if (cmd == CMD_LIST_LONG)
                        mpp->bestpg = select_path_group(mpp);
 
-               disassemble_status(status, mpp);
-
                if (cmd == CMD_LIST_SHORT ||
                    cmd == CMD_LIST_LONG) {
                        struct config *conf = get_multipath_config();
@@ -321,7 +251,6 @@ static int check_usable_paths(struct config *conf,
        struct path *pp;
        char *mapname;
        vector pathvec = NULL;
-       char params[PARAMS_SIZE], status[PARAMS_SIZE];
        dev_t devt;
        int r = 1, i, j;
 
@@ -355,10 +284,9 @@ static int check_usable_paths(struct config *conf,
        if (mpp == NULL)
                goto free;
 
-       dm_get_map(mpp->alias, &mpp->size, params);
-       dm_get_status(mpp->alias, status);
-       disassemble_map(pathvec, params, mpp, 0);
-       disassemble_status(status, mpp);
+       if (update_multipath_table(mpp, pathvec, 0) != DMP_OK ||
+                   update_multipath_status(mpp) != DMP_OK)
+                   goto free;
 
        vector_foreach_slot (mpp->pg, pg, i) {
                vector_foreach_slot (pg->paths, pp, j) {
@@ -491,10 +419,11 @@ static int print_cmd_valid(int k, const vector pathvec,
        struct timespec until;
        struct path *pp;
 
-       if (k != RTVL_YES && k != RTVL_NO && k != RTVL_MAYBE)
-               return RTVL_NO;
+       if (k != PATH_IS_VALID && k != PATH_IS_NOT_VALID &&
+           k != PATH_IS_MAYBE_VALID)
+               return PATH_IS_NOT_VALID;
 
-       if (k == RTVL_MAYBE) {
+       if (k == PATH_IS_MAYBE_VALID) {
                /*
                 * Caller ensures that pathvec[0] is the path to
                 * examine.
@@ -504,7 +433,7 @@ static int print_cmd_valid(int k, const vector pathvec,
                wait = find_multipaths_check_timeout(
                        pp, pp->find_multipaths_timeout, &until);
                if (wait != FIND_MULTIPATHS_WAITING)
-                       k = RTVL_NO;
+                       k = PATH_IS_NOT_VALID;
        } else if (pathvec != NULL && (pp = VECTOR_SLOT(pathvec, 0)))
                wait = find_multipaths_check_timeout(pp, 0, &until);
        if (wait == FIND_MULTIPATHS_WAITING)
@@ -513,9 +442,9 @@ static int print_cmd_valid(int k, const vector pathvec,
        else if (wait == FIND_MULTIPATHS_WAIT_DONE)
                printf("FIND_MULTIPATHS_WAIT_UNTIL=\"0\"\n");
        printf("DM_MULTIPATH_DEVICE_PATH=\"%d\"\n",
-              k == RTVL_MAYBE ? 2 : k == RTVL_YES ? 1 : 0);
+              k == PATH_IS_MAYBE_VALID ? 2 : k == PATH_IS_VALID ? 1 : 0);
        /* Never return RTVL_MAYBE */
-       return k == RTVL_NO ? RTVL_NO : RTVL_YES;
+       return k == PATH_IS_NOT_VALID ? PATH_IS_NOT_VALID : PATH_IS_VALID;
 }
 
 /*
@@ -548,7 +477,6 @@ configure (struct config *conf, enum mpath_cmds cmd,
        int di_flag = 0;
        char * refwwid = NULL;
        char * dev = NULL;
-       bool released = released_to_systemd();
 
        /*
         * allocate core vectors to store paths and multipaths
@@ -573,7 +501,7 @@ configure (struct config *conf, enum mpath_cmds cmd,
            cmd != CMD_REMOVE_WWID &&
            (filter_devnode(conf->blist_devnode,
                            conf->elist_devnode, dev) > 0)) {
-               goto print_valid;
+               goto out;
        }
 
        /*
@@ -581,14 +509,10 @@ configure (struct config *conf, enum mpath_cmds cmd,
         * failing the translation is fatal (by policy)
         */
        if (devpath) {
-               int failed = get_refwwid(cmd, devpath, dev_type,
-                                        pathvec, &refwwid);
+               get_refwwid(cmd, devpath, dev_type, pathvec, &refwwid);
                if (!refwwid) {
                        condlog(4, "%s: failed to get wwid", devpath);
-                       if (failed == 2 && cmd == CMD_VALID_PATH)
-                               goto print_valid;
-                       else
-                               condlog(3, "scope is null");
+                       condlog(3, "scope is null");
                        goto out;
                }
                if (cmd == CMD_REMOVE_WWID) {
@@ -614,52 +538,6 @@ configure (struct config *conf, enum mpath_cmds cmd,
                        goto out;
                }
                condlog(3, "scope limited to %s", refwwid);
-               /* If you are ignoring the wwids file and find_multipaths is
-                * set, you need to actually check if there are two available
-                * paths to determine if this path should be multipathed. To
-                * do this, we put off the check until after discovering all
-                * the paths.
-                * Paths listed in the wwids file are always considered valid.
-                */
-               if (cmd == CMD_VALID_PATH) {
-                       if (is_failed_wwid(refwwid) == WWID_IS_FAILED) {
-                               r = RTVL_NO;
-                               goto print_valid;
-                       }
-                       if ((!find_multipaths_on(conf) &&
-                                   ignore_wwids_on(conf)) ||
-                                  check_wwids_file(refwwid, 0) == 0)
-                               r = RTVL_YES;
-                       if (!ignore_wwids_on(conf))
-                               goto print_valid;
-                       /* At this point, either r==0 or find_multipaths_on. */
-
-                       /*
-                        * Shortcut for find_multipaths smart:
-                        * Quick check if path is already multipathed.
-                        */
-                       if (sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0))) {
-                               r = RTVL_YES;
-                               goto print_valid;
-                       }
-
-                       /*
-                        * DM_MULTIPATH_DEVICE_PATH=="0" means that we have
-                        * been called for this device already, and have
-                        * released it to systemd. Unless the device is now
-                        * already multipathed (see above), we can't try to
-                        * grab it, because setting SYSTEMD_READY=0 would
-                        * cause file systems to be unmounted.
-                        * Leave DM_MULTIPATH_DEVICE_PATH="0".
-                        */
-                       if (released) {
-                               r = RTVL_NO;
-                               goto print_valid;
-                       }
-                       if (r == RTVL_YES)
-                               goto print_valid;
-                       /* find_multipaths_on: Fall through to path detection */
-               }
        }
 
        /*
@@ -700,59 +578,6 @@ configure (struct config *conf, enum mpath_cmds cmd,
                goto out;
        }
 
-       if (cmd == CMD_VALID_PATH) {
-               struct path *pp;
-               int fd;
-
-               /* This only happens if find_multipaths and
-                * ignore_wwids is set, and the path is not in WWIDs
-                * file, not currently multipathed, and has
-                * never been released to systemd.
-                * If there is currently a multipath device matching
-                * the refwwid, or there is more than one path matching
-                * the refwwid, then the path is valid */
-               if (VECTOR_SIZE(curmp) != 0) {
-                       r = RTVL_YES;
-                       goto print_valid;
-               } else if (VECTOR_SIZE(pathvec) > 1)
-                       r = RTVL_YES;
-               else
-                       r = RTVL_MAYBE;
-
-               /*
-                * If opening the path with O_EXCL fails, the path
-                * is in use (e.g. mounted during initramfs processing).
-                * We know that it's not used by dm-multipath.
-                * We may not set SYSTEMD_READY=0 on such devices, it
-                * might cause systemd to umount the device.
-                * Use O_RDONLY, because udevd would trigger another
-                * uevent for close-after-write.
-                *
-                * The O_EXCL check is potentially dangerous, because it may
-                * race with other tasks trying to access the device. Therefore
-                * this code is only executed if the path hasn't been released
-                * to systemd earlier (see above).
-                *
-                * get_refwwid() above stores the path we examine in slot 0.
-                */
-               pp = VECTOR_SLOT(pathvec, 0);
-               fd = open(udev_device_get_devnode(pp->udev),
-                         O_RDONLY|O_EXCL);
-               if (fd >= 0)
-                       close(fd);
-               else {
-                       condlog(3, "%s: path %s is in use: %s",
-                               __func__, pp->dev,
-                               strerror(errno));
-                       /*
-                        * Check if we raced with multipathd
-                        */
-                       r = sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0)) ?
-                               RTVL_YES : RTVL_NO;
-               }
-               goto print_valid;
-       }
-
        if (cmd != CMD_CREATE && cmd != CMD_DRY_RUN) {
                r = RTVL_OK;
                goto out;
@@ -765,10 +590,6 @@ configure (struct config *conf, enum mpath_cmds cmd,
                           conf->force_reload, cmd);
        r = rc == CP_RETRY ? RTVL_RETRY : rc == CP_OK ? RTVL_OK : RTVL_FAIL;
 
-print_valid:
-       if (cmd == CMD_VALID_PATH)
-               r = print_cmd_valid(r, pathvec, conf);
-
 out:
        if (refwwid)
                FREE(refwwid);
@@ -780,6 +601,112 @@ out:
 }
 
 static int
+check_path_valid(const char *name, struct config *conf, bool is_uevent)
+{
+       int fd, r = PATH_IS_ERROR;
+       struct path *pp = NULL;
+       vector pathvec = NULL;
+
+       pp = alloc_path();
+       if (!pp)
+               return RTVL_FAIL;
+
+       r = is_path_valid(name, conf, pp, is_uevent);
+       if (r <= PATH_IS_ERROR || r >= PATH_MAX_VALID_RESULT)
+               goto fail;
+
+       /* set path values if is_path_valid() didn't */
+       if (!pp->udev)
+               pp->udev = udev_device_new_from_subsystem_sysname(udev, "block",
+                                                                 name);
+       if (!pp->udev)
+               goto fail;
+
+       if (!strlen(pp->dev_t)) {
+               dev_t devt = udev_device_get_devnum(pp->udev);
+               if (major(devt) == 0 && minor(devt) == 0)
+                       goto fail;
+               snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt),
+                        minor(devt));
+       }
+
+       if ((r == PATH_IS_VALID || r == PATH_IS_MAYBE_VALID) &&
+           released_to_systemd())
+               r = PATH_IS_NOT_VALID;
+
+       /* This state is only used to skip the released_to_systemd() check */
+       if (r == PATH_IS_VALID_NO_CHECK)
+               r = PATH_IS_VALID;
+
+       if (r != PATH_IS_MAYBE_VALID)
+               goto out;
+
+       /*
+        * If opening the path with O_EXCL fails, the path
+        * is in use (e.g. mounted during initramfs processing).
+        * We know that it's not used by dm-multipath.
+        * We may not set SYSTEMD_READY=0 on such devices, it
+        * might cause systemd to umount the device.
+        * Use O_RDONLY, because udevd would trigger another
+        * uevent for close-after-write.
+        *
+        * The O_EXCL check is potentially dangerous, because it may
+        * race with other tasks trying to access the device. Therefore
+        * this code is only executed if the path hasn't been released
+        * to systemd earlier (see above).
+        */
+       fd = open(udev_device_get_devnode(pp->udev), O_RDONLY|O_EXCL);
+       if (fd >= 0)
+               close(fd);
+       else {
+               condlog(3, "%s: path %s is in use: %m", __func__, pp->dev);
+               /* Check if we raced with multipathd */
+               if (sysfs_is_multipathed(pp, false))
+                       r = PATH_IS_VALID;
+               else
+                       r = PATH_IS_NOT_VALID;
+               goto out;
+       }
+
+       pathvec = vector_alloc();
+       if (!pathvec)
+               goto fail;
+
+       if (store_path(pathvec, pp) != 0) {
+               free_path(pp);
+               goto fail;
+       }
+
+       /* For find_multipaths = SMART, if there is more than one path
+        * matching the refwwid, then the path is valid */
+       if (path_discovery(pathvec, DI_SYSFS | DI_WWID) < 0)
+               goto fail;
+       filter_pathvec(pathvec, pp->wwid);
+       if (VECTOR_SIZE(pathvec) > 1)
+               r = PATH_IS_VALID;
+       else
+               r = PATH_IS_MAYBE_VALID;
+
+out:
+       r = print_cmd_valid(r, pathvec, conf);
+       free_pathvec(pathvec, FREE_PATHS);
+       /*
+        * multipath -u must exit with status 0, otherwise udev won't
+        * import its output.
+        */
+       if (!is_uevent && r == PATH_IS_NOT_VALID)
+               return RTVL_FAIL;
+       return RTVL_OK;
+
+fail:
+       if (pathvec)
+               free_pathvec(pathvec, FREE_PATHS);
+       else
+               free_path(pp);
+       return RTVL_FAIL;
+}
+
+static int
 get_dev_type(char *dev) {
        struct stat buf;
        int i;
@@ -823,9 +750,26 @@ int delegate_to_multipathd(enum mpath_cmds cmd,
        *p = '\0';
        n = sizeof(command);
 
+       if (conf->skip_delegate)
+               return NOT_DELEGATED;
+
        if (cmd == CMD_CREATE && conf->force_reload == FORCE_RELOAD_YES) {
                p += snprintf(p, n, "reconfigure");
        }
+       else if (cmd == CMD_FLUSH_ONE && dev && dev_type == DEV_DEVMAP) {
+               p += snprintf(p, n, "del map %s", dev);
+               /* multipathd doesn't try as hard, to avoid potentially
+                * hanging. If it fails, retry with the regular multipath
+                * command */
+               r = NOT_DELEGATED;
+       }
+       else if (cmd == CMD_FLUSH_ALL) {
+               p += snprintf(p, n, "del maps");
+               /* multipathd doesn't try as hard, to avoid potentially
+                * hanging. If it fails, retry with the regular multipath
+                * command */
+               r = NOT_DELEGATED;
+       }
        /* Add other translations here */
 
        if (strlen(command) == 0)
@@ -850,9 +794,12 @@ int delegate_to_multipathd(enum mpath_cmds cmd,
                goto out;
        }
 
-       if (reply != NULL && *reply != '\0' && strcmp(reply, "ok\n"))
-               printf("%s", reply);
-       r = DELEGATE_OK;
+       if (reply != NULL && *reply != '\0') {
+               if (strcmp(reply, "fail\n"))
+                       r = DELEGATE_OK;
+               if (r != NOT_DELEGATED && strcmp(reply, "ok\n"))
+                       printf("%s", reply);
+       }
 
 out:
        FREE(reply);
@@ -860,32 +807,6 @@ out:
        return r;
 }
 
-static int test_multipathd_socket(void)
-{
-       int fd;
-       /*
-        * "multipath -u" may be run before the daemon is started. In this
-        * case, systemd might own the socket but might delay multipathd
-        * startup until some other unit (udev settle!)  has finished
-        * starting. With many LUNs, the listen backlog may be exceeded, which
-        * would cause connect() to block. This causes udev workers calling
-        * "multipath -u" to hang, and thus creates a deadlock, until "udev
-        * settle" times out.  To avoid this, call connect() in non-blocking
-        * mode here, and take EAGAIN as indication for a filled-up systemd
-        * backlog.
-        */
-
-       fd = __mpath_connect(1);
-       if (fd == -1) {
-               if (errno == EAGAIN)
-                       condlog(3, "daemon backlog exceeded");
-               else
-                       return 0;
-       } else
-               close(fd);
-       return 1;
-}
-
 int
 main (int argc, char *argv[])
 {
@@ -898,6 +819,7 @@ main (int argc, char *argv[])
        char *dev = NULL;
        struct config *conf;
        int retries = -1;
+       bool enable_foreign = false;
 
        udev = udev_new();
        logsink = 0;
@@ -907,7 +829,7 @@ main (int argc, char *argv[])
        multipath_conf = conf;
        conf->retrigger_tries = 0;
        conf->force_sync = 1;
-       while ((arg = getopt(argc, argv, ":adcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) {
+       while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
                switch(arg) {
                case 1: printf("optarg : %s\n",optarg);
                        break;
@@ -939,11 +861,14 @@ main (int argc, char *argv[])
                        if (cmd == CMD_CREATE)
                                cmd = CMD_DRY_RUN;
                        break;
+               case 'D':
+                       conf->skip_delegate = 1;
+                       break;
                case 'f':
-                       conf->remove = FLUSH_ONE;
+                       cmd = CMD_FLUSH_ONE;
                        break;
                case 'F':
-                       conf->remove = FLUSH_ALL;
+                       cmd = CMD_FLUSH_ALL;
                        break;
                case 'l':
                        if (optarg && !strncmp(optarg, "l", 1))
@@ -969,7 +894,11 @@ main (int argc, char *argv[])
                        conf->force_reload = FORCE_RELOAD_YES;
                        break;
                case 'i':
-                       conf->find_multipaths |= _FIND_MULTIPATHS_I;
+                       if (conf->find_multipaths == FIND_MULTIPATHS_ON ||
+                           conf->find_multipaths == FIND_MULTIPATHS_STRICT)
+                               conf->find_multipaths = FIND_MULTIPATHS_SMART;
+                       else if (conf->find_multipaths == FIND_MULTIPATHS_OFF)
+                               conf->find_multipaths = FIND_MULTIPATHS_GREEDY;
                        break;
                case 't':
                        r = dump_config(conf, NULL, NULL) ? RTVL_FAIL : RTVL_OK;
@@ -1000,6 +929,9 @@ main (int argc, char *argv[])
                case 'R':
                        retries = atoi(optarg);
                        break;
+               case 'e':
+                       enable_foreign = true;
+                       break;
                case ':':
                        fprintf(stderr, "Missing option argument\n");
                        usage(argv[0]);
@@ -1019,6 +951,8 @@ main (int argc, char *argv[])
                exit(RTVL_FAIL);
        }
 
+       check_alias_settings(conf);
+
        if (optind < argc) {
                dev = MALLOC(FILE_NAME_SIZE);
 
@@ -1032,6 +966,8 @@ main (int argc, char *argv[])
                        condlog(0, "'%s' is not a valid argument\n", dev);
                        goto out;
                }
+               if (dev_type == DEV_DEVNODE || dev_type == DEV_DEVT)
+                       strchop(dev);
        }
        if (dev_type == DEV_UEVENT) {
                openlog("multipath", 0, LOG_DAEMON);
@@ -1051,6 +987,10 @@ main (int argc, char *argv[])
                condlog(0, "failed to initialize prioritizers");
                goto out;
        }
+
+       if ((cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG) && enable_foreign)
+               conf->enable_foreign = strdup("");
+
        /* Failing here is non-fatal */
        init_foreign(conf->multipath_dir, conf->enable_foreign);
        if (cmd == CMD_USABLE_PATHS) {
@@ -1063,21 +1003,20 @@ main (int argc, char *argv[])
                condlog(0, "the -c option requires a path to check");
                goto out;
        }
-       if (cmd == CMD_VALID_PATH &&
-           dev_type == DEV_UEVENT) {
-               if (!test_multipathd_socket()) {
-                       condlog(3, "%s: daemon is not running", dev);
-                       if (!systemd_service_enabled(dev)) {
-                               r = print_cmd_valid(RTVL_NO, NULL, conf);
-                               goto out;
-                       }
-               }
+       if (cmd == CMD_VALID_PATH) {
+               char * name = convert_dev(dev, (dev_type == DEV_DEVNODE));
+               r = check_path_valid(name, conf, dev_type == DEV_UEVENT);
+               goto out;
        }
 
        if (cmd == CMD_REMOVE_WWID && !dev) {
                condlog(0, "the -w option requires a device");
                goto out;
        }
+       if (cmd == CMD_FLUSH_ONE && dev_type != DEV_DEVMAP) {
+               condlog(0, "the -f option requires a map name to remove");
+               goto out;
+       }
 
        switch(delegate_to_multipathd(cmd, dev, dev_type, conf)) {
        case DELEGATE_OK:
@@ -1111,17 +1050,13 @@ main (int argc, char *argv[])
        }
        if (retries < 0)
                retries = conf->remove_retries;
-       if (conf->remove == FLUSH_ONE) {
-               if (dev_type == DEV_DEVMAP) {
-                       r = dm_suspend_and_flush_map(dev, retries) ?
-                               RTVL_FAIL : RTVL_OK;
-               } else
-                       condlog(0, "must provide a map name to remove");
-
+       if (cmd == CMD_FLUSH_ONE) {
+               r = dm_suspend_and_flush_map(dev, retries) ?
+                   RTVL_FAIL : RTVL_OK;
                goto out;
        }
-       else if (conf->remove == FLUSH_ALL) {
-               r = dm_flush_maps(retries) ? RTVL_FAIL : RTVL_OK;
+       else if (cmd == CMD_FLUSH_ALL) {
+               r = dm_flush_maps(1, retries) ? RTVL_FAIL : RTVL_OK;
                goto out;
        }
        while ((r = configure(conf, cmd, dev_type, dev)) == RTVL_RETRY)
@@ -1135,13 +1070,6 @@ out:
        cleanup_prio();
        cleanup_checkers();
 
-       /*
-        * multipath -u must exit with status 0, otherwise udev won't
-        * import its output.
-        */
-       if (cmd == CMD_VALID_PATH && dev_type == DEV_UEVENT && r == RTVL_NO)
-               r = RTVL_OK;
-
        if (dev_type == DEV_UEVENT)
                closelog();
 
index 9cdd05a..5b29a5d 100644 (file)
@@ -125,11 +125,11 @@ the system.
 Other operation modes are chosen by using one of the following command line switches:
 .TP
 .B \-f
-Flush (remove) a multipath device map specified as parameter, if unused.
+Flush (remove) a multipath device map specified as parameter, if unused. This operation is delegated to the multipathd daemon if it's running.
 .
 .TP
 .B \-F
-Flush (remove) all unused multipath device maps.
+Flush (remove) all unused multipath device maps. This operation is delegated to the multipathd daemon if it's running.
 .
 .TP
 .B \-l
@@ -223,6 +223,12 @@ The verbosity level also controls the level of log and debug messages printed to
 Dry run, do not create or update devmaps.
 .
 .TP
+.B \-e
+Enable all foreign libraries. This overrides the
+.I enable_foreign 
+option from \fBmultipath.conf(5)\fR.
+.
+.TP
 .B \-i
 Ignore WWIDs file when processing devices. If
 \fIfind_multipaths strict\fR or \fIfind_multipaths no\fR is set in
index 05a5e8f..d2101ed 100644 (file)
@@ -205,6 +205,11 @@ of outstanding I/O to the path.
 (Since 2.6.31 kernel) Choose the path for the next bunch of I/O based on the amount
 of outstanding I/O to the path and its relative throughput.
 .TP
+.I "historical-service-time 0"
+(Since 5.8 kernel) Choose the path for the next bunch of IOs based on the
+estimation of future service time based on the history of previous I/O submitted
+to each path.
+.TP
 The default is: \fBservice-time 0\fR
 .RE
 .
@@ -643,6 +648,18 @@ The default is: in \fB/sys/block/sd<x>/device/timeout\fR
 .
 .
 .TP
+.B allow_usb_devices
+If set to
+.I no
+, all USB devices will be skipped during path discovery. If you intend to use
+multipath on USB attached devices, set this to \fIyes\fR.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
 .B flush_on_last_del
 If set to
 .I yes
@@ -1228,10 +1245,11 @@ Enables or disables foreign libraries (see section
 .I FOREIGN MULTIPATH SUPPORT
 below). The value is a regular expression; foreign libraries are loaded
 if their name (e.g. \(dqnvme\(dq) matches the expression. By default,
-all foreign libraries are enabled.
+no foreign libraries are enabled. Set this to \(dqnvme\(dq to enable NVMe native
+multipath support, or \(dq.*\(dq to enable all foreign libraries.
 .RS
 .TP
-The default is: \fB\(dq\(dq\fR (the empty regular expression)
+The default is: \fB\(dqNONE\(dq\fR
 .RE
 .
 .
@@ -1248,6 +1266,16 @@ being handled by multipath-tools.
 .LP
 .
 .
+In the \fIblacklist\fR and \fIblacklist_exceptions\fR sections, starting a
+quoted value with an exclamation mark \fB"!"\fR will invert the matching
+of the rest of the regular expression. For instance, \fB"!^sd[a-z]"\fR will
+match all values that do not start with \fB"sd[a-z]"\fR. The exclamation mark
+can be escaped \fB"\\!"\fR to match a literal \fB!\fR at the start of a
+regular expression. \fBNote:\fR The exclamation mark must be inside quotes,
+otherwise it will be treated as starting a comment.
+.LP
+.
+.
 The \fIblacklist_exceptions\fR section is used to revert the actions of the
 \fIblacklist\fR section. This allows one to selectively include ("whitelist") devices which
 would normally be excluded via the \fIblacklist\fR section. A common usage is
@@ -1264,10 +1292,9 @@ unless explicitly stated.
 Regular expression matching the device nodes to be excluded/included.
 .RS
 .PP
-The default \fIblacklist\fR consists of the regular expressions
-"^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]" and
-"^(td|hd|vd)[a-z]". This causes virtual devices, non-disk devices, and some other
-device types to be excluded from multipath handling by default.
+The default \fIblacklist\fR consists of the regular expression
+\fB"!^(sd[a-z]|dasd[a-z]|nvme[0-9])"\fR. This causes all device types other
+than scsi, dasd, and nvme to be excluded from multipath handling by default.
 .RE
 .TP
 .B wwid
index 8d90117..632b82b 100644 (file)
@@ -1,5 +1,9 @@
 include ../Makefile.inc
 
+ifneq ($(call check_func,dm_task_get_errno,/usr/include/libdevmapper.h),0)
+       CFLAGS += -DLIBDM_API_GET_ERRNO
+endif
+
 #
 # debugging stuff
 #
index 800c0fb..bdc9fb1 100644 (file)
@@ -568,6 +568,7 @@ cli_init (void) {
        add_handler(DEL+PATH, NULL);
        add_handler(ADD+MAP, NULL);
        add_handler(DEL+MAP, NULL);
+       add_handler(DEL+MAPS, NULL);
        add_handler(SWITCH+MAP+GROUP, NULL);
        add_handler(RECONFIGURE, NULL);
        add_handler(SUSPEND+MAP, NULL);
index 7d878c8..235e2a2 100644 (file)
@@ -66,7 +66,7 @@ show_paths (char ** r, int * len, struct vectors * vecs, char * style,
                c += snprint_foreign_paths(c, reply + maxlen - c,
                                           style, pretty);
 
-               again = ((c - reply) == (maxlen - 1));
+               again = (c == reply + maxlen - 1);
 
                REALLOC_REPLY(reply, again, maxlen);
        }
@@ -102,7 +102,7 @@ show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
 
                c += snprint_path(c, reply + maxlen - c, style, pp, 0);
 
-               again = ((c - reply) == (maxlen - 1));
+               again = (c == reply + maxlen - 1);
 
                REALLOC_REPLY(reply, again, maxlen);
        }
@@ -131,7 +131,7 @@ show_map_topology (char ** r, int * len, struct multipath * mpp,
                c = reply;
 
                c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2);
-               again = ((c - reply) == (maxlen - 1));
+               again = (c == reply + maxlen - 1);
 
                REALLOC_REPLY(reply, again, maxlen);
        }
@@ -171,7 +171,7 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs)
                }
                c += snprint_foreign_topology(c, reply + maxlen - c, 2);
 
-               again = ((c - reply) == (maxlen - 1));
+               again = (c == reply + maxlen - 1);
 
                REALLOC_REPLY(reply, again, maxlen);
        }
@@ -209,7 +209,7 @@ show_maps_json (char ** r, int * len, struct vectors * vecs)
                c = reply;
 
                c += snprint_multipath_topology_json(c, maxlen, vecs);
-               again = ((c - reply) == maxlen);
+               again = (c == reply + maxlen);
 
                REALLOC_REPLY(reply, again, maxlen);
        }
@@ -238,7 +238,7 @@ show_map_json (char ** r, int * len, struct multipath * mpp,
                c = reply;
 
                c += snprint_multipath_map_json(c, maxlen, mpp);
-               again = ((c - reply) == maxlen);
+               again = (c == reply + maxlen);
 
                REALLOC_REPLY(reply, again, maxlen);
        }
@@ -487,7 +487,7 @@ show_map (char ** r, int *len, struct multipath * mpp, char * style,
                c += snprint_multipath(c, reply + maxlen - c, style,
                                       mpp, pretty);
 
-               again = ((c - reply) == (maxlen - 1));
+               again = (c == reply + maxlen - 1);
 
                REALLOC_REPLY(reply, again, maxlen);
        }
@@ -533,7 +533,7 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style,
                }
                c += snprint_foreign_multipaths(c, reply + maxlen - c,
                                                style, pretty);
-               again = ((c - reply) == (maxlen - 1));
+               again = (c == reply + maxlen - 1);
 
                REALLOC_REPLY(reply, again, maxlen);
        }
@@ -713,11 +713,61 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
                goto blacklisted;
 
        pp = find_path_by_dev(vecs->pathvec, param);
-       if (pp) {
+       if (pp && pp->initialized != INIT_REMOVED) {
                condlog(2, "%s: path already in pathvec", param);
                if (pp->mpp)
                        return 0;
-       } else {
+       } else if (pp) {
+               /* Trying to add a path in INIT_REMOVED state */
+               struct multipath *prev_mpp;
+
+               prev_mpp = pp->mpp;
+               if (prev_mpp == NULL)
+                       condlog(0, "Bug: %s was in INIT_REMOVED state without being a multipath member",
+                               pp->dev);
+               pp->mpp = NULL;
+               pp->initialized = INIT_NEW;
+               pp->wwid[0] = '\0';
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
+               r = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST);
+               pthread_cleanup_pop(1);
+
+               if (prev_mpp) {
+                       /* Similar logic as in uev_add_path() */
+                       pp->mpp = prev_mpp;
+                       if (r == PATHINFO_OK &&
+                           !strncmp(prev_mpp->wwid, pp->wwid, WWID_SIZE)) {
+                               condlog(2, "%s: path re-added to %s", pp->dev,
+                                       pp->mpp->alias);
+                               /* Have the checker reinstate this path asap */
+                               pp->tick = 1;
+                               return 0;
+                       } else if (!ev_remove_path(pp, vecs, true))
+                               /* Path removed in ev_remove_path() */
+                               pp = NULL;
+                       else {
+                               /* Init state is now INIT_REMOVED again */
+                               pp->dmstate = PSTATE_FAILED;
+                               dm_fail_path(pp->mpp->alias, pp->dev_t);
+                               condlog(1, "%s: failed to re-add path still mapped in %s",
+                                       pp->dev, pp->mpp->alias);
+                               return 1;
+                       }
+               } else {
+                       switch (r) {
+                       case PATHINFO_SKIPPED:
+                               goto blacklisted;
+                       case PATHINFO_OK:
+                               break;
+                       default:
+                               condlog(0, "%s: failed to get pathinfo", param);
+                               return 1;
+                       }
+               }
+       }
+
+       if (!pp) {
                struct udev_device *udevice;
 
                udevice = udev_device_new_from_subsystem_sysname(udev,
@@ -770,7 +820,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
 {
        struct vectors * vecs = (struct vectors *)data;
        char * param = get_keyparam(v, MAP);
-       int major, minor;
+       int major = -1, minor = -1;
        char dev_path[PATH_SIZE];
        char *refwwid, *alias = NULL;
        int rc, count = 0;
@@ -853,6 +903,25 @@ cli_del_map (void * v, char ** reply, int * len, void * data)
 }
 
 int
+cli_del_maps (void *v, char **reply, int *len, void *data)
+{
+       struct vectors * vecs = (struct vectors *)data;
+       struct multipath *mpp;
+       int i, ret = 0;
+
+       condlog(2, "remove maps (operator)");
+       vector_foreach_slot(vecs->mpvec, mpp, i) {
+               if (flush_map(mpp, vecs, 0))
+                       ret++;
+               else
+                       i--;
+       }
+       /* flush any multipath maps that aren't currently known by multipathd */
+       ret |= dm_flush_maps(0, 0);
+       return ret;
+}
+
+int
 cli_reload(void *v, char **reply, int *len, void *data)
 {
        struct vectors * vecs = (struct vectors *)data;
@@ -877,7 +946,7 @@ cli_reload(void *v, char **reply, int *len, void *data)
                return 1;
        }
 
-       return update_path_groups(mpp, vecs, 0);
+       return reload_and_sync_map(mpp, vecs, 0);
 }
 
 int resize_map(struct multipath *mpp, unsigned long long size,
@@ -1297,7 +1366,7 @@ show_blacklist (char ** r, int * len)
 
                c = reply;
                c += snprint_blacklist_report(conf, c, maxlen);
-               again = ((c - reply) == maxlen);
+               again = (c == reply + maxlen);
                REALLOC_REPLY(reply, again, maxlen);
        }
        pthread_cleanup_pop(1);
@@ -1339,7 +1408,7 @@ show_devices (char ** r, int * len, struct vectors *vecs)
 
                c = reply;
                c += snprint_devices(conf, c, maxlen, vecs);
-               again = ((c - reply) == maxlen);
+               again = (c == reply + maxlen);
                REALLOC_REPLY(reply, again, maxlen);
        }
        pthread_cleanup_pop(1);
@@ -1462,6 +1531,8 @@ cli_getprkey(void * v, char ** reply, int * len, void * data)
                return 1;
 
        *reply = malloc(26);
+       if (!*reply)
+               return 1;
 
        if (!get_be64(mpp->reservation_key)) {
                sprintf(*reply, "none\n");
@@ -1557,7 +1628,7 @@ int cli_set_marginal(void * v, char ** reply, int * len, void * data)
        }
        pp->marginal = 1;
 
-       return update_path_groups(pp->mpp, vecs, 0);
+       return reload_and_sync_map(pp->mpp, vecs, 0);
 }
 
 int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
@@ -1584,7 +1655,7 @@ int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
        }
        pp->marginal = 0;
 
-       return update_path_groups(pp->mpp, vecs, 0);
+       return reload_and_sync_map(pp->mpp, vecs, 0);
 }
 
 int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
@@ -1621,5 +1692,5 @@ int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
                vector_foreach_slot (pgp->paths, pp, j)
                        pp->marginal = 0;
 
-       return update_path_groups(mpp, vecs, 0);
+       return reload_and_sync_map(mpp, vecs, 0);
 }
index 0f45106..6f57b42 100644 (file)
@@ -26,6 +26,7 @@ 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);
 int cli_del_map (void * v, char ** reply, int * len, void * data);
+int cli_del_maps (void * v, char ** reply, int * len, void * data);
 int cli_switch_group(void * v, char ** reply, int * len, void * data);
 int cli_reconfigure(void * v, char ** reply, int * len, void * data);
 int cli_resize(void * v, char ** reply, int * len, void * data);
index b22b47d..5f2d210 100644 (file)
@@ -156,8 +156,10 @@ static int dm_get_events(void)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt))
+       if (!dm_task_run(dmt)) {
+               dm_log_error(3, DM_DEVICE_LIST, dmt);
                goto fail;
+       }
 
        if (!(names = dm_task_get_names(dmt)))
                goto fail;
@@ -355,7 +357,7 @@ static int dmevent_loop (void)
                pthread_testcancel();
                r = 0;
                if (curr_dev.action == EVENT_REMOVE)
-                       remove_map_by_alias(curr_dev.name, waiter->vecs, 1);
+                       remove_map_by_alias(curr_dev.name, waiter->vecs, PURGE_VEC);
                else
                        r = update_multipath(waiter->vecs, curr_dev.name, 1);
                pthread_cleanup_pop(1);
index 8baf9ab..a4abbb2 100644 (file)
@@ -63,6 +63,7 @@
 #include "uevent.h"
 #include "log.h"
 #include "uxsock.h"
+#include "alias.h"
 
 #include "mpath_cmd.h"
 #include "mpath_persist.h"
@@ -85,6 +86,7 @@
 
 #define FILE_NAME_SIZE 256
 #define CMDSIZE 160
+#define MSG_SIZE 32
 
 #define LOG_MSG(lvl, verb, pp)                                 \
 do {                                                           \
@@ -153,52 +155,35 @@ static volatile sig_atomic_t exit_sig;
 static volatile sig_atomic_t reconfig_sig;
 static volatile sig_atomic_t log_reset_sig;
 
+static const char *daemon_status_msg[DAEMON_STATUS_SIZE] = {
+       [DAEMON_INIT] = "init",
+       [DAEMON_START] = "startup",
+       [DAEMON_CONFIGURE] = "configure",
+       [DAEMON_IDLE] = "idle",
+       [DAEMON_RUNNING] = "running",
+       [DAEMON_SHUTDOWN] = "shutdown",
+};
+
 const char *
 daemon_status(void)
 {
-       switch (get_running_state()) {
-       case DAEMON_INIT:
-               return "init";
-       case DAEMON_START:
-               return "startup";
-       case DAEMON_CONFIGURE:
-               return "configure";
-       case DAEMON_IDLE:
-               return "idle";
-       case DAEMON_RUNNING:
-               return "running";
-       case DAEMON_SHUTDOWN:
-               return "shutdown";
-       }
-       return NULL;
+       int status = get_running_state();
+
+       if (status < DAEMON_INIT || status >= DAEMON_STATUS_SIZE)
+               return NULL;
+
+       return daemon_status_msg[status];
 }
 
 /*
  * I love you too, systemd ...
  */
-static const char *
-sd_notify_status(enum daemon_status state)
-{
-       switch (state) {
-       case DAEMON_INIT:
-               return "STATUS=init";
-       case DAEMON_START:
-               return "STATUS=startup";
-       case DAEMON_CONFIGURE:
-               return "STATUS=configure";
-       case DAEMON_IDLE:
-       case DAEMON_RUNNING:
-               return "STATUS=up";
-       case DAEMON_SHUTDOWN:
-               return "STATUS=shutdown";
-       }
-       return NULL;
-}
-
 #ifdef USE_SYSTEMD
 static void do_sd_notify(enum daemon_status old_state,
                         enum daemon_status new_state)
 {
+       char notify_msg[MSG_SIZE];
+       const char *msg;
        /*
         * Checkerloop switches back and forth between idle and running state.
         * No need to tell systemd each time.
@@ -207,7 +192,14 @@ static void do_sd_notify(enum daemon_status old_state,
        if ((new_state == DAEMON_IDLE || new_state == DAEMON_RUNNING) &&
            (old_state == DAEMON_IDLE || old_state == DAEMON_RUNNING))
                return;
-       sd_notify(0, sd_notify_status(new_state));
+
+       if (new_state == DAEMON_IDLE || new_state == DAEMON_RUNNING)
+               msg = "up";
+       else
+               msg = daemon_status_msg[new_state];
+
+       if (msg && !safe_sprintf(notify_msg, "STATUS=%s", msg))
+               sd_notify(0, notify_msg);
 }
 #endif
 
@@ -247,7 +239,9 @@ enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
 static void __post_config_state(enum daemon_status state)
 {
        if (state != running_state && running_state != DAEMON_SHUTDOWN) {
+#ifdef USE_SYSTEMD
                enum daemon_status old_state = running_state;
+#endif
 
                running_state = state;
                pthread_cond_broadcast(&config_cond);
@@ -272,7 +266,9 @@ int set_config_state(enum daemon_status state)
        pthread_cleanup_push(config_cleanup, NULL);
        pthread_mutex_lock(&config_lock);
        if (running_state != state) {
+#ifdef USE_SYSTEMD
                enum daemon_status old_state = running_state;
+#endif
 
                if (running_state == DAEMON_SHUTDOWN)
                        rc = EINVAL;
@@ -374,7 +370,7 @@ remove_map_and_stop_waiter(struct multipath *mpp, struct vectors *vecs)
        condlog(3, "%s: removing map from internal tables", mpp->alias);
        if (!poll_dmevents)
                stop_waiter_thread(mpp);
-       remove_map(mpp, vecs, PURGE_VEC);
+       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
 }
 
 static void
@@ -396,25 +392,16 @@ remove_maps_and_stop_waiters(struct vectors *vecs)
        remove_maps(vecs);
 }
 
-static void
-set_multipath_wwid (struct multipath * mpp)
-{
-       if (strlen(mpp->wwid))
-               return;
-
-       dm_get_uuid(mpp->alias, mpp->wwid, WWID_SIZE);
-}
-
 int __setup_multipath(struct vectors *vecs, struct multipath *mpp,
                      int reset)
 {
        if (dm_get_info(mpp->alias, &mpp->dmi)) {
                /* Error accessing table */
-               condlog(3, "%s: cannot access table", mpp->alias);
+               condlog(2, "%s: cannot access table", mpp->alias);
                goto out;
        }
 
-       if (update_multipath_strings(mpp, vecs->pathvec, 1)) {
+       if (update_multipath_strings(mpp, vecs->pathvec) != DMP_OK) {
                condlog(0, "%s: failed to setup multipath", mpp->alias);
                goto out;
        }
@@ -497,7 +484,7 @@ retry:
                retries = -1;
                goto fail;
        }
-       verify_paths(mpp, vecs);
+       verify_paths(mpp);
        mpp->action = ACT_RELOAD;
 
        if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
@@ -515,7 +502,7 @@ retry:
 fail:
        if (new_map && (retries < 0 || wait_for_events(mpp, vecs))) {
                condlog(0, "%s: failed to create new map", mpp->alias);
-               remove_map(mpp, vecs, 1);
+               remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
                return 1;
        }
 
@@ -548,14 +535,17 @@ add_map_without_path (struct vectors *vecs, const char *alias)
                condlog(3, "%s: cannot access table", mpp->alias);
                goto out;
        }
-       set_multipath_wwid(mpp);
+       if (!strlen(mpp->wwid))
+               dm_get_uuid(mpp->alias, mpp->wwid, WWID_SIZE);
+       if (!strlen(mpp->wwid))
+               condlog(1, "%s: adding map with empty WWID", mpp->alias);
        conf = get_multipath_config();
        mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
        put_multipath_config(conf);
 
-       if (update_multipath_table(mpp, vecs->pathvec, 1))
+       if (update_multipath_table(mpp, vecs->pathvec, 0) != DMP_OK)
                goto out;
-       if (update_multipath_status(mpp))
+       if (update_multipath_status(mpp) != DMP_OK)
                goto out;
 
        if (!vector_alloc_slot(vecs->mpvec))
@@ -568,7 +558,7 @@ add_map_without_path (struct vectors *vecs, const char *alias)
 
        return mpp;
 out:
-       remove_map(mpp, vecs, PURGE_VEC);
+       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
        return NULL;
 }
 
@@ -631,7 +621,7 @@ sync_maps_state(vector mpvec)
                sync_map_state(mpp);
 }
 
-static int
+int
 flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths)
 {
        int r;
@@ -794,6 +784,7 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs)
                goto out;
        }
 
+       dm_queue_if_no_path(alias, 0);
        remove_map_and_stop_waiter(mpp, vecs);
 out:
        lock_cleanup_pop(vecs->lock);
@@ -844,9 +835,23 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
        pp = find_path_by_dev(vecs->pathvec, uev->kernel);
        if (pp) {
                int r;
+               struct multipath *prev_mpp = NULL;
+
+               if (pp->initialized == INIT_REMOVED) {
+                       condlog(3, "%s: re-adding removed path", pp->dev);
+                       pp->initialized = INIT_NEW;
+                       prev_mpp = pp->mpp;
+                       if (prev_mpp == NULL)
+                               condlog(0, "Bug: %s was in INIT_REMOVED state without being a multipath member",
+                                       pp->dev);
+                       pp->mpp = NULL;
+                       /* make sure get_uid() is called */
+                       pp->wwid[0] = '\0';
+               } else
+                       condlog(3,
+                               "%s: spurious uevent, path already in pathvec",
+                               uev->kernel);
 
-               condlog(3, "%s: spurious uevent, path already in pathvec",
-                       uev->kernel);
                if (!pp->mpp && !strlen(pp->wwid)) {
                        condlog(3, "%s: reinitialize path", uev->kernel);
                        udev_device_unref(pp->udev);
@@ -856,9 +861,44 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
                        r = pathinfo(pp, conf,
                                     DI_ALL | DI_BLACKLIST);
                        pthread_cleanup_pop(1);
-                       if (r == PATHINFO_OK)
+                       if (r == PATHINFO_OK && !prev_mpp)
                                ret = ev_add_path(pp, vecs, need_do_map);
-                       else if (r == PATHINFO_SKIPPED) {
+                       else if (r == PATHINFO_OK &&
+                                !strncmp(pp->wwid, prev_mpp->wwid, WWID_SIZE)) {
+                               /*
+                                * Path was unsuccessfully removed, but now
+                                * re-added, and still belongs to the right map
+                                * - all fine, reinstate asap
+                                */
+                               pp->mpp = prev_mpp;
+                               pp->tick = 1;
+                               ret = 0;
+                       } else if (prev_mpp) {
+                               /*
+                                * Bad: re-added path still hangs in wrong map
+                                * Make another attempt to remove the path
+                                */
+                               pp->mpp = prev_mpp;
+                               ret = ev_remove_path(pp, vecs, true);
+                               if (r == PATHINFO_OK && !ret)
+                                       /*
+                                        * Path successfully freed, move on to
+                                        * "new path" code path below
+                                        */
+                                       pp = NULL;
+                               else {
+                                       /*
+                                        * Failure in ev_remove_path will keep
+                                        * path in pathvec in INIT_REMOVED state
+                                        * Fail the path to make sure it isn't
+                                        * used any more.
+                                        */
+                                       pp->dmstate = PSTATE_FAILED;
+                                       dm_fail_path(pp->mpp->alias, pp->dev_t);
+                                       condlog(1, "%s: failed to re-add path still mapped in %s",
+                                               pp->dev, pp->mpp->alias);
+                               }
+                       } else if (r == PATHINFO_SKIPPED) {
                                condlog(3, "%s: remove blacklisted path",
                                        uev->kernel);
                                i = find_slot(vecs->pathvec, (void *)pp);
@@ -958,10 +998,11 @@ rescan:
        if (mpp) {
                condlog(4,"%s: adopting all paths for path %s",
                        mpp->alias, pp->dev);
-               if (adopt_paths(vecs->pathvec, mpp))
+               if (adopt_paths(vecs->pathvec, mpp) ||
+                   find_slot(vecs->pathvec, pp) == -1)
                        goto fail; /* leave path added to pathvec */
 
-               verify_paths(mpp, vecs);
+               verify_paths(mpp);
                mpp->action = ACT_RELOAD;
        } else {
                if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) {
@@ -1049,7 +1090,7 @@ rescan:
                goto fail;
 
 fail_map:
-       remove_map(mpp, vecs, 1);
+       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
 fail:
        orphan_path(pp, "failed to add path");
        return 1;
@@ -1101,15 +1142,24 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                }
 
                /*
+                * Mark the path as removed. In case of success, we
+                * will delete it for good. Otherwise, it will be deleted
+                * later, unless all attempts to reload this map fail.
+                * Note: we have to explicitly remove pp from mpp->paths,
+                * update_mpp_paths() doesn't do that.
+                */
+               set_path_removed(pp);
+               i = find_slot(mpp->paths, pp);
+               if (i != -1)
+                       vector_del_slot(mpp->paths, i);
+
+               /*
                 * Make sure mpp->hwe doesn't point to freed memory
                 * We call extract_hwe_from_path() below to restore mpp->hwe
                 */
                if (mpp->hwe == pp->hwe)
                        mpp->hwe = NULL;
 
-               if ((i = find_slot(mpp->paths, (void *)pp)) != -1)
-                       vector_del_slot(mpp->paths, i);
-
                /*
                 * remove the map IF removing the last path
                 */
@@ -1133,6 +1183,7 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                                        " removing all paths",
                                        alias);
                                retval = 0;
+                               /* flush_map() has freed the path */
                                goto out;
                        }
                        /*
@@ -1169,21 +1220,27 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                        /*
                         * update our state from kernel
                         */
+                       char devt[BLK_DEV_SIZE];
+
+                       strlcpy(devt, pp->dev_t, sizeof(devt));
                        if (setup_multipath(vecs, mpp))
                                return 1;
+                       /*
+                        * Successful map reload without this path:
+                        * sync_map_state() will free it.
+                        */
                        sync_map_state(mpp);
 
-                       condlog(2, "%s [%s]: path removed from map %s",
-                               pp->dev, pp->dev_t, mpp->alias);
+                       condlog(2, "%s: path removed from map %s",
+                               devt, mpp->alias);
                }
+       } else {
+               /* mpp == NULL */
+               if ((i = find_slot(vecs->pathvec, (void *)pp)) != -1)
+                       vector_del_slot(vecs->pathvec, i);
+               free_path(pp);
        }
-
 out:
-       if ((i = find_slot(vecs->pathvec, (void *)pp)) != -1)
-               vector_del_slot(vecs->pathvec, i);
-
-       free_path(pp);
-
        return retval;
 
 fail:
@@ -1263,7 +1320,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
                        else {
                                if (ro == 1)
                                        pp->mpp->force_readonly = 1;
-                               retval = update_path_groups(mpp, vecs, 0);
+                               retval = reload_and_sync_map(mpp, vecs, 0);
                                if (retval == 2)
                                        condlog(2, "%s: map removed during reload", pp->dev);
                                else {
@@ -1346,9 +1403,9 @@ map_discovery (struct vectors * vecs)
                return 1;
 
        vector_foreach_slot (vecs->mpvec, mpp, i)
-               if (update_multipath_table(mpp, vecs->pathvec, 1) ||
-                   update_multipath_status(mpp)) {
-                       remove_map(mpp, vecs, 1);
+               if (update_multipath_table(mpp, vecs->pathvec, 0) != DMP_OK ||
+                   update_multipath_status(mpp) != DMP_OK) {
+                       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
                        i--;
                }
 
@@ -1444,6 +1501,31 @@ uev_trigger (struct uevent * uev, void * trigger_data)
                        uev_pathfail_check(uev, vecs);
                } else if (!strncmp(uev->action, "remove", 6)) {
                        r = uev_remove_map(uev, vecs);
+               } else if (!strncmp(uev->action, "add", 3)) {
+                       const char *ev_name;
+                       char *dm_name;
+                       int major = -1, minor = -1;
+
+                       /*
+                        * If DM_NAME is not set for a valid map, trigger a
+                        * change event. This can happen during coldplug
+                        * if udev was killed between handling the 'add' and
+                        * 'change' events before.
+                        */
+                       ev_name = uevent_get_dm_name(uev);
+                       if (!ev_name) {
+                               major = uevent_get_major(uev);
+                               minor = uevent_get_minor(uev);
+                               dm_name = dm_mapname(major, minor);
+                               if (dm_name && *dm_name) {
+                                       condlog(2, "%s: received incomplete 'add' uevent, triggering change",
+                                               dm_name);
+                                       udev_device_set_sysattr_value(uev->udev,
+                                                                     "uevent",
+                                                                     "change");
+                                       free(dm_name);
+                               }
+                       }
                }
                goto out;
        }
@@ -1551,6 +1633,7 @@ uxlsnrloop (void * ap)
        set_handler_callback(DEL+PATH, cli_del_path);
        set_handler_callback(ADD+MAP, cli_add_map);
        set_handler_callback(DEL+MAP, cli_del_map);
+       set_handler_callback(DEL+MAPS, cli_del_maps);
        set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
        set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure);
        set_handler_callback(SUSPEND+MAP, cli_suspend);
@@ -1611,22 +1694,18 @@ fail_path (struct path * pp, int del_active)
 /*
  * caller must have locked the path list before calling that function
  */
-static int
+static void
 reinstate_path (struct path * pp)
 {
-       int ret = 0;
-
        if (!pp->mpp)
-               return 0;
+               return;
 
-       if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) {
+       if (dm_reinstate_path(pp->mpp->alias, pp->dev_t))
                condlog(0, "%s: reinstate failed", pp->dev_t);
-               ret = 1;
-       } else {
+       else {
                condlog(2, "%s: reinstated", pp->dev_t);
                update_queue_mode_add_path(pp->mpp);
        }
-       return ret;
 }
 
 static void
@@ -1823,7 +1902,45 @@ int update_prio(struct path *pp, int refresh_all)
        return 1;
 }
 
-int update_path_groups(struct multipath *mpp, struct vectors *vecs, int refresh)
+static int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
+                     int is_daemon)
+{
+       char params[PARAMS_SIZE] = {0};
+       struct path *pp;
+       int i, r;
+
+       update_mpp_paths(mpp, vecs->pathvec);
+       if (refresh) {
+               vector_foreach_slot (mpp->paths, pp, i) {
+                       struct config *conf = get_multipath_config();
+                       pthread_cleanup_push(put_multipath_config, conf);
+                       r = pathinfo(pp, conf, DI_PRIO);
+                       pthread_cleanup_pop(1);
+                       if (r) {
+                               condlog(2, "%s: failed to refresh pathinfo",
+                                       mpp->alias);
+                               return 1;
+                       }
+               }
+       }
+       if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
+               condlog(0, "%s: failed to setup map", mpp->alias);
+               return 1;
+       }
+       select_action(mpp, vecs->mpvec, 1);
+
+       r = domap(mpp, params, is_daemon);
+       if (r == DOMAP_FAIL || r == DOMAP_RETRY) {
+               condlog(3, "%s: domap (%u) failure "
+                       "for reload map", mpp->alias, r);
+               return 1;
+       }
+
+       return 0;
+}
+
+int reload_and_sync_map(struct multipath *mpp,
+                       struct vectors *vecs, int refresh)
 {
        if (reload_map(vecs, mpp, refresh, 1))
                return 1;
@@ -1963,8 +2080,9 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
        int marginal_pathgroups, marginal_changed = 0;
        int ret;
 
-       if ((pp->initialized == INIT_OK ||
-            pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp)
+       if (((pp->initialized == INIT_OK ||
+             pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp) ||
+           pp->initialized == INIT_REMOVED)
                return 0;
 
        if (pp->tick)
@@ -2087,9 +2205,16 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
        /*
         * Synchronize with kernel state
         */
-       if (update_multipath_strings(pp->mpp, vecs->pathvec, 1)) {
-               condlog(1, "%s: Could not synchronize with kernel state",
-                       pp->dev);
+       ret = update_multipath_strings(pp->mpp, vecs->pathvec);
+       if (ret != DMP_OK) {
+               if (ret == DMP_NOT_FOUND) {
+                       /* multipath device missing. Likely removed */
+                       condlog(1, "%s: multipath device '%s' not found",
+                               pp->dev, pp->mpp->alias);
+                       return 0;
+               } else
+                       condlog(1, "%s: Couldn't synchronize with kernel state",
+                               pp->dev);
                pp->dmstate = PSTATE_UNDEF;
        }
        /* if update_multipath_strings orphaned the path, quit early */
@@ -2179,12 +2304,8 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
                /*
                 * reinstate this path
                 */
-               if (!disable_reinstate && reinstate_path(pp)) {
-                       condlog(3, "%s: reload map", pp->dev);
-                       ev_add_path(pp, vecs, 1);
-                       pp->tick = 1;
-                       return 0;
-               }
+               if (!disable_reinstate)
+                       reinstate_path(pp);
                new_path_up = 1;
 
                if (oldchkrstate != PATH_UP && oldchkrstate != PATH_GHOST)
@@ -2200,15 +2321,10 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
        else if (newstate == PATH_UP || newstate == PATH_GHOST) {
                if ((pp->dmstate == PSTATE_FAILED ||
                    pp->dmstate == PSTATE_UNDEF) &&
-                   !disable_reinstate) {
+                   !disable_reinstate)
                        /* Clear IO errors */
-                       if (reinstate_path(pp)) {
-                               condlog(3, "%s: reload map", pp->dev);
-                               ev_add_path(pp, vecs, 1);
-                               pp->tick = 1;
-                               return 0;
-                       }
-               } else {
+                       reinstate_path(pp);
+               else {
                        LOG_MSG(4, verbosity, pp);
                        if (pp->checkint != max_checkint) {
                                /*
@@ -2252,13 +2368,18 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
         */
        condlog(4, "path prio refresh");
 
-       if (marginal_changed)
-               update_path_groups(pp->mpp, vecs, 1);
+       if (marginal_changed) {
+               condlog(2, "%s: path is %s marginal", pp->dev,
+                       (pp->marginal)? "now" : "no longer");
+               reload_and_sync_map(pp->mpp, vecs, 1);
+       }
        else if (update_prio(pp, new_path_up) &&
            (pp->mpp->pgpolicyfn == (pgpolicyfn *)group_by_prio) &&
-            pp->mpp->pgfailback == -FAILBACK_IMMEDIATE)
-               update_path_groups(pp->mpp, vecs, !new_path_up);
-       else if (need_switch_pathgroup(pp->mpp, 0)) {
+            pp->mpp->pgfailback == -FAILBACK_IMMEDIATE) {
+               condlog(2, "%s: path priorities changed. reloading",
+                       pp->mpp->alias);
+               reload_and_sync_map(pp->mpp, vecs, !new_path_up);
+       } else if (need_switch_pathgroup(pp->mpp, 0)) {
                if (pp->mpp->pgfailback > 0 &&
                    (new_path_up || pp->mpp->failback_tick <= 0))
                        pp->mpp->failback_tick =
@@ -2280,7 +2401,9 @@ checkerloop (void *ap)
        struct timespec last_time;
        struct config *conf;
        int foreign_tick = 0;
+#ifdef USE_SYSTEMD
        bool use_watchdog;
+#endif
 
        pthread_cleanup_push(rcu_unregister, NULL);
        rcu_register_thread();
@@ -2294,7 +2417,9 @@ checkerloop (void *ap)
 
        /* use_watchdog is set from process environment and never changes */
        conf = get_multipath_config();
+#ifdef USE_SYSTEMD
        use_watchdog = conf->use_watchdog;
+#endif
        put_multipath_config(conf);
 
        while (1) {
@@ -2374,7 +2499,7 @@ checkerloop (void *ap)
                                conf = get_multipath_config();
                                max_checkint = conf->max_checkint;
                                put_multipath_config(conf);
-                               if (diff_time.tv_sec > max_checkint)
+                               if (diff_time.tv_sec > (time_t)max_checkint)
                                        condlog(1, "path checkers took longer "
                                                "than %lu seconds, consider "
                                                "increasing max_polling_interval",
@@ -2519,7 +2644,7 @@ configure (struct vectors * vecs)
         */
        vector_foreach_slot(vecs->mpvec, mpp, i) {
                if (wait_for_events(mpp, vecs)) {
-                       remove_map(mpp, vecs, 1);
+                       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
                        i--;
                        continue;
                }
@@ -2584,6 +2709,8 @@ reconfigure (struct vectors * vecs)
                conf->verbosity = verbosity;
        if (bindings_read_only)
                conf->bindings_read_only = bindings_read_only;
+       check_alias_settings(conf);
+
        uxsock_timeout = conf->uxsock_timeout;
 
        old = rcu_dereference(multipath_conf);
@@ -3229,7 +3356,6 @@ void *  mpath_pr_event_handler_fn (void * pathp )
        if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
        {
                condlog(1, "%s: No key found. Device may not be registered.", pp->dev);
-               ret = MPATH_PR_SUCCESS;
                goto out;
        }
        condlog(2, "Multipath  reservation_key: 0x%" PRIx64 " ",
@@ -3251,12 +3377,13 @@ void *  mpath_pr_event_handler_fn (void * pathp )
        {
                condlog(0, "%s: Either device not registered or ", pp->dev);
                condlog(0, "host is not authorised for registration. Skip path");
-               ret = MPATH_PR_OTHER;
                goto out;
        }
 
-       param= malloc(sizeof(struct prout_param_descriptor));
-       memset(param, 0 , sizeof(struct prout_param_descriptor));
+       param = (struct prout_param_descriptor *)MALLOC(sizeof(struct prout_param_descriptor));
+       if (!param)
+               goto out;
+
        param->sa_flags = mpp->sa_flags;
        memcpy(param->sa_key, &mpp->reservation_key, 8);
        param->num_transportid = 0;
index 7bb8463..5abbe97 100644 (file)
@@ -4,12 +4,13 @@
 #define MAPGCINT 5
 
 enum daemon_status {
-       DAEMON_INIT,
+       DAEMON_INIT = 0,
        DAEMON_START,
        DAEMON_CONFIGURE,
        DAEMON_IDLE,
        DAEMON_RUNNING,
        DAEMON_SHUTDOWN,
+       DAEMON_STATUS_SIZE,
 };
 
 struct prout_param_descriptor;
@@ -28,6 +29,7 @@ int ev_add_path (struct path *, struct vectors *, int);
 int ev_remove_path (struct path *, struct vectors *, int);
 int ev_add_map (char *, const char *, struct vectors *);
 int ev_remove_map (char *, char *, int, struct vectors *);
+int flush_map(struct multipath *, struct vectors *, int);
 int set_config_state(enum daemon_status);
 void * mpath_alloc_prin_response(int prin_sa);
 int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp,
@@ -45,7 +47,7 @@ int __setup_multipath (struct vectors * vecs, struct multipath * mpp,
                       int reset);
 #define setup_multipath(vecs, mpp) __setup_multipath(vecs, mpp, 1)
 int update_multipath (struct vectors *vecs, char *mapname, int reset);
-int update_path_groups(struct multipath *mpp, struct vectors *vecs,
-                      int refresh);
+int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs,
+                       int refresh);
 
 #endif /* MAIN_H */
index e645766..3bc6980 100644 (file)
@@ -64,7 +64,7 @@ void stop_waiter_thread (struct multipath *mpp)
                return;
 
        condlog(3, "%s: stop event checker thread (%lu)", mpp->alias,
-               mpp->waiter);
+               (unsigned long)mpp->waiter);
        thread = mpp->waiter;
        mpp->waiter = (pthread_t)0;
        pthread_cleanup_push(cleanup_lock, &waiter_lock);
@@ -119,6 +119,8 @@ static int waiteventloop (struct event_thread *waiter)
 
        pthread_testcancel();
        r = dm_task_run(waiter->dmt);
+       if (!r)
+               dm_log_error(2, DM_DEVICE_WAITEVENT, waiter->dmt);
        pthread_testcancel();
 
        pthread_sigmask(SIG_SETMASK, &oldset, NULL);
index 77ff324..d26b3ce 100644 (file)
@@ -10,15 +10,17 @@ W_MISSING_INITIALIZERS := $(call TEST_MISSING_INITIALIZERS)
 
 CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \
        -Wno-unused-parameter $(W_MISSING_INITIALIZERS)
-LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka
+LIBDEPS += -L$(multipathdir) -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka
 
 TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
-        alias directio
+        alias directio valid devt
+HELPERS := test-lib.o test-log.o
 
 .SILENT: $(TESTS:%=%.o)
 .PRECIOUS: $(TESTS:%=%-test)
 
 all:   $(TESTS:%=%.out)
+valgrind:      $(TESTS:%=%.vgr)
 
 # test-specific compiler flags
 # XYZ-test_FLAGS: Additional compiler flags for this test
@@ -41,7 +43,7 @@ endif
 dmevents-test_LIBDEPS = -lpthread -ldevmapper -lurcu
 hwtable-test_TESTDEPS := test-lib.o
 hwtable-test_OBJDEPS := ../libmultipath/discovery.o ../libmultipath/blacklist.o \
-       ../libmultipath/prio.o ../libmultipath/callout.o ../libmultipath/structs.o
+       ../libmultipath/structs.o
 hwtable-test_LIBDEPS := -ludev -lpthread -ldl
 blacklist-test_TESTDEPS := test-log.o
 blacklist-test_OBJDEPS := ../libmultipath/blacklist.o
@@ -50,6 +52,9 @@ vpd-test_OBJDEPS :=  ../libmultipath/discovery.o
 vpd-test_LIBDEPS := -ludev -lpthread -ldl
 alias-test_TESTDEPS := test-log.o
 alias-test_LIBDEPS := -lpthread -ldl
+valid-test_OBJDEPS := ../libmultipath/valid.o
+valid-test_LIBDEPS := -ludev -lpthread -ldl
+devt-test_LIBDEPS := -ludev
 ifneq ($(DIO_TEST_DEV),)
 directio-test_LIBDEPS := -laio
 endif
@@ -58,19 +63,27 @@ endif
        $(CC) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
 
 lib/libchecktur.so:
-       mkdir lib
-       ln -t lib ../libmultipath/{checkers,prioritizers,foreign}/*.so
+       mkdir -p lib
+       ln ../libmultipath/*/*.so lib
 
 %.out: %-test lib/libchecktur.so
        @echo == running $< ==
        @LD_LIBRARY_PATH=$(multipathdir):$(mpathcmddir) ./$< >$@
 
-OBJS = $(TESTS:%=%.o) test-lib.o
+%.vgr:  %-test lib/libchecktur.so
+       @echo == running valgrind for $< ==
+       @LD_LIBRARY_PATH=$(multipathdir):$(mpathcmddir) \
+               valgrind --leak-check=full --error-exitcode=128 ./$< >$@ 2>&1
+
+OBJS = $(TESTS:%=%.o) $(HELPERS)
 
 test_clean:
-       $(RM) $(TESTS:%=%.out)
+       $(RM) $(TESTS:%=%.out) $(TESTS:%=%.vgr)
+
+valgrind_clean:
+       $(RM) $(TESTS:%=%.vgr)
 
-clean: test_clean dep_clean
+clean: test_clean valgrind_clean dep_clean
        $(RM) $(TESTS:%=%-test) $(OBJS) *.o.wrap
        $(RM) -rf lib
 
index 6438a82..6e7ad40 100644 (file)
@@ -5,6 +5,14 @@ or simply `make` in the `tests` subdirectory. The test output is saved as
 `<testname>.out`. The test programs are called `<testname>-test`, and can
 be run standalone e.g. for debugging purposes.
 
+## Running tests under valgrind
+
+The unit tests can be run under the valgrind debugger with `make valgrind`
+in the `tests` directory, or `make valgrind-test` in the top directory.
+If valgrind detects a bad memory access or leak, the test will fail. The
+output of the test run, including valgrind output, is stored as
+`<testname>.vgr`.
+
 ## Notes on individual tests
 
 ### Tests that require root permissions
index 30414db..7fda679 100644 (file)
@@ -552,7 +552,7 @@ static void rl_match_a(void **state)
 
        buf[0] = '\0';
        will_return(__wrap_fgets, "MPATHa WWID0\n");
-       expect_condlog(3, "Found matching alias [MPATHa] in bindings file.\n"
+       expect_condlog(3, "Found matching alias [MPATHa] in bindings file. "
                       "Setting wwid to WWID0\n");
        rc = rlookup_binding(NULL, buf, "MPATHa");
        assert_int_equal(rc, 0);
@@ -617,7 +617,7 @@ static void rl_match_b(void **state)
        will_return(__wrap_fgets, "MPATHa WWID0\n");
        will_return(__wrap_fgets, "MPATHz WWID26\n");
        will_return(__wrap_fgets, "MPATHb WWID2\n");
-       expect_condlog(3, "Found matching alias [MPATHb] in bindings file.\n"
+       expect_condlog(3, "Found matching alias [MPATHb] in bindings file. "
                       "Setting wwid to WWID2\n");
        rc = rlookup_binding(NULL, buf, "MPATHb");
        assert_int_equal(rc, 0);
@@ -652,6 +652,7 @@ static void al_a(void **state)
        alias = allocate_binding(0, "WWIDa", 1, "MPATH");
        assert_ptr_not_equal(alias, NULL);
        assert_string_equal(alias, "MPATHa");
+       free(alias);
 }
 
 static void al_zz(void **state)
@@ -668,6 +669,7 @@ static void al_zz(void **state)
        alias = allocate_binding(0, "WWIDzz", 26*26 + 26, "MPATH");
        assert_ptr_not_equal(alias, NULL);
        assert_string_equal(alias, "MPATHzz");
+       free(alias);
 }
 
 static void al_0(void **state)
@@ -710,7 +712,7 @@ static void al_write_err(void **state)
        will_return(__wrap_write, strlen(ln) - 1);
        expect_value(__wrap_ftruncate, length, offset);
        will_return(__wrap_ftruncate, 0);
-       expect_condlog(0, "Cannot write binding to bindings file : Success\n");
+       expect_condlog(0, "Cannot write binding to bindings file :");
 
        alias = allocate_binding(0, "WWIDa", 1, "MPATH");
        assert_ptr_equal(alias, NULL);
index 6e7c186..84a3ba2 100644 (file)
@@ -60,50 +60,96 @@ __wrap_udev_list_entry_get_name(struct udev_list_entry *list_entry)
        return *(const char **)list_entry;
 }
 
+vector elist_property_default;
+vector blist_devnode_default;
 vector blist_devnode_sdb;
+vector blist_devnode_sdb_inv;
 vector blist_all;
 vector blist_device_foo_bar;
+vector blist_device_foo_inv_bar;
+vector blist_device_foo_bar_inv;
 vector blist_device_all;
 vector blist_wwid_xyzzy;
+vector blist_wwid_xyzzy_inv;
 vector blist_protocol_fcp;
+vector blist_protocol_fcp_inv;
 vector blist_property_wwn;
+vector blist_property_wwn_inv;
 
 static int setup(void **state)
 {
+       struct config conf;
+
+       memset(&conf, 0, sizeof(conf));
+       conf.blist_devnode = vector_alloc();
+       if (!conf.blist_devnode)
+               return -1;
+       conf.elist_property = vector_alloc();
+       if (!conf.elist_property)
+               return -1;
+       if (setup_default_blist(&conf) != 0)
+               return -1;
+       elist_property_default = conf.elist_property;
+       blist_devnode_default = conf.blist_devnode;
+
        blist_devnode_sdb = vector_alloc();
        if (!blist_devnode_sdb ||
-           store_ble(blist_devnode_sdb, strdup("sdb"), ORIGIN_CONFIG))
+           store_ble(blist_devnode_sdb, "sdb", ORIGIN_CONFIG))
+               return -1;
+       blist_devnode_sdb_inv = vector_alloc();
+       if (!blist_devnode_sdb_inv ||
+           store_ble(blist_devnode_sdb_inv, "!sdb", ORIGIN_CONFIG))
                return -1;
 
        blist_all = vector_alloc();
-       if (!blist_all || store_ble(blist_all, strdup(".*"), ORIGIN_CONFIG))
+       if (!blist_all || store_ble(blist_all, ".*", ORIGIN_CONFIG))
                return -1;
 
        blist_device_foo_bar = vector_alloc();
        if (!blist_device_foo_bar || alloc_ble_device(blist_device_foo_bar) ||
-           set_ble_device(blist_device_foo_bar, strdup("foo"), strdup("bar"),
-                          ORIGIN_CONFIG))
+           set_ble_device(blist_device_foo_bar, "foo", "bar", ORIGIN_CONFIG))
+               return -1;
+       blist_device_foo_inv_bar = vector_alloc();
+       if (!blist_device_foo_inv_bar ||
+           alloc_ble_device(blist_device_foo_inv_bar) ||
+           set_ble_device(blist_device_foo_inv_bar, "!foo", "bar", ORIGIN_CONFIG))
+               return -1;
+       blist_device_foo_bar_inv = vector_alloc();
+       if (!blist_device_foo_bar_inv ||
+           alloc_ble_device(blist_device_foo_bar_inv) ||
+           set_ble_device(blist_device_foo_bar_inv, "foo", "!bar", ORIGIN_CONFIG))
                return -1;
 
        blist_device_all = vector_alloc();
        if (!blist_device_all || alloc_ble_device(blist_device_all) ||
-           set_ble_device(blist_device_all, strdup(".*"), strdup(".*"),
-                          ORIGIN_CONFIG))
+           set_ble_device(blist_device_all, ".*", ".*", ORIGIN_CONFIG))
                return -1;
 
        blist_wwid_xyzzy = vector_alloc();
        if (!blist_wwid_xyzzy ||
-           store_ble(blist_wwid_xyzzy, strdup("xyzzy"), ORIGIN_CONFIG))
+           store_ble(blist_wwid_xyzzy, "xyzzy", ORIGIN_CONFIG))
+               return -1;
+       blist_wwid_xyzzy_inv = vector_alloc();
+       if (!blist_wwid_xyzzy_inv ||
+           store_ble(blist_wwid_xyzzy_inv, "!xyzzy", ORIGIN_CONFIG))
                return -1;
 
        blist_protocol_fcp = vector_alloc();
        if (!blist_protocol_fcp ||
-           store_ble(blist_protocol_fcp, strdup("scsi:fcp"), ORIGIN_CONFIG))
+           store_ble(blist_protocol_fcp, "scsi:fcp", ORIGIN_CONFIG))
+               return -1;
+       blist_protocol_fcp_inv = vector_alloc();
+       if (!blist_protocol_fcp_inv ||
+           store_ble(blist_protocol_fcp_inv, "!scsi:fcp", ORIGIN_CONFIG))
                return -1;
 
        blist_property_wwn = vector_alloc();
        if (!blist_property_wwn ||
-           store_ble(blist_property_wwn, strdup("ID_WWN"), ORIGIN_CONFIG))
+           store_ble(blist_property_wwn, "ID_WWN", ORIGIN_CONFIG))
+               return -1;
+       blist_property_wwn_inv = vector_alloc();
+       if (!blist_property_wwn_inv ||
+           store_ble(blist_property_wwn_inv, "!ID_WWN", ORIGIN_CONFIG))
                return -1;
 
        return 0;
@@ -111,13 +157,21 @@ static int setup(void **state)
 
 static int teardown(void **state)
 {
+       free_blacklist(elist_property_default);
+       free_blacklist(blist_devnode_default);
        free_blacklist(blist_devnode_sdb);
+       free_blacklist(blist_devnode_sdb_inv);
        free_blacklist(blist_all);
        free_blacklist_device(blist_device_foo_bar);
+       free_blacklist_device(blist_device_foo_inv_bar);
+       free_blacklist_device(blist_device_foo_bar_inv);
        free_blacklist_device(blist_device_all);
        free_blacklist(blist_wwid_xyzzy);
+       free_blacklist(blist_wwid_xyzzy_inv);
        free_blacklist(blist_protocol_fcp);
+       free_blacklist(blist_protocol_fcp_inv);
        free_blacklist(blist_property_wwn);
+       free_blacklist(blist_property_wwn_inv);
        return 0;
 }
 
@@ -141,6 +195,11 @@ static void test_devnode_blacklist(void **state)
        expect_condlog(3, "sdb: device node name blacklisted\n");
        assert_int_equal(filter_devnode(blist_devnode_sdb, NULL, "sdb"),
                         MATCH_DEVNODE_BLIST);
+       assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdb"),
+                        MATCH_NOTHING);
+       expect_condlog(3, "sdc: device node name blacklisted\n");
+       assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdc"),
+                        MATCH_DEVNODE_BLIST);
 }
 
 static void test_devnode_whitelist(void **state)
@@ -159,12 +218,39 @@ static void test_devnode_missing(void **state)
                         MATCH_NOTHING);
 }
 
+static void test_devnode_default(void **state)
+{
+       assert_int_equal(filter_devnode(blist_devnode_default, NULL, "sdaa"),
+                        MATCH_NOTHING);
+       assert_int_equal(filter_devnode(blist_devnode_default, NULL, "nvme0n1"),
+                        MATCH_NOTHING);
+       assert_int_equal(filter_devnode(blist_devnode_default, NULL, "dasda"),
+                        MATCH_NOTHING);
+       expect_condlog(3, "hda: device node name blacklisted\n");
+       assert_int_equal(filter_devnode(blist_devnode_default, NULL, "hda"),
+                        MATCH_DEVNODE_BLIST);
+}
+
 static void test_device_blacklist(void **state)
 {
        expect_condlog(3, "sdb: (foo:bar) vendor/product blacklisted\n");
        assert_int_equal(filter_device(blist_device_foo_bar, NULL, "foo",
                                       "bar", "sdb"),
                         MATCH_DEVICE_BLIST);
+       assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "foo",
+                                       "bar", "sdb"),
+                        MATCH_NOTHING);
+       assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo",
+                                       "bar", "sdb"),
+                        MATCH_NOTHING);
+       expect_condlog(3, "sdb: (baz:bar) vendor/product blacklisted\n");
+       assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "baz",
+                                       "bar", "sdb"),
+                        MATCH_DEVICE_BLIST);
+       expect_condlog(3, "sdb: (foo:baz) vendor/product blacklisted\n");
+       assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo",
+                                       "baz", "sdb"),
+                        MATCH_DEVICE_BLIST);
 }
 
 static void test_device_whitelist(void **state)
@@ -191,6 +277,11 @@ static void test_wwid_blacklist(void **state)
        expect_condlog(3, "sdb: wwid xyzzy blacklisted\n");
        assert_int_equal(filter_wwid(blist_wwid_xyzzy, NULL, "xyzzy", "sdb"),
                         MATCH_WWID_BLIST);
+       assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "xyzzy",
+                                    "sdb"), MATCH_NOTHING);
+       expect_condlog(3, "sdb: wwid plugh blacklisted\n");
+       assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "plugh",
+                                    "sdb"), MATCH_WWID_BLIST);
 }
 
 static void test_wwid_whitelist(void **state)
@@ -218,6 +309,12 @@ static void test_protocol_blacklist(void **state)
        expect_condlog(3, "sdb: protocol scsi:fcp blacklisted\n");
        assert_int_equal(filter_protocol(blist_protocol_fcp, NULL, &pp),
                         MATCH_PROTOCOL_BLIST);
+       assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp),
+                        MATCH_NOTHING);
+       pp.sg_id.proto_id = SCSI_PROTOCOL_ATA;
+       expect_condlog(3, "sdb: protocol scsi:ata blacklisted\n");
+       assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp),
+                        MATCH_PROTOCOL_BLIST);
 }
 
 static void test_protocol_whitelist(void **state)
@@ -245,10 +342,17 @@ static void test_protocol_missing(void **state)
 static void test_property_blacklist(void **state)
 {
        static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } };
+       static struct udev_device udev_inv = { "sdb", { "ID_WWN", NULL } };
        conf.blist_property = blist_property_wwn;
        expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n");
        assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
                         MATCH_PROPERTY_BLIST);
+       conf.blist_property = blist_property_wwn_inv;
+       expect_condlog(3, "sdb: udev property ID_FOO blacklisted\n");
+       assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
+                        MATCH_PROPERTY_BLIST);
+       assert_int_equal(filter_property(&conf, &udev_inv, 3, "ID_SERIAL"),
+                        MATCH_NOTHING);
 }
 
 /* the property check works different in that you check all the property
@@ -484,6 +588,7 @@ int test_blacklist(void)
                cmocka_unit_test(test_devnode_blacklist),
                cmocka_unit_test(test_devnode_whitelist),
                cmocka_unit_test(test_devnode_missing),
+               cmocka_unit_test(test_devnode_default),
                cmocka_unit_test(test_device_blacklist),
                cmocka_unit_test(test_device_whitelist),
                cmocka_unit_test(test_device_missing),
diff --git a/tests/devt.c b/tests/devt.c
new file mode 100644 (file)
index 0000000..fd4d74a
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2020 Martin Wilck, SUSE
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <cmocka.h>
+#include <libudev.h>
+#include <sys/sysmacros.h>
+#include "util.h"
+#include "debug.h"
+
+#include "globals.c"
+
+static int get_one_devt(char *devt, size_t len)
+{
+       struct udev_enumerate *enm;
+       int r, ret = -1;
+       struct udev_list_entry *first;
+       struct udev_device *u_dev;
+       const char *path;
+       dev_t devnum;
+
+       enm = udev_enumerate_new(udev);
+       if (!enm)
+               return -1;
+       r = udev_enumerate_add_match_subsystem(enm, "block");
+       r = udev_enumerate_scan_devices(enm);
+       if (r < 0)
+               goto out;
+       first = udev_enumerate_get_list_entry(enm);
+       if (!first)
+               goto out;
+       path = udev_list_entry_get_name(first);
+       u_dev = udev_device_new_from_syspath(udev, path);
+       if (!u_dev)
+               goto out;
+       devnum = udev_device_get_devnum(u_dev);
+       snprintf(devt, len, "%d:%d",
+                major(devnum), minor(devnum));
+       udev_device_unref(u_dev);
+       condlog(3, "found block device: %s", devt);
+       ret = 0;
+out:
+       udev_enumerate_unref(enm);
+       return ret;
+}
+
+int setup(void **state)
+{
+       static char dev_t[BLK_DEV_SIZE];
+
+       udev = udev_new();
+       if (udev == NULL)
+               return -1;
+       *state = dev_t;
+       return get_one_devt(dev_t, sizeof(dev_t));
+}
+
+int teardown(void **state)
+{
+       udev_unref(udev);
+       return 0;
+}
+
+static void test_devt2devname_devt_good(void **state)
+{
+       char dummy[BLK_DEV_SIZE];
+
+       assert_int_equal(devt2devname(dummy, sizeof(dummy), *state), 0);
+}
+
+static void test_devt2devname_devname_null(void **state)
+{
+       assert_int_equal(devt2devname(NULL, 0, ""), 1);
+}
+
+/* buffer length 0 */
+static void test_devt2devname_length_0(void **state)
+{
+       char dummy[] = "";
+
+       assert_int_equal(devt2devname(dummy, 0, ""), 1);
+}
+
+/* buffer too small */
+static void test_devt2devname_length_1(void **state)
+{
+       char dummy[] = "";
+
+       assert_int_equal(devt2devname(dummy, sizeof(dummy), *state), 1);
+}
+
+static void test_devt2devname_devt_null(void **state)
+{
+       char dummy[32];
+
+       assert_int_equal(devt2devname(dummy, sizeof(dummy), NULL), 1);
+}
+
+static void test_devt2devname_devt_empty(void **state)
+{
+       char dummy[32];
+
+       assert_int_equal(devt2devname(dummy, sizeof(dummy), ""), 1);
+}
+
+static void test_devt2devname_devt_invalid_1(void **state)
+{
+       char dummy[32];
+
+       assert_int_equal(devt2devname(dummy, sizeof(dummy), "foo"), 1);
+}
+
+static void test_devt2devname_devt_invalid_2(void **state)
+{
+       char dummy[32];
+
+       assert_int_equal(devt2devname(dummy, sizeof(dummy), "1234"), 1);
+}
+
+static void test_devt2devname_devt_invalid_3(void **state)
+{
+       char dummy[32];
+
+       assert_int_equal(devt2devname(dummy, sizeof(dummy), "0:0"), 1);
+}
+
+static void test_devt2devname_real(void **state)
+{
+       struct udev_enumerate *enm;
+       int r;
+       struct udev_list_entry *first, *item;
+       unsigned int i = 0;
+
+       enm = udev_enumerate_new(udev);
+       assert_non_null(enm);
+       r = udev_enumerate_add_match_subsystem(enm, "block");
+       assert_in_range(r, 0, INT_MAX);
+       r = udev_enumerate_scan_devices(enm);
+       first = udev_enumerate_get_list_entry(enm);
+       udev_list_entry_foreach(item, first) {
+               const char *path = udev_list_entry_get_name(item);
+               struct udev_device *u_dev;
+               dev_t devnum;
+               char devt[BLK_DEV_SIZE];
+               char devname[FILE_NAME_SIZE];
+
+               u_dev = udev_device_new_from_syspath(udev, path);
+               assert_non_null(u_dev);
+               devnum = udev_device_get_devnum(u_dev);
+               snprintf(devt, sizeof(devt), "%d:%d",
+                        major(devnum), minor(devnum));
+               r = devt2devname(devname, sizeof(devname), devt);
+               assert_int_equal(r, 0);
+               assert_string_equal(devname, udev_device_get_sysname(u_dev));
+               i++;
+               udev_device_unref(u_dev);
+       }
+       udev_enumerate_unref(enm);
+       condlog(2, "devt2devname test passed for %u block devices", i);
+}
+
+static int devt2devname_tests(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_devt2devname_devt_good),
+               cmocka_unit_test(test_devt2devname_devname_null),
+               cmocka_unit_test(test_devt2devname_length_0),
+               cmocka_unit_test(test_devt2devname_length_1),
+               cmocka_unit_test(test_devt2devname_devt_null),
+               cmocka_unit_test(test_devt2devname_devt_empty),
+               cmocka_unit_test(test_devt2devname_devt_invalid_1),
+               cmocka_unit_test(test_devt2devname_devt_invalid_2),
+               cmocka_unit_test(test_devt2devname_devt_invalid_3),
+               cmocka_unit_test(test_devt2devname_real),
+       };
+
+       return cmocka_run_group_tests(tests, setup, teardown);
+}
+
+int main(void)
+{
+       int ret = 0;
+
+       ret += devt2devname_tests();
+       return ret;
+}
index 3cd7a52..9895409 100644 (file)
@@ -31,7 +31,7 @@ int test_fd = 111;
 int ioctx_count = 0;
 struct io_event mock_events[AIO_GROUP_SIZE]; /* same as the checker max */
 int ev_off = 0;
-struct timespec zero_timeout = {0};
+struct timespec zero_timeout = { .tv_sec = 0 };
 struct timespec full_timeout = { .tv_sec = -1 };
 
 int __real_ioctl(int fd, unsigned long request, void *argp);
@@ -287,7 +287,7 @@ static void test_reset(void **state)
 /* tests initializing, then resetting, and then initializing again */
 static void test_init_reset_init(void **state)
 {
-       struct checker c = {0};
+       struct checker c = {.cls = NULL};
        struct aio_group *aio_grp, *tmp_grp;
 
        assert_true(list_empty(&aio_grp_list));
@@ -315,8 +315,8 @@ static void test_init_reset_init(void **state)
 static void test_init_free(void **state)
 {
        int i, count = 0;
-       struct checker c[4096] = {0};
-       struct aio_group *aio_grp;
+       struct checker c[4096] = {{.cls = NULL}};
+       struct aio_group *aio_grp = NULL;
 
        assert_true(list_empty(&aio_grp_list));
        will_return(__wrap_io_setup, 0);
@@ -361,7 +361,7 @@ static void test_init_free(void **state)
 static void test_multi_init_free(void **state)
 {
        int i, count;
-       struct checker c[4096] = {0};
+       struct checker c[4096] = {{.cls = NULL}};
        struct aio_group *aio_grp;
 
        assert_true(list_empty(&aio_grp_list));
@@ -401,7 +401,7 @@ static void test_multi_init_free(void **state)
 /* simple single checker sync test */
 static void test_check_state_simple(void **state)
 {
-       struct checker c = {0};
+       struct checker c = {.cls = NULL};
        struct async_req *req;
        int res = 0;
 
@@ -417,7 +417,7 @@ static void test_check_state_simple(void **state)
 /* test sync timeout */
 static void test_check_state_timeout(void **state)
 {
-       struct checker c = {0};
+       struct checker c = {.cls = NULL};
        struct aio_group *aio_grp;
 
        assert_true(list_empty(&aio_grp_list));
@@ -440,7 +440,7 @@ static void test_check_state_timeout(void **state)
 /* test async timeout */
 static void test_check_state_async_timeout(void **state)
 {
-       struct checker c = {0};
+       struct checker c = {.cls = NULL};
        struct aio_group *aio_grp;
 
        assert_true(list_empty(&aio_grp_list));
@@ -467,7 +467,7 @@ static void test_check_state_async_timeout(void **state)
 /* test freeing checkers with outstanding requests */
 static void test_free_with_pending(void **state)
 {
-        struct checker c[2] = {0};
+        struct checker c[2] = {{.cls = NULL}};
         struct aio_group *aio_grp;
        struct async_req *req;
        int res = 0;
@@ -500,7 +500,7 @@ static void test_free_with_pending(void **state)
 /* test removing orpahed aio_group on free */
 static void test_orphaned_aio_group(void **state)
 {
-       struct checker c[AIO_GROUP_SIZE] = {0};
+       struct checker c[AIO_GROUP_SIZE] = {{.cls = NULL}};
        struct aio_group *aio_grp, *tmp_grp;
        int i;
 
@@ -533,7 +533,7 @@ static void test_orphaned_aio_group(void **state)
  * checker */
 static void test_timeout_cancel_failed(void **state)
 {
-       struct checker c[2] = {0};
+       struct checker c[2] = {{.cls = NULL}};
        struct aio_group *aio_grp;
        struct async_req *reqs[2];
        int res[] = {0,0};
@@ -568,7 +568,7 @@ static void test_timeout_cancel_failed(void **state)
  * checker */
 static void test_async_timeout_cancel_failed(void **state)
 {
-       struct checker c[2] = {0};
+       struct checker c[2] = {{.cls = NULL}};
        struct async_req *reqs[2];
        int res[] = {0,0};
        int i;
@@ -610,7 +610,7 @@ static void test_async_timeout_cancel_failed(void **state)
 /* test orphaning a request, and having another checker clean it up */
 static void test_orphan_checker_cleanup(void **state)
 {
-       struct checker c[2] = {0};
+       struct checker c[2] = {{.cls = NULL}};
        struct async_req *reqs[2];
        int res[] = {0,0};
        struct aio_group *aio_grp;
@@ -667,7 +667,7 @@ static void test_orphan_reset_cleanup(void **state)
 static void test_check_state_blksize(void **state)
 {
        int i;
-       struct checker c[3] = {0};
+       struct checker c[3] = {{.cls = NULL}};
        int blksize[] = {4096, 1024, 512};
        struct async_req *reqs[3];
        int res[] = {0,1,0};
@@ -698,7 +698,7 @@ static void test_check_state_blksize(void **state)
 static void test_check_state_async(void **state)
 {
        int i;
-       struct checker c[257] = {0};
+       struct checker c[257] = {{.cls = NULL}};
        struct async_req *reqs[257];
        int res[257] = {0};
 
index 473028b..12660da 100644 (file)
@@ -25,6 +25,7 @@
 #include "test-lib.h"
 #include "print.h"
 #include "util.h"
+#include "foreign.h"
 
 #define N_CONF_FILES 2
 
@@ -187,6 +188,9 @@ static int teardown(void **state)
 
        free_hwt(*state);
        *state = NULL;
+       cleanup_prio();
+       cleanup_checkers();
+       cleanup_foreign();
 
        return 0;
 }
@@ -468,6 +472,7 @@ static void replicate_config(const struct hwt_state *hwt, bool local)
                /* "local" configuration */
                hwtable = get_used_hwes(hwt->vecs->pathvec);
                cfg1 = snprint_config(conf, NULL, hwtable, hwt->vecs->mpvec);
+               vector_free(hwtable);
        }
 
        assert_non_null(cfg1);
index 29859da..5772391 100644 (file)
@@ -440,6 +440,46 @@ static void test18(void **state)
        free_strvec(v);
 }
 
+static void test19(void **state)
+{
+#define QUOTED19 "!value"
+       vector v = alloc_strvec("key \"" QUOTED19 "\"");
+       char *val;
+
+       assert_int_equal(VECTOR_SIZE(v), 4);
+       assert_string_equal(VECTOR_SLOT(v, 0), "key");
+       assert_true(is_quote(VECTOR_SLOT(v, 1)));
+       assert_string_equal(VECTOR_SLOT(v, 2), QUOTED19);
+       assert_true(is_quote(VECTOR_SLOT(v, 3)));
+       assert_int_equal(validate_config_strvec(v, test_file), 0);
+
+       val = set_value(v);
+       assert_string_equal(val, QUOTED19);
+
+       free(val);
+       free_strvec(v);
+}
+
+static void test20(void **state)
+{
+#define QUOTED20 "#value"
+       vector v = alloc_strvec("key \"" QUOTED20 "\"");
+       char *val;
+
+       assert_int_equal(VECTOR_SIZE(v), 4);
+       assert_string_equal(VECTOR_SLOT(v, 0), "key");
+       assert_true(is_quote(VECTOR_SLOT(v, 1)));
+       assert_string_equal(VECTOR_SLOT(v, 2), QUOTED20);
+       assert_true(is_quote(VECTOR_SLOT(v, 3)));
+       assert_int_equal(validate_config_strvec(v, test_file), 0);
+
+       val = set_value(v);
+       assert_string_equal(val, QUOTED20);
+
+       free(val);
+       free_strvec(v);
+}
+
 int test_config_parser(void)
 {
        const struct CMUnitTest tests[] = {
@@ -461,6 +501,8 @@ int test_config_parser(void)
                cmocka_unit_test(test16),
                cmocka_unit_test(test17),
                cmocka_unit_test(test18),
+               cmocka_unit_test(test19),
+               cmocka_unit_test(test20),
        };
        return cmocka_run_group_tests(tests, setup, teardown);
 }
index 5927516..b7c09cc 100644 (file)
@@ -15,7 +15,7 @@
 #include "test-lib.h"
 
 const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO);
-const char default_devnode[] = "sdTEST";
+const char default_devnode[] = "sdxTEST";
 const char default_wwid[] = "TEST-WWID";
 /* default_wwid should be a substring of default_wwid_1! */
 const char default_wwid_1[] = "TEST-WWID-1";
@@ -56,12 +56,6 @@ int __wrap_execute_program(char *path, char *value, int len)
        return 0;
 }
 
-bool __wrap_is_claimed_by_foreign(struct udev_device *ud)
-{
-       condlog(5, "%s: %p", __func__, ud);
-       return false;
-}
-
 struct udev_list_entry
 *__wrap_udev_device_get_properties_list_entry(struct udev_device *ud)
 {
index d685d58..1c901cb 100644 (file)
@@ -2,6 +2,7 @@
 #include <stddef.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <string.h>
 #include <cmocka.h>
 #include "log.h"
 #include "test-log.h"
@@ -11,17 +12,18 @@ void __wrap_dlog (int sink, int prio, const char * fmt, ...)
 {
        char buff[MAX_MSG_SIZE];
        va_list ap;
+       char *expected;
 
-       assert_int_equal(prio, mock_type(int));
+       check_expected(prio);
        va_start(ap, fmt);
        vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
        va_end(ap);
-       assert_string_equal(buff, mock_ptr_type(char *));
+       expected = mock_ptr_type(char *);
+       assert_memory_equal(buff, expected, strlen(expected));
 }
 
 void expect_condlog(int prio, char *string)
 {
-       will_return(__wrap_dlog, prio);
+       expect_value(__wrap_dlog, prio, prio);
        will_return(__wrap_dlog, string);
 }
-
index f4afd9b..9ffcd2d 100644 (file)
 
 #include "globals.c"
 
-/* Private prototypes missing in uevent.h */
-struct uevent * alloc_uevent(void);
-void uevent_get_wwid(struct uevent *uev);
-
 /* Stringify helpers */
 #define _str_(x) #x
 #define str(x) _str_(x)
index 7c486fc..c3c49b6 100644 (file)
@@ -22,6 +22,7 @@
 #include <setjmp.h>
 #include <stdlib.h>
 #include <cmocka.h>
+#include <endian.h>
 #include "util.h"
 
 #include "globals.c"
@@ -141,61 +142,109 @@ static void test_basenamecpy_bad5(void **state)
         assert_int_equal(basenamecpy("baz/qux", NULL, sizeof(dst)), 0);
 }
 
+static int test_basenamecpy(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_basenamecpy_good0),
+               cmocka_unit_test(test_basenamecpy_good1),
+               cmocka_unit_test(test_basenamecpy_good2),
+               cmocka_unit_test(test_basenamecpy_good3),
+               cmocka_unit_test(test_basenamecpy_good4),
+               cmocka_unit_test(test_basenamecpy_good5),
+               cmocka_unit_test(test_basenamecpy_good6),
+               cmocka_unit_test(test_basenamecpy_good7),
+               cmocka_unit_test(test_basenamecpy_bad0),
+               cmocka_unit_test(test_basenamecpy_bad1),
+               cmocka_unit_test(test_basenamecpy_bad2),
+               cmocka_unit_test(test_basenamecpy_bad3),
+               cmocka_unit_test(test_basenamecpy_bad4),
+               cmocka_unit_test(test_basenamecpy_bad5),
+       };
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+/*
+ * On big endian systems, if bitfield_t is 32bit, we need
+ * to swap the two 32 bit parts of a 64bit value to make
+ * the tests below work.
+ */
+static uint64_t maybe_swap(uint64_t v)
+{
+       uint32_t *s = (uint32_t *)&v;
+
+       if (sizeof(bitfield_t) == 4)
+               /* this is identity for little endian */
+               return ((uint64_t)s[1] << 32) | s[0];
+       else
+               return v;
+}
+
 static void test_bitmask_1(void **state)
 {
-       uint64_t arr[BITARR_SZ];
+       struct bitfield *bf;
+       uint64_t *arr;
        int i, j, k, m, b;
 
-       memset(arr, 0, sizeof(arr));
+       bf = alloc_bitfield(BITARR_SZ * 64);
+       assert_non_null(bf);
+       assert_int_equal(bf->len, BITARR_SZ * 64);
+       arr = (uint64_t *)bf->bits;
 
        for (j = 0; j < BITARR_SZ; j++) {
                for (i = 0; i < 64; i++) {
                        b = 64 * j + i;
-                       assert(!is_bit_set_in_array(b, arr));
-                       set_bit_in_array(b, arr);
+                       assert(!is_bit_set_in_bitfield(b, bf));
+                       set_bit_in_bitfield(b, bf);
                        for (k = 0; k < BITARR_SZ; k++) {
+#if 0
                                printf("b = %d j = %d k = %d a = %"PRIx64"\n",
                                       b, j, k, arr[k]);
+#endif
                                if (k == j)
-                                       assert_int_equal(arr[j], 1ULL << i);
+                                       assert_int_equal(maybe_swap(arr[j]), 1ULL << i);
                                else
                                        assert_int_equal(arr[k], 0ULL);
                        }
                        for (m = 0; m < 64; m++)
                                if (i == m)
-                                       assert(is_bit_set_in_array(64 * j + m,
-                                                                  arr));
+                                       assert(is_bit_set_in_bitfield(64 * j + m,
+                                                                     bf));
                                else
-                                       assert(!is_bit_set_in_array(64 * j + m,
-                                                                   arr));
-                       clear_bit_in_array(b, arr);
-                       assert(!is_bit_set_in_array(b, arr));
+                                       assert(!is_bit_set_in_bitfield(64 * j + m,
+                                                                      bf));
+                       clear_bit_in_bitfield(b, bf);
+                       assert(!is_bit_set_in_bitfield(b, bf));
                        for (k = 0; k < BITARR_SZ; k++)
                                assert_int_equal(arr[k], 0ULL);
                }
        }
+       free(bf);
 }
 
 static void test_bitmask_2(void **state)
 {
-       uint64_t arr[BITARR_SZ];
+       struct bitfield *bf;
+       uint64_t *arr;
        int i, j, k, m, b;
 
-       memset(arr, 0, sizeof(arr));
+       bf = alloc_bitfield(BITARR_SZ * 64);
+       assert_non_null(bf);
+       assert_int_equal(bf->len, BITARR_SZ * 64);
+       arr = (uint64_t *)bf->bits;
 
        for (j = 0; j < BITARR_SZ; j++) {
                for (i = 0; i < 64; i++) {
                        b = 64 * j + i;
-                       assert(!is_bit_set_in_array(b, arr));
-                       set_bit_in_array(b, arr);
+                       assert(!is_bit_set_in_bitfield(b, bf));
+                       set_bit_in_bitfield(b, bf);
                        for (m = 0; m < 64; m++)
                                if (m <= i)
-                                       assert(is_bit_set_in_array(64 * j + m,
-                                                                  arr));
+                                       assert(is_bit_set_in_bitfield(64 * j + m,
+                                                                     bf));
                                else
-                                       assert(!is_bit_set_in_array(64 * j + m,
-                                                                   arr));
-                       assert(is_bit_set_in_array(b, arr));
+                                       assert(!is_bit_set_in_bitfield(64 * j + m,
+                                                                      bf));
+                       assert(is_bit_set_in_bitfield(b, bf));
                        for (k = 0; k < BITARR_SZ; k++) {
                                if (k < j || (k == j && i == 63))
                                        assert_int_equal(arr[k], ~0ULL);
@@ -203,7 +252,7 @@ static void test_bitmask_2(void **state)
                                        assert_int_equal(arr[k], 0ULL);
                                else
                                        assert_int_equal(
-                                               arr[k],
+                                               maybe_swap(arr[k]),
                                                (1ULL << (i + 1)) - 1);
                        }
                }
@@ -211,16 +260,16 @@ static void test_bitmask_2(void **state)
        for (j = 0; j < BITARR_SZ; j++) {
                for (i = 0; i < 64; i++) {
                        b = 64 * j + i;
-                       assert(is_bit_set_in_array(b, arr));
-                       clear_bit_in_array(b, arr);
+                       assert(is_bit_set_in_bitfield(b, bf));
+                       clear_bit_in_bitfield(b, bf);
                        for (m = 0; m < 64; m++)
                                if (m <= i)
-                                       assert(!is_bit_set_in_array(64 * j + m,
-                                                                   arr));
+                                       assert(!is_bit_set_in_bitfield(64 * j + m,
+                                                                      bf));
                                else
-                                       assert(is_bit_set_in_array(64 * j + m,
-                                                                  arr));
-                       assert(!is_bit_set_in_array(b, arr));
+                                       assert(is_bit_set_in_bitfield(64 * j + m,
+                                                                     bf));
+                       assert(!is_bit_set_in_bitfield(b, bf));
                        for (k = 0; k < BITARR_SZ; k++) {
                                if (k < j || (k == j && i == 63))
                                        assert_int_equal(arr[k], 0ULL);
@@ -228,78 +277,315 @@ static void test_bitmask_2(void **state)
                                        assert_int_equal(arr[k], ~0ULL);
                                else
                                        assert_int_equal(
-                                               arr[k],
+                                               maybe_swap(arr[k]),
                                                ~((1ULL << (i + 1)) - 1));
                        }
                }
        }
+       free(bf);
+}
+
+/*
+ *  Test operations on a 0-length bitfield
+ */
+static void test_bitmask_len_0(void **state)
+{
+       struct bitfield *bf;
+
+       bf = alloc_bitfield(0);
+       assert_null(bf);
+}
+
+/*
+ * We use uint32_t in the "small bitmask" tests below.
+ * This means that we may have to swap 32bit words if bitfield_t
+ * is 64bit wide.
+ */
+static unsigned int maybe_swap_idx(unsigned int i)
+{
+       if (BYTE_ORDER == LITTLE_ENDIAN || sizeof(bitfield_t) == 4)
+               return i;
+       else
+               /* 0<->1, 2<->3, ... */
+               return i + (i % 2 == 0 ? 1 : -1);
+}
+
+static void _test_bitmask_small(unsigned int n)
+{
+       struct bitfield *bf;
+       uint32_t *arr;
+       unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
+
+       assert(sizeof(bitfield_t) == 4 || sizeof(bitfield_t) == 8);
+       assert(n <= 64);
+       assert(n >= 1);
+
+       bf = alloc_bitfield(n);
+       assert_non_null(bf);
+       assert_int_equal(bf->len, n);
+       arr = (uint32_t *)bf->bits;
+
+       for (i = 0; i < size; i++)
+               assert_int_equal(arr[i], 0);
+
+       set_bit_in_bitfield(n + 1, bf);
+       for (i = 0; i < size; i++)
+               assert_int_equal(arr[i], 0);
+
+       set_bit_in_bitfield(n, bf);
+       for (i = 0; i < size; i++)
+               assert_int_equal(arr[i], 0);
+
+       set_bit_in_bitfield(n - 1, bf);
+       for (i = 0; i < size; i++) {
+               unsigned int k = (n - 1) / 32;
+               unsigned int j = (n - 1) - k * 32;
+               unsigned int i1 = maybe_swap_idx(i);
+
+               if (i == k)
+                       assert_int_equal(arr[i1], 1UL << j);
+               else
+                       assert_int_equal(arr[i1], 0);
+       }
+
+       clear_bit_in_bitfield(n - 1, bf);
+       for (i = 0; i < size; i++)
+               assert_int_equal(arr[i], 0);
+
+       set_bit_in_bitfield(0, bf);
+       assert_int_equal(arr[maybe_swap_idx(0)], 1);
+       for (i = 1; i < size; i++)
+               assert_int_equal(arr[maybe_swap_idx(i)], 0);
+
+       free(bf);
 }
 
-int test_basenamecpy(void)
+static void _test_bitmask_small_2(unsigned int n)
+{
+       struct bitfield *bf;
+       uint32_t *arr;
+       unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
+
+       assert(n <= 128);
+       assert(n >= 65);
+
+       bf = alloc_bitfield(n);
+       assert_non_null(bf);
+       assert_int_equal(bf->len, n);
+       arr = (uint32_t *)bf->bits;
+
+       for (i = 0; i < size; i++)
+               assert_int_equal(arr[i], 0);
+
+       set_bit_in_bitfield(n + 1, bf);
+       for (i = 0; i < size; i++)
+               assert_int_equal(arr[i], 0);
+
+       set_bit_in_bitfield(n, bf);
+       for (i = 0; i < size; i++)
+               assert_int_equal(arr[i], 0);
+
+       set_bit_in_bitfield(n - 1, bf);
+       assert_int_equal(arr[0], 0);
+       for (i = 0; i < size; i++) {
+               unsigned int k = (n - 1) / 32;
+               unsigned int j = (n - 1) - k * 32;
+               unsigned int i1 = maybe_swap_idx(i);
+
+               if (i == k)
+                       assert_int_equal(arr[i1], 1UL << j);
+               else
+                       assert_int_equal(arr[i1], 0);
+       }
+
+       set_bit_in_bitfield(0, bf);
+       for (i = 0; i < size; i++) {
+               unsigned int k = (n - 1) / 32;
+               unsigned int j = (n - 1) - k * 32;
+               unsigned int i1 = maybe_swap_idx(i);
+
+               if (i == k && k == 0)
+                       assert_int_equal(arr[i1], (1UL << j) | 1);
+               else if (i == k)
+                       assert_int_equal(arr[i1], 1UL << j);
+               else if (i == 0)
+                       assert_int_equal(arr[i1], 1);
+               else
+                       assert_int_equal(arr[i1], 0);
+       }
+
+       set_bit_in_bitfield(64, bf);
+       for (i = 0; i < size; i++) {
+               unsigned int k = (n - 1) / 32;
+               unsigned int j = (n - 1) - k * 32;
+               unsigned int i1 = maybe_swap_idx(i);
+
+               if (i == k && (k == 0 || k == 2))
+                       assert_int_equal(arr[i1], (1UL << j) | 1);
+               else if (i == k)
+                       assert_int_equal(arr[i1], 1UL << j);
+               else if (i == 2 || i == 0)
+                       assert_int_equal(arr[i1], 1);
+               else
+                       assert_int_equal(arr[i1], 0);
+       }
+
+       clear_bit_in_bitfield(0, bf);
+       for (i = 0; i < size; i++) {
+               unsigned int k = (n - 1) / 32;
+               unsigned int j = (n - 1) - k * 32;
+               unsigned int i1 = maybe_swap_idx(i);
+
+               if (i == k && k == 2)
+                       assert_int_equal(arr[i1], (1UL << j) | 1);
+               else if (i == k)
+                       assert_int_equal(arr[i1], 1UL << j);
+               else if (i == 2)
+                       assert_int_equal(arr[i1], 1);
+               else
+                       assert_int_equal(arr[i1], 0);
+       }
+
+       free(bf);
+}
+
+static void test_bitmask_len_1(void **state)
+{
+       _test_bitmask_small(1);
+}
+
+static void test_bitmask_len_2(void **state)
+{
+       _test_bitmask_small(2);
+}
+
+static void test_bitmask_len_3(void **state)
+{
+       _test_bitmask_small(3);
+}
+
+static void test_bitmask_len_23(void **state)
+{
+       _test_bitmask_small(23);
+}
+
+static void test_bitmask_len_63(void **state)
+{
+       _test_bitmask_small(63);
+}
+
+static void test_bitmask_len_64(void **state)
+{
+       _test_bitmask_small(63);
+}
+
+static void test_bitmask_len_65(void **state)
+{
+       _test_bitmask_small_2(65);
+}
+
+static void test_bitmask_len_66(void **state)
+{
+       _test_bitmask_small_2(66);
+}
+
+static void test_bitmask_len_67(void **state)
+{
+       _test_bitmask_small_2(67);
+}
+
+static void test_bitmask_len_103(void **state)
+{
+       _test_bitmask_small_2(103);
+}
+
+static void test_bitmask_len_126(void **state)
+{
+       _test_bitmask_small_2(126);
+}
+
+static void test_bitmask_len_127(void **state)
+{
+       _test_bitmask_small_2(127);
+}
+
+static void test_bitmask_len_128(void **state)
+{
+       _test_bitmask_small_2(128);
+}
+
+
+static int test_bitmasks(void)
 {
        const struct CMUnitTest tests[] = {
-               cmocka_unit_test(test_basenamecpy_good0),
-               cmocka_unit_test(test_basenamecpy_good1),
-               cmocka_unit_test(test_basenamecpy_good2),
-               cmocka_unit_test(test_basenamecpy_good3),
-               cmocka_unit_test(test_basenamecpy_good4),
-               cmocka_unit_test(test_basenamecpy_good5),
-               cmocka_unit_test(test_basenamecpy_good6),
-               cmocka_unit_test(test_basenamecpy_good7),
-               cmocka_unit_test(test_basenamecpy_bad0),
-               cmocka_unit_test(test_basenamecpy_bad1),
-               cmocka_unit_test(test_basenamecpy_bad2),
-               cmocka_unit_test(test_basenamecpy_bad3),
-               cmocka_unit_test(test_basenamecpy_bad4),
-               cmocka_unit_test(test_basenamecpy_bad5),
                cmocka_unit_test(test_bitmask_1),
                cmocka_unit_test(test_bitmask_2),
+               cmocka_unit_test(test_bitmask_len_0),
+               cmocka_unit_test(test_bitmask_len_1),
+               cmocka_unit_test(test_bitmask_len_2),
+               cmocka_unit_test(test_bitmask_len_3),
+               cmocka_unit_test(test_bitmask_len_23),
+               cmocka_unit_test(test_bitmask_len_63),
+               cmocka_unit_test(test_bitmask_len_64),
+               cmocka_unit_test(test_bitmask_len_65),
+               cmocka_unit_test(test_bitmask_len_66),
+               cmocka_unit_test(test_bitmask_len_67),
+               cmocka_unit_test(test_bitmask_len_103),
+               cmocka_unit_test(test_bitmask_len_126),
+               cmocka_unit_test(test_bitmask_len_127),
+               cmocka_unit_test(test_bitmask_len_128),
        };
        return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
-static const char src_str[] = "Hello";
+#define DST_STR "Hello"
+static const char dst_str[] = DST_STR;
+/* length of src_str and dst_str should be different */
+static const char src_str[] = " World";
+/* Must be big enough to hold dst_str and src_str */
+#define ARRSZ 16
+#define FILL '@'
 
 /* strlcpy with length 0 */
 static void test_strlcpy_0(void **state)
 {
-       char tst[] = "word";
+       char tst[] = DST_STR;
        int rc;
 
        rc = strlcpy(tst, src_str, 0);
        assert_int_equal(rc, strlen(src_str));
-       assert_string_equal(tst, "word");
+       assert_string_equal(tst, dst_str);
 }
 
 /* strlcpy with length 1 */
 static void test_strlcpy_1(void **state)
 {
-       char tst[] = "word";
+       char tst[] = DST_STR;
        int rc;
 
        rc = strlcpy(tst, src_str, 1);
        assert_int_equal(rc, strlen(src_str));
        assert_int_equal(tst[0], '\0');
-       assert_string_equal(tst + 1, "ord");
+       assert_string_equal(tst + 1, dst_str + 1);
 }
 
 /* strlcpy with length 2 */
 static void test_strlcpy_2(void **state)
 {
-       char tst[] = "word";
+       char tst[] = DST_STR;
        int rc;
 
        rc = strlcpy(tst, src_str, 2);
        assert_int_equal(rc, strlen(src_str));
        assert_int_equal(tst[0], src_str[0]);
        assert_int_equal(tst[1], '\0');
-       assert_string_equal(tst + 2, "rd");
+       assert_string_equal(tst + 2, dst_str + 2);
 }
 
 /* strlcpy with dst length < src length */
 static void test_strlcpy_3(void **state)
 {
-       char tst[] = "word";
+       char tst[] = DST_STR;
        int rc;
 
        rc = strlcpy(tst, src_str, sizeof(tst));
@@ -331,6 +617,7 @@ static void test_strlcpy_5(void **state)
        const int sz = sizeof(src_str);
 
        tst = malloc(sz);
+       assert_non_null(tst);
        memset(tst, 'f', sizeof(src_str));
 
        rc = strlcpy(tst, src_str, sz);
@@ -348,6 +635,7 @@ static void test_strlcpy_6(void **state)
        const int sz = sizeof(src_str);
 
        tst = malloc(sz + 2);
+       assert_non_null(tst);
        memset(tst, 'f', sz + 2);
 
        rc = strlcpy(tst, src_str, sz + 2);
@@ -362,26 +650,26 @@ static void test_strlcpy_6(void **state)
 /* strlcpy with empty src */
 static void test_strlcpy_7(void **state)
 {
-       char tst[] = "word";
+       char tst[] = DST_STR;
        static const char empty[] = "";
        int rc;
 
        rc = strlcpy(tst, empty, sizeof(tst));
        assert_int_equal(rc, strlen(empty));
        assert_string_equal(empty, tst);
-       assert_string_equal(tst + 1, "ord");
+       assert_string_equal(tst + 1, dst_str + 1);
 }
 
 /* strlcpy with empty src, length 0 */
 static void test_strlcpy_8(void **state)
 {
-       char tst[] = "word";
+       char tst[] = DST_STR;
        static const char empty[] = "";
        int rc;
 
        rc = strlcpy(tst, empty, 0);
        assert_int_equal(rc, strlen(empty));
-       assert_string_equal("word", tst);
+       assert_string_equal(dst_str, tst);
 }
 
 static int test_strlcpy(void)
@@ -401,11 +689,267 @@ static int test_strlcpy(void)
        return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
+
+/* 0-terminated string, filled with non-0 after the terminator */
+static void prep_buf(char *buf, size_t size, const char *word)
+{
+       memset(buf, FILL, size);
+       assert_in_range(strlen(word), 0, size - 1);
+       memcpy(buf, word, strlen(word) + 1);
+}
+
+/* strlcat with size 0, dst not 0-terminated  */
+static void test_strlcat_0(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), dst_str);
+       rc = strlcat(tst, src_str, 0);
+       assert_int_equal(rc, strlen(src_str));
+       assert_string_equal(tst, dst_str);
+       assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with length 1, dst not 0-terminated */
+static void test_strlcat_1(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), dst_str);
+       rc = strlcat(tst, src_str, 1);
+       assert_int_equal(rc, 1 + strlen(src_str));
+       assert_string_equal(tst, dst_str);
+       assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with length = dst - 1 */
+static void test_strlcat_2(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), dst_str);
+       rc = strlcat(tst, src_str, strlen(dst_str));
+       assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+       assert_string_equal(tst, dst_str);
+       assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with length = dst */
+static void test_strlcat_3(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), dst_str);
+       rc = strlcat(tst, src_str, strlen(dst_str) + 1);
+       assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+       assert_string_equal(tst, dst_str);
+       assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with len = dst + 1 */
+static void test_strlcat_4(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), dst_str);
+       rc = strlcat(tst, src_str, strlen(dst_str) + 2);
+       assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+       assert_false(strncmp(tst, dst_str, strlen(dst_str)));
+       assert_int_equal(tst[strlen(dst_str)], src_str[0]);
+       assert_int_equal(tst[strlen(dst_str) + 1], '\0');
+       assert_int_equal(tst[strlen(dst_str) + 2], FILL);
+}
+
+/* strlcat with len = needed - 1 */
+static void test_strlcat_5(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), dst_str);
+       rc = strlcat(tst, src_str, strlen(dst_str) + strlen(src_str));
+       assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+       assert_false(strncmp(tst, dst_str, strlen(dst_str)));
+       assert_false(strncmp(tst + strlen(dst_str), src_str,
+                            strlen(src_str) - 1));
+       assert_int_equal(tst[strlen(dst_str) + strlen(src_str) - 1], '\0');
+       assert_int_equal(tst[strlen(dst_str) + strlen(src_str)], FILL);
+}
+
+/* strlcat with exactly sufficient space */
+static void test_strlcat_6(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), dst_str);
+       rc = strlcat(tst, src_str, strlen(dst_str) + strlen(src_str) + 1);
+       assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+       assert_false(strncmp(tst, dst_str, strlen(dst_str)));
+       assert_string_equal(tst + strlen(dst_str), src_str);
+       assert_int_equal(tst[strlen(dst_str) + strlen(src_str) + 1], FILL);
+}
+
+/* strlcat with sufficient space */
+static void test_strlcat_7(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), dst_str);
+       rc = strlcat(tst, src_str, sizeof(tst));
+       assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+       assert_false(strncmp(tst, dst_str, strlen(dst_str)));
+       assert_string_equal(tst + strlen(dst_str), src_str);
+}
+
+/* strlcat with 0-length string */
+static void test_strlcat_8(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), dst_str);
+       rc = strlcat(tst, "", sizeof(tst));
+       assert_int_equal(rc, strlen(dst_str));
+       assert_string_equal(tst, dst_str);
+       assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with empty dst */
+static void test_strlcat_9(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), "");
+       rc = strlcat(tst, src_str, ARRSZ);
+       assert_int_equal(rc, strlen(src_str));
+       assert_string_equal(tst, src_str);
+       assert_int_equal(tst[sizeof(src_str)], FILL);
+}
+
+/* strlcat with empty dst and src */
+static void test_strlcat_10(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), "");
+       rc = strlcat(tst, "", ARRSZ);
+       assert_int_equal(rc, 0);
+       assert_string_equal(tst, "");
+       assert_int_equal(tst[1], FILL);
+}
+
+/* strlcat with no space to store 0 */
+static void test_strlcat_11(void **state)
+{
+       char tst[ARRSZ];
+       int rc;
+
+       prep_buf(tst, sizeof(tst), "");
+       tst[0] = FILL;
+       rc = strlcat(tst, src_str, 0);
+       assert_int_equal(rc, strlen(src_str));
+       assert_int_equal(tst[0], FILL);
+}
+
+static int test_strlcat(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_strlcat_0),
+               cmocka_unit_test(test_strlcat_1),
+               cmocka_unit_test(test_strlcat_2),
+               cmocka_unit_test(test_strlcat_3),
+               cmocka_unit_test(test_strlcat_4),
+               cmocka_unit_test(test_strlcat_5),
+               cmocka_unit_test(test_strlcat_6),
+               cmocka_unit_test(test_strlcat_7),
+               cmocka_unit_test(test_strlcat_8),
+               cmocka_unit_test(test_strlcat_9),
+               cmocka_unit_test(test_strlcat_10),
+               cmocka_unit_test(test_strlcat_11),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+static void test_strchop_nochop(void **state)
+{
+       char hello[] = "hello";
+
+       assert_int_equal(strchop(hello), 5);
+       assert_string_equal(hello, "hello");
+}
+
+static void test_strchop_newline(void **state)
+{
+       char hello[] = "hello\n";
+
+       assert_int_equal(strchop(hello), 5);
+       assert_string_equal(hello, "hello");
+}
+
+static void test_strchop_space(void **state)
+{
+       char hello[] = " ello      ";
+
+       assert_int_equal(strchop(hello), 5);
+       assert_string_equal(hello, " ello");
+}
+
+static void test_strchop_mix(void **state)
+{
+       char hello[] = " el\no \t  \n\n \t    \n";
+
+       assert_int_equal(strchop(hello), 5);
+       assert_string_equal(hello, " el\no");
+}
+
+static void test_strchop_blank(void **state)
+{
+       char hello[] = "  \t  \n\n \t    \n";
+
+       assert_int_equal(strchop(hello), 0);
+       assert_string_equal(hello, "");
+}
+
+static void test_strchop_empty(void **state)
+{
+       char hello[] = "";
+
+       assert_int_equal(strchop(hello), 0);
+       assert_string_equal(hello, "");
+}
+
+static int test_strchop(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_strchop_nochop),
+               cmocka_unit_test(test_strchop_newline),
+               cmocka_unit_test(test_strchop_space),
+               cmocka_unit_test(test_strchop_mix),
+               cmocka_unit_test(test_strchop_blank),
+               cmocka_unit_test(test_strchop_empty),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
 int main(void)
 {
        int ret = 0;
 
        ret += test_basenamecpy();
+       ret += test_bitmasks();
        ret += test_strlcpy();
+       ret += test_strlcat();
+       ret += test_strchop();
        return ret;
 }
diff --git a/tests/valid.c b/tests/valid.c
new file mode 100644 (file)
index 0000000..693c72c
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2020 Benjamin Marzinski, Redhat
+ *
+ * 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 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 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <cmocka.h>
+#include "globals.c"
+#include "util.h"
+#include "discovery.h"
+#include "wwids.h"
+#include "blacklist.h"
+#include "valid.h"
+
+int test_fd;
+struct udev_device {
+       int unused;
+} test_udev;
+
+bool __wrap_sysfs_is_multipathed(struct path *pp, bool set_wwid)
+{
+       bool is_multipathed = mock_type(bool);
+       assert_non_null(pp);
+       assert_int_not_equal(strlen(pp->dev), 0);
+       if (is_multipathed && set_wwid)
+               strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+       return is_multipathed;
+}
+
+int __wrap___mpath_connect(int nonblocking)
+{
+       bool connected = mock_type(bool);
+       assert_int_equal(nonblocking, 1);
+       if (connected)
+               return test_fd;
+       errno = mock_type(int);
+       return -1;
+}
+
+int __wrap_systemd_service_enabled(const char *dev)
+{
+       return (int)mock_type(bool);
+}
+
+/* There's no point in checking the return value here */
+int __wrap_mpath_disconnect(int fd)
+{
+       assert_int_equal(fd, test_fd);
+       return 0;
+}
+
+struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname)
+{
+       bool passed = mock_type(bool);
+       assert_string_equal(sysname, mock_ptr_type(char *));
+       if (passed)
+               return &test_udev;
+       return NULL;
+}
+
+int __wrap_pathinfo(struct path *pp, struct config *conf, int mask)
+{
+       int ret = mock_type(int);
+       assert_string_equal(pp->dev, mock_ptr_type(char *));
+       assert_int_equal(mask, DI_SYSFS | DI_WWID | DI_BLACKLIST);
+       if (ret == PATHINFO_OK) {
+               pp->uid_attribute = "ID_TEST";
+               strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+       } else
+               memset(pp->wwid, 0, WWID_SIZE);
+       return ret;
+}
+
+int __wrap_filter_property(struct config *conf, struct udev_device *udev,
+                          int lvl, const char *uid_attribute)
+{
+       int ret = mock_type(int);
+       assert_string_equal(uid_attribute, "ID_TEST");
+       return ret;
+}
+
+int __wrap_is_failed_wwid(const char *wwid)
+{
+       int ret = mock_type(int);
+       assert_string_equal(wwid, mock_ptr_type(char *));
+       return ret;
+}
+
+int __wrap_check_wwids_file(char *wwid, int write_wwid)
+{
+       bool passed = mock_type(bool);
+       assert_int_equal(write_wwid, 0);
+       assert_string_equal(wwid, mock_ptr_type(char *));
+       if (passed)
+               return 0;
+       else
+               return -1;
+}
+
+int __wrap_dm_map_present_by_uuid(const char *uuid)
+{
+       int ret = mock_type(int);
+       assert_string_equal(uuid, mock_ptr_type(char *));
+       return ret;
+}
+
+enum {
+       STAGE_IS_MULTIPATHED,
+       STAGE_CHECK_MULTIPATHD,
+       STAGE_GET_UDEV_DEVICE,
+       STAGE_PATHINFO,
+       STAGE_FILTER_PROPERTY,
+       STAGE_IS_FAILED,
+       STAGE_CHECK_WWIDS,
+       STAGE_UUID_PRESENT,
+};
+
+enum {
+       CHECK_MPATHD_RUNNING,
+       CHECK_MPATHD_EAGAIN,
+       CHECK_MPATHD_ENABLED,
+       CHECK_MPATHD_SKIP,
+};
+
+/* setup the test to continue past the given stage in is_path_valid() */
+static void setup_passing(char *name, char *wwid, unsigned int check_multipathd,
+                         unsigned int stage)
+{
+       will_return(__wrap_sysfs_is_multipathed, false);
+       if (stage == STAGE_IS_MULTIPATHED)
+               return;
+       if (check_multipathd == CHECK_MPATHD_RUNNING)
+               will_return(__wrap___mpath_connect, true);
+       else if (check_multipathd == CHECK_MPATHD_EAGAIN) {
+               will_return(__wrap___mpath_connect, false);
+               will_return(__wrap___mpath_connect, EAGAIN);
+       } else if (check_multipathd == CHECK_MPATHD_ENABLED) {
+               will_return(__wrap___mpath_connect, false);
+               will_return(__wrap___mpath_connect, ECONNREFUSED);
+               will_return(__wrap_systemd_service_enabled, true);
+       }
+       /* nothing for CHECK_MPATHD_SKIP */
+       if (stage == STAGE_CHECK_MULTIPATHD)
+               return;
+       will_return(__wrap_udev_device_new_from_subsystem_sysname, true);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname,
+                   name);
+       if (stage == STAGE_GET_UDEV_DEVICE)
+               return;
+       will_return(__wrap_pathinfo, PATHINFO_OK);
+       will_return(__wrap_pathinfo, name);
+       will_return(__wrap_pathinfo, wwid);
+       if (stage == STAGE_PATHINFO)
+               return;
+       will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_EXCEPT);
+       if (stage == STAGE_FILTER_PROPERTY)
+               return;
+       will_return(__wrap_is_failed_wwid, WWID_IS_NOT_FAILED);
+       will_return(__wrap_is_failed_wwid, wwid);
+       if (stage == STAGE_IS_FAILED)
+               return;
+       will_return(__wrap_check_wwids_file, false);
+       will_return(__wrap_check_wwids_file, wwid);
+       if (stage == STAGE_CHECK_WWIDS)
+               return;
+       will_return(__wrap_dm_map_present_by_uuid, 0);
+       will_return(__wrap_dm_map_present_by_uuid, wwid);
+}
+
+static void test_bad_arguments(void **state)
+{
+       struct path pp;
+       char too_long[FILE_NAME_SIZE + 1];
+
+       memset(&pp, 0, sizeof(pp));
+       /* test NULL pointers */
+       assert_int_equal(is_path_valid("test", &conf, NULL, true),
+                        PATH_IS_ERROR);
+       assert_int_equal(is_path_valid("test", NULL, &pp, true),
+                        PATH_IS_ERROR);
+       assert_int_equal(is_path_valid(NULL, &conf, &pp, true),
+                        PATH_IS_ERROR);
+       /* test undefined find_multipaths */
+       conf.find_multipaths = FIND_MULTIPATHS_UNDEF;
+       assert_int_equal(is_path_valid("test", &conf, &pp, true),
+                        PATH_IS_ERROR);
+       /* test name too long */
+       memset(too_long, 'x', sizeof(too_long));
+       too_long[sizeof(too_long) - 1] = '\0';
+       conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+       assert_int_equal(is_path_valid(too_long, &conf, &pp, true),
+                        PATH_IS_ERROR);
+}
+
+static void test_sysfs_is_multipathed(void **state)
+{
+       struct path pp;
+       char *name = "test";
+       char *wwid = "test_wwid";
+
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+       /* test for already existing multiapthed device */
+       will_return(__wrap_sysfs_is_multipathed, true);
+       will_return(__wrap_sysfs_is_multipathed, wwid);
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_VALID_NO_CHECK);
+       assert_string_equal(pp.dev, name);
+       assert_string_equal(pp.wwid, wwid);
+       /* test for wwid device with empty wwid */
+       will_return(__wrap_sysfs_is_multipathed, true);
+       will_return(__wrap_sysfs_is_multipathed, "");
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_ERROR);
+}
+
+static void test_check_multipathd(void **state)
+{
+       struct path pp;
+       char *name = "test";
+
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+       /* test failed check to see if multipathd is active */
+       will_return(__wrap_sysfs_is_multipathed, false);
+       will_return(__wrap___mpath_connect, false);
+       will_return(__wrap___mpath_connect, ECONNREFUSED);
+       will_return(__wrap_systemd_service_enabled, false);
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_NOT_VALID);
+       assert_string_equal(pp.dev, name);
+       /* test pass because service is enabled. fail getting udev */
+       memset(&pp, 0, sizeof(pp));
+       setup_passing(name, NULL, CHECK_MPATHD_ENABLED, STAGE_CHECK_MULTIPATHD);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname,
+                   name);
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_ERROR);
+       assert_string_equal(pp.dev, name);
+       /* test pass because connect returned EAGAIN. fail getting udev */
+       setup_passing(name, NULL, CHECK_MPATHD_EAGAIN, STAGE_CHECK_MULTIPATHD);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname,
+                   name);
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_ERROR);
+       /* test pass because connect succeeded. fail getting udev */
+       memset(&pp, 0, sizeof(pp));
+       setup_passing(name, NULL, CHECK_MPATHD_RUNNING, STAGE_CHECK_MULTIPATHD);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname,
+                   name);
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_ERROR);
+       assert_string_equal(pp.dev, name);
+}
+
+static void test_pathinfo(void **state)
+{
+       struct path pp;
+       char *name = "test";
+
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+       /* Test pathinfo blacklisting device */
+       setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+       will_return(__wrap_pathinfo, PATHINFO_SKIPPED);
+       will_return(__wrap_pathinfo, name);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_NOT_VALID);
+       assert_string_equal(pp.dev, name);
+       assert_ptr_equal(pp.udev, &test_udev);
+       /* Test pathinfo failing */
+       memset(&pp, 0, sizeof(pp));
+       setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+       will_return(__wrap_pathinfo, PATHINFO_FAILED);
+       will_return(__wrap_pathinfo, name);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_ERROR);
+       /* Test blank wwid */
+       memset(&pp, 0, sizeof(pp));
+       setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+       will_return(__wrap_pathinfo, PATHINFO_OK);
+       will_return(__wrap_pathinfo, name);
+       will_return(__wrap_pathinfo, "");
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_NOT_VALID);
+}
+
+static void test_filter_property(void **state)
+{
+       struct path pp;
+       char *name = "test";
+       char *wwid = "test-wwid";
+
+       /* test blacklist property */
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+       will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_NOT_VALID);
+       assert_ptr_equal(pp.udev, &test_udev);
+       assert_string_equal(pp.wwid, wwid);
+       /* test missing property */
+       memset(&pp, 0, sizeof(pp));
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+       will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_MISSING);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_NOT_VALID);
+       /* test MATCH_NOTHING fail on is_failed_wwid */
+       memset(&pp, 0, sizeof(pp));
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+       will_return(__wrap_filter_property, MATCH_NOTHING);
+       will_return(__wrap_is_failed_wwid, WWID_IS_FAILED);
+       will_return(__wrap_is_failed_wwid, wwid);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_NOT_VALID);
+}
+
+static void test_is_failed_wwid(void **state)
+{
+       struct path pp;
+       char *name = "test";
+       char *wwid = "test-wwid";
+
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+       /* Test wwid failed */
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY);
+       will_return(__wrap_is_failed_wwid, WWID_IS_FAILED);
+       will_return(__wrap_is_failed_wwid, wwid);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_NOT_VALID);
+       assert_string_equal(pp.dev, name);
+       assert_ptr_equal(pp.udev, &test_udev);
+       assert_string_equal(pp.wwid, wwid);
+       /* test is_failed_wwid error */
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY);
+       will_return(__wrap_is_failed_wwid, WWID_FAILED_ERROR);
+       will_return(__wrap_is_failed_wwid, wwid);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_ERROR);
+}
+
+static void test_greedy(void **state)
+{
+       struct path pp;
+       char *name = "test";
+       char *wwid = "test-wwid";
+
+       /* test greedy success with checking multipathd */
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_GREEDY;
+       setup_passing(name, wwid, CHECK_MPATHD_RUNNING, STAGE_IS_FAILED);
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_VALID);
+       assert_string_equal(pp.dev, name);
+       assert_ptr_equal(pp.udev, &test_udev);
+       assert_string_equal(pp.wwid, wwid);
+       /* test greedy success without checking multiapthd */
+       memset(&pp, 0, sizeof(pp));
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_IS_FAILED);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_VALID);
+}
+
+static void test_check_wwids(void **state)
+{
+       struct path pp;
+       char *name = "test";
+       char *wwid = "test-wwid";
+
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+       setup_passing(name, wwid, CHECK_MPATHD_EAGAIN, STAGE_IS_FAILED);
+       will_return(__wrap_check_wwids_file, true);
+       will_return(__wrap_check_wwids_file, wwid);
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_VALID_NO_CHECK);
+       assert_string_equal(pp.dev, name);
+       assert_ptr_equal(pp.udev, &test_udev);
+       assert_string_equal(pp.wwid, wwid);
+}
+
+static void test_check_uuid_present(void **state)
+{
+       struct path pp;
+       char *name = "test";
+       char *wwid = "test-wwid";
+
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+       setup_passing(name, wwid, CHECK_MPATHD_ENABLED, STAGE_CHECK_WWIDS);
+       will_return(__wrap_dm_map_present_by_uuid, 1);
+       will_return(__wrap_dm_map_present_by_uuid, wwid);
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_VALID);
+       assert_string_equal(pp.dev, name);
+       assert_ptr_equal(pp.udev, &test_udev);
+       assert_string_equal(pp.wwid, wwid);
+}
+
+
+static void test_find_multipaths(void **state)
+{
+       struct path pp;
+       char *name = "test";
+       char *wwid = "test-wwid";
+
+       /* test find_multipaths = FIND_MULTIPATHS_STRICT */
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_NOT_VALID);
+       assert_string_equal(pp.dev, name);
+       assert_ptr_equal(pp.udev, &test_udev);
+       assert_string_equal(pp.wwid, wwid);
+       /* test find_multipaths = FIND_MULTIPATHS_OFF */
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_OFF;
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_NOT_VALID);
+       /* test find_multipaths = FIND_MULTIPATHS_ON */
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_ON;
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_NOT_VALID);
+       /* test find_multipaths = FIND_MULTIPATHS_SMART */
+       memset(&pp, 0, sizeof(pp));
+       conf.find_multipaths = FIND_MULTIPATHS_SMART;
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+       assert_int_equal(is_path_valid(name, &conf, &pp, false),
+                        PATH_IS_MAYBE_VALID);
+       assert_string_equal(pp.dev, name);
+       assert_ptr_equal(pp.udev, &test_udev);
+       assert_string_equal(pp.wwid, wwid);
+}
+
+int test_valid(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_bad_arguments),
+               cmocka_unit_test(test_sysfs_is_multipathed),
+               cmocka_unit_test(test_check_multipathd),
+               cmocka_unit_test(test_pathinfo),
+               cmocka_unit_test(test_filter_property),
+               cmocka_unit_test(test_is_failed_wwid),
+               cmocka_unit_test(test_greedy),
+               cmocka_unit_test(test_check_wwids),
+               cmocka_unit_test(test_check_uuid_present),
+               cmocka_unit_test(test_find_multipaths),
+       };
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+int main(void)
+{
+       int ret = 0;
+       ret += test_valid();
+       return ret;
+}
index 3cbad81..e2ec65e 100644 (file)
@@ -28,13 +28,17 @@ struct vpdtest {
        char wwid[WWID_SIZE];
 };
 
+static regex_t space_re;
 static int setup(void **state)
 {
        struct vpdtest *vt = malloc(sizeof(*vt));
+       int rc;
 
        if (vt == NULL)
                return -1;
        *state = vt;
+       rc = regcomp(&space_re, " +", REG_EXTENDED);
+       assert_int_equal(rc, 0);
        return 0;
 }
 
@@ -44,6 +48,7 @@ static int teardown(void **state)
 
        free(vt);
        *state = NULL;
+       regfree(&space_re);
        return 0;
 }
 
@@ -360,21 +365,14 @@ static char *subst_spaces(const char *src)
 {
        char *dst = calloc(1, strlen(src) + 1);
        char *p;
-       static regex_t *re;
        regmatch_t match;
-       int rc;
+       int rc = 0;
 
        assert_non_null(dst);
-       if (re == NULL) {
-               re = calloc(1, sizeof(*re));
-               assert_non_null(re);
-               rc = regcomp(re, " +", REG_EXTENDED);
-               assert_int_equal(rc, 0);
-       }
 
-       for (rc = regexec(re, src, 1, &match, 0), p = dst;
+       for (rc = regexec(&space_re, src, 1, &match, 0), p = dst;
            rc == 0;
-           src += match.rm_eo, rc = regexec(re, src, 1, &match, 0)) {
+           src += match.rm_eo, rc = regexec(&space_re, src, 1, &match, 0)) {
                memcpy(p, src, match.rm_so);
                p += match.rm_so;
                *p = '_';