multipathd: fix reservation_key check
[multipath-tools/.git] / libmultipath / dmparser.c
index b3fbd01..620f507 100644 (file)
@@ -1,31 +1,35 @@
 /*
- * Christophe Varoqui (2004)
- * This code is GPLv2, see license file
- *
+ * Copyright (c) 2004, 2005 Christophe Varoqui
+ * Copyright (c) 2005 Stefan Bader, IBM
+ * Copyright (c) 2005 Edward Goggin, EMC
  */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "checkers.h"
 #include "vector.h"
 #include "memory.h"
 #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;
 
@@ -42,8 +46,89 @@ merge_words (char ** dst, char * word, int space)
        return 0;
 }
 
-extern int
-disassemble_map (vector pathvec, char * params, struct multipath * mpp)
+#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, char * params, int len)
+{
+       int i, j;
+       int minio;
+       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 = 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);
+       }
+       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);
+               APPEND(p, end, " %s %i 1", mp->selector,
+                      VECTOR_SIZE(pgp->paths));
+
+               vector_foreach_slot (pgp->paths, pp, j) {
+                       int tmp_minio = minio;
+
+                       if (mp->rr_weight == RR_WEIGHT_PRIO
+                           && pp->priority > 0)
+                               tmp_minio = minio * pp->priority;
+                       if (!strlen(pp->dev_t) ) {
+                               condlog(0, "dev_t not set for '%s'", pp->dev);
+                               goto err;
+                       }
+                       APPEND(p, end, " %s %d", pp->dev_t, tmp_minio);
+               }
+       }
+
+       FREE(f);
+       condlog(3, "%s: assembled map [%s]", mp->alias, params);
+       return 0;
+
+err:
+       FREE(f);
+       return 1;
+}
+
+#undef APPEND
+
+int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
+                   int is_daemon)
 {
        char * word;
        char * p;
@@ -54,11 +139,14 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
        int num_pg_args = 0;
        int num_paths = 0;
        int num_paths_args = 0;
+       int def_minio = 0;
        struct path * pp;
        struct pathgroup * pgp;
 
        p = params;
 
+       condlog(3, "%s: disassemble map [%s]", mpp->alias, params);
+
        /*
         * features
         */
@@ -79,6 +167,7 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                        FREE(word);
                        return 1;
                }
+
                FREE(word);
        }
 
@@ -116,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
         */
@@ -152,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);
@@ -170,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);
@@ -194,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();
@@ -209,7 +315,20 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                                if (!pp)
                                        goto out1;
 
-                               strncpy(pp->dev_t, word, BLK_DEV_SIZE);
+                               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);
 
@@ -217,24 +336,53 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                                goto out;
 
                        /*
-                        * Update wwid for multipaths which are not set
+                        * Update wwid for multipaths which are not setup
                         * 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++)
-                               p += get_word(p, NULL);
+                               if (k == 0) {
+                                       p += get_word(p, &word);
+                                       def_minio = atoi(word);
+                                       FREE(word);
+
+                                       if (!strncmp(mpp->selector,
+                                                    "round-robin", 11)) {
+
+                                               if (mpp->rr_weight == RR_WEIGHT_PRIO
+                                                   && pp->priority > 0)
+                                                       def_minio /= pp->priority;
+
+                                       }
+
+                                       if (def_minio != mpp->minio)
+                                               mpp->minio = def_minio;
+                               }
+                               else
+                                       p += get_word(p, NULL);
+
                }
        }
        return 0;
@@ -242,25 +390,28 @@ 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;
-       int i, j;
+       int i, j, k;
        int num_feature_args;
        int num_hwhandler_args;
        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
         */
@@ -311,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
         */
@@ -340,13 +494,13 @@ disassemble_status (char * params, struct multipath * mpp)
                        pgp->status = PGSTATE_ENABLED;
                        break;
                default:
-                       pgp->status = PGSTATE_RESERVED;
+                       pgp->status = PGSTATE_UNDEF;
                        break;
                }
                FREE(word);
 
                /*
-                * undef ?
+                * PG Status (discarded, would be '0' anyway)
                 */
                p += get_word(p, NULL);
 
@@ -405,12 +559,22 @@ disassemble_status (char * params, struct multipath * mpp)
 
                        pp->failcount = atoi(word);
                        FREE(word);
+
+                       /*
+                        * selector args
+                        */
+                       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);
+                       }
                }
-               /*
-                * selector args
-                */
-               for (j = 0; j < num_pg_args; j++)
-                       p += get_word(p, NULL);
        }
        return 0;
 }