multipathd: fix reservation_key check
[multipath-tools/.git] / libmultipath / dmparser.c
index fe7d986..620f507 100644 (file)
 #include "structs.h"
 #include "util.h"
 #include "debug.h"
+#include "dmparser.h"
 
 #define WORD_SIZE 64
 
 static int
 merge_words (char ** dst, char * word, int space)
 {
-       char * p;
+       char * p = *dst;
        int len;
 
        len = strlen(*dst) + strlen(word) + space;
        *dst = REALLOC(*dst, len + 1);
 
-       if (!*dst)
+       if (!*dst) {
+               free(p);
                return 1;
+       }
 
        p = *dst;
 
@@ -43,44 +46,61 @@ merge_words (char ** dst, char * word, int space)
        return 0;
 }
 
+#define APPEND(p, end, args...)                                                \
+({                                                                     \
+       int ret;                                                        \
+                                                                       \
+       ret = snprintf(p, end - p, ##args);                             \
+       if (ret < 0) {                                                  \
+               condlog(0, "%s: conversion error", mp->alias);          \
+               goto err;                                               \
+       }                                                               \
+       p += ret;                                                       \
+       if (p >= end) {                                                 \
+               condlog(0, "%s: params too small", mp->alias);          \
+               goto err;                                               \
+       }                                                               \
+})
+
 /*
  * Transforms the path group vector into a proper device map string
  */
 int
-assemble_map (struct multipath * mp)
+assemble_map (struct multipath * mp, char * params, int len)
 {
        int i, j;
-       int shift, freechar;
        int minio;
-       char * p;
+       int nr_priority_groups, initial_pg_nr;
+       char * p, * f;
+       const char *const end = params + len;
+       char no_path_retry[] = "queue_if_no_path";
+       char retain_hwhandler[] = "retain_attached_hw_handler";
        struct pathgroup * pgp;
        struct path * pp;
 
        minio = mp->minio;
-       p = mp->params;
-       freechar = sizeof(mp->params);
-       
-       shift = snprintf(p, freechar, "%s %s %i %i",
-                        mp->features, mp->hwhandler,
-                        VECTOR_SIZE(mp->pg), mp->bestpg);
-
-       if (shift >= freechar) {
-               fprintf(stderr, "mp->params too small\n");
-               return 1;
+       p = params;
+
+       nr_priority_groups = VECTOR_SIZE(mp->pg);
+       initial_pg_nr = (nr_priority_groups ? mp->bestpg : 0);
+
+       if (mp->no_path_retry != NO_PATH_RETRY_UNDEF  &&
+           mp->no_path_retry != NO_PATH_RETRY_FAIL) {
+               add_feature(&mp->features, no_path_retry);
        }
-       p += shift;
-       freechar -= shift;
-       
+       if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON &&
+           get_linux_version_code() < KERNEL_VERSION(4, 3, 0))
+               add_feature(&mp->features, retain_hwhandler);
+
+       f = STRDUP(mp->features);
+
+       APPEND(p, end, "%s %s %i %i", f, mp->hwhandler, nr_priority_groups,
+              initial_pg_nr);
+
        vector_foreach_slot (mp->pg, pgp, i) {
                pgp = VECTOR_SLOT(mp->pg, i);
-               shift = snprintf(p, freechar, " %s %i 1", mp->selector,
-                                VECTOR_SIZE(pgp->paths));
-               if (shift >= freechar) {
-                       fprintf(stderr, "mp->params too small\n");
-                       return 1;
-               }
-               p += shift;
-               freechar -= shift;
+               APPEND(p, end, " %s %i 1", mp->selector,
+                      VECTOR_SIZE(pgp->paths));
 
                vector_foreach_slot (pgp->paths, pp, j) {
                        int tmp_minio = minio;
@@ -88,27 +108,27 @@ assemble_map (struct multipath * mp)
                        if (mp->rr_weight == RR_WEIGHT_PRIO
                            && pp->priority > 0)
                                tmp_minio = minio * pp->priority;
-
-                       shift = snprintf(p, freechar, " %s %d",
-                                        pp->dev_t, tmp_minio);
-                       if (shift >= freechar) {
-                               fprintf(stderr, "mp->params too small\n");
-                               return 1;
+                       if (!strlen(pp->dev_t) ) {
+                               condlog(0, "dev_t not set for '%s'", pp->dev);
+                               goto err;
                        }
-                       p += shift;
-                       freechar -= shift;
+                       APPEND(p, end, " %s %d", pp->dev_t, tmp_minio);
                }
        }
-       if (freechar < 1) {
-               fprintf(stderr, "mp->params too small\n");
-               return 1;
-       }
-       snprintf(p, 1, "\n");
+
+       FREE(f);
+       condlog(3, "%s: assembled map [%s]", mp->alias, params);
        return 0;
+
+err:
+       FREE(f);
+       return 1;
 }
 
-extern int
-disassemble_map (vector pathvec, char * params, struct multipath * mpp)
+#undef APPEND
+
+int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
+                   int is_daemon)
 {
        char * word;
        char * p;
@@ -125,6 +145,8 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
 
        p = params;
 
+       condlog(3, "%s: disassemble map [%s]", mpp->alias, params);
+
        /*
         * features
         */
@@ -145,6 +167,7 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                        FREE(word);
                        return 1;
                }
+
                FREE(word);
        }
 
@@ -182,11 +205,17 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
        num_pg = atoi(word);
        FREE(word);
 
-       if (num_pg > 0 && !mpp->pg)
-               mpp->pg = vector_alloc();
-       
-       if (!mpp->pg)
-               return 1;
+       if (num_pg > 0) {
+               if (!mpp->pg) {
+                       mpp->pg = vector_alloc();
+                       if (!mpp->pg)
+                               return 1;
+               }
+       } else {
+               free_pgvec(mpp->pg, KEEP_PATHS);
+               mpp->pg = NULL;
+       }
+
        /*
         * first pg to try
         */
