Merge branch 'master' of git://git.kernel.org/pub/scm/linux/storage/multipath-tools/
[platform/upstream/multipath-tools.git] / libmultipath / devmapper.c
index 913c3dd..fb69ee8 100644 (file)
  */
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
 #include <string.h>
 #include <libdevmapper.h>
 #include <ctype.h>
-#include <linux/kdev_t.h>
 #include <unistd.h>
+#include <errno.h>
 
+#include "checkers.h"
 #include "vector.h"
 #include "structs.h"
 #include "debug.h"
 #include "memory.h"
 #include "devmapper.h"
+#include "config.h"
+
+#include "log_pthread.h"
+#include <sys/types.h>
+#include <time.h>
 
 #define MAX_WAIT 5
 #define LOOPS_PER_SEC 5
 
+#define UUID_PREFIX "mpath-"
+#define UUID_PREFIX_LEN 6
+
 static void
-dm_dummy_log (int level, const char *file, int line, const char *f, ...)
+dm_write_log (int level, const char *file, int line, const char *f, ...)
 {
+       va_list ap;
+       int thres;
+
+       if (level > 6)
+               level = 6;
+
+       thres = (conf) ? conf->verbosity : 0;
+       if (thres <= 3 || level > thres)
+               return;
+
+       va_start(ap, f);
+       if (!logsink) {
+               time_t t = time(NULL);
+               struct tm *tb = localtime(&t);
+               char buff[16];
+
+               strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb);
+               buff[sizeof(buff)-1] = '\0';
+
+               fprintf(stdout, "%s | ", buff);
+               fprintf(stdout, "libdevmapper: %s(%i): ", file, line);
+               vfprintf(stdout, f, ap);
+               fprintf(stdout, "\n");
+       } else {
+               condlog(level, "libdevmapper: %s(%i): ", file, line);
+               log_safe(level + 3, f, ap);
+       }
+       va_end(ap);
+
        return;
 }
 
-void
-dm_restore_log (void)
-{
-       dm_log_init(NULL);
+extern void
+dm_init(void) {
+       dm_log_init(&dm_write_log);
+       dm_log_init_verbose(conf ? conf->verbosity + 3 : 0);
 }
 
-void
-dm_shut_log (void)
+static int
+dm_libprereq (void)
 {
-       dm_log_init(&dm_dummy_log);
+       char version[64];
+       int v[3];
+       int minv[3] = {1, 2, 38};
+
+       dm_get_library_version(version, sizeof(version));
+       condlog(3, "libdevmapper version %s", version);
+       sscanf(version, "%d.%d.%d ", &v[0], &v[1], &v[2]);
+
+       if ((v[0] > minv[0]) ||
+           ((v[0] ==  minv[0]) && (v[1] > minv[1])) ||
+           ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2])))
+               return 0;
+       condlog(0, "libdevmapper version must be >= %d.%.2d.%.2d",
+               minv[0], minv[1], minv[2]);
+       return 1;
 }
 
-extern int
-dm_prereq (char * str, int x, int y, int z)
+static int
+dm_drvprereq (char * str)
 {
        int r = 2;
        struct dm_task *dmt;
        struct dm_versions *target;
        struct dm_versions *last_target;
+       int minv[3] = {1, 0, 3};
+       unsigned int *v;
 
        if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
                return 3;
@@ -56,39 +111,48 @@ dm_prereq (char * str, int x, int y, int z)
                condlog(0, "Can not communicate with kernel DM");
                goto out;
        }
-
        target = dm_task_get_versions(dmt);
 
        do {
                last_target = target;
-
                if (!strncmp(str, target->name, strlen(str))) {
-                       r--;
-                       
-                       if (target->version[0] >= x &&
-                           target->version[1] >= y &&
-                           target->version[2] >= z)
-                               r--;
-
+                       r = 1;
                        break;
                }
-
                target = (void *) target + target->next;
        } while (last_target != target);
 
-       if (r == 2)
+       if (r == 2) {
                condlog(0, "DM multipath kernel driver not loaded");
-       else if (r == 1)
-               condlog(0, "DM multipath kernel driver version too old");
-
+               goto out;
+       }
+       v = target->version;
+       if ((v[0] > minv[0]) ||
+           ((v[0] == minv[0]) && (v[1] > minv[1])) ||
+           ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2]))) {
+               r = 0;
+               goto out;
+       }
+       condlog(0, "DM multipath kernel driver must be >= %u.%.2u.%.2u",
+               minv[0], minv[1], minv[2]);
 out:
        dm_task_destroy(dmt);
        return r;
 }
 
 extern int
