Imported Upstream version 0.8.2 upstream/0.8.2
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)
55 files changed:
kpartx/Makefile
kpartx/dasd.c
kpartx/dasd.h
kpartx/kpartx.8
kpartx/kpartx.c
libmpathcmd/mpath_cmd.c
libmpathcmd/mpath_cmd.h
libmpathpersist/mpath_persist.c
libmpathpersist/mpath_persist.h
libmultipath/blacklist.c
libmultipath/blacklist.h
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/debug.c
libmultipath/defaults.c
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/devmapper.h
libmultipath/dict.c
libmultipath/discovery.c
libmultipath/dmparser.c
libmultipath/hwtable.c
libmultipath/parser.c
libmultipath/pgpolicies.c
libmultipath/prio.c
libmultipath/prioritizers/ana.c
libmultipath/propsel.c
libmultipath/structs_vec.c
libmultipath/uevent.c
libmultipath/util.c
libmultipath/util.h
libmultipath/uxsock.c
libmultipath/version.h
libmultipath/wwids.c
mpathpersist/main.c
mpathpersist/main.h
mpathpersist/mpathpersist.8
multipath/main.c
multipath/multipath.conf.5
multipathd/cli.c
multipathd/cli.h
multipathd/cli_handlers.c
multipathd/main.c
multipathd/main.h
multipathd/multipathd.8
multipathd/uxlsnr.c
multipathd/waiter.c
tests/Makefile
tests/blacklist.c
tests/globals.c
tests/hwtable.c
tests/uevent.c
tests/util.c
tests/vpd.c [new file with mode: 0644]

index 7eb467ee3efbc7f738f1dac00b51cd3ad45ea524..2906a984d16a274ee631ebdf28bd0a15cb3d1c66 100644 (file)
@@ -3,7 +3,7 @@
 #
 include ../Makefile.inc
 
-CFLAGS += $(BIN_CFLAGS) -I. -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+CFLAGS += $(BIN_CFLAGS) -I. -I$(multipathdir) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
 LDFLAGS += $(BIN_LDFLAGS)
 
 LIBDEPS += -ldevmapper
index 61b609a507be108957abbccef3fe4328396eecbe..d95d8ca0e71708aa6c2af20e80bb42e3e44e7abd 100644 (file)
@@ -138,6 +138,8 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
                        return -1;
        } else {
                fd_dasd = dup(fd);
+               if (fd_dasd < 0)
+                       return -1;
        }
 
        if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) {
index 749af49a9fd114cba5db88f38791e83c76b11a22..8ad5d6202034d4de4f045ddd33616c847be79da0 100644 (file)
@@ -68,7 +68,7 @@ typedef struct volume_label
        char res3[28];          /* reserved                                  */
        uint8_t ldl_version;    /* version number, valid for ldl format      */
        uint64_t formatted_blocks; /* valid when ldl_version >= f2           */
-} __attribute__ ((packed)) volume_label_t;
+} __attribute__ ((packed, aligned(__alignof__(int)))) volume_label_t;
 
 
 typedef struct extent
index ba58acb3d23444ef8aae802912d8e8cf07a49d9f..08bb349be0645f2784b8f232f1bab46f63a9eae6 100644 (file)
@@ -25,7 +25,7 @@ kpartx \- Create device maps from partition tables.
 .RB [\| \-p \|]
 .RB [\| \-f \|]
 .RB [\| \-g \|]
-.RB [\| \-s \|]
+.RB [\| \-s | \-n \|]
 .RB [\| \-v \|]
 .B wholedisk
 .
@@ -77,7 +77,11 @@ Force GUID partition table (GPT).
 .
 .TP
 .B \-s
-Sync mode. Don't return until the partitions are created.
+Sync mode (Default). Don't return until the partitions are created.
+.
+.TP
+.B \-n
+Nosync mode. Return before the partitions are created.
 .
 .TP
 .B \-v
index d4fb53bede4fd542da324fbea22d780437c8960b..d3620c5c4177e012cdd4e43ecf9f21cae9c75499 100644 (file)
@@ -37,6 +37,7 @@
 #include "crc32.h"
 #include "lopart.h"
 #include "kpartx.h"
+#include "version.h"
 
 #define SIZE(a) (sizeof(a)/sizeof((a)[0]))
 
@@ -95,7 +96,9 @@ int force_devmap=0;
 
 static int
 usage(void) {
-       printf("usage : kpartx [-a|-d|-l] [-f] [-v] wholedisk\n");
+       printf(VERSION_STRING);
+       printf("Usage:\n");
+       printf("  kpartx [-a|-d|-u|-l] [-r] [-p] [-f] [-g] [-s|-n] [-v] wholedisk\n");
        printf("\t-a add partition devmappings\n");
        printf("\t-r devmappings will be readonly\n");
        printf("\t-d del partition devmappings\n");
@@ -106,7 +109,7 @@ usage(void) {
        printf("\t-f force devmap create\n");
        printf("\t-v verbose\n");
        printf("\t-n nosync mode. Return before the partitions are created\n");
-       printf("\t-s sync mode. Don't return until the partitions are created. Default.\n");
+       printf("\t-s sync mode (Default). Don't return until the partitions are created\n");
        return 1;
 }
 
index df4ca541f07f2cad0ee467e13ab35b0c3661f698..f00bf7e1b76d7b59b30ecb025fceae3cc34dc6da 100644 (file)
@@ -26,6 +26,7 @@
 #include <poll.h>
 #include <string.h>
 #include <errno.h>
+#include <fcntl.h>
 
 #include "mpath_cmd.h"
 
@@ -93,29 +94,52 @@ static size_t write_all(int fd, const void *buf, size_t len)
 /*
  * connect to a unix domain socket
  */
-int mpath_connect(void)
+int __mpath_connect(int nonblocking)
 {
        int fd, len;
        struct sockaddr_un addr;
+       int flags = 0;
 
        memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_LOCAL;
        addr.sun_path[0] = '\0';
+       strncpy(&addr.sun_path[1], DEFAULT_SOCKET, sizeof(addr.sun_path) - 1);
        len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t);
-       strncpy(&addr.sun_path[1], DEFAULT_SOCKET, len);
+       if (len > sizeof(struct sockaddr_un))
+               len = sizeof(struct sockaddr_un);
 
        fd = socket(AF_LOCAL, SOCK_STREAM, 0);
        if (fd == -1)
                return -1;
 
+       if (nonblocking) {
+               flags = fcntl(fd, F_GETFL, 0);
+               if (flags != -1)
+                       (void)fcntl(fd, F_SETFL, flags|O_NONBLOCK);
+       }
+
        if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
+               int err = errno;
+
                close(fd);
+               errno = err;
                return -1;
        }
 
+       if (nonblocking && flags != -1)
+               (void)fcntl(fd, F_SETFL, flags);
+
        return fd;
 }
 
+/*
+ * connect to a unix domain socket
+ */
+int mpath_connect(void)
+{
+       return __mpath_connect(0);
+}
+
 int mpath_disconnect(int fd)
 {
        return close(fd);
index 15aeb0673527a4f8d882fa1622634a1d9f7b89d1..ccfd35f26931d6874091ff6d9ff1fb8d8b17372c 100644 (file)
@@ -34,6 +34,21 @@ extern "C" {
 #define DEFAULT_REPLY_TIMEOUT  4000
 
 
+/*
+ * DESCRIPTION:
+ *     Same as mpath_connect() (see below) except for the "nonblocking"
+ *     parameter.
+ *     If "nonblocking" is set, connects in non-blocking mode. This is
+ *     useful to avoid blocking if the listening socket's backlog is
+ *     exceeded. In this case, errno will be set to EAGAIN.
+ *     In case of success, the returned file descriptor is in in blocking
+ *     mode, even if "nonblocking" was true.
+ *
+ * RETURNS:
+ *     A file descriptor on success. -1 on failure (with errno set).
+ */
+int __mpath_connect(int nonblocking);
+
 /*
  * DESCRIPTION:
  *     Connect to the running multipathd daemon. On systems with the
index 6505774f98dad44b54bdd59430d8d75cc02ef313..53022f5bb492ef87bad19d9c96346c71172f6a6d 100644 (file)
@@ -16,6 +16,7 @@
 #include "config.h"
 #include "switchgroup.h"
 #include "discovery.h"
+#include "configure.h"
 #include "dmparser.h"
 #include <ctype.h>
 #include "propsel.h"
@@ -96,16 +97,21 @@ updatepaths (struct multipath * mpp)
                                continue;
                        }
                        pp->mpp = mpp;
-                       if (pp->state == PATH_UNCHECKED ||
-                                       pp->state == PATH_WILD) {
+                       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_CHECKER);
+                               pathinfo(pp, conf, DI_SYSFS|DI_CHECKER);
                                put_multipath_config(conf);
+                               continue;
                        }
-
-                       if (pp->priority == PRIO_UNDEF) {
+                       if (pp->state == PATH_UNCHECKED ||
+                                       pp->state == PATH_WILD) {
                                conf = get_multipath_config();
-                               pathinfo(pp, conf, DI_PRIO);
+                               pathinfo(pp, conf, DI_CHECKER);
                                put_multipath_config(conf);
                        }
                }
@@ -152,48 +158,47 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact,
 int mpath_persistent_reserve_in (int fd, int rq_servact,
        struct prin_resp *resp, int noisy, int verbose)
 {
-       struct stat info;
-       vector curmp = NULL;
-       vector pathvec = NULL;
-       char * alias;
-       struct multipath * mpp;
-       int map_present;
-       int major, minor;
-       int ret;
-       struct config *conf;
+       int ret = mpath_persistent_reserve_init_vecs(verbose);
 
-       conf = get_multipath_config();
-       conf->verbosity = verbose;
-       put_multipath_config(conf);
+       if (ret != MPATH_PR_SUCCESS)
+               return ret;
+       ret = __mpath_persistent_reserve_in(fd, rq_servact, resp, noisy);
+       mpath_persistent_reserve_free_vecs();
+       return ret;
+}
 
-       if (fstat( fd, &info) != 0){
-               condlog(0, "stat error %d", fd);
-               return MPATH_PR_FILE_ERROR;
-       }
-       if(!S_ISBLK(info.st_mode)){
-               condlog(0, "Failed to get major:minor. fd = %d", fd);
-               return MPATH_PR_FILE_ERROR;
-       }
+int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
+       unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose)
+{
+       int ret = mpath_persistent_reserve_init_vecs(verbose);
 
-       major = major(info.st_rdev);
-       minor = minor(info.st_rdev);
-       condlog(4, "Device %d:%d:  ", major, minor);
+       if (ret != MPATH_PR_SUCCESS)
+               return ret;
+       ret = __mpath_persistent_reserve_out(fd, rq_servact, rq_scope, rq_type,
+                                            paramp, noisy);
+       mpath_persistent_reserve_free_vecs();
+       return ret;
+}
 
-       /* get alias from major:minor*/
-       alias = dm_mapname(major, minor);
-       if (!alias){
-               condlog(0, "%d:%d failed to get device alias.", major, minor);
-               return MPATH_PR_DMMP_ERROR;
-       }
+static vector curmp;
+static vector pathvec;
 
-       condlog(3, "alias = %s", alias);
-       map_present = dm_map_present(alias);
-       if (map_present && dm_is_mpath(alias) != 1){
-               condlog( 0, "%s: not a multipath device.", alias);
-               ret = MPATH_PR_DMMP_ERROR;
-               goto out;
-       }
+void mpath_persistent_reserve_free_vecs(void)
+{
+       free_multipathvec(curmp, KEEP_PATHS);
+       free_pathvec(pathvec, FREE_PATHS);
+       curmp = pathvec = NULL;
+}
+
+int mpath_persistent_reserve_init_vecs(int verbose)
+{
+       struct config *conf = get_multipath_config();
+
+       conf->verbosity = verbose;
+       put_multipath_config(conf);
 
+       if (curmp)
+               return MPATH_PR_SUCCESS;
        /*
         * allocate core vectors to store paths and multipaths
         */
@@ -201,70 +206,32 @@ int mpath_persistent_reserve_in (int fd, int rq_servact,
        pathvec = vector_alloc ();
 
        if (!curmp || !pathvec){
-               condlog (0, "%s: vector allocation failed.", alias);
-               ret = MPATH_PR_DMMP_ERROR;
-               if (curmp)
-                       vector_free(curmp);
-               if (pathvec)
-                       vector_free(pathvec);
-               goto out;
+               condlog (0, "vector allocation failed.");
+               goto err;
        }
 
-       if (path_discovery(pathvec, DI_SYSFS | DI_CHECKER) < 0) {
-               ret = MPATH_PR_DMMP_ERROR;
-               goto out1;
-       }
+       if (dm_get_maps(curmp))
+               goto err;
 
-       /* get info of all paths from the dm device     */
-       if (get_mpvec (curmp, pathvec, alias)){
-               condlog(0, "%s: failed to get device info.", alias);
-               ret = MPATH_PR_DMMP_ERROR;
-               goto out1;
-       }
-
-       mpp = find_mp_by_alias(curmp, alias);
-       if (!mpp){
-               condlog(0, "%s: devmap not registered.", alias);
-               ret = MPATH_PR_DMMP_ERROR;
-               goto out1;
-       }
-
-       ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy);
+       return MPATH_PR_SUCCESS;
 
-out1:
-       free_multipathvec(curmp, KEEP_PATHS);
-       free_pathvec(pathvec, FREE_PATHS);
-out:
-       FREE(alias);
-       return ret;
+err:
+       mpath_persistent_reserve_free_vecs();
+       return MPATH_PR_DMMP_ERROR;
 }
 
-int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
-       unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose)
+static int mpath_get_map(int fd, char **palias, struct multipath **pmpp)
 {
-
+       int ret = MPATH_PR_DMMP_ERROR;
        struct stat info;
-
-       vector curmp = NULL;
-       vector pathvec = NULL;
-
-       char * alias;
-       struct multipath * mpp;
-       int map_present;
        int major, minor;
-       int ret;
-       uint64_t prkey;
-       struct config *conf;
-
-       conf = get_multipath_config();
-       conf->verbosity = verbose;
-       put_multipath_config(conf);
+       char *alias;
+       struct multipath *mpp;
 
-       if (fstat( fd, &info) != 0){
+       if (fstat(fd, &info) != 0){
                condlog(0, "stat error fd=%d", fd);
                return MPATH_PR_FILE_ERROR;
        }
-
        if(!S_ISBLK(info.st_mode)){
                condlog(3, "Failed to get major:minor. fd=%d", fd);
                return MPATH_PR_FILE_ERROR;
@@ -274,56 +241,72 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
        minor = minor(info.st_rdev);
        condlog(4, "Device  %d:%d", major, minor);
 
-       /* get WWN of the device from major:minor*/
+       /* get alias from major:minor*/
        alias = dm_mapname(major, minor);
        if (!alias){
+               condlog(0, "%d:%d failed to get device alias.", major, minor);
                return MPATH_PR_DMMP_ERROR;
        }
 
        condlog(3, "alias = %s", alias);
-       map_present = dm_map_present(alias);
 
-       if (map_present && dm_is_mpath(alias) != 1){
+       if (dm_map_present(alias) && dm_is_mpath(alias) != 1){
                condlog(3, "%s: not a multipath device.", alias);
-               ret = MPATH_PR_DMMP_ERROR;
                goto out;
        }
 
-       /*
-        * allocate core vectors to store paths and multipaths
-        */
-       curmp = vector_alloc ();
-       pathvec = vector_alloc ();
-
-       if (!curmp || !pathvec){
-               condlog (0, "%s: vector allocation failed.", alias);
-               ret = MPATH_PR_DMMP_ERROR;
-               if (curmp)
-                       vector_free(curmp);
-               if (pathvec)
-                       vector_free(pathvec);
-               goto out;
-       }
-
-       if (path_discovery(pathvec, DI_SYSFS | DI_CHECKER) < 0) {
-               ret = MPATH_PR_DMMP_ERROR;
-               goto out1;
-       }
-
        /* get info of all paths from the dm device     */
        if (get_mpvec(curmp, pathvec, alias)){
                condlog(0, "%s: failed to get device info.", alias);
-               ret = MPATH_PR_DMMP_ERROR;
-               goto out1;
+               goto out;
        }
 
        mpp = find_mp_by_alias(curmp, alias);
 
        if (!mpp) {
                condlog(0, "%s: devmap not registered.", alias);
-               ret = MPATH_PR_DMMP_ERROR;
-               goto out1;
+               goto out;
+       }
+
+       ret = MPATH_PR_SUCCESS;
+       if (pmpp)
+               *pmpp = mpp;
+       if (palias) {
+               *palias = alias;
+               alias = NULL;
        }
+out:
+       FREE(alias);
+       return ret;
+}
+
+int __mpath_persistent_reserve_in (int fd, int rq_servact,
+       struct prin_resp *resp, int noisy)
+{
+       struct multipath *mpp;
+       int ret;
+
+       ret = mpath_get_map(fd, NULL, &mpp);
+       if (ret != MPATH_PR_SUCCESS)
+               return ret;
+
+       ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy);
+
+       return ret;
+}
+
+int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
+       unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy)
+{
+       struct multipath *mpp;
+       char *alias;
+       int ret;
+       uint64_t prkey;
+       struct config *conf;
+
+       ret = mpath_get_map(fd, &alias, &mpp);
+       if (ret != MPATH_PR_SUCCESS)
+               return ret;
 
        conf = get_multipath_config();
        select_reservation_key(conf, mpp);
@@ -385,10 +368,6 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
                update_prkey(alias, 0);
        }
 out1:
-       free_multipathvec(curmp, KEEP_PATHS);
-       free_pathvec(pathvec, FREE_PATHS);
-
-out:
        FREE(alias);
        return ret;
 }
@@ -400,22 +379,22 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid)
        struct multipath *mpp;
        char params[PARAMS_SIZE], status[PARAMS_SIZE];
 