@@ -218,9 +247,8 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                                goto out;
 
                        num_pg_args = atoi(word);
-                       
+
                        if (merge_words(&mpp->selector, word, 1)) {
-                               FREE(word);
                                goto out1;
                        }
                        FREE(word);
@@ -236,11 +264,11 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                 * paths
                 */
                pgp = alloc_pathgroup();
-               
+
                if (!pgp)
                        goto out;
 
-               if (store_pathgroup(mpp->pg, pgp))
+               if (add_pathgroup(mpp, pgp))
                        goto out;
 
                p += get_word(p, &word);
@@ -260,14 +288,26 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                FREE(word);
 
                for (j = 0; j < num_paths; j++) {
+                       char devname[FILE_NAME_SIZE];
+
                        pp = NULL;
                        p += get_word(p, &word);
 
                        if (!word)
                                goto out;
 
-                       if (pathvec)
-                               pp = find_path_by_devt(pathvec, word);
+                       if (devt2devname(devname, FILE_NAME_SIZE, word)) {
+                               condlog(2, "%s: cannot find block device",
+                                       word);
+                               devname[0] = '\0';
+                       }
+
+                       if (pathvec) {
+                               if (strlen(devname))
+                                       pp = find_path_by_dev(pathvec, devname);
+                               else
+                                       pp = find_path_by_devt(pathvec, word);
+                       }
 
                        if (!pp) {
                                pp = alloc_path();
@@ -275,12 +315,20 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                                if (!pp)
                                        goto out1;
 
-                               strncpy(pp->dev_t, word, BLK_DEV_SIZE);
-
-#ifndef DAEMON
-                               if (store_path(pathvec, pp))
-                                       goto out;
-#endif
+                               strncpy(pp->dev_t, word, BLK_DEV_SIZE - 1);
+                               strncpy(pp->dev, devname, FILE_NAME_SIZE - 1);
+                               if (strlen(mpp->wwid)) {
+                                       strncpy(pp->wwid, mpp->wwid,
+                                               WWID_SIZE - 1);
+                               }
+                               /* Only call this in multipath client mode */
+                               if (!is_daemon && store_path(pathvec, pp))
+                                       goto out1;
+                       } else {
+                               if (!strlen(pp->wwid) &&
+                                   strlen(mpp->wwid))
+                                       strncpy(pp->wwid, mpp->wwid,
+                                               WWID_SIZE - 1);
                        }
                        FREE(word);
 
@@ -292,29 +340,43 @@ 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);
+                               strncpy(mpp->wwid, pp->wwid,
+                                       WWID_SIZE - 1);
 
                        /*
                         * 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);
+
+                       /*
+                        * 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);
+                       }
 
                        pgp->id ^= (long)pp;
                        pp->pgindex = i + 1;
 
                        for (k = 0; k < num_paths_args; k++)
-                               if (k == 0 && !strncmp(mpp->selector,
-                                                      "round-robin", 11)) {
+                               if (k == 0) {
                                        p += get_word(p, &word);
                                        def_minio = atoi(word);
+                                       FREE(word);
 
-                                       if (mpp->rr_weight == RR_WEIGHT_PRIO
-                                           && pp->priority > 0)
-                                               def_minio /= pp->priority;
+                                       if (!strncmp(mpp->selector,
+                                                    "round-robin", 11)) {
+
+                                               if (mpp->rr_weight == RR_WEIGHT_PRIO
+                                                   && pp->priority > 0)
+                                                       def_minio /= pp->priority;
+
+                                       }
 
-                                       FREE(word);
                                        if (def_minio != mpp->minio)
                                                mpp->minio = def_minio;
                                }
@@ -328,11 +390,11 @@ out1:
        FREE(word);
 out:
        free_pgvec(mpp->pg, KEEP_PATHS);
+       mpp->pg = NULL;
        return 1;
 }
 
-extern int
-disassemble_status (char * params, struct multipath * mpp)
+int disassemble_status(char *params, struct multipath *mpp)
 {
        char * word;
        char * p;
@@ -342,11 +404,14 @@ disassemble_status (char * params, struct multipath * mpp)
        int num_pg;
        int num_pg_args;
        int num_paths;
+       int def_minio = 0;
        struct path * pp;
        struct pathgroup * pgp;
 
        p = params;
 
+       condlog(3, "%s: disassemble status [%s]", mpp->alias, params);
+
        /*
         * features
         */
@@ -397,6 +462,9 @@ disassemble_status (char * params, struct multipath * mpp)
        num_pg = atoi(word);
        FREE(word);
 
+       if (num_pg == 0)
+               return 0;
+
        /*
         * next pg to try
         */
@@ -432,7 +500,7 @@ disassemble_status (char * params, struct multipath * mpp)
                FREE(word);
 
                /*
-                * undef ?
+                * PG Status (discarded, would be '0' anyway)
                 */
                p += get_word(p, NULL);
 
@@ -495,8 +563,17 @@ disassemble_status (char * params, struct multipath * mpp)
                        /*
                         * selector args
                         */
-                       for (k = 0; k < num_pg_args; k++)
-                               p += get_word(p, NULL);
+                       for (k = 0; k < num_pg_args; k++) {
+                               if (!strncmp(mpp->selector,
+                                            "least-pending", 13)) {
+                                       p += get_word(p, &word);
+                                       if (sscanf(word,"%d:*d",
+                                                  &def_minio) == 1 &&
+                                           def_minio != mpp->minio)
+                                                       mpp->minio = def_minio;
+                               } else
+                                       p += get_word(p, NULL);
+                       }
                }
        }
        return 0;