-dm_simplecmd (int task, const char *name) {
+dm_prereq (void)
+{
+       if (dm_libprereq())
+               return 1;
+       return dm_drvprereq(TGT_MPATH);
+}
+
+static int
+dm_simplecmd (int task, const char *name, int no_flush, int need_sync) {
        int r = 0;
+       int udev_wait_flag = (need_sync && (task == DM_DEVICE_RESUME ||
+                                           task == DM_DEVICE_REMOVE));
        struct dm_task *dmt;
 
        if (!(dmt = dm_task_create (task)))
@@ -98,7 +162,14 @@ dm_simplecmd (int task, const char *name) {
                goto out;
 
        dm_task_no_open_count(dmt);
+       dm_task_skip_lockfs(dmt);       /* for DM_DEVICE_RESUME */
+#ifdef LIBDM_API_FLUSH
+       if (no_flush)
+               dm_task_no_flush(dmt);          /* for DM_DEVICE_SUSPEND/RESUME */
+#endif
 
+       if (udev_wait_flag && !dm_task_set_cookie(dmt, &conf->cookie, 0))
+               goto out;
        r = dm_task_run (dmt);
 
        out:
@@ -107,34 +178,114 @@ dm_simplecmd (int task, const char *name) {
 }
 
 extern int
-dm_addmap (int task, const char *name, const char *target,
-          const char *params, unsigned long long size, const char *uuid) {
+dm_simplecmd_flush (int task, const char *name, int needsync) {
+       return dm_simplecmd(task, name, 0, needsync);
+}
+
+extern int
+dm_simplecmd_noflush (int task, const char *name) {
+       return dm_simplecmd(task, name, 1, 1);
+}
+
+extern int
+dm_addmap (int task, const char *target, struct multipath *mpp, int use_uuid,
+          int ro) {
        int r = 0;
        struct dm_task *dmt;
+       char *prefixed_uuid = NULL;
 
        if (!(dmt = dm_task_create (task)))
                return 0;
 
-       if (!dm_task_set_name (dmt, name))
+       if (!dm_task_set_name (dmt, mpp->alias))
                goto addout;
 
-       if (!dm_task_add_target (dmt, 0, size, target, params))
+       if (!dm_task_add_target (dmt, 0, mpp->size, target, mpp->params))
                goto addout;
 
-       if (uuid && !dm_task_set_uuid(dmt, uuid))
-               goto addout;
+       if (ro)
+               dm_task_set_ro(dmt);
+
+       if (use_uuid && mpp->wwid){
+               prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(mpp->wwid) + 1);
+               if (!prefixed_uuid) {
+                       condlog(0, "cannot create prefixed uuid : %s\n",
+                               strerror(errno));
+                       goto addout;
+               }
+               sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid);
+               if (!dm_task_set_uuid(dmt, prefixed_uuid))
+                       goto freeout;
+       }
+
+       if (mpp->attribute_flags & (1 << ATTR_MODE) &&
+           !dm_task_set_mode(dmt, mpp->mode))
+               goto freeout;
+       if (mpp->attribute_flags & (1 << ATTR_UID) &&
+           !dm_task_set_uid(dmt, mpp->uid))
+               goto freeout;
+       if (mpp->attribute_flags & (1 << ATTR_GID) &&
+           !dm_task_set_gid(dmt, mpp->gid))
+               goto freeout;
 
        dm_task_no_open_count(dmt);
 
+       if (task == DM_DEVICE_CREATE &&
+           !dm_task_set_cookie(dmt, &conf->cookie, 0))
+               goto freeout;
        r = dm_task_run (dmt);
 
+       freeout:
+       if (prefixed_uuid)
+               FREE(prefixed_uuid);
+
        addout:
        dm_task_destroy (dmt);
+
        return r;
 }
 
+static int
+_dm_addmap_create (struct multipath *mpp, int ro) {
+       int r;
+       r = dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, 1, ro);
+       /*
+        * DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD.
+        * Failing the second part leaves an empty map. Clean it up.
+        */
+       if (!r && dm_map_present(mpp->alias)) {
+               condlog(3, "%s: failed to load map (a path might be in use)",
+                       mpp->alias);
+               dm_flush_map_nosync(mpp->alias);
+       }
+       return r;
+}
+
+#define ADDMAP_RW 0
+#define ADDMAP_RO 1
+
+extern int
+dm_addmap_create (struct multipath *mpp) {
+       return _dm_addmap_create(mpp, ADDMAP_RW);
+}
+
+extern int
+dm_addmap_create_ro (struct multipath *mpp) {
+       return _dm_addmap_create(mpp, ADDMAP_RO);
+}
+
+extern int
+dm_addmap_reload (struct multipath *mpp) {
+       return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, 0, ADDMAP_RW);
+}
+
+extern int
+dm_addmap_reload_ro (struct multipath *mpp) {
+       return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, 0, ADDMAP_RO);
+}
+
 extern int