-       if (dm_get_maps (curmp)){
-               return 1;
-       }
-
        vector_foreach_slot (curmp, mpp, i){
                /*
                 * discard out of scope maps
                 */
-               if (mpp->alias && refwwid &&
-                   strncmp (mpp->alias, refwwid, WWID_SIZE - 1)){
-                       free_multipath (mpp, KEEP_PATHS);
-                       vector_del_slot (curmp, i);
-                       i--;
+               if (!mpp->alias) {
+                       condlog(0, "%s: map with empty alias!", __func__);
                        continue;
                }
 
+               if (mpp->pg != NULL)
+                       /* Already seen this one */
+                       continue;
+
+               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);
@@ -428,7 +407,6 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid)
                 * about them
                 */
                updatepaths(mpp);
-               mpp->bestpg = select_path_group (mpp);
                disassemble_status (status, mpp);
 
        }
@@ -519,8 +497,8 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
                                if (k < count)
                                        continue;
                        }
-                       strncpy(thread[count].param.dev, pp->dev,
-                               FILE_NAME_SIZE - 1);
+                       strlcpy(thread[count].param.dev, pp->dev,
+                               FILE_NAME_SIZE);
 
                        if (count && (thread[count].param.paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK)){
                                /*
@@ -640,7 +618,7 @@ int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
        int rc;
 
        memset(&thread, 0, sizeof(thread));
-       strncpy(param.dev, dev, FILE_NAME_SIZE - 1);
+       strlcpy(param.dev, dev, FILE_NAME_SIZE);
        /* Initialize and set thread joinable attribute */
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
@@ -708,8 +686,8 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
                                continue;
                        }
 
-                       strncpy(thread[count].param.dev, pp->dev,
-                               FILE_NAME_SIZE - 1);
+                       strlcpy(thread[count].param.dev, pp->dev,
+                               FILE_NAME_SIZE);
                        condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
                        rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn,
                                        (void *) (&thread[count].param));
index 9a84bc9c42f470feeb1d03411928a3bbfe0c1da6..7cf4faf91069744cefc8e2b2cffd3828710531dc 100644 (file)
@@ -213,6 +213,15 @@ extern int mpath_lib_exit (struct config *conf);
 extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp,
                int noisy, int verbose);
 
+/*
+ * DESCRIPTION :
+ * This function is like mpath_persistent_reserve_in(), except that it doesn't call
+ * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs()
+ * before and after the actual PR call.
+ */
+extern int __mpath_persistent_reserve_in(int fd, int rq_servact,
+               struct prin_resp *resp, int noisy);
+
 /*
  * DESCRIPTION :
  * This function sends PROUT command to the DM device and get the response.
@@ -238,6 +247,37 @@ extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp
 extern int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
                unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy,
                int verbose);
+/*
+ * DESCRIPTION :
+ * This function is like mpath_persistent_reserve_out(), except that it doesn't call
+ * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs()
+ * before and after the actual PR call.
+ */
+extern int __mpath_persistent_reserve_out( int fd, int rq_servact, int rq_scope,
+               unsigned int rq_type, struct prout_param_descriptor *paramp,
+               int noisy);
+
+/*
+ * DESCRIPTION :
+ * This function allocates data structures and performs basic initialization and
+ * device discovery for later calls of __mpath_persistent_reserve_in() or
+ * __mpath_persistent_reserve_out().
+ * @verbose: Set verbosity level. Input argument. value:0 to 3. 0->disabled, 3->Max verbose
+ *
+ * RESTRICTIONS:
+ *
+ * RETURNS: MPATH_PR_SUCCESS if successful else returns any of the status specified
+ *       above in RETURN_STATUS.
+ */
+int mpath_persistent_reserve_init_vecs(int verbose);
+
+/*
+ * DESCRIPTION :
+ * This function frees data structures allocated by
+ * mpath_persistent_reserve_init_vecs().
+ */
+void mpath_persistent_reserve_free_vecs(void);
+
 
 #ifdef __cplusplus
 }
index e0d02794c72dac10a7da39302def8d422102f61a..00e8dbdb49bb9e9a1de68ab5333d2cadd7a24c9f 100644 (file)
@@ -366,7 +366,7 @@ filter_path (struct config * conf, struct path * pp)
 {
        int r;
 
-       r = filter_property(conf, pp->udev, 3);
+       r = filter_property(conf, pp->udev, 3, pp->uid_attribute);
        if (r > 0)
                return r;
        r = filter_devnode(conf->blist_devnode, conf->elist_devnode, pp->dev);
@@ -384,7 +384,8 @@ filter_path (struct config * conf, struct path * pp)
 }
 
 int
-filter_property(struct config *conf, struct udev_device *udev, int lvl)
+filter_property(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;
@@ -395,7 +396,21 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl)
                /*
                 * This is the inverse of the 'normal' matching;
                 * the environment variable _has_ to match.
+                * But only if the uid_attribute used for determining the WWID
+                * of the path is is present in the environment
+                * (uid_attr_seen). If this is not the case, udev probably
+                * just failed to access the device, which should not cause the
+                * device to be blacklisted (it won't be used by multipath
+                * anyway without WWID).
+                * Likewise, if no uid attribute is defined, udev-based WWID
+                * determination is effectively off, and devices shouldn't be
+                * blacklisted by missing properties (check_missing_prop).
                 */
+
+               bool check_missing_prop = uid_attribute != NULL &&
+                       *uid_attribute != '\0';
+               bool uid_attr_seen = false;
+
                r = MATCH_PROPERTY_BLIST_MISSING;
                udev_list_entry_foreach(list_entry,
                                udev_device_get_properties_list_entry(udev)) {
@@ -403,6 +418,10 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl)
                        env = udev_list_entry_get_name(list_entry);
                        if (!env)
                                continue;
+
+                       if (check_missing_prop && !strcmp(env, uid_attribute))
+                               uid_attr_seen = true;
+
                        if (_blacklist_exceptions(conf->elist_property, env)) {
                                r = MATCH_PROPERTY_BLIST_EXCEPT;
                                break;
@@ -413,6 +432,9 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl)
                        }
                        env = NULL;
                }
+               if (r == MATCH_PROPERTY_BLIST_MISSING &&
+                   (!check_missing_prop || !uid_attr_seen))
+                       r = MATCH_NOTHING;
        }
 
        log_filter(devname, NULL, NULL, NULL, env, NULL, r, lvl);
index 4c8ec99e084e1a32cde07e378a5b3a8f6be6c150..2d721f60eb23ac1a7c6bbed36a52484549edd07b 100644 (file)
@@ -37,7 +37,7 @@ 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);
+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);
index 141f092b05216a3c4dc1a4b18996de9cbd2c514a..20e3b8bf2741cec175867a816529ba0651025940 100644 (file)
@@ -585,8 +585,7 @@ free_config (struct config * conf)
        if (conf->uid_attribute)
                FREE(conf->uid_attribute);
 
-       if (conf->uid_attrs)
-               FREE(conf->uid_attrs);
+       vector_reset(&conf->uid_attrs);
 
        if (conf->getuid)
                FREE(conf->getuid);
@@ -718,7 +717,6 @@ load_config (char * file)
        conf->remove_retries = 0;
        conf->ghost_delay = DEFAULT_GHOST_DELAY;
        conf->all_tg_pt = DEFAULT_ALL_TG_PT;
-
        /*
         * preload default hwtable
         */
@@ -853,3 +851,50 @@ out:
        free_config(conf);
        return NULL;
 }
+
+char *get_uid_attribute_by_attrs(struct config *conf,
+                                const char *path_dev)
+{
+       vector uid_attrs = &conf->uid_attrs;
+       int j;
+       char *att, *col;
+
+       vector_foreach_slot(uid_attrs, att, j) {
+               col = strrchr(att, ':');
+               if (!col)
+                       continue;
+               if (!strncmp(path_dev, att, col - att))
+                       return col + 1;
+       }
+       return NULL;
+}
+
+int parse_uid_attrs(char *uid_attrs, struct config *conf)
+{
+       vector attrs  = &conf->uid_attrs;
+       char *uid_attr_record, *tmp;
+       int  ret = 0, count;
+
+       if (!uid_attrs)
+               return 1;
+
+       count = get_word(uid_attrs, &uid_attr_record);
+       while (uid_attr_record) {
+               tmp = strchr(uid_attr_record, ':');
+               if (!tmp) {
+                       condlog(2, "invalid record in uid_attrs: %s",
+                               uid_attr_record);
+                       free(uid_attr_record);
+                       ret = 1;
+               } else if (!vector_alloc_slot(attrs)) {
+                       free(uid_attr_record);
+                       ret = 1;
+               } else
+                       vector_set_slot(attrs, uid_attr_record);
+               if (!count)
+                       break;
+               uid_attrs += count;
+               count = get_word(uid_attrs, &uid_attr_record);
+       }
+       return ret;
+}
index f5bf5b1b0387ce0029900694394603f59700d7c8..ff2b4e86589da5bb048e97bd8a2c830527f205f4 100644 (file)
@@ -190,7 +190,7 @@ struct config {
 
        char * multipath_dir;
        char * selector;
-       char * uid_attrs;
+       struct _vector uid_attrs;
        char * uid_attribute;
        char * getuid;
        char * features;
@@ -250,4 +250,8 @@ void free_config (struct config * conf);
 extern struct config *get_multipath_config(void);
 extern void put_multipath_config(void *);
 
+int parse_uid_attrs(char *uid_attrs, struct config *conf);
+char *get_uid_attribute_by_attrs(struct config *conf,
+                                const char *path_dev);
+
 #endif
index af4d78de3153e3f82b7a4900983e8afc82da89a3..b09ef529d7385d6417b6c59bb0684e6753d81175 100644 (file)
@@ -73,7 +73,7 @@ int group_by_host_adapter(struct pathgroup *pgp, vector adapters)
                        goto out;
                agp->pgp = pgp;
 
-               strncpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE - 1);
+               strlcpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE);
                store_adaptergroup(adapters, agp);
 
                /* create a new host port group
@@ -650,7 +650,7 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
                if (cmpp) {
                        condlog(2, "%s: rename %s to %s", mpp->wwid,
                                cmpp->alias, mpp->alias);
-                       strncpy(mpp->alias_old, cmpp->alias, WWID_SIZE - 1);
+                       strlcpy(mpp->alias_old, cmpp->alias, WWID_SIZE);
                        mpp->action = ACT_RENAME;
                        if (force_reload) {
                                mpp->force_udev_reload = 1;
@@ -667,7 +667,7 @@ 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);
-               strncpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE - 1);
+               strlcpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE);
                drop_multipath(curmp, cmpp_by_name->wwid, KEEP_PATHS);
                mpp->action = ACT_CREATE;
                condlog(3, "%s: set ACT_CREATE (map wwid change)",
@@ -1257,7 +1257,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
                        if (!deadmap(mpp))
                                continue;
 
-                       strncpy(alias, mpp->alias, WWID_SIZE - 1);
+                       strlcpy(alias, mpp->alias, WWID_SIZE);
 
                        vector_del_slot(newmp, i);
                        i--;
@@ -1369,7 +1369,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
                conf = get_multipath_config();
                pthread_cleanup_push(put_multipath_config, conf);
                if (pp->udev && pp->uid_attribute &&
-                   filter_property(conf, pp->udev, 3) > 0)
+                   filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
                        invalid = 1;
                pthread_cleanup_pop(1);
                if (invalid)
@@ -1409,7 +1409,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
                conf = get_multipath_config();
                pthread_cleanup_push(put_multipath_config, conf);
                if (pp->udev && pp->uid_attribute &&
-                   filter_property(conf, pp->udev, 3) > 0)
+                   filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
                        invalid = 1;
                pthread_cleanup_pop(1);
                if (invalid)
@@ -1438,7 +1438,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
                conf = get_multipath_config();
                pthread_cleanup_push(put_multipath_config, conf);
                if (pp->udev && pp->uid_attribute &&
-                   filter_property(conf, pp->udev, 3) > 0)
+                   filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
                        invalid = 1;
                pthread_cleanup_pop(1);
                if (invalid)
@@ -1451,7 +1451,8 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 
                conf = get_multipath_config();
                pthread_cleanup_push(put_multipath_config, conf);
-               if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) {
+               if (((dm_get_uuid(dev, tmpwwid, WWID_SIZE)) == 0)
+                   && (strlen(tmpwwid))) {
                        refwwid = tmpwwid;
                        goto check;
                }
index cbf1e5701952a6bc98d603198662bac3b91ca346..4128cb90b91fb78bcbf1619ea0d2164d5328a31c 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <string.h>
 
 #include "log_pthread.h"
 #include <sys/types.h>
index c20bb0d26f4cc664aaf8964d5e8a9566264661ce..082640d383f86025d246e57494effe25d0506925 100644 (file)
@@ -7,20 +7,3 @@
 #include "memory.h"
 
 const char * const default_partition_delim = DEFAULT_PARTITION_DELIM;
-
-char *
-set_default (char * str)
-{
-       int len;
-       char * p;
-
-       len = strlen(str);
-       p = MALLOC(len + 1);
-
-       if (!p)
-               return NULL;
-
-       strncat(p, str, len);
-
-       return p;
-}
index 65769398e20378a50bbeea36cb06ca2736b126d6..decc9335585df1b8d88435437580c31e7026070a 100644 (file)
@@ -1,8 +1,11 @@
+#ifndef _DEFAULTS_H
+#define _DEFAULTS_H
 /*
  * If you add or modify a value also update multipath/multipath.conf.5
  * and the TEMPLATE in libmultipath/hwtable.c
  */
 #define DEFAULT_UID_ATTRIBUTE  "ID_SERIAL"
+#define DEFAULT_NVME_UID_ATTRIBUTE     "ID_WWN"
 #define DEFAULT_UDEVDIR                "/dev"
 #define DEFAULT_MULTIPATHDIR   "/" LIB_STRING "/multipath"
 #define DEFAULT_SELECTOR       "service-time 0"
 #define DEFAULT_CONFIG_DIR     "/etc/multipath/conf.d"
 #define MULTIPATH_SHM_BASE     "/dev/shm/multipath/"
 
-char * set_default (char * str);
+
+static inline char *set_default(char *str)
+{
+       return strdup(str);
+}
 extern const char *const default_partition_delim;
+#endif /* _DEFAULTS_H */
index 2e79667df08ba802269828dd899e6e8e9b6ee74f..0f0c3a346060141150514d8e346a734283a2c664 100644 (file)
@@ -13,7 +13,9 @@
 #include <unistd.h>
 #include <errno.h>
 #include <sys/sysmacros.h>
+#include <linux/dm-ioctl.h>
 
+#include "util.h"
 #include "checkers.h"
 #include "vector.h"
 #include "structs.h"
@@ -554,7 +556,7 @@ out:
 }
 
 static int
-dm_get_prefixed_uuid(const char *name, char *uuid)
+dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
 {
        struct dm_task *dmt;
        const char *uuidtmp;
@@ -572,7 +574,7 @@ dm_get_prefixed_uuid(const char *name, char *uuid)
 
        uuidtmp = dm_task_get_uuid(dmt);
        if (uuidtmp)
-               strcpy(uuid, uuidtmp);
+               strlcpy(uuid, uuidtmp, uuid_len);
        else
                uuid[0] = '\0';
 
@@ -582,14 +584,18 @@ uuidout:
        return r;
 }
 
-int dm_get_uuid(const char *name, char *uuid)
+int dm_get_uuid(const char *name, char *uuid, int uuid_len)
 {
-       if (dm_get_prefixed_uuid(name, uuid))
+       char tmp[DM_UUID_LEN];
+
+       if (dm_get_prefixed_uuid(name, tmp, sizeof(tmp)))
                return 1;
 
-       if (!strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN))
-               memmove(uuid, uuid + UUID_PREFIX_LEN,
-                       strlen(uuid + UUID_PREFIX_LEN) + 1);
+       if (!strncmp(tmp, UUID_PREFIX, UUID_PREFIX_LEN))
+               strlcpy(uuid, tmp + UUID_PREFIX_LEN, uuid_len);
+       else
+               uuid[0] = '\0';
+
        return 0;
 }
 