-dm_map_present (char * str)
+dm_map_present (const char * str)
 {
        int r = 0;
        struct dm_task *dmt;
@@ -201,27 +352,32 @@ dm_get_uuid(char *name, char *uuid)
 {
        struct dm_task *dmt;
        const char *uuidtmp;
+       int r = 1;
 
        dmt = dm_task_create(DM_DEVICE_INFO);
        if (!dmt)
                return 1;
 
-        if (!dm_task_set_name (dmt, name))
-                goto uuidout;
+       if (!dm_task_set_name (dmt, name))
+               goto uuidout;
 
        if (!dm_task_run(dmt))
-                goto uuidout;
+               goto uuidout;
 
        uuidtmp = dm_task_get_uuid(dmt);
-       if (uuidtmp)
-               strcpy(uuid, uuidtmp);
+       if (uuidtmp) {
+               if (!strncmp(uuidtmp, UUID_PREFIX, UUID_PREFIX_LEN))
+                       strcpy(uuid, uuidtmp + UUID_PREFIX_LEN);
+               else
+                       strcpy(uuid, uuidtmp);
+       }
        else
                uuid[0] = '\0';
 
+       r = 0;
 uuidout:
        dm_task_destroy(dmt);
-
-       return 0;
+       return r;
 }
 
 extern int
@@ -266,7 +422,7 @@ out:
  *   -1 : empty map
  */
 extern int
-dm_type(char * name, char * type)
+dm_type(const char * name, char * type)
 {
        int r = 0;
        struct dm_task *dmt;
@@ -301,7 +457,7 @@ out:
 }
 
 static int
-dm_dev_t (char * mapname, char * dev_t, int len)
+dm_dev_t (const char * mapname, char * dev_t, int len)
 {
        int r = 1;
        struct dm_task *dmt;
@@ -328,9 +484,9 @@ out:
        dm_task_destroy(dmt);
        return r;
 }
-       
+
 int
-dm_get_opencount (char * mapname)
+dm_get_opencount (const char * mapname)
 {
        int r = -1;
        struct dm_task *dmt;
@@ -353,7 +509,7 @@ out:
        dm_task_destroy(dmt);
        return r;
 }
-       
+
 int
 dm_get_minor (char * mapname)
 {
@@ -378,27 +534,27 @@ out:
        dm_task_destroy(dmt);
        return r;
 }
-       
+
 extern int
-dm_flush_map (char * mapname, char * type)
+_dm_flush_map (const char * mapname, int need_sync)
 {
        int r;
 
        if (!dm_map_present(mapname))
                return 0;
 
-       if (dm_type(mapname, type) <= 0)
-               return 1;
+       if (dm_type(mapname, TGT_MPATH) <= 0)
+               return 0; /* nothing to do */
 
-       if (dm_remove_partmaps(mapname))
+       if (dm_remove_partmaps(mapname, need_sync))
                return 1;
 
        if (dm_get_opencount(mapname)) {
                condlog(2, "%s: map in use", mapname);
                return 1;
-       }       
+       }
 
-       r = dm_simplecmd(DM_DEVICE_REMOVE, mapname);
+       r = dm_simplecmd_flush(DM_DEVICE_REMOVE, mapname, need_sync);
 
        if (r) {
                condlog(4, "multipath map %s removed", mapname);
@@ -408,7 +564,7 @@ dm_flush_map (char * mapname, char * type)
 }
 
 extern int
-dm_flush_maps (char * type)
+dm_flush_maps (void)
 {
        int r = 0;
        struct dm_task *dmt;
@@ -430,7 +586,7 @@ dm_flush_maps (char * type)
                goto out;
 
        do {
-               r += dm_flush_map(names->name, type);
+               r |= dm_flush_map(names->name);
                next = names->next;
                names = (void *) names + next;
        } while (next);
@@ -507,6 +663,16 @@ dm_queue_if_no_path(char *mapname, int enable)
        return dm_message(mapname, message);
 }
 
+int
+dm_set_pg_timeout(char *mapname, int timeout_val)
+{
+       char message[24];
+
+       if (snprintf(message, 24, "set_pg_timeout %d", timeout_val) >= 24)
+               return 1;
+       return dm_message(mapname, message);
+}
+
 static int
 dm_groupmsg (char * msg, char * mapname, int index)
 {
@@ -537,7 +703,7 @@ dm_disablegroup(char * mapname, int index)
 }
 
 int
-dm_get_maps (vector mp, char * type)
+dm_get_maps (vector mp)
 {
        struct multipath * mpp;
        int r = 1;
@@ -546,7 +712,7 @@ dm_get_maps (vector mp, char * type)
        struct dm_names *names;
        unsigned next = 0;
 
-       if (!type || !mp)
+       if (!mp)
                return 1;
 
        if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
@@ -566,7 +732,7 @@ dm_get_maps (vector mp, char * type)
        }
 
        do {
-               info = dm_type(names->name, type);
+               info = dm_type(names->name, TGT_MPATH);
 
                if (info <= 0)
                        goto next;
@@ -589,6 +755,7 @@ dm_get_maps (vector mp, char * type)
                                goto out1;
 
                        dm_get_uuid(names->name, mpp->wwid);
+                       dm_get_info(names->name, &mpp->dmi);
                }
 
                if (!vector_alloc_slot(mp))
@@ -597,8 +764,8 @@ dm_get_maps (vector mp, char * type)
                vector_set_slot(mp, mpp);
                mpp = NULL;
 next:
-                next = names->next;
-                names = (void *) names + next;
+               next = names->next;
+               names = (void *) names + next;
        } while (next);
 
        r = 0;
@@ -610,33 +777,49 @@ out:
        return r;
 }
 
-extern int
-dm_get_name(char *uuid, char *type, char *name)
+extern char *
+dm_get_name(char *uuid)
 {
-       vector vec;
-       struct multipath *mpp;
-       int i;
-
-       vec = vector_alloc();
+       struct dm_task *dmt;
+       struct dm_info info;
+       char *prefixed_uuid, *name = NULL;
+       const char *nametmp;
 
-       if (!vec)
-               return 0;
+       dmt = dm_task_create(DM_DEVICE_INFO);
+       if (!dmt)
+               return NULL;
 
-       if (dm_get_maps(vec, type)) {
-               vector_free(vec);
-               return 0;
+       prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(uuid) + 1);
+       if (!prefixed_uuid) {
+               condlog(0, "cannot create prefixed uuid : %s\n",
+                       strerror(errno));
+               goto freeout;
        }
+       sprintf(prefixed_uuid, UUID_PREFIX "%s", uuid);
+       if (!dm_task_set_uuid(dmt, prefixed_uuid))
+               goto freeout;
 
-       vector_foreach_slot(vec, mpp, i) {
-               if (!strcmp(uuid, mpp->wwid)) {
-                       vector_free(vec);
-                       strcpy(name, mpp->alias);
-                       return 1;
-               }
+       if (!dm_task_run(dmt))
+               goto freeout;
+
+       if (!dm_task_get_info(dmt, &info) || !info.exists)
+               goto freeout;
+
+       nametmp = dm_task_get_name(dmt);
+       if (nametmp && strlen(nametmp)) {
+               name = MALLOC(strlen(nametmp) + 1);
+               if (name)
+                       strcpy(name, nametmp);
+       } else {
+               condlog(2, "%s: no device-mapper name found", uuid);
        }
 
-       vector_free(vec);
-       return 0;
+freeout:
+       if (prefixed_uuid)
+               FREE(prefixed_uuid);
+       dm_task_destroy(dmt);
+
+       return name;
 }
 
 int
@@ -675,7 +858,8 @@ out:
 char *
 dm_mapname(int major, int minor)
 {
-       char * response = NULL, *map;
+       char * response = NULL;
+       const char *map;
        struct dm_task *dmt;
        int r;
        int loop = MAX_WAIT * LOOPS_PER_SEC;
@@ -694,9 +878,7 @@ dm_mapname(int major, int minor)
         * daemon uev_trigger -> uev_add_map
         */
        while (--loop) {
-               dm_shut_log();
                r = dm_task_run(dmt);
-               dm_restore_log();
 
                if (r)
                        break;
@@ -722,7 +904,7 @@ bad:
 }
 
 int
-dm_remove_partmaps (char * mapname)
+dm_remove_partmaps (const char * mapname, int need_sync)
 {
        struct dm_task *dmt;
        struct dm_names *names;
@@ -756,7 +938,7 @@ dm_remove_partmaps (char * mapname)
                    /*
                     * if devmap target is "linear"
                     */
-                   (dm_type(names->name, "linear") > 0) &&
+                   (dm_type(names->name, TGT_PART) > 0) &&
 
                    /*
                     * and the multipath mapname and the part mapname start
@@ -779,13 +961,13 @@ dm_remove_partmaps (char * mapname)
                     */
                    strstr(params, dev_t)
                   ) {
-                               /*
+                               /*
                                 * then it's a kpartx generated partition.
                                 * remove it.
                                 */
                                condlog(4, "partition map %s removed",
                                        names->name);
-                               dm_simplecmd(DM_DEVICE_REMOVE, names->name);
+                               dm_simplecmd_flush(DM_DEVICE_REMOVE, names->name, need_sync);
                   }
 
                next = names->next;
@@ -809,7 +991,7 @@ dm_get_info (char * mapname, struct dm_info ** dmi)
 {
        int r = 1;
        struct dm_task *dmt = NULL;
-       
+
        if (!mapname)
                return 1;
 
@@ -835,8 +1017,11 @@ dm_get_info (char * mapname, struct dm_info ** dmi)
 
        r = 0;
 out:
-       if (r)
+       if (r) {
                memset(*dmi, 0, sizeof(struct dm_info));
+               FREE(*dmi);
+               *dmi = NULL;
+       }
 
        if (dmt)
                dm_task_destroy(dmt);
@@ -845,11 +1030,88 @@ out:
 }
 
 int
+dm_rename_partmaps (char * old, char * new)
+{
+       struct dm_task *dmt;
+       struct dm_names *names;
+       unsigned next = 0;
+       char buff[PARAMS_SIZE];
+       unsigned long long size;
+       char dev_t[32];
+       int r = 1;
+
+       if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+               return 1;
+
+       dm_task_no_open_count(dmt);
+
+       if (!dm_task_run(dmt))
+               goto out;
+
+       if (!(names = dm_task_get_names(dmt)))
+               goto out;
+
+       if (!names->dev) {
+               r = 0; /* this is perfectly valid */
+               goto out;
+       }
+
+       if (dm_dev_t(old, &dev_t[0], 32))
+               goto out;
+
+       do {
+               if (
+                   /*
+                    * if devmap target is "linear"
+                    */
+                   (dm_type(names->name, TGT_PART) > 0) &&
+
+                   /*
+                    * and the multipath mapname and the part mapname start
+                    * the same
+                    */
+                   !strncmp(names->name, old, strlen(old)) &&
+
+                   /*
+                    * and we can fetch the map table from the kernel
+                    */
+                   !dm_get_map(names->name, &size, &buff[0]) &&
+
+                   /*
+                    * and the table maps over the multipath map
+                    */
+                   strstr(buff, dev_t)
+                  ) {
+                               /*
+                                * then it's a kpartx generated partition.
+                                * Rename it.
+                                */
+                               snprintf(buff, PARAMS_SIZE, "%s%s",
+                                        new, names->name + strlen(old));
+                               dm_rename(names->name, buff);
+                               condlog(4, "partition map %s renamed",
+                                       names->name);
+                  }
+
+               next = names->next;
+               names = (void *) names + next;
+       } while (next);
+
+       r = 0;
+out:
+       dm_task_destroy (dmt);
+       return r;
+}
+
+int
 dm_rename (char * old, char * new)
 {
        int r = 0;
        struct dm_task *dmt;
 
+       if (dm_rename_partmaps(old, new))
+               return r;
+
        if (!(dmt = dm_task_create(DM_DEVICE_RENAME)))
                return r;
 
@@ -858,9 +1120,11 @@ dm_rename (char * old, char * new)
 
        if (!dm_task_set_newname(dmt, new))
                goto out;
-       
+
        dm_task_no_open_count(dmt);
 
+       if (!dm_task_set_cookie(dmt, &conf->cookie, 0))
+               goto out;
        if (!dm_task_run(dmt))
                goto out;