@@ -597,12 +603,12 @@ static int
 is_mpath_part(const char *part_name, const char *map_name)
 {
        char *p;
-       char part_uuid[WWID_SIZE], map_uuid[WWID_SIZE];
+       char part_uuid[DM_UUID_LEN], map_uuid[DM_UUID_LEN];
 
-       if (dm_get_prefixed_uuid(part_name, part_uuid))
+       if (dm_get_prefixed_uuid(part_name, part_uuid, sizeof(part_uuid)))
                return 0;
 
-       if (dm_get_prefixed_uuid(map_name, map_uuid))
+       if (dm_get_prefixed_uuid(map_name, map_uuid, sizeof(map_uuid)))
                return 0;
 
        if (strncmp(part_uuid, "part", 4) != 0)
@@ -1066,7 +1072,7 @@ struct multipath *dm_get_multipath(const char *name)
        if (dm_get_map(name, &mpp->size, NULL))
                goto out;
 
-       dm_get_uuid(name, mpp->wwid);
+       dm_get_uuid(name, mpp->wwid, WWID_SIZE);
        dm_get_info(name, &mpp->dmi);
 
        return mpp;
index db75526cf06425959516d78e5617cd73cc01501b..7557a86b8f967f9a13f21a026a67e35b3c596c89 100644 (file)
@@ -63,7 +63,7 @@ int dm_get_major_minor (const char *name, int *major, int *minor);
 char * dm_mapname(int major, int minor);
 int dm_remove_partmaps (const char * mapname, int need_sync,
                        int deferred_remove);
-int dm_get_uuid(const char *name, char *uuid);
+int dm_get_uuid(const char *name, char *uuid, int uuid_len);
 int dm_get_info (const char * mapname, struct dm_info ** dmi);
 int dm_rename (const char * old, char * new, char * delim, int skip_kpartx);
 int dm_reassign(const char * mapname);
index 96815f8a0cc67dd45c1f5178cb4eec94232d133b..c6eba0f6271a04f4f9385b1ebaba9397ce5af19e 100644 (file)
@@ -374,8 +374,38 @@ declare_ovr_snprint(selector, print_str)
 declare_mp_handler(selector, set_str)
 declare_mp_snprint(selector, print_str)
 
-declare_def_handler(uid_attrs, set_str)
-declare_def_snprint(uid_attrs, print_str)
+static int snprint_uid_attrs(struct config *conf, char *buff, int len,
+                            const void *dummy)
+{
+       char *p = buff;
+       int n, j;
+       const char *att;
+
+       vector_foreach_slot(&conf->uid_attrs, att, j) {
+               n = snprintf(p, len, "%s%s", j == 0 ? "" : " ", att);
+               if (n >= len)
+                       return (p - buff) + n;
+               p += n;
+               len -= n;
+       }
+       return p - buff;
+}
+
+static int uid_attrs_handler(struct config *conf, vector strvec)
+{
+       char *val;
+
+       vector_reset(&conf->uid_attrs);
+       val = set_value(strvec);
+       if (!val)
+               return 1;
+       if (parse_uid_attrs(val, conf))
+               condlog(1, "error parsing uid_attrs: \"%s\"", val);
+       condlog(3, "parsed %d uid_attrs", VECTOR_SIZE(&conf->uid_attrs));
+       FREE(val);
+       return 0;
+}
+
 declare_def_handler(uid_attribute, set_str)
 declare_def_snprint_defstr(uid_attribute, print_str, DEFAULT_UID_ATTRIBUTE)
 declare_ovr_handler(uid_attribute, set_str)
@@ -1618,7 +1648,7 @@ init_keywords(vector keywords)
        install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);
        install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
        install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy);
-       install_keyword("uid_attrs", &def_uid_attrs_handler, &snprint_def_uid_attrs);
+       install_keyword("uid_attrs", &uid_attrs_handler, &snprint_uid_attrs);
        install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute);
        install_keyword("getuid_callout", &def_getuid_handler, &snprint_def_getuid);
        install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name);
index 00ffd063151a20b7626f3f63305e5d69e0fe1586..acca466c4de1ce2e3a7c531cfed4014ec01606a4 100644 (file)
@@ -134,7 +134,8 @@ path_discover (vector pathvec, struct config * conf,
                pp = find_path_by_devt(pathvec, devt);
                if (!pp)
                        return store_pathinfo(pathvec, conf,
-                                             udevice, flag, NULL);
+                                             udevice, flag | DI_BLACKLIST,
+                                             NULL);
        }
        return pathinfo(pp, conf, flag);
 }
@@ -296,8 +297,8 @@ sysfs_get_timeout(const struct path *pp, unsigned int *timeout)
        return 1;
 }
 
-int
-sysfs_get_tgt_nodename (struct path *pp, char * node)
+static int
+sysfs_get_tgt_nodename(struct path *pp, char *node)
 {
        const char *tgtname, *value;
        struct udev_device *parent, *tgtdev;
@@ -321,7 +322,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node)
                if (tgtid >= 0) {
                        pp->sg_id.proto_id = SCSI_PROTOCOL_SAS;
                        pp->sg_id.transport_id = tgtid;
-                       strncpy(node, value, NODE_NAME_SIZE);
+                       strlcpy(node, value, NODE_NAME_SIZE);
                        return 0;
                }
        }
@@ -333,7 +334,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node)
                if (value && !strcmp(value, "usb")) {
                        pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
                        tgtname = udev_device_get_sysname(tgtdev);
-                       strncpy(node, tgtname, strlen(tgtname));
+                       strlcpy(node, tgtname, NODE_NAME_SIZE);
                        condlog(3, "%s: skip USB device %s", pp->dev, node);
                        return 1;
                }
@@ -360,7 +361,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node)
                        if (value) {
                                pp->sg_id.proto_id = SCSI_PROTOCOL_FCP;
                                pp->sg_id.transport_id = tgtid;
-                               strncpy(node, value, NODE_NAME_SIZE);
+                               strlcpy(node, value, NODE_NAME_SIZE);
                                udev_device_unref(tgtdev);
                                return 0;
                        } else
@@ -389,7 +390,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node)
                        if (value) {
                                pp->sg_id.proto_id = SCSI_PROTOCOL_ISCSI;
                                pp->sg_id.transport_id = tgtid;
-                               strncpy(node, value, NODE_NAME_SIZE);
+                               strlcpy(node, value, NODE_NAME_SIZE);
                                udev_device_unref(tgtdev);
                                return 0;
                        }
@@ -906,27 +907,27 @@ get_geometry(struct path *pp)
 static int
 parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len)
 {
-       char *p = NULL;
        int len = get_unaligned_be16(&in[2]);
 
+       /*
+        * Strip leading and trailing whitespace
+        */
+       while (len > 0 && in[len + 3] == ' ')
+               --len;
+       while (len > 0 && in[4] == ' ') {
+               ++in;
+               --len;
+       }
+
        if (len >= out_len) {
                condlog(2, "vpd pg80 overflow, %d/%d bytes required",
-                       len, (int)out_len);
-               len = out_len;
+                       len + 1, (int)out_len);
+               len = out_len - 1;
        }
        if (len > 0) {
                memcpy(out, in + 4, len);
                out[len] = '\0';
        }
-       /*
-        * Strip trailing whitspaces
-        */
-       p = out + len - 1;
-       while (p > out && *p == ' ') {
-               *p = '\0';
-               p--;
-               len --;
-       }
        return len;
 }
 
@@ -1010,51 +1011,45 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                if (vpd_type == 0x2 || vpd_type == 0x3) {
                        int i;
 
+                       assert(out_len >= 2);
                        len = sprintf(out, "%d", vpd_type);
-                       for (i = 0; i < vpd_len; i++) {
+                       if (2 * vpd_len >= out_len - len) {
+                               condlog(1, "%s: WWID overflow, type %d, %d/%lu bytes required",
+                                       __func__, vpd_type,
+                                       2 * vpd_len + len + 1, out_len);
+                               vpd_len = (out_len - len - 1) / 2;
+                       }
+                       for (i = 0; i < vpd_len; i++)
                                len += sprintf(out + len,
                                               "%02x", vpd[i]);
-                               if (len >= out_len)
-                                       break;
-                       }
                } else if (vpd_type == 0x8) {
-                       if (!memcmp("eui.", vpd, 4)) {
+                       if (!memcmp("eui.", vpd, 4))
                                out[0] =  '2';
-                               len = 1;
-                               vpd += 4;
-                               vpd_len -= 4;
-                               for (i = 0; i < vpd_len; i++) {
-                                       len += sprintf(out + len, "%c",
-                                                      tolower(vpd[i]));
-                                       if (len >= out_len)
-                                               break;
-                               }
-                               len = vpd_len + 1;
-                               out[len] = '\0';
-                       } else if (!memcmp("naa.", vpd, 4)) {
+                       else if (!memcmp("naa.", vpd, 4))
                                out[0] = '3';
-                               len = 1;
-                               vpd += 4;
-                               vpd_len -= 4;
-                               for (i = 0; i < vpd_len; i++) {
-                                       len += sprintf(out + len, "%c",
-                                                      tolower(vpd[i]));
-                                       if (len >= out_len)
-                                               break;
-                               }
-                               len = vpd_len + 1;
-                               out[len] = '\0';
-                       } else {
+                       else
                                out[0] = '8';
-                               len = 1;
-                               vpd += 4;
-                               vpd_len -= 4;
-                               if (vpd_len > out_len + 2)
-                                       vpd_len = out_len - 2;
-                               memcpy(out, vpd, vpd_len);
-                               len = vpd_len + 1;
-                               out[len] = '\0';
+
+                       vpd += 4;
+                       len = vpd_len - 4;
+                       while (len > 2 && vpd[len - 2] == '\0')
+                               --len;
+                       if (len > out_len - 1) {
+                               condlog(1, "%s: WWID overflow, type 8/%c, %d/%lu bytes required",
+                                       __func__, out[0], len + 1, out_len);
+                               len = out_len - 1;
                        }
+
+                       if (out[0] == '8')
+                               for (i = 0; i < len; ++i)
+                                       out[1 + i] = vpd[i];
+                       else
+                               for (i = 0; i < len; ++i)
+                                       out[1 + i] = tolower(vpd[i]);
+
+                       /* designator should be 0-terminated, but let's make sure */
+                       out[len] = '\0';
+
                } else if (vpd_type == 0x1) {
                        const unsigned char *p;
                        int p_len;
@@ -1064,8 +1059,11 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                        p = vpd;
                        while ((p = memchr(vpd, ' ', vpd_len))) {
                                p_len = p - vpd;
-                               if (len + p_len > out_len - 1)
-                                       p_len = out_len - len - 2;
+                               if (len + p_len > out_len - 1) {
+                                       condlog(1, "%s: WWID overflow, type 1, %d/%lu bytes required",
+                                               __func__, len + p_len, out_len);
+                                       p_len = out_len - len - 1;
+                               }
                                memcpy(out + len, vpd, p_len);
                                len += p_len;
                                if (len >= out_len - 1) {
@@ -1074,6 +1072,10 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                                }
                                out[len] = '_';
                                len ++;
+                               if (len >= out_len - 1) {
+                                       out[len] = '\0';
+                                       break;
+                               }
                                vpd = p;
                                vpd_len -= p_len;
                                while (vpd && *vpd == ' ') {
@@ -1081,6 +1083,17 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                                        vpd_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, %d/%lu bytes required",
+                                               __func__, len + p_len + 1, out_len);
+                                       p_len = out_len - len - 1;
+                               }
+                               memcpy(out + len, vpd, p_len);
+                               len += p_len;
+                               out[len] = '\0';
+                       }
                        if (len > 1 && out[len - 1] == '_') {
                                out[len - 1] = '\0';
                                len--;
@@ -1645,8 +1658,18 @@ get_prio (struct path * pp)
        old_prio = pp->priority;
        pp->priority = prio_getprio(p, pp, checker_timeout);
        if (pp->priority < 0) {
-               condlog(3, "%s: %s prio error", pp->dev, prio_name(p));
-               pp->priority = PRIO_UNDEF;
+               /* this changes pp->offline, but why not */
+               int state = path_offline(pp);
+
+               if (state == PATH_DOWN || state == PATH_PENDING) {
+                       pp->priority = old_prio;
+                       condlog(3, "%s: %s prio error in state %d, keeping prio = %d",
+                               pp->dev, prio_name(p), state, pp->priority);
+               } else {
+                       condlog(3, "%s: %s prio error in state %d",
+                               pp->dev, prio_name(p), state);
+                       pp->priority = PRIO_UNDEF;
+               }
                return 1;
        }
        condlog((old_prio == pp->priority ? 4 : 3), "%s: %s prio = %u",
@@ -1779,8 +1802,7 @@ static ssize_t uid_fallback(struct path *pp, int path_state,
 {
        ssize_t len = -1;
 
-       if (pp->bus == SYSFS_BUS_SCSI &&
-           !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) {
+       if (pp->bus == SYSFS_BUS_SCSI) {
                len = get_vpd_uid(pp);
                *origin = "sysfs";
                if (len < 0 && path_state == PATH_UP) {
@@ -1810,11 +1832,22 @@ static ssize_t uid_fallback(struct path *pp, int path_state,
        return len;
 }
 
-static int has_uid_fallback(struct path *pp)
+static bool has_uid_fallback(struct path *pp)
 {
+       /*
+        * Falling back to direct WWID determination is dangerous
+        * if uid_attribute is set to something non-standard.
+        * Allow it only if it's either the default, or if udev
+        * has been disabled by setting 'uid_attribute ""'.
+        */
+       if (!pp->uid_attribute)
+               return false;
        return ((pp->bus == SYSFS_BUS_SCSI &&
-                !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) ||
-               pp->bus == SYSFS_BUS_NVME);
+                (!strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE) ||
+                 !strcmp(pp->uid_attribute, ""))) ||
+               (pp->bus == SYSFS_BUS_NVME &&
+                (!strcmp(pp->uid_attribute, DEFAULT_NVME_UID_ATTRIBUTE) ||
+                 !strcmp(pp->uid_attribute, ""))));
 }
 
 int
@@ -1853,20 +1886,20 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev,
                        len = strlen(pp->wwid);
                origin = "callout";
        } else {
+               bool udev_available = udev && pp->uid_attribute
+                       && *pp->uid_attribute;
 
-               if (udev && pp->uid_attribute) {
+               if (udev_available) {
                        len = get_udev_uid(pp, pp->uid_attribute, udev);
-                       origin = "udev";
                        if (len <= 0)
                                condlog(1,
                                        "%s: failed to get udev uid: %s",
                                        pp->dev, strerror(-len));
-
-               } else if (pp->bus == SYSFS_BUS_SCSI) {
-                       len = get_vpd_uid(pp);
-                       origin = "sysfs";
+                       else
+                               origin = "udev";
                }
-               if (len <= 0 && allow_fallback && has_uid_fallback(pp)) {
+               if ((!udev_available || (len <= 0 && allow_fallback))
+                   && has_uid_fallback(pp)) {
                        used_fallback = 1;
                        len = uid_fallback(pp, path_state, &origin);
                }
@@ -1912,7 +1945,7 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
                        return PATHINFO_SKIPPED;
                }
                if (is_claimed_by_foreign(pp->udev) ||
-                   filter_property(conf, pp->udev, 4) > 0)
+                   filter_property(conf, pp->udev, 4, pp->uid_attribute) > 0)
                        return PATHINFO_SKIPPED;
        }
 
@@ -2002,9 +2035,6 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
                } else {
                        condlog(3, "%s: path inaccessible", pp->dev);
                        pp->chkrstate = pp->state = path_state;
-                       if (path_state == PATH_PENDING ||
-                           path_state == PATH_DOWN)
-                               pp->priority = 0;
                }
        }
 
index ac13ec06035085e8f4ba61a0668ec6ac328984c3..b856a07f4c4b66bcabc9a13a2c925c1be4ef8b20 100644 (file)
 #define WORD_SIZE 64
 
 static int
-merge_words (char ** dst, char * word, int space)
+merge_words(char **dst, char *word)
 {
        char * p = *dst;
-       int len;
+       int len, dstlen;
 
-       len = strlen(*dst) + strlen(word) + space;
-       *dst = REALLOC(*dst, len + 1);
+       dstlen = strlen(*dst);
+       len = dstlen + strlen(word) + 2;
+       *dst = REALLOC(*dst, len);
 
        if (!*dst) {
                free(p);
                return 1;
        }
 
-       p = *dst;
-
-       while (*p != '\0')
-               p++;
-
-       while (space) {
-               *p = ' ';
-               p++;
-               space--;
-       }
-       strncpy(p, word, strlen(word) + 1);
+       p = *dst + dstlen;
+       *p = ' ';
+       ++p;
+       strncpy(p, word, len - dstlen - 1);
 
        return 0;
 }
@@ -163,7 +157,7 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
                if (!word)
                        return 1;
 
-               if (merge_words(&mpp->features, word, 1)) {
+               if (merge_words(&mpp->features, word)) {
                        FREE(word);
                        return 1;
                }
@@ -187,7 +181,7 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
                if (!word)
                        return 1;
 
-               if (merge_words(&mpp->hwhandler, word, 1)) {
+               if (merge_words(&mpp->hwhandler, word)) {
                        FREE(word);
                        return 1;
                }
@@ -248,9 +242,8 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
 
                        num_pg_args = atoi(word);
 
-                       if (merge_words(&mpp->selector, word, 1)) {
+                       if (merge_words(&mpp->selector, word))
                                goto out1;
-                       }
                        FREE(word);
                } else {
                        p += get_word(p, NULL);
@@ -315,11 +308,11 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
                                if (!pp)
                                        goto out1;
 
-                               strncpy(pp->dev_t, word, BLK_DEV_SIZE - 1);
-                               strncpy(pp->dev, devname, FILE_NAME_SIZE - 1);
+                               strlcpy(pp->dev_t, word, BLK_DEV_SIZE);
+                               strlcpy(pp->dev, devname, FILE_NAME_SIZE);
                                if (strlen(mpp->wwid)) {
-                                       strncpy(pp->wwid, mpp->wwid,
-                                               WWID_SIZE - 1);
+                                       strlcpy(pp->wwid, mpp->wwid,
+                                               WWID_SIZE);
                                }
                                /* Only call this in multipath client mode */
                                if (!is_daemon && store_path(pathvec, pp))
@@ -327,8 +320,8 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
                        } else {
                                if (!strlen(pp->wwid) &&
                                    strlen(mpp->wwid))
-                                       strncpy(pp->wwid, mpp->wwid,
-                                               WWID_SIZE - 1);
+                                       strlcpy(pp->wwid, mpp->wwid,
+                                               WWID_SIZE);
                        }
                        FREE(word);
 
@@ -340,23 +333,21 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
                         * in the get_dm_mpvec() code path
                         */
                        if (!strlen(mpp->wwid))
-                               strncpy(mpp->wwid, pp->wwid,
-                                       WWID_SIZE - 1);
+                               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))
-                               strncpy(pp->wwid, mpp->wwid,
-                                       WWID_SIZE - 1);
+                               strlcpy(pp->wwid, mpp->wwid, WWID_SIZE);
 
                        /*
                         * 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);
-                               strncpy(pp->wwid, mpp->wwid, WWID_SIZE);
+                               strlcpy(pp->wwid, mpp->wwid, WWID_SIZE);
                        }
 
                        pgp->id ^= (long)pp;
index 1d964333a2c6459ee03bfc6c965de79fdd17f639..96e8b25d17737205d85b6f5ed3727d841dd204c6 100644 (file)
@@ -38,7 +38,7 @@
         *
         * COMPANY_NAME
         *
-        * Maintainer: XXX <email>
+        * Maintainer: NAME <email>
         */
        {
                /* Product Name */
@@ -88,7 +88,7 @@ static struct hwentry default_hw[] = {
                /* Generic NVMe */
                .vendor        = "NVME",
                .product       = ".*",
-               .uid_attribute = "ID_WWN",
+               .uid_attribute = DEFAULT_NVME_UID_ATTRIBUTE,
                .checker_name  = NONE,
                .retain_hwhandler = RETAIN_HWHANDLER_OFF,
        },
@@ -708,7 +708,7 @@ static struct hwentry default_hw[] = {
                 /*
                 * DE Series
                 *
-                * Maintainer: ng-eseries-upstream-maintainers@netapp.com
+                * Maintainer: NetApp RDAC team <ng-eseries-upstream-maintainers@netapp.com>
                 */
                 .vendor        = "LENOVO",
                 .product       = "DE_Series",
index 92ef7cf52ea995d0849faa238f13ea06ebf54d92..e00c5ffff8280db90567066342dcb799aefb89a5 100644 (file)
@@ -345,17 +345,13 @@ set_value(vector strvec)
                if (alloc)
                        memcpy(alloc, str, size);
                else
-                       condlog(0, "can't allocate memeory for option '%s'",
-                               (char *)VECTOR_SLOT(strvec, 0));
+                       goto oom;
                return alloc;
        }
        /* Even empty quotes counts as a value (An empty string) */
        alloc = (char *) MALLOC(sizeof (char));
-       if (!alloc) {
-               condlog(0, "can't allocate memeory for option '%s'",
-                       (char *)VECTOR_SLOT(strvec, 0));
-               return NULL;
-       }
+       if (!alloc)
+               goto oom;
        for (i = 2; i < VECTOR_SIZE(strvec); i++) {
                str = VECTOR_SLOT(strvec, i);
                if (!str) {
@@ -373,15 +369,17 @@ set_value(vector strvec)
                alloc = REALLOC(alloc, sizeof (char) * len);
                if (!alloc) {
                        FREE(tmp);
-                       condlog(0, "can't allocate memeory for option '%s'",
-                               (char *)VECTOR_SLOT(strvec, 0));
-                       return NULL;
+                       goto oom;
                }
                if (*alloc != '\0')
                        strncat(alloc, " ", 1);
-               strncat(alloc, str, strlen(str));
+               strncat(alloc, str, len - strlen(alloc) - 1);
        }
        return alloc;
+oom:
+       condlog(0, "can't allocate memory for option '%s'",
+               (char *)VECTOR_SLOT(strvec, 0));
+       return NULL;
 }
 
 /* non-recursive configuration stream handler */
index ac2596ada442b2aa9a3caa6499e52b9fea81a54c..660768a4d64069e96fd2bad493c362deacbfc9dc 100644 (file)
@@ -312,7 +312,7 @@ out:
 int group_by_prio(struct multipath *mp)
 {
        int i;
-       unsigned int prio;
+       int prio;
        struct path * pp;
        struct pathgroup * pgp;
        vector pathvec = NULL;
index 0590218d0df1d362f9dbc511a73a2c467c58f367..87de1f9745ae73a6d586b030979f55ef77f75dd0 100644 (file)
@@ -5,6 +5,7 @@
 #include <sys/stat.h>
 
 #include "debug.h"
+#include "util.h"
 #include "prio.h"
 
 static LIST_HEAD(prioritizers);
@@ -166,7 +167,7 @@ void prio_get (char *multipath_dir, struct prio * dst, char * name, char * args)
 
        strncpy(dst->name, src->name, PRIO_NAME_LEN);
        if (args)
-               strncpy(dst->args, args, PRIO_ARGS_LEN - 1);
+               strlcpy(dst->args, args, PRIO_ARGS_LEN);
        dst->getprio = src->getprio;
        dst->handle = NULL;
 
index 990d935f53faec98403df779aaea576d1067eed7..2673d9d923cfc8c1943a22f1a76a4f4e98b75516 100644 (file)
@@ -164,7 +164,7 @@ int get_ana_info(struct path * pp, unsigned int timeout)
                                   ana_log, ana_log_len);
        pthread_cleanup_pop(1);
        if (rc >= 0)
-               condlog(3, "%s: ana state = %02x [%s]", pp->dev, rc,
+               condlog(4, "%s: ana state = %02x [%s]", pp->dev, rc,
                        aas_print_string(rc));
        return rc;
 }
index e6263e9b8874bbfe9694ddd9e638fe2250820055..6af2513d048e3da568ae6f0fdb0f7edee0b88a01 100644 (file)
@@ -536,9 +536,9 @@ int select_getuid(struct config *conf, struct path *pp)
 {
        const char *origin;
 
-       pp->uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, pp->dev);
+       pp->uid_attribute = get_uid_attribute_by_attrs(conf, pp->dev);
        if (pp->uid_attribute) {
-               origin = "(setting: multipath.conf defaults section)";
+               origin = "(setting: multipath.conf defaults section / uid_attrs)";
                goto out;
        }
 
index db5d19daec9b301ddcfa8ecacd957a7a8b1f9c13..c43b58fb5af3ea066be5ebd2b71d9d0c26da2cfc 100644 (file)
@@ -2,6 +2,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "util.h"
 #include "checkers.h"
 #include "vector.h"
 #include "defaults.h"
@@ -350,7 +351,7 @@ find_existing_alias (struct multipath * mpp,
 
        vector_foreach_slot (vecs->mpvec, mp, i)
                if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) {
-                       strncpy(mpp->alias_old, mp->alias, WWID_SIZE - 1);
+                       strlcpy(mpp->alias_old, mp->alias, WWID_SIZE);
                        return;
                }
 }
index f73de8cc745e95f96e8187e9ef6cee7c0212cb24..8f7b2ef58d0a4790fb6323761035730f92b20a88 100644 (file)
@@ -163,13 +163,12 @@ uevent_get_wwid(struct uevent *uev)
 
        conf = get_multipath_config();
        pthread_cleanup_push(put_multipath_config, conf);
-       uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, uev->kernel);
+       uid_attribute = get_uid_attribute_by_attrs(conf, uev->kernel);
        pthread_cleanup_pop(1);
 
        val = uevent_get_env_var(uev, uid_attribute);
        if (val)
                uev->wwid = val;
-       FREE(uid_attribute);
 }
 
 bool
@@ -179,7 +178,7 @@ uevent_need_merge(void)
        bool need_merge = false;
 
        conf = get_multipath_config();
-       if (conf->uid_attrs)
+       if (VECTOR_SIZE(&conf->uid_attrs) > 0)
                need_merge = true;
        put_multipath_config(conf);
 
index 5b838d51b01d1e647a158c2c40051c623ae39efd..28cbf4b93de2236c082601067ff7f1848f9ff97f 100644 (file)
@@ -188,7 +188,7 @@ int devt2devname(char *devname, int devname_len, char *devt)
                                        return 1;
                                }
                                p++;
-                               strncpy(devname, p, devname_len);
+                               strlcpy(devname, p, devname_len);
                                return 0;
                        }
                }
@@ -273,48 +273,6 @@ dev_t parse_devt(const char *dev_t)
        return makedev(maj, min);
 }
 
-char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev)
-{
-       char *uid_attribute;
-       char *uid_attr_record;
-       char *dev;
-       char *attr;
-       char *tmp;
-       int  count;
-
-       if(!uid_attrs || !path_dev)
-               return NULL;
-
-       count = get_word(uid_attrs, &uid_attr_record);
-       while (uid_attr_record) {
-               tmp = strrchr(uid_attr_record, ':');
-               if (!tmp) {
-                       free(uid_attr_record);
-                       if (!count)
-                               break;
-                       uid_attrs += count;
-                       count = get_word(uid_attrs, &uid_attr_record);
-                       continue;
-               }
-               dev = uid_attr_record;
-               attr = tmp + 1;
-               *tmp = '\0';
-
-               if(!strncmp(path_dev, dev, strlen(dev))) {
-                       uid_attribute = STRDUP(attr);
-                       free(uid_attr_record);
-                       return uid_attribute;
-               }
-
-               free(uid_attr_record);
-               if (!count)
-                       break;
-               uid_attrs += count;
-               count = get_word(uid_attrs, &uid_attr_record);
-       }
-       return NULL;
-}
-
 void
 setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached)
 {
index 1e0d832c60330273457e63e759e61b18bd42f7e0..693991c1db73c886cf66b8ab5137869ce5c38638 100644 (file)
@@ -15,7 +15,6 @@ size_t strlcat(char *dst, const char *src, size_t size);
 int devt2devname (char *, int, char *);
 dev_t parse_devt(const char *dev_t);
 char *convert_dev(char *dev, int is_path_device);
-char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev);
 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);
index 7e5a1449840f2673d22d04e79985c3486ec4658e..9b4e9784763fb2e3192bd866491f0e427ed11d24 100644 (file)
@@ -66,9 +66,12 @@ int ux_socket_listen(const char *name)
        memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_LOCAL;
        addr.sun_path[0] = '\0';
-       len = strlen(name) + 1 + sizeof(sa_family_t);
-       strncpy(&addr.sun_path[1], name, len);
+       len = strlen(name) + 1;
+       if (len >= sizeof(addr.sun_path))
+               len = sizeof(addr.sun_path) - 1;
+       memcpy(&addr.sun_path[1], name, len);
 
+       len += sizeof(sa_family_t);
        if (bind(fd, (struct sockaddr *)&addr, len) == -1) {
                condlog(3, "Couldn't bind to ux_socket, error %d", errno);
                close(fd);
index 1edd65f13630dbca5445d8695f3d533cdc1275ac..f751c30932fddd47f0a92a3e1db220500f320e9b 100644 (file)
@@ -20,8 +20,8 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000801
-#define DATE_CODE    0x041213
+#define VERSION_CODE 0x000802
+#define DATE_CODE    0x070313
 
 #define PROG    "multipath-tools"
 
index 53e7951151b3bafe3bbc3504d2fb793e47b69f6a..ef748125063b10ea8ddd439c59a37e84838aa9c2 100644 (file)
@@ -294,7 +294,8 @@ should_multipath(struct path *pp1, vector pathvec, vector mpvec)
                char tmp_wwid[WWID_SIZE];
                struct multipath *mp = find_mp_by_wwid(mpvec, pp1->wwid);
 
-               if (mp != NULL && dm_get_uuid(mp->alias, tmp_wwid) == 0 &&
+               if (mp != NULL &&
+                   dm_get_uuid(mp->alias, tmp_wwid, WWID_SIZE) == 0 &&
                    !strncmp(tmp_wwid, pp1->wwid, WWID_SIZE)) {
                        condlog(3, "wwid %s is already multipathed, keeping it",
                                pp1->wwid);
index 10cba45256731f6704de5076aade6e2f42d29966..5ad06a9724b37aabc9501979d635ef52a262e84e 100644 (file)
@@ -15,6 +15,8 @@
 #include <pthread.h>
 #include <ctype.h>
 #include <string.h>
+#include <errno.h>
+#include "version.h"
 
 static const char * pr_type_strs[] = {
        "obsolete [0]",
@@ -59,9 +61,101 @@ void rcu_unregister_thread_memb(void) {}
 
 struct udev *udev;
 
-int main (int argc, char * argv[])
+static int verbose, loglevel, noisy;
+
+static int handle_args(int argc, char * argv[], int line);
+
+static int do_batch_file(const char *batch_fn)
+{
+       char command[] = "mpathpersist";
+       const int ARGV_CHUNK = 2;
+       const char delims[] = " \t\n";
+       size_t len = 0;
+       char *line = NULL;
+       ssize_t n;
+       int nline = 0;
+       int argl = ARGV_CHUNK;
+       FILE *fl;
+       char **argv = calloc(argl, sizeof(*argv));
+       int ret = MPATH_PR_SUCCESS;
+
+       if (argv == NULL)
+               return MPATH_PR_OTHER;
+
+       fl = fopen(batch_fn, "r");
+       if (fl == NULL) {
+               fprintf(stderr, "unable to open %s: %s\n",
+                       batch_fn, strerror(errno));
+               free(argv);
+               return MPATH_PR_SYNTAX_ERROR;
+       } else {
+               if (verbose >= 2)
+                       fprintf(stderr, "running batch file %s\n",
+                               batch_fn);
+       }
+
+       while ((n = getline(&line, &len, fl)) != -1) {
+               char *_token, *token;
+               int argc = 0;
+               int rv;
+
+               nline++;
+               argv[argc++] = command;
+
+               if (line[n-1] == '\n')
+                       line[n-1] = '\0';
+               if (verbose >= 3)
+                       fprintf(stderr, "processing line %d: %s\n",
+                               nline, line);
+
+               for (token = strtok_r(line, delims, &_token);
+                    token != NULL && *token != '#';
+                    token = strtok_r(NULL, delims, &_token)) {
+
+                       if (argc >= argl) {
+                               int argn = argl + ARGV_CHUNK;
+                               char **tmp;
+
+                               tmp = realloc(argv, argn * sizeof(*argv));
+                               if (tmp == NULL)
+                                       break;
+                               argv = tmp;
+                               argl = argn;
+                       }
+
+                       if (argc == 1 && !strcmp(token, command))
+                               continue;
+
+                       argv[argc++] = token;
+               }
+
+               if (argc <= 1)
+                       continue;
+
+               if (verbose >= 2) {
+                       int i;
+
+                       fprintf(stderr, "## file %s line %d:", batch_fn, nline);
+                       for (i = 0; i < argc; i++)
+                               fprintf(stderr, " %s", argv[i]);
+                       fprintf(stderr, "\n");
+               }
+
+               optind = 0;
+               rv = handle_args(argc, argv, nline);
+               if (rv != MPATH_PR_SUCCESS)
+                       ret = rv;
+       }
+
+       fclose(fl);
+       free(argv);
+       free(line);
+       return ret;
+}
+
+static int handle_args(int argc, char * argv[], int nline)
 {
-       int fd, c, res;
+       int fd, c;
        const char *device_name = NULL;
        int num_prin_sa = 0;
        int num_prout_sa = 0;
@@ -82,51 +176,41 @@ int main (int argc, char * argv[])
        int prin = 1;
        int prin_sa = -1;
        int prout_sa = -1;
-       int verbose = 0;
-       int loglevel = 0;
-       int noisy = 0;
        int num_transport =0;
+       char *batch_fn = NULL;
        void *resp = NULL;
        struct transportid * tmp;
-       struct config *conf;
-
-       if (optind == argc)
-       {
-
-               fprintf (stderr, "No parameter used\n");
-               usage ();
-               exit (1);
-       }
-
-       if (getuid () != 0)
-       {
-               fprintf (stderr, "need to be root\n");
-               exit (1);
-       }
-
-       udev = udev_new();
-       conf = mpath_lib_init();
-       if(!conf) {
-               udev_unref(udev);
-               exit(1);
-       }
 
        memset(transportids, 0, MPATH_MX_TIDS * sizeof(struct transportid));
-       multipath_conf = conf;
 
        while (1)
        {
                int option_index = 0;
 
-               c = getopt_long (argc, argv, "v:Cd:hHioYZK:S:PAT:skrGILcRX:l:",
+               c = getopt_long (argc, argv, "v:Cd:hHioYZK:S:PAT:skrGILcRX:l:f:",
                                long_options, &option_index);
                if (c == -1)
                        break;
 
                switch (c)
                {
+                       case 'f':
+                               if (nline != 0) {
+                                       fprintf(stderr,
+                                               "ERROR: -f option not allowed in batch file\n");
+                                       ret = MPATH_PR_SYNTAX_ERROR;
+                                       goto out;
+                               }
+                               if (batch_fn != NULL) {
+                                       fprintf(stderr,
+                                               "ERROR: -f option can be used at most once\n");
+                                       ret = MPATH_PR_SYNTAX_ERROR;
+                                       goto out;
+                               }
+                               batch_fn = strdup(optarg);
+                               break;
                        case 'v':
-                               if (1 != sscanf (optarg, "%d", &loglevel))
+                               if (nline == 0 && 1 != sscanf (optarg, "%d", &loglevel))
                                {
                                        fprintf (stderr, "bad argument to '--verbose'\n");
                                        return MPATH_PR_SYNTAX_ERROR;
@@ -265,7 +349,6 @@ int main (int argc, char * argv[])
 
                        default:
                                fprintf(stderr, "unrecognised switch " "code 0x%x ??\n", c);
-                               usage ();
                                ret = MPATH_PR_SYNTAX_ERROR;
                                goto out;
                }
@@ -283,27 +366,29 @@ int main (int argc, char * argv[])
                {
                        for (; optind < argc; ++optind)
                                fprintf (stderr, "Unexpected extra argument: %s\n", argv[optind]);
-                       usage ();
                        ret = MPATH_PR_SYNTAX_ERROR;
                        goto out;
                }
        }
 
-       /* set verbosity */
-       noisy = (loglevel >= 3) ? 1 : hex;
-       verbose = (loglevel >= 3)? 3: loglevel;
+       if (nline == 0) {
+               /* set verbosity */
+               noisy = (loglevel >= 3) ? 1 : hex;
+               verbose = (loglevel >= 3)? 3: loglevel;
+               ret = mpath_persistent_reserve_init_vecs(verbose);
+               if (ret != MPATH_PR_SUCCESS)
+                       goto out;
+       }
 
-       if ((prout_flag + prin_flag) == 0)
+       if ((prout_flag + prin_flag) == 0 && batch_fn == NULL)
        {
                fprintf (stderr, "choose either '--in' or '--out' \n");
-               usage ();
                ret = MPATH_PR_SYNTAX_ERROR;
                goto out;
        }
        if ((prout_flag + prin_flag) > 1)
        {
                fprintf (stderr, "choose either '--in' or '--out' \n");
-               usage ();
                ret = MPATH_PR_SYNTAX_ERROR;
                goto out;
        }
@@ -334,21 +419,19 @@ int main (int argc, char * argv[])
                {
                        fprintf (stderr,
                                        " No service action given for Persistent Reserve IN\n");
-                       usage();
                        ret = MPATH_PR_SYNTAX_ERROR;
                }
                else if (num_prin_sa > 1)
                {
                        fprintf (stderr, " Too many service actions given; choose "
                                        "one only\n");
-                       usage();
                        ret = MPATH_PR_SYNTAX_ERROR;
                }
        }
        else
        {
-               usage ();
-               ret = MPATH_PR_SYNTAX_ERROR;
+               if (batch_fn == NULL)
+                       ret = MPATH_PR_SYNTAX_ERROR;
                goto out;
        }
 
@@ -356,7 +439,6 @@ int main (int argc, char * argv[])
        {
                fprintf (stderr, " --relative-target-port"
                                " only useful with --register-move\n");
-               usage ();
                ret = MPATH_PR_SYNTAX_ERROR;
                goto out;
        }
@@ -378,7 +460,6 @@ int main (int argc, char * argv[])
        if (device_name == NULL)
        {
                fprintf (stderr, "No device name given \n");
-               usage ();
                ret = MPATH_PR_SYNTAX_ERROR;
                goto out;
        }
@@ -403,7 +484,7 @@ int main (int argc, char * argv[])
                        goto out;
                }
 
-               ret = mpath_persistent_reserve_in (fd, prin_sa, resp, noisy, verbose);
+               ret = __mpath_persistent_reserve_in (fd, prin_sa, resp, noisy);
                if (ret != MPATH_PR_SUCCESS )
                {
                        fprintf (stderr, "Persistent Reserve IN command failed\n");
@@ -463,8 +544,8 @@ int main (int argc, char * argv[])
                }
 
                /* PROUT commands other than 'register and move' */
-               ret = mpath_persistent_reserve_out (fd, prout_sa, 0, prout_type,
-                               paramp, noisy, verbose);
+               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];
@@ -487,17 +568,57 @@ int main (int argc, char * argv[])
                printf("PR out: command failed\n");
        }
 
-       res = close (fd);
-       if (res < 0)
+       close (fd);
+
+out :
+       if (ret == MPATH_PR_SYNTAX_ERROR) {
+               free(batch_fn);
+               if (nline == 0)
+                       usage();
+               else
+                       fprintf(stderr, "syntax error on line %d in batch file\n",
+                               nline);
+       } else if (batch_fn != NULL) {
+               int rv = do_batch_file(batch_fn);
+
+               free(batch_fn);
+               ret = ret == 0 ? rv : ret;
+       }
+       if (nline == 0)
+               mpath_persistent_reserve_free_vecs();
+       return (ret >= 0) ? ret : MPATH_PR_OTHER;
+}
+
+int main(int argc, char *argv[])
+{
+       int ret;
+
+       if (optind == argc)
+       {
+
+               fprintf (stderr, "No parameter used\n");
+               usage ();
+               exit (1);
+       }
+
+       if (getuid () != 0)
        {
-               mpath_lib_exit(conf);
+               fprintf (stderr, "need to be root\n");
+               exit (1);
+       }
+
+       udev = udev_new();
+       multipath_conf = mpath_lib_init();
+       if(!multipath_conf) {
                udev_unref(udev);
-               return MPATH_PR_FILE_ERROR;
+               exit(1);
        }
 
-out :
-       mpath_lib_exit(conf);
+       ret = handle_args(argc, argv, 0);
+
+       mpath_lib_exit(multipath_conf);
        udev_unref(udev);
+
        return (ret >= 0) ? ret : MPATH_PR_OTHER;
 }
 
@@ -687,6 +808,7 @@ void mpath_print_buf_readfullstat( struct prin_resp *pr_buff)
 
 static void usage(void)
 {
+       fprintf(stderr, VERSION_STRING);
        fprintf(stderr,
                        "Usage: mpathpersist [OPTIONS] [DEVICE]\n"
                        " Options:\n"
@@ -698,6 +820,7 @@ static void usage(void)
                        "                   4           Informational messages with trace enabled\n"
                        "    --clear|-C                 PR Out: Clear\n"
                        "    --device=DEVICE|-d DEVICE  query or change DEVICE\n"
+                       "    --batch-file|-f FILE       run commands from FILE\n"
                        "    --help|-h                  output this usage message\n"
                        "    --hex|-H                   output response in hex\n"
                        "    --in|-i                    request PR In command \n"
@@ -706,10 +829,8 @@ static void usage(void)
                        "    --param-aptpl|-Z           PR Out parameter 'APTPL'\n"
                        "    --read-keys|-k             PR In: Read Keys\n"
                        "    --param-rk=RK|-K RK        PR Out parameter reservation key\n"
-                       "    --param-sark=SARK|-S SARK  PR Out parameter service "
-                       "action\n"
-                       "                               reservation key (SARK is in "
-                       "hex)\n"
+                       "    --param-sark=SARK|-S SARK  PR Out parameter service action\n"
+                       "                               reservation key (SARK is in hex)\n"
                        "    --preempt|-P               PR Out: Preempt\n"
                        "    --preempt-abort|-A         PR Out: Preempt and Abort\n"
                        "    --prout-type=TYPE|-T TYPE  PR Out command type\n"
@@ -721,11 +842,14 @@ static void usage(void)
                        "    --release|-L               PR Out: Release\n"
                        "    --report-capabilities|-c   PR In: Report Capabilities\n"
                        "    --reserve|-R               PR Out: Reserve\n"
-                       "    --transport-id=TIDS|-X TIDS  TransportIDs can be mentioned \n"
-                       "                               in several forms\n"
+                       "    --transport-id=TIDS|-X TIDS  TransportIDs can be mentioned\n"
+                       "                                 in several forms\n"
+                       "    --alloc-length=LEN|-l LEN  PR In: maximum allocation length\n"
                        " Examples:\n"
                        "     mpathpersist --out --register --param-sark=123abc --prout-type=5 /dev/mapper/mpath9\n"
-                       "     mpathpersist -i -k /dev/mapper/mpath9\n"  );
+                       "     mpathpersist -i -k /dev/mapper/mpath9\n"
+                       "     mpathpersist --out --reserve --param-sark=123abc --prout-type=8 -d /dev/mapper/mpath9\n"
+                       "     mpathpersist -i -s -d /dev/mapper/mpath9\n");
 }
 
 void
index beb8a21b20ba0e7c26edcfb0ddb3bfc67a32f6b3..bfbb82e290f67e516c919421e8a6dfe99f0fa2f3 100644 (file)
@@ -2,6 +2,7 @@ static struct option long_options[] = {
        {"verbose", 1, NULL, 'v'},
        {"clear", 0, NULL, 'C'},
        {"device", 1, NULL, 'd'},
+       {"batch-file", 1, NULL, 'f' },
        {"help", 0, NULL, 'h'},
        {"hex", 0, NULL, 'H'},
        {"in", 0, NULL, 'i'},
index 885491dda927d174e30cf4796c081f6f1b2d9779..882043ae040429caf0f625dbc733a4a54c507cf6 100644 (file)
@@ -5,7 +5,7 @@
 .\"
 .\" ----------------------------------------------------------------------------
 .
-.TH MPATHPERSIST 8 2016-10-30 "Linux"
+.TH MPATHPERSIST 8 2019-05-27 "Linux"
 .
 .
 .\" ----------------------------------------------------------------------------
@@ -71,6 +71,11 @@ Informational messages with trace enabled.
 Query or change DEVICE.
 .
 .TP
+.BI \--batch-file=\fIDEVICE\fB|\-f " FILE"
+Read commands from \fIFILE\fR. See section \(dqBATCH FILES\(dq below. This
+option can be given at most once.
+.
+.TP
 .B \--help|\-h
 Output this usage message.
 .
@@ -167,18 +172,114 @@ PR In: maximum allocation length. LEN is a decimal number between 0 and 8192.
 .SH EXAMPLE
 .\" ----------------------------------------------------------------------------
 .
-.TP
-Register the Service Action Reservation Key for the /dev/mapper/mpath9 device:
-\fBmpathpersist --out --register --param-sark=\fI123abc \fB--prout-type=\fI5 /dev/mapper/mpath9\fR
-.TP
-Read the Service Action Reservation Key for the /dev/mapper/mpath9 device:
+.PP
+Register the key \(dq123abc\(dq for the /dev/mapper/mpath9 device:
+.RS
+\fBmpathpersist --out --register --param-sark=\fI123abc /dev/mapper/mpath9\fR
+.RE
+.PP
+Read registered reservation keys for the /dev/mapper/mpath9 device:
+.RS
 \fBmpathpersist -i -k \fI/dev/mapper/mpath9\fR
-.TP
-Reserve the Service Action Reservation Key for the /dev/mapper/mpath9 device:
-\fBmpathpersist --out --reserve --param-sark=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
-.TP
+.RE
+.PP
+Create a reservation for the /dev/mapper/mpath9 device with the given
+reservation key:
+.RS
+\fBmpathpersist --out --reserve --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
+.RE
+.PP
 Read the reservation status of the /dev/mapper/mpath9 device:
+.RS
 \fBmpathpersist -i -s -d \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Release the previously created reservation (note that the prout-type needs to
+be the same as above):
+.RS
+\fBmpathpersist --out --release --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Remove the current key registered for this host (i.e. reset it to 0):
+.RS
+\fBmpathpersist --out --register-ignore -K \fI123abc\fB -S \fI0\fB \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Remove current reservation, and unregister all registered keys from all I_T nexuses:
+.RS
+\fBmpathpersist -oCK \fI123abc \fI/dev/mapper/mpath9\fR
+.RE
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH BATCH FILES
+.\" ----------------------------------------------------------------------------
+.
+.PP
+The option \fI--batch-file\fR (\fI-f\fR) sets an input file to be processed
+by \fBmpathpersist\fR. Grouping commands in batch files can provide a speed
+improvement in particular on large installments, because \fBmpathpersist\fR
+needs to scan existing paths and maps only once during startup.
+.
+.PP
+The input file is a text file that is parsed
+line by line. Every line of the file is interpreted as a command line
+(i.e. list of options and parameters) for \fBmpathpersist\fR. Options
+and parameters are separated by one or more whitespace characters (space or TAB).
+Lines can, but do not have to, begin with the word \(dqmpathpersist\(dq.
+The \(dq#\(dq character, either at the beginning of the line or following
+some whitespace, denotes the start of a comment that lasts until the end of the
+line. Empty lines are allowed. Continuation of mpathpersist commands over
+multiple lines is not supported.
+.
+.PP
+All options listed in this man page, except \fI-f\fR and
+\fI-v\fR, are allowed in batch files. Both short and long option formats may be used.
+Using the  \fI-f\fR option inside the batch file is an error. The \fI-v\fR
+option is ignored in batch files.
+.
+.PP
+The multipath map on which to act must be specified on every input line, e.g. using the \fI-d\fR option.
+Commands acting on different multipath maps may be combined in a
+batch file, and multiple commands may act on the same multipath
+map. Commands are executed one by one, so
+that commands further down in the file see status changes caused by previous
+commands.
+If \fBmpathpersist\fR encounters an error while processing a line in the
+batch file, batch file processing is \fBnot\fR aborted; subsequent commands
+are executed nonetheless. The exit status of \fBmpathpersist\fR is the status
+of the first failed command, or 0 if all commands succeeded.
+.
+.PP
+If other options and parameters are used along with
+\fI-f\fR on the \fBmpathpersist\fR command line, the command line will be executed first, followed
+by the commands from the the batch file.
+.
+.PP
+Below is an example of a valid batch input file.
+.
+.PP
+.RS
+.EX
+# This is an mpathpersist input file.
+# Short and long forms of the same command
+-i -k /dev/dm-1 # short form, this comment is ignored
+mpathpersist --in --read-keys --device=/dev/dm-1
+
+# Mixing of long and short options, variable white space
+  --out  --register    -S  abcde     /dev/dm-1
+
+# Mixing of commands for different maps
+-ir /dev/dm-0
+-ir /dev/dm-1
+
+mpathpersist --out --param-rk abcde --reserve --prout-type 5 /dev/dm-1
+# This should now show a reservation
+-ir /dev/dm-1
+-oCK abcde /dev/dm-1
+--in --read-reservation /dev/dm-1
+.EE
+.RE
 .
 .
 .\" ----------------------------------------------------------------------------
index 69141dbcf36403f8cc30a6b99e1a2cfbcd989c6b..96a114680a0c5f593a7405187238587a4fe033ba 100644 (file)
@@ -133,11 +133,15 @@ usage (char * progname)
 {
        fprintf (stderr, VERSION_STRING);
        fprintf (stderr, "Usage:\n");
-       fprintf (stderr, "  %s [-a|-c|-w|-W] [-d] [-r] [-i] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname);
-       fprintf (stderr, "  %s -l|-ll|-f [-v lvl] [-b fil] [-R num] [dev]\n", progname);
-       fprintf (stderr, "  %s -F [-v lvl] [-R num]\n", progname);
-       fprintf (stderr, "  %s [-t|-T]\n", progname);
-       fprintf (stderr, "  %s -h\n", progname);
+       fprintf (stderr, "  %s [-v level] [-B|-d|-i|-q|-r] [-b file] [-p policy] [device]\n", progname);
+       fprintf (stderr, "  %s [-v level] [-R retries] -f device\n", progname);
+       fprintf (stderr, "  %s [-v level] [-R retries] -F\n", progname);
+       fprintf (stderr, "  %s [-v level] [-l|-ll] [device]\n", progname);
+       fprintf (stderr, "  %s [-v level] [-a|-w] device\n", progname);
+       fprintf (stderr, "  %s [-v level] -W\n", progname);
+       fprintf (stderr, "  %s [-v level] [-i] [-c|-C] device\n", progname);
+       fprintf (stderr, "  %s [-v level] [-i] [-u|-U]\n", progname);
+       fprintf (stderr, "  %s [-h|-t|-T]\n", progname);
        fprintf (stderr,
                "\n"
                "Where:\n"
@@ -159,23 +163,28 @@ usage (char * progname)
                "  -b fil  bindings file location\n"
                "  -w      remove a device from the wwids file\n"
                "  -W      reset the wwids file include only the current devices\n"
-               "  -p pol  force all maps to specified path grouping policy :\n"
+               "  -R num  number of times to retry removes of in-use devices\n"
+               "  -u      check if the device specified in the program environment should be a\n"
+               "          path in a multipath device\n"
+               "  -U      check if the device specified in the program environment is a\n"
+               "          multipath device with usable paths, see -C flag\n"
+               "  -p pol  force all maps to specified path grouping policy:\n"
                "          . failover            one path per priority group\n"
                "          . multibus            all paths in one priority group\n"
                "          . group_by_serial     one priority group per serial\n"
                "          . group_by_prio       one priority group per priority lvl\n"
                "          . group_by_node_name  one priority group per target node\n"
-               "  -v lvl  verbosity level\n"
+               "  -v lvl  verbosity level:\n"
                "          . 0 no output\n"
                "          . 1 print created devmap names only\n"
                "          . 2 default verbosity\n"
                "          . 3 print debug information\n"
-               "  -R num  number of times to retry removes of in-use devices\n"
-               "  dev     action limited to:\n"
-               "          . multipath named 'dev' (ex: mpath0) or\n"
-               "          . multipath whose wwid is 'dev' (ex: 60051..)\n"
-               "          . multipath including the path named 'dev' (ex: /dev/sda)\n"
-               "          . multipath including the path with maj:min 'dev' (ex: 8:0)\n"
+               "  device  action limited to:\n"
+               "          . multipath named 'device' (ex: mpath0)\n"
+               "          . multipath whose wwid is 'device' (ex: 60051...)\n"
+               "          . multipath including the path named 'device' (ex: /dev/sda or\n"
+               "            /dev/dm-0)\n"
+               "          . multipath including the path with maj:min 'device' (ex: 8:0)\n"
                );
 
 }
@@ -850,6 +859,32 @@ 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[])
 {
@@ -1028,17 +1063,13 @@ main (int argc, char *argv[])
        }
        if (cmd == CMD_VALID_PATH &&
            dev_type == DEV_UEVENT) {
-               int fd;
-
-               fd = mpath_connect();
-               if (fd == -1) {
+               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;
                        }
-               } else
-                       mpath_disconnect(fd);
+               }
        }
 
        if (cmd == CMD_REMOVE_WWID && !dev) {
index 6f08980aa40eee8e097cdfc5f11628facaa5b3b1..f7d21b4c77c30c420001e410ad0f7d57d61aa1c4 100644 (file)
@@ -241,23 +241,32 @@ The default is: \fBfailover\fR
 .
 .TP
 .B uid_attrs
-The udev attribute providing a unique path identifier for corresponding
-type of path devices. If this field is configured and matched with type
-of device, it would override any other methods providing for device
-unique identifier in config file, and it would activate merging uevents
-according to the identifier to promote effiecncy in processing uevents.
-It has no default value, if you want to identify path by udev attribute
-and to activate merging uevents for SCSI and DASD devices, you can set
-its value as: \fIuid_attrs "sd:ID_SERIAL dasd:ID_UID"\fR.
+.
+Setting this option activates \fBmerging uevents\fR by WWID, which may improve
+uevent processing effiency. Moreover, it's an alternative method to configure
+the udev properties to use for determining unique path identifiers (WWIDs).
 .RS
-.TP
-The default is: \fB<unset>\fR
+.PP
+The value of this option is a space separated list of records like
+\(dq\fItype:ATTR\fR\(dq, where \fItype\fR is matched against the beginning
+of the device node name (e.g. \fIsd:ATTR\fR matches \fIsda\fR), and
+\fIATTR\fR is the name of the udev property to use for matching devices.
+.PP
+If this option is configured and matches the device
+node name of a device, it overrides any other configured  methods for
+determining the WWID for this device.
+.PP
+The default is: \fB<unset>\fR. To enable uevent merging, set it e.g. to
+\(dqsd:ID_SERIAL dasd:ID_UID nvme:ID_WWN\(dq.
 .RE
 .
 .
 .TP
 .B uid_attribute
-The udev attribute providing a unique path identifier.
+The udev attribute providing a unique path identifier (WWID). If
+\fIuid_attribute\fR is set to the empty string, WWID determination is done
+using the \fIsysfs\fR method rather then using udev (not recommended in
+production; see \fBWWID generation\fR below).
 .RS
 .TP
 The default is: \fBID_SERIAL\fR, for SCSI devices
@@ -1241,6 +1250,14 @@ otherwise they're treated as blacklisted, and the message
 .
 .RS
 .PP
+.B Note:
+The behavior of this option has changed in \fBmultipath-tools\fR 0.8.2
+compared to previous versions.
+Blacklisting by missing properties is only applied to devices which do have the
+property specified by \fIuid_attribute\fR (e.g. \fIID_SERIAL\fR)
+set. Previously, it was applied to every device, possibly causing devices to be
+blacklisted because of temporary I/O error conditions.
+.PP
 The default \fIblacklist exception\fR is: \fB(SCSI_IDENT_|ID_WWN)\fR, causing
 well-behaved SCSI devices and devices that provide a WWN (World Wide Number)
 to be included, and all others to be excluded.
@@ -1627,8 +1644,8 @@ WWID is assumed to point to the same device.
 The WWID is generated by four methods (in the order of preference):
 .TP 17
 .B uid_attrs
-The WWID is derived from udev attributes by matching the device node name. See
-description of \fIuid_attrs\fR in the defaults section above.
+The WWID is derived from udev attributes by matching the device node name; cf
+\fIuid_attrs\fR above.
 .TP
 .B getuid_callout
 Use the specified external program; cf \fIgetuid_callout\fR above.
@@ -1643,10 +1660,15 @@ does not need to call any external programs here. However, under
 certain circumstances udev might not be able to generate the requested
 variable.
 .TP
-.B vpd_pg83
-If none of the \fIgetuid_callout\fR or \fIuid_attribute\fR parameters
-are present multipath will try to use the sysfs attribute
-\fIvpd_pg83\fR to generate the WWID.
+.B sysfs
+Try to determine the WWID from sysfs attributes.
+For SCSI devices, this means reading the Vital Product Data (VPD) page
+\(dqDevice Identification\(dq (0x83).
+.PP
+The default settings (using udev and \fBuid_attribute\fR configured from
+the built-in hardware table) should work fine
+in most scenarios. Users who want to enable uevent merging must set
+\fBuid_attrs\fR.
 .
 .
 .\" ----------------------------------------------------------------------------
index ca176a9964210d295d1b554c2e74d38f3d9d0130..17795b61e3fc845471469c6723e316736ab1b700 100644 (file)
@@ -467,6 +467,8 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout )
 
        if (r) {
                *reply = genhelp_handler(cmd, r);
+               if (*reply == NULL)
+                       return EINVAL;
                *len = strlen(*reply) + 1;
                return 0;
        }
@@ -474,9 +476,11 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout )
        h = find_handler(fingerprint(cmdvec));
 
        if (!h || !h->fn) {
+               free_keys(cmdvec);
                *reply = genhelp_handler(cmd, EINVAL);
+               if (*reply == NULL)
+                       return EINVAL;
                *len = strlen(*reply) + 1;
-               free_keys(cmdvec);
                return 0;
        }
 
index f3fa077aab1ba490cb0b733c872f3add815e2207..32dcffac1de2d774bbdab8bdc1e1d5d5bf3be7bf 100644 (file)
@@ -100,15 +100,16 @@ enum {
                        if (m >= MAX_REPLY_LEN) {               \
                                condlog(1, "Warning: max reply length exceeded"); \
                                free(tmp);                      \
-                               r = NULL;                       \
+                               (r) = NULL;                     \
+                       } else {                                \
+                               (r) = REALLOC((r), (m) * 2);    \
+                               if ((r)) {                      \
+                                       memset((r) + (m), 0, (m)); \
+                                       (m) *= 2;               \
+                               }                               \
+                               else                            \
+                                       free(tmp);              \
                        }                                       \
-                       (r) = REALLOC((r), (m) * 2);            \
-                       if ((r)) {                              \
-                               memset((r) + (m), 0, (m));      \
-                               (m) *= 2;                       \
-                       }                                       \
-                       else                                    \
-                               free(tmp);                      \
                }                                               \
        } while (0)
 
index 60e17d6835eba32932881694460716055666e065..4c32d953483eee9bd966edee30297caee9005168 100644 (file)
@@ -730,7 +730,7 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
                conf = get_multipath_config();
                pthread_cleanup_push(put_multipath_config, conf);
                r = store_pathinfo(vecs->pathvec, conf,
-                                  udevice, DI_ALL, &pp);
+                                  udevice, DI_ALL | DI_BLACKLIST, &pp);
                pthread_cleanup_pop(1);
                udev_device_unref(udevice);
                if (!pp) {
index f203d77f250bc91c512cc3185f45de861522fc52..7a5cd115791bb869af240dc84fa26361303c5556 100644 (file)
@@ -220,6 +220,33 @@ static void config_cleanup(void *arg)
        pthread_mutex_unlock(&config_lock);
 }
 
+/*
+ * If the current status is @oldstate, wait for at most @ms milliseconds
+ * for the state to change, and return the new state, which may still be
+ * @oldstate.
+ */
+enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
+                                           unsigned long ms)
+{
+       enum daemon_status st;
+       struct timespec tmo;
+
+       if (oldstate == DAEMON_SHUTDOWN)
+               return DAEMON_SHUTDOWN;
+
+       pthread_mutex_lock(&config_lock);
+       pthread_cleanup_push(config_cleanup, NULL);
+       st = running_state;
+       if (st == oldstate && clock_gettime(CLOCK_MONOTONIC, &tmo) == 0) {
+               tmo.tv_nsec += ms * 1000 * 1000;
+               normalize_timespec(&tmo);
+               (void)pthread_cond_timedwait(&config_cond, &config_lock, &tmo);
+               st = running_state;
+       }
+       pthread_cleanup_pop(1);
+       return st;
+}
+
 /* must be called with config_lock held */
 static void __post_config_state(enum daemon_status state)
 {
@@ -380,7 +407,7 @@ set_multipath_wwid (struct multipath * mpp)
        if (strlen(mpp->wwid))
                return;
 
-       dm_get_uuid(mpp->alias, mpp->wwid);
+       dm_get_uuid(mpp->alias, mpp->wwid, WWID_SIZE);
 }
 
 static void set_no_path_retry(struct multipath *mpp)
index e5c1398f1c5db3e5d95431013873164201dbdac1..7bb8463fd00b8a291eff36dc67651cb83a125693 100644 (file)
@@ -20,6 +20,8 @@ extern int uxsock_timeout;
 
 void exit_daemon(void);
 const char * daemon_status(void);
+enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
+                                           unsigned long ms);
 int need_to_delay_reconfig (struct vectors *);
 int reconfigure (struct vectors *);
 int ev_add_path (struct path *, struct vectors *, int);
index 94c3f97341f4e3c4a34121980dfd5937e7c9883e..edac7a92b450b57a478408e9239ed6ef56c1e273 100644 (file)
@@ -25,6 +25,7 @@ multipathd \- Multipath daemon.
 .RB [\| \-v\ \c
 .IR verbosity \|]
 .RB [\| \-B \|]
+.RB [\| \-w \|]
 .
 .
 .\" ----------------------------------------------------------------------------
@@ -77,6 +78,12 @@ be viewed by entering '\fIhelp\fR'. When you are finished entering commands, pre
 multipathd. See
 .BR multipath.conf(5).
 .
+.TP
+.B \-w
+Since kernel 4.14 a new device-mapper event polling interface is used for updating
+multipath devices on dmevents. Use this flag to force it to use the old event
+waiting method, based on creating a seperate thread for each device.
+.
 .
 .
 .\" ----------------------------------------------------------------------------
index 773bc878a9f47befcdd81a3bc366125e068e7449..04cbb7c71e81004900611182e0c0a5cb44a45742 100644 (file)
@@ -249,6 +249,18 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
                        continue;
                }
 
+               /*
+                * Client connection. We shouldn't answer while we're
+                * configuring - nothing may be configured yet.
+                * But we can't wait forever either, because this thread
+                * must handle signals. So wait a short while only.
+                */
+               if (wait_for_state_change_if(DAEMON_CONFIGURE, 10)
+                   == DAEMON_CONFIGURE) {
+                       handle_signals(false);
+                       continue;
+               }
+
                /* see if a client wants to speak to us */
                for (i = 1; i < num_clients + 1; i++) {
                        if (polls[i].revents & POLLIN) {
index 5895f157e0ed2f03ddd4ec0cb609faa3fc751832..eb8d699620687f23f4b5d14b36c170bb364e1f75 100644 (file)
@@ -11,6 +11,7 @@
 #include <signal.h>
 #include <urcu.h>
 
+#include "util.h"
 #include "vector.h"
 #include "memory.h"
 #include "checkers.h"
@@ -210,7 +211,7 @@ int start_waiter_thread (struct multipath *mpp, struct vectors *vecs)
        if (!wp)
                goto out;
 
-       strncpy(wp->mapname, mpp->alias, WWID_SIZE - 1);
+       strlcpy(wp->mapname, mpp->alias, WWID_SIZE);
        wp->vecs = vecs;
 
        if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) {
index ef900866f6fbc62464e67f51c19caee6679bf42f..bf159b2d70f187e834d1f3e499be320ee8c6ae4f 100644 (file)
@@ -3,7 +3,7 @@ include ../Makefile.inc
 CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
 LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka
 
-TESTS := uevent parser util dmevents hwtable blacklist unaligned
+TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd
 
 .SILENT: $(TESTS:%=%.o)
 .PRECIOUS: $(TESTS:%=%-test)
@@ -25,6 +25,8 @@ hwtable-test_OBJDEPS := ../libmultipath/discovery.o ../libmultipath/blacklist.o
 hwtable-test_LIBDEPS := -ludev -lpthread -ldl
 blacklist-test_OBJDEPS := ../libmultipath/blacklist.o
 blacklist-test_LIBDEPS := -ludev
+vpd-test_OBJDEPS :=  ../libmultipath/discovery.o
+vpd-test_LIBDEPS := -ludev -lpthread -ldl
 
 lib/libchecktur.so:
        mkdir lib
index 54d568f5a8eddfe4ca07a044c259aed472cbe05f..362c44d93f663ffd904ab6809c3f26f14d012792 100644 (file)
@@ -267,7 +267,7 @@ static void test_property_blacklist(void **state)
        static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", 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),
+       assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
                         MATCH_PROPERTY_BLIST);
 }
 
@@ -281,17 +281,23 @@ static void test_property_whitelist(void **state)
        static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } };
        conf.elist_property = blist_property_wwn;
        expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n");
-       assert_int_equal(filter_property(&conf, &udev, 3),
+       assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
                         MATCH_PROPERTY_BLIST_EXCEPT);
 }
 
 static void test_property_missing(void **state)
 {
-       static struct udev_device udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", NULL } };
+       static struct udev_device udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", "ID_SERIAL", NULL } };
        conf.blist_property = blist_property_wwn;
        expect_condlog(3, "sdb: blacklisted, udev property missing\n");
-       assert_int_equal(filter_property(&conf, &udev, 3),
+       assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
                         MATCH_PROPERTY_BLIST_MISSING);
+       assert_int_equal(filter_property(&conf, &udev, 3, "ID_BLAH"),
+                        MATCH_NOTHING);
+       assert_int_equal(filter_property(&conf, &udev, 3, ""),
+                        MATCH_NOTHING);
+       assert_int_equal(filter_property(&conf, &udev, 3, NULL),
+                        MATCH_NOTHING);
 }
 
 struct udev_device test_udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } };
@@ -347,16 +353,25 @@ static void test_filter_path_wwid(void **state)
        assert_int_equal(filter_path(&conf, &test_pp), MATCH_WWID_BLIST);
 }
 
-struct udev_device miss_udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", NULL } };
+struct udev_device miss_udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", "ID_SERIAL", NULL } };
 
 struct path miss1_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI,
                        .udev = &miss_udev,
+                        .uid_attribute = "ID_SERIAL",
                        .sg_id.proto_id = SCSI_PROTOCOL_ISCSI,
                        .vendor_id = "foo", .product_id = "baz",
                        .wwid = "plugh" };
 
 struct path miss2_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI,
                        .udev = &test_udev,
+                        .uid_attribute = "ID_SERIAL",
+                       .sg_id.proto_id = SCSI_PROTOCOL_ISCSI,
+                       .vendor_id = "foo", .product_id = "baz",
+                       .wwid = "plugh" };
+
+struct path miss3_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI,
+                       .udev = &miss_udev,
+                        .uid_attribute = "ID_EGGS",
                        .sg_id.proto_id = SCSI_PROTOCOL_ISCSI,
                        .vendor_id = "foo", .product_id = "baz",
                        .wwid = "plugh" };
@@ -387,6 +402,19 @@ static void test_filter_path_missing2(void **state)
                         MATCH_NOTHING);
 }
 
+/* Here we use a different uid_attribute which is also missing, thus
+   the path is not blacklisted */
+static void test_filter_path_missing3(void **state)
+{
+       conf.blist_property = blist_property_wwn;
+       conf.blist_devnode = blist_devnode_sdb;
+       conf.blist_device = blist_device_foo_bar;
+       conf.blist_protocol = blist_protocol_fcp;
+       conf.blist_wwid = blist_wwid_xyzzy;
+       assert_int_equal(filter_path(&conf, &miss3_pp),
+                        MATCH_NOTHING);
+}
+
 static void test_filter_path_whitelist(void **state)
 {
        conf.elist_property = blist_property_wwn;
@@ -495,6 +523,7 @@ int test_blacklist(void)
                test_and_reset(test_filter_path_wwid),
                test_and_reset(test_filter_path_missing1),
                test_and_reset(test_filter_path_missing2),
+               test_and_reset(test_filter_path_missing3),
                test_and_reset(test_filter_path_whitelist),
                test_and_reset(test_filter_path_whitelist_property),
                test_and_reset(test_filter_path_whitelist_devnode),
index 07d970ea1d1dc1b019ca56533febefbe67fbf212..8add5eb74b67897e1d182bac1023033409413397 100644 (file)
@@ -3,9 +3,8 @@
 
 /* Required globals */
 struct udev *udev;
-int logsink = 0;
+int logsink = -1;
 struct config conf = {
-       .uid_attrs = "sd:ID_BOGUS",
        .verbosity = 4,
 };
 
index ad863b0899ed46065745b4124979df916a4cecda..977a5663afae96b5442cfe3db3b7f715a6b5c5cb 100644 (file)
@@ -54,7 +54,7 @@ struct hwt_state {
 
 static struct config *_conf;
 struct udev *udev;
-int logsink;
+int logsink = -1;
 
 struct config *get_multipath_config(void)
 {
@@ -457,7 +457,7 @@ static void replicate_config(const struct hwt_state *hwt, bool local)
        vector hwtable;
        struct config *conf;
 
-       condlog(1, "--- %s: replicating %s configuration", __func__,
+       condlog(3, "--- %s: replicating %s configuration", __func__,
                local ? "local" : "full");
 
        conf = get_multipath_config();
@@ -571,7 +571,7 @@ static void test_internal_nvme(const struct hwt_state *hwt)
        mp = mock_multipath(pp);
        assert_ptr_not_equal(mp, NULL);
        TEST_PROP(checker_name(&pp->checker), NONE);
-       TEST_PROP(pp->uid_attribute, "ID_WWN");
+       TEST_PROP(pp->uid_attribute, DEFAULT_NVME_UID_ATTRIBUTE);
        assert_int_equal(mp->pgpolicy, DEFAULT_PGPOLICY);
        assert_int_equal(mp->no_path_retry, DEFAULT_NO_PATH_RETRY);
        assert_int_equal(mp->retain_hwhandler, RETAIN_HWHANDLER_OFF);
index b0d0bfda5c07ae78c3df997578057aacb9a73e7c..f4afd9b483bd9cd1118c916d7806904407c0064d 100644 (file)
@@ -43,7 +43,11 @@ void uevent_get_wwid(struct uevent *uev);
 
 static int setup_uev(void **state)
 {
+       static char test_uid_attrs[] =
+               "dasd:ID_SPAM   sd:ID_BOGUS nvme:ID_EGGS    ";
+
        struct uevent *uev = alloc_uevent();
+       struct config *conf;
 
        if (uev == NULL)
                return -1;
@@ -51,11 +55,16 @@ static int setup_uev(void **state)
        *state = uev;
        uev->kernel = "sdo";
        uev->envp[0] = "MAJOR=" str(MAJOR);
+       uev->envp[1] = "ID_SPAM=nonsense";
        uev->envp[1] = "ID_BOGUS=" WWID;
        uev->envp[2] = "MINOR=" str(MINOR);
        uev->envp[3] = "DM_NAME=" DM_NAME;
        uev->envp[4] = "DISK_RO=" str(DISK_RO);
        uev->envp[5] = NULL;
+
+       conf = get_multipath_config();
+       parse_uid_attrs(test_uid_attrs, conf);
+       put_multipath_config(conf);
        return 0;
 }
 
@@ -86,6 +95,23 @@ static void test_ro_good(void **state)
        assert_int_equal(uevent_get_disk_ro(uev), DISK_RO);
 }
 
+static void test_uid_attrs(void **state)
+{
+       /* see test_uid_attrs above */
+       struct config *conf = get_multipath_config();
+       vector attrs = &conf->uid_attrs;
+
+       assert_int_equal(VECTOR_SIZE(attrs), 3);
+       assert_null(get_uid_attribute_by_attrs(conf, "hda"));
+       assert_string_equal("ID_BOGUS",
+                           get_uid_attribute_by_attrs(conf, "sdaw"));
+       assert_string_equal("ID_SPAM",
+                           get_uid_attribute_by_attrs(conf, "dasdu"));
+       assert_string_equal("ID_EGGS",
+                           get_uid_attribute_by_attrs(conf, "nvme2n4"));
+       put_multipath_config(conf);
+}
+
 static void test_wwid(void **state)
 {
        struct uevent *uev = *state;
@@ -271,6 +297,7 @@ int test_uevent_get_XXX(void)
                cmocka_unit_test(test_minor_good),
                cmocka_unit_test(test_ro_good),
                cmocka_unit_test(test_dm_name_good),
+               cmocka_unit_test(test_uid_attrs),
                cmocka_unit_test(test_wwid),
                cmocka_unit_test(test_major_bad_0),
                cmocka_unit_test(test_major_bad_1),
index e6d4b9ab0c37783436b0e80d9e844e87e4e78c63..4e04a48054aeb76f875014d0336b943e66e57742 100644 (file)
@@ -258,10 +258,152 @@ int test_basenamecpy(void)
        return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
+static const char src_str[] = "Hello";
+
+/* strlcpy with length 0 */
+static void test_strlcpy_0(void **state)
+{
+       char tst[] = "word";
+       int rc;
+
+       rc = strlcpy(tst, src_str, 0);
+       assert_int_equal(rc, strlen(src_str));
+       assert_string_equal(tst, "word");
+}
+
+/* strlcpy with length 1 */
+static void test_strlcpy_1(void **state)
+{
+       char tst[] = "word";
+       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");
+}
+
+/* strlcpy with length 2 */
+static void test_strlcpy_2(void **state)
+{
+       char tst[] = "word";
+       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");
+}
+
+/* strlcpy with dst length < src length */
+static void test_strlcpy_3(void **state)
+{
+       char tst[] = "word";
+       int rc;
+
+       rc = strlcpy(tst, src_str, sizeof(tst));
+       assert_int_equal(rc, strlen(src_str));
+       assert_int_equal(sizeof(tst) - 1, strlen(tst));
+       assert_true(strncmp(tst, src_str, sizeof(tst) - 1) == 0);
+}
+
+/* strlcpy with dst length > src length */
+static void test_strlcpy_4(void **state)
+{
+       static const char old[] = "0123456789";
+       char *tst;
+       int rc;
+
+       tst = strdup(old);
+       rc = strlcpy(tst, src_str, sizeof(old));
+       assert_int_equal(rc, strlen(src_str));
+       assert_string_equal(src_str, tst);
+       assert_string_equal(tst + sizeof(src_str), old + sizeof(src_str));
+       free(tst);
+}
+
+/* strlcpy with dst length = src length, dst not terminated */
+static void test_strlcpy_5(void **state)
+{
+       char *tst;
+       int rc;
+
+       tst = malloc(sizeof(src_str));
+       memset(tst, 'f', sizeof(src_str));
+
+       rc = strlcpy(tst, src_str, sizeof(src_str));
+       assert_int_equal(rc, strlen(src_str));
+       assert_string_equal(src_str, tst);
+
+       free(tst);
+}
+
+/* strlcpy with dst length > src length, dst not terminated */
+static void test_strlcpy_6(void **state)
+{
+       char *tst;
+       int rc;
+
+       tst = malloc(sizeof(src_str) + 2);
+       memset(tst, 'f', sizeof(src_str) + 2);
+
+       rc = strlcpy(tst, src_str, sizeof(src_str) + 2);
+       assert_int_equal(rc, strlen(src_str));
+       assert_string_equal(src_str, tst);
+       assert_int_equal(tst[sizeof(src_str)], 'f');
+       assert_int_equal(tst[sizeof(src_str) + 1], 'f');
+
+       free(tst);
+}
+
+/* strlcpy with empty src */
+static void test_strlcpy_7(void **state)
+{
+       char tst[] = "word";
+       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");
+}
+
+/* strlcpy with empty src, length 0 */
+static void test_strlcpy_8(void **state)
+{
+       char tst[] = "word";
+       static const char empty[] = "";
+       int rc;
+
+       rc = strlcpy(tst, empty, 0);
+       assert_int_equal(rc, strlen(empty));
+       assert_string_equal("word", tst);
+}
+
+static int test_strlcpy(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_strlcpy_0),
+               cmocka_unit_test(test_strlcpy_1),
+               cmocka_unit_test(test_strlcpy_2),
+               cmocka_unit_test(test_strlcpy_3),
+               cmocka_unit_test(test_strlcpy_4),
+               cmocka_unit_test(test_strlcpy_5),
+               cmocka_unit_test(test_strlcpy_6),
+               cmocka_unit_test(test_strlcpy_7),
+               cmocka_unit_test(test_strlcpy_8),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
 int main(void)
 {
        int ret = 0;
 
        ret += test_basenamecpy();
+       ret += test_strlcpy();
        return ret;
 }
diff --git a/tests/vpd.c b/tests/vpd.c
new file mode 100644 (file)
index 0000000..d9f80ea
--- /dev/null
@@ -0,0 +1,790 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (c) 2019 Martin Wilck, SUSE Linux GmbH, Nuremberg */
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <regex.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <scsi/sg.h>
+#include "unaligned.h"
+#include "debug.h"
+#include "vector.h"
+#include "structs.h"
+#include "discovery.h"
+#include "globals.c"
+
+#define VPD_BUFSIZ 4096
+
+struct vpdtest {
+       unsigned char vpdbuf[VPD_BUFSIZ];
+       char wwid[WWID_SIZE];
+};
+
+static int setup(void **state)
+{
+       struct vpdtest *vt = malloc(sizeof(*vt));
+
+       if (vt == NULL)
+               return -1;
+       *state = vt;
+       return 0;
+}
+
+static int teardown(void **state)
+{
+       struct vpdtest *vt = *state;
+
+       free(vt);
+       *state = NULL;
+       return 0;
+}
+
+/* vendor_id should have less than 8 chars to test space handling */
+static const char vendor_id[] = "Linux";
+static const char test_id[] =
+       "A123456789AbcDefB123456789AbcDefC123456789AbcDefD123456789AbcDef";
+
+int __wrap_ioctl(int fd, unsigned long request, void *param)
+{
+       int len;
+       struct sg_io_hdr *io_hdr;
+       unsigned char *val;
+
+       len = mock();
+       io_hdr = (struct sg_io_hdr *)param;
+       assert_in_range(len, 0, io_hdr->dxfer_len);
+       val = mock_ptr_type(unsigned char *);
+       io_hdr->status = 0;
+       memcpy(io_hdr->dxferp, val, len);
+       return 0;
+}
+
+
+/**
+ * create_vpd80() - create a "unit serial number" VPD page.
+ * @buf:       VPD buffer
+ * @bufsiz:    length of VPD buffer
+ * @id:                input ID
+ * @size:      value for the "page length" field
+ * @len:       actual number of characters to use from @id
+ *
+ * If len < size, the content will be right aligned, as mandated by the
+ * SPC spec.
+ *
+ * Return:     VPD length.
+ */
+static int create_vpd80(unsigned char *buf, size_t bufsiz, const char *id,
+                       int size, int len)
+{
+       assert_true(len <= size);
+
+       memset(buf, 0, bufsiz);
+       buf[1] = 0x80;
+       put_unaligned_be16(size, buf + 2);
+
+       memset(buf + 4, ' ', size - len);
+       memcpy(buf + 4 + size - len, id, len);
+       return size + 4;
+}
+
+static int _hex2bin(const char hx)
+{
+       assert_true(isxdigit(hx));
+       if (hx >= '0' && hx <= '9')
+               return hx - '0';
+       if (hx >= 'a' && hx <= 'f')
+               return hx - 'a' + 10;
+       if (hx >= 'A' && hx <= 'F')
+               return hx - 'A' + 10;
+       return -1;
+}
+
+static void hex2bin(unsigned char *dst, const char *src,
+                   size_t dstlen, size_t srclen)
+{
+       const char *sc;
+       unsigned char *ds;
+
+       assert(srclen % 2 == 0);
+       for (sc = src, ds = dst;
+            sc < src + srclen &&  ds < dst + dstlen;
+            sc += 2, ++ds)
+               *ds = 16 * _hex2bin(sc[0]) + _hex2bin(sc[1]);
+}
+
+/**
+ * create_t10_vendor_id_desc() - Create T10 vendor ID
+ * @desc:      descriptor buffer
+ * @id:                input ID
+ * @n:         number of characters to use for ID
+ *             (includes 8 bytes for vendor ID!)
+ *
+ * Create a "T10 vendor specific ID" designation descriptor.
+ * The vendor field (8 bytes) is filled with vendor_id (above).
+ *
+ * Return:     descriptor length.
+ */
+static int create_t10_vendor_id_desc(unsigned char *desc,
+                                    const char *id, size_t n)
+{
+       int vnd_len = sizeof(vendor_id) - 1;
+
+       /* code set: ascii */
+       desc[0] = 2;
+       /* type: 10 vendor ID */
+       desc[1] = 1;
+       desc[2] = 0;
+       desc[3] = n;
+
+       memcpy(desc + 4, (const unsigned char *)vendor_id, vnd_len);
+       memset(desc + 4 + vnd_len, ' ', 8 - vnd_len);
+       memcpy(desc + 4 + 8, (const unsigned char *)id, n - 8);
+
+       return n + 4;
+}
+
+/**
+ * create_eui64_desc() - create EUI64 descriptor.
+ * @desc, @id: see above.
+ * @n:         number of bytes (8, 12, or 16).
+ *
+ * Create an EUI64 designation descriptor.
+ *
+ * Return:     descriptor length.
+ */
+static int create_eui64_desc(unsigned char *desc,
+                                const char *id, size_t n)
+{
+       assert_true(n == 8 || n == 12 || n == 16);
+
+       /* code set: binary */
+       desc[0] = 1;
+       /* type: EUI64 */
+       desc[1] = 2;
+       desc[2] = 0;
+       desc[3] = n;
+
+       hex2bin(desc + 4, id, n, 2 * n);
+       return n + 4;
+}
+
+/**
+ * create_naa_desc() - create an NAA designation descriptor
+ * @desc, @id: see above.
+ * @naa:       Name Address Authority field (2, 3, 5, or 6).
+ *
+ * Return:     descriptor length.
+ */
+static int create_naa_desc(unsigned char *desc,
+                              const char *id, int naa)
+{
+       assert_true(naa == 2 || naa == 3 || naa == 5 || naa == 6);
+
+       /* code set: binary */
+       desc[0] = 1;
+       /* type: NAA */
+       desc[1] = 3;
+       desc[2] = 0;
+       desc[4] = _hex2bin(id[0]) | (naa << 4);
+       switch (naa) {
+       case 2:
+       case 3:
+       case 5:
+               hex2bin(desc + 5, id + 1, 7, 14);
+               desc[3] = 8;
+               return 12;
+       case 6:
+               hex2bin(desc + 5, id + 1, 15, 30);
+               desc[3] = 16;
+               return 20;
+       default:
+               return 0;
+       }
+}
+
+/* type and flags for SCSI name string designation descriptor */
+enum {
+       STR_EUI = 0,
+       STR_NAA,
+       STR_IQN,
+       STR_MASK = 0xf,
+       ZERO_LAST = 0x10,  /* flag to zero out some bytes at string end */
+};
+
+static const char * const str_prefix[] = {
+       [STR_EUI] = "eui.",
+       [STR_NAA] = "naa.",
+       [STR_IQN] = "iqn.",
+};
+
+static const char byte0[] = {
+       [STR_EUI] = '2',
+       [STR_NAA] = '3',
+       [STR_IQN] = '8',
+};
+
+/**
+ * create_scsi_string_desc() - create a SCSI name string descriptor.
+ * @desc, @id: see above.
+ * @typ:       one of STR_EUI, STR_NAA, STR_IQN, possibly ORd with ZERO_LAST
+ * @maxlen:    number of characters to use from input ID.
+ *
+ * If ZERO_LAST is set, zero out the last byte.
+ *
+ * Return:     descriptor length.
+ */
+static int create_scsi_string_desc(unsigned char *desc,
+                                  const char *id, int typ, int maxlen)
+{
+       int len, plen;
+       int type = typ & STR_MASK;
+
+       /* code set: UTF-8 */
+       desc[0] = 3;
+       /* type: SCSI string */
+       desc[1] = 8;
+       desc[2] = 0;
+
+       assert_in_range(type, STR_EUI, STR_IQN);
+       assert_true(maxlen % 4 == 0);
+       len = snprintf((char *)(desc + 4), maxlen, "%s%s",
+                      str_prefix[type], id);
+       if (len > maxlen)
+               len = maxlen;
+       /* zero-pad */
+       if (typ & ZERO_LAST)
+               len -= 2;
+       plen = 4 * ((len - 1) / 4) + 4;
+       memset(desc + 4 + len, '\0', plen - len);
+       desc[3] = plen;
+       return plen + 4;
+}
+
+/**
+ * create_vpd83() - create "device identification" VPD page
+ * @buf, @bufsiz, @id: see above.
+ * @type:      descriptor type to use (1, 2, 3, 8)
+ * @parm:      opaque parameter (e.g. means "naa" for NAA type)
+ * @len:       designator length (exact meaning depends on designator type)
+ *
+ * Create a "device identification" VPD page with a single
+ * designation descriptor.
+ *
+ * Return:     VPD page length.
+ */
+static int create_vpd83(unsigned char *buf, size_t bufsiz, const char *id,
+                       uint8_t type, int parm, int len)
+{
+       unsigned char *desc;
+       int n = 0;
+
+       memset(buf, 0, bufsiz);
+       buf[1] = 0x83;
+
+       desc = buf + 4;
+       switch (type) {
+       case 1:
+               n = create_t10_vendor_id_desc(desc, id, len);
+               break;
+       case 2:
+               n = create_eui64_desc(desc, id, len);
+               break;
+       case 3:
+               n = create_naa_desc(desc, id, parm);
+               break;
+       case 8:
+               n = create_scsi_string_desc(desc, id, parm, len);
+               break;
+       default:
+               break;
+       }
+       put_unaligned_be16(n, buf + 2);
+       return n + 4;
+}
+
+/**
+ * assert_correct_wwid() - test that a retrieved WWID matches expectations
+ * @test:      test name
+ * @expected:  expected WWID length
+ * @returned:  WWID length as returned by code under test
+ * @byte0, @byte1: leading chars that our code prepends to the ID
+ *             (e.g. "36" for "NAA registered extended" type)
+ * @lowercase: set if lower case WWID is expected
+ * @orig:      original ID string, may be longer than wwid
+ * @wwid:      WWID as returned by code under test
+ */
+static void assert_correct_wwid(const char *test,
+                               int expected, int returned,
+                               int byte0, int byte1, bool lowercase,
+                               const char *orig,
+                               const char *wwid)
+{
+       int ofs = 0, i;
+
+       condlog(2, "%s: exp/ret: %d/%d, wwid: %s", test,
+               expected, returned, wwid);
+       /*
+        * byte0 and byte1 are the leading chars that our code prepends
+        * to the ID to indicate the designation descriptor type, .
+        */
+       if (byte0 != 0) {
+               assert_int_equal(byte0, wwid[0]);
+               ++ofs;
+       }
+       if (byte1 != 0) {
+               assert_int_equal(byte1, wwid[1]);
+               ++ofs;
+       }
+       /* check matching length, and length of WWID string */
+       assert_int_equal(expected, returned);
+       assert_int_equal(returned, strlen(wwid));
+       /* check expected string value up to expected length */
+       for (i = 0; i < returned - ofs; i++)
+               assert_int_equal(wwid[ofs + i],
+                                lowercase ? tolower(orig[i]) : orig[i]);
+}
+
+/*
+ * For T10 vendor ID - replace sequences of spaces with a single underscore.
+ * Use a different implementation then libmultipath, deliberately.
+ */
+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;
+
+       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;
+           rc == 0;
+           src += match.rm_eo, rc = regexec(re, src, 1, &match, 0)) {
+               memcpy(p, src, match.rm_so);
+               p += match.rm_so;
+               *p = '_';
+               ++p;
+       }
+       assert_int_equal(rc, REG_NOMATCH);
+       strcpy(p, src);
+       return dst;
+}
+
+/**
+ * test_vpd_vnd_LEN_WLEN() - test code for VPD 83, T10 vendor ID
+ * @LEN:       ID length in the VPD page (includes 8 byte vendor ID)
+ * @WLEN:      WWID buffer size
+ *
+ * The input ID is modified by inserting some spaces, to be able to
+ * test the handling of spaces by the code. This is relevant only for
+ * a minimum input length of 24.
+ * The expected result must be adjusted accordingly.
+ */
+#define make_test_vpd_vnd(len, wlen)                                   \
+static void test_vpd_vnd_ ## len ## _ ## wlen(void **state)             \
+{                                                                       \
+       struct vpdtest *vt = *state;                                    \
+       int n, ret, rc;                                                 \
+       int exp_len;                                                    \
+       char *exp_wwid, *exp_subst, *input;                             \
+                                                                       \
+       input = strdup(test_id);                                        \
+       /* 8 vendor bytes collapsed to actual vendor ID length + 1 */   \
+       /* and one '1' prepended */                                     \
+       exp_len = len - 8 + sizeof(vendor_id) + 1;                      \
+                                                                       \
+       /* insert some spaces to test space collapsing */               \
+       input[15] = input[17] = input[18] = ' ';                        \
+       /* adjust expectation for space treatment */                    \
+       /* drop char for 2nd space on offset 17/18 */                   \
+       if (len >= 18 + 9)                                              \
+               --exp_len;                                              \
+       /* drop trailing single '_' if input ends with space */         \
+       if (len == 15 + 9 || len == 17 + 9 || len == 18 + 9)            \
+               --exp_len;                                              \
+       if (exp_len >= wlen)                                            \
+               exp_len = wlen - 1;                                     \
+       n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), input,         \
+                        1, 0, len);                                    \
+       rc = asprintf(&exp_wwid, "%s_%s", vendor_id, input);            \
+       assert_int_not_equal(rc, -1);                                   \
+       free(input);                                                    \
+       /* Replace spaces, like code under test */                      \
+       exp_subst = subst_spaces(exp_wwid);                             \
+       free(exp_wwid);                                                 \
+       will_return(__wrap_ioctl, n);                                   \
+       will_return(__wrap_ioctl, vt->vpdbuf);                          \
+       ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen);                   \
+       assert_correct_wwid("test_vpd_vnd_" #len "_" #wlen,             \
+                           exp_len, ret, '1', 0, false,                \
+                           exp_subst, vt->wwid);                       \
+       free(exp_subst);                                                \
+}
+
+/**
+ * test_vpd_str_TYP_LEN_WLEN() - test code for VPD 83, SCSI name string
+ * @TYP:       numeric value of STR_EUI, STR_NAA, STR_IQN above
+ * @LEN:       ID length the VPD page
+ * @WLEN:      WWID buffer size
+ */
+#define make_test_vpd_str(typ, len, wlen)                              \
+static void test_vpd_str_ ## typ ## _ ## len ## _ ## wlen(void **state) \
+{                                                                       \
+       struct vpdtest *vt = *state;                                    \
+       int n, ret;                                                     \
+       int exp_len;                                                    \
+       int type = typ & STR_MASK;                                      \
+                                                                       \
+       n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id,       \
+                        8, typ, len);                                  \
+       exp_len = len - strlen(str_prefix[type]);                       \
+       if (typ & ZERO_LAST)                                            \
+               exp_len--;                                              \
+       if (exp_len >= wlen)                                            \
+               exp_len = wlen - 1;                                     \
+       will_return(__wrap_ioctl, n);                                   \
+       will_return(__wrap_ioctl, vt->vpdbuf);                          \
+       ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen);                   \
+       assert_correct_wwid("test_vpd_str_" #typ "_" #len "_" #wlen,    \
+                           exp_len, ret, byte0[type], 0,               \
+                           type != STR_IQN,                            \
+                           test_id, vt->wwid);                         \
+}
+
+/**
+ * test_vpd_naa_NAA_WLEN() - test code for VPD 83 NAA designation
+ * @NAA:       Network Name Authority (2, 3, 5, or 6)
+ * @WLEN:      WWID buffer size
+ */
+#define make_test_vpd_naa(naa, wlen)                                   \
+static void test_vpd_naa_ ## naa ## _ ## wlen(void **state)             \
+{                                                                       \
+       struct vpdtest *vt = *state;                                    \
+       int n, ret;                                                     \
+       int len, exp_len;                                               \
+                                                                       \
+       switch (naa) {                                                  \
+       case 2:                                                         \
+       case 3:                                                         \
+       case 5:                                                         \
+               len = 17;                                               \
+               break;                                                  \
+       case 6:                                                         \
+               len = 33;                                               \
+               break;                                                  \
+       }                                                               \
+       /* returned size is always uneven */                            \
+       exp_len = wlen > len ? len :                                    \
+               wlen % 2 == 0 ? wlen - 1 : wlen - 2;                    \
+                                                                       \
+       n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id,       \
+                        3, naa, 0);                                    \
+       will_return(__wrap_ioctl, n);                                   \
+       will_return(__wrap_ioctl, vt->vpdbuf);                          \
+       ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen);                   \
+       assert_correct_wwid("test_vpd_naa_" #naa "_" #wlen,             \
+                           exp_len, ret, '3', '0' + naa, true,         \
+                           test_id, vt->wwid);                         \
+}
+
+/**
+ * test_vpd_eui_LEN_WLEN() - test code for VPD 83, EUI64
+ * @LEN:       EUI64 length (8, 12, or 16)
+ * @WLEN:      WWID buffer size
+ */
+#define make_test_vpd_eui(len, wlen)                                   \
+static void test_vpd_eui_ ## len ## _ ## wlen(void **state)             \
+{                                                                      \
+       struct vpdtest *vt = *state;                                    \
+       int n, ret;                                                     \
+       /* returned size is always uneven */                            \
+       int exp_len = wlen > 2 * len + 1 ? 2 * len + 1 :                \
+               wlen % 2 == 0 ? wlen - 1 : wlen - 2;                    \
+                                                                       \
+       n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id,       \
+                        2, 0, len);                                    \
+       will_return(__wrap_ioctl, n);                                   \
+       will_return(__wrap_ioctl, vt->vpdbuf);                          \
+       ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen);                   \
+       assert_correct_wwid("test_vpd_eui_" #len "_" #wlen,             \
+                           exp_len, ret, '2', 0, true,                 \
+                           test_id, vt->wwid);                         \
+}
+
+/**
+ * test_vpd80_SIZE_LEN_WLEN() - test code for VPD 80
+ * @SIZE, @LEN:        see create_vpd80()
+ * @WLEN:      WWID buffer size
+ */
+#define make_test_vpd80(size, len, wlen)                               \
+static void test_vpd80_ ## size ## _ ## len ## _ ## wlen(void **state)  \
+{                                                                      \
+       struct vpdtest *vt = *state;                                    \
+       int n, ret;                                                     \
+       int exp_len = len > 20 ? 20 : len;                              \
+       char *input = strdup(test_id);                                  \
+                                                                       \
+       /* insert trailing whitespace after pos 20 */                   \
+       memset(input + 20, ' ', sizeof(test_id) - 20);                  \
+       if (exp_len >= wlen)                                            \
+               exp_len = wlen - 1;                                     \
+       n = create_vpd80(vt->vpdbuf, sizeof(vt->vpdbuf), input,         \
+                        size, len);                                    \
+       will_return(__wrap_ioctl, n);                                   \
+       will_return(__wrap_ioctl, vt->vpdbuf);                          \
+       ret = get_vpd_sgio(10, 0x80, vt->wwid, wlen);                   \
+       assert_correct_wwid("test_vpd80_" #size "_" #len "_" #wlen,     \
+                           exp_len, ret, 0, 0, false,                  \
+                           input, vt->wwid);                           \
+       free(input);                                                    \
+}
+
+/* VPD 80 */
+/* Tests without trailing whitespace: 21 WWID bytes required */
+make_test_vpd80(20, 20, 30);
+make_test_vpd80(20, 20, 21);
+make_test_vpd80(20, 20, 20);
+make_test_vpd80(20, 20, 10);
+
+/* Tests with 4 byte trailing whitespace: 21 WWID bytes required */
+make_test_vpd80(24, 24, 30);
+make_test_vpd80(24, 24, 25);
+make_test_vpd80(24, 24, 24);
+make_test_vpd80(24, 24, 21);
+make_test_vpd80(24, 24, 20);
+
+/* Tests with 4 byte leading whitespace: 17 WWID bytes required */
+make_test_vpd80(20, 16, 30);
+make_test_vpd80(20, 16, 17);
+make_test_vpd80(20, 16, 16);
+
+/* Tests with 4 byte leading whitespace: 21 WWID bytes required */
+make_test_vpd80(24, 20, 21);
+make_test_vpd80(24, 20, 20);
+
+/* Tests with leading and trailing whitespace: 21 WWID bytes required */
+make_test_vpd80(30, 24, 30);
+make_test_vpd80(30, 24, 21);
+make_test_vpd80(30, 24, 20);
+
+/* VPD 83, T10 vendor ID */
+make_test_vpd_vnd(40, 40);
+make_test_vpd_vnd(40, 30);
+make_test_vpd_vnd(30, 20);
+make_test_vpd_vnd(29, 30);
+make_test_vpd_vnd(28, 30);
+make_test_vpd_vnd(27, 30); /* space at end */
+make_test_vpd_vnd(26, 30); /* space at end */
+make_test_vpd_vnd(25, 30);
+make_test_vpd_vnd(24, 30); /* space at end */
+make_test_vpd_vnd(23, 30);
+make_test_vpd_vnd(24, 20);
+make_test_vpd_vnd(23, 20);
+make_test_vpd_vnd(22, 20);
+make_test_vpd_vnd(21, 20);
+make_test_vpd_vnd(20, 20);
+make_test_vpd_vnd(19, 20);
+make_test_vpd_vnd(20, 10);
+make_test_vpd_vnd(10, 10);
+
+/* EUI64 tests */
+/* 64bit, WWID size: 18 */
+make_test_vpd_eui(8, 32);
+make_test_vpd_eui(8, 18);
+make_test_vpd_eui(8, 17);
+make_test_vpd_eui(8, 16);
+make_test_vpd_eui(8, 10);
+
+/* 96 bit, WWID size: 26 */
+make_test_vpd_eui(12, 32);
+make_test_vpd_eui(12, 26);
+make_test_vpd_eui(12, 25);
+make_test_vpd_eui(12, 20);
+make_test_vpd_eui(12, 10);
+
+/* 128 bit, WWID size: 34 */
+make_test_vpd_eui(16, 40);
+make_test_vpd_eui(16, 34);
+make_test_vpd_eui(16, 33);
+make_test_vpd_eui(16, 20);
+
+/* NAA IEEE registered extended (36), WWID size: 34 */
+make_test_vpd_naa(6, 40);
+make_test_vpd_naa(6, 34);
+make_test_vpd_naa(6, 33);
+make_test_vpd_naa(6, 32);
+make_test_vpd_naa(6, 20);
+
+/* NAA IEEE registered (35), WWID size: 18 */
+make_test_vpd_naa(5, 20);
+make_test_vpd_naa(5, 18);
+make_test_vpd_naa(5, 17);
+make_test_vpd_naa(5, 16);
+
+/* NAA local (33), WWID size: 18 */
+make_test_vpd_naa(3, 20);
+make_test_vpd_naa(3, 18);
+make_test_vpd_naa(3, 17);
+make_test_vpd_naa(3, 16);
+
+/* NAA IEEE extended (32), WWID size: 18 */
+make_test_vpd_naa(2, 20);
+make_test_vpd_naa(2, 18);
+make_test_vpd_naa(2, 17);
+make_test_vpd_naa(2, 16);
+
+/* SCSI Name string: EUI64, WWID size: 17 */
+make_test_vpd_str(0, 20, 18)
+make_test_vpd_str(0, 20, 17)
+make_test_vpd_str(0, 20, 16)
+make_test_vpd_str(0, 20, 15)
+
+/* SCSI Name string: EUI64, zero padded, WWID size: 16 */
+make_test_vpd_str(16, 20, 18)
+make_test_vpd_str(16, 20, 17)
+make_test_vpd_str(16, 20, 16)
+make_test_vpd_str(16, 20, 15)
+
+/* SCSI Name string: NAA, WWID size: 17 */
+make_test_vpd_str(1, 20, 18)
+make_test_vpd_str(1, 20, 17)
+make_test_vpd_str(1, 20, 16)
+make_test_vpd_str(1, 20, 15)
+
+/* SCSI Name string: NAA, zero padded, WWID size: 16 */
+make_test_vpd_str(17, 20, 18)
+make_test_vpd_str(17, 20, 17)
+make_test_vpd_str(17, 20, 16)
+make_test_vpd_str(17, 20, 15)
+
+/* SCSI Name string: IQN, WWID size: 17 */
+make_test_vpd_str(2, 20, 18)
+make_test_vpd_str(2, 20, 17)
+make_test_vpd_str(2, 20, 16)
+make_test_vpd_str(2, 20, 15)
+
+/* SCSI Name string: IQN, zero padded, WWID size: 16 */
+make_test_vpd_str(18, 20, 18)
+make_test_vpd_str(18, 20, 17)
+make_test_vpd_str(18, 20, 16)
+make_test_vpd_str(18, 20, 15)
+
+static int test_vpd(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_vpd80_20_20_30),
+               cmocka_unit_test(test_vpd80_20_20_21),
+               cmocka_unit_test(test_vpd80_20_20_20),
+               cmocka_unit_test(test_vpd80_20_20_10),
+               cmocka_unit_test(test_vpd80_24_24_30),
+               cmocka_unit_test(test_vpd80_24_24_25),
+               cmocka_unit_test(test_vpd80_24_24_24),
+               cmocka_unit_test(test_vpd80_24_24_21),
+               cmocka_unit_test(test_vpd80_24_24_20),
+               cmocka_unit_test(test_vpd80_20_16_30),
+               cmocka_unit_test(test_vpd80_20_16_17),
+               cmocka_unit_test(test_vpd80_20_16_16),
+               cmocka_unit_test(test_vpd80_24_20_21),
+               cmocka_unit_test(test_vpd80_24_20_20),
+               cmocka_unit_test(test_vpd80_30_24_30),
+               cmocka_unit_test(test_vpd80_30_24_21),
+               cmocka_unit_test(test_vpd80_30_24_20),
+               cmocka_unit_test(test_vpd_vnd_40_40),
+               cmocka_unit_test(test_vpd_vnd_40_30),
+               cmocka_unit_test(test_vpd_vnd_30_20),
+               cmocka_unit_test(test_vpd_vnd_29_30),
+               cmocka_unit_test(test_vpd_vnd_28_30),
+               cmocka_unit_test(test_vpd_vnd_27_30),
+               cmocka_unit_test(test_vpd_vnd_26_30),
+               cmocka_unit_test(test_vpd_vnd_25_30),
+               cmocka_unit_test(test_vpd_vnd_24_30),
+               cmocka_unit_test(test_vpd_vnd_23_30),
+               cmocka_unit_test(test_vpd_vnd_24_20),
+               cmocka_unit_test(test_vpd_vnd_23_20),
+               cmocka_unit_test(test_vpd_vnd_22_20),
+               cmocka_unit_test(test_vpd_vnd_21_20),
+               cmocka_unit_test(test_vpd_vnd_20_20),
+               cmocka_unit_test(test_vpd_vnd_19_20),
+               cmocka_unit_test(test_vpd_vnd_20_10),
+               cmocka_unit_test(test_vpd_vnd_10_10),
+               cmocka_unit_test(test_vpd_eui_8_32),
+               cmocka_unit_test(test_vpd_eui_8_18),
+               cmocka_unit_test(test_vpd_eui_8_17),
+               cmocka_unit_test(test_vpd_eui_8_16),
+               cmocka_unit_test(test_vpd_eui_8_10),
+               cmocka_unit_test(test_vpd_eui_12_32),
+               cmocka_unit_test(test_vpd_eui_12_26),
+               cmocka_unit_test(test_vpd_eui_12_25),
+               cmocka_unit_test(test_vpd_eui_12_20),
+               cmocka_unit_test(test_vpd_eui_12_10),
+               cmocka_unit_test(test_vpd_eui_16_40),
+               cmocka_unit_test(test_vpd_eui_16_34),
+               cmocka_unit_test(test_vpd_eui_16_33),
+               cmocka_unit_test(test_vpd_eui_16_20),
+               cmocka_unit_test(test_vpd_naa_6_40),
+               cmocka_unit_test(test_vpd_naa_6_34),
+               cmocka_unit_test(test_vpd_naa_6_33),
+               cmocka_unit_test(test_vpd_naa_6_32),
+               cmocka_unit_test(test_vpd_naa_6_20),
+               cmocka_unit_test(test_vpd_naa_5_20),
+               cmocka_unit_test(test_vpd_naa_5_18),
+               cmocka_unit_test(test_vpd_naa_5_17),
+               cmocka_unit_test(test_vpd_naa_5_16),
+               cmocka_unit_test(test_vpd_naa_3_20),
+               cmocka_unit_test(test_vpd_naa_3_18),
+               cmocka_unit_test(test_vpd_naa_3_17),
+               cmocka_unit_test(test_vpd_naa_3_16),
+               cmocka_unit_test(test_vpd_naa_2_20),
+               cmocka_unit_test(test_vpd_naa_2_18),
+               cmocka_unit_test(test_vpd_naa_2_17),
+               cmocka_unit_test(test_vpd_naa_2_16),
+               cmocka_unit_test(test_vpd_str_0_20_18),
+               cmocka_unit_test(test_vpd_str_0_20_17),
+               cmocka_unit_test(test_vpd_str_0_20_16),
+               cmocka_unit_test(test_vpd_str_0_20_15),
+               cmocka_unit_test(test_vpd_str_16_20_18),
+               cmocka_unit_test(test_vpd_str_16_20_17),
+               cmocka_unit_test(test_vpd_str_16_20_16),
+               cmocka_unit_test(test_vpd_str_16_20_15),
+               cmocka_unit_test(test_vpd_str_1_20_18),
+               cmocka_unit_test(test_vpd_str_1_20_17),
+               cmocka_unit_test(test_vpd_str_1_20_16),
+               cmocka_unit_test(test_vpd_str_1_20_15),
+               cmocka_unit_test(test_vpd_str_17_20_18),
+               cmocka_unit_test(test_vpd_str_17_20_17),
+               cmocka_unit_test(test_vpd_str_17_20_16),
+               cmocka_unit_test(test_vpd_str_17_20_15),
+               cmocka_unit_test(test_vpd_str_2_20_18),
+               cmocka_unit_test(test_vpd_str_2_20_17),
+               cmocka_unit_test(test_vpd_str_2_20_16),
+               cmocka_unit_test(test_vpd_str_2_20_15),
+               cmocka_unit_test(test_vpd_str_18_20_18),
+               cmocka_unit_test(test_vpd_str_18_20_17),
+               cmocka_unit_test(test_vpd_str_18_20_16),
+               cmocka_unit_test(test_vpd_str_18_20_15),
+       };
+       return cmocka_run_group_tests(tests, setup, teardown);
+}
+
+int main(void)
+{
+       int ret = 0;
+
+       ret += test_vpd();
+       return ret;
+}