[libmultipath] Remove libsysfs
authorChristophe Varoqui <cvaroqui@zezette.localdomain>
Thu, 7 Jun 2007 20:32:50 +0000 (22:32 +0200)
committerChristophe Varoqui <cvaroqui@zezette.localdomain>
Thu, 7 Jun 2007 20:32:50 +0000 (22:32 +0200)
libsysfs is deprecated and doesn't work with recent kernels.
Copied over stuff from udev and implemented our own sysfs handling.
Much saner now.

Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Guido Guenther <agx@sigxcpu.org>
20 files changed:
Makefile.inc
libmultipath/Makefile
libmultipath/config.h
libmultipath/configure.c
libmultipath/discovery.c
libmultipath/discovery.h
libmultipath/list.h [new file with mode: 0644]
libmultipath/print.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/sysfs.c [new file with mode: 0644]
libmultipath/sysfs.h [new file with mode: 0644]
libmultipath/util.c
libmultipath/util.h
multipath/Makefile
multipath/main.c
multipathd/Makefile
multipathd/cli_handlers.c
multipathd/main.c
multipathd/main.h

index 71970ef..7eb3dfb 100644 (file)
@@ -17,7 +17,6 @@ ifeq ($(strip $(BUILD)),klibc)
        CC = klcc
        klibcdir = /usr/lib/klibc
        libdm    = $(klibcdir)/lib/libdevmapper.a
-       libsysfs = $(klibcdir)/lib/libsysfs.a
 endif
 
 prefix      = 
index ef561a8..511f5ad 100644 (file)
@@ -13,7 +13,7 @@ OBJS = memory.o parser.o vector.o devmapper.o callout.o \
        structs.o discovery.o propsel.o dict.o \
        pgpolicies.o debug.o regex.o defaults.o uevent.o \
        switchgroup.o uxsock.o print.o alias.o log_pthread.o \
-       log.o configure.o structs_vec.o
+       log.o configure.o structs_vec.o sysfs.o
 
 PREVBUILD = $(shell nm debug.o 2> /dev/null|grep log_safe)
 
index 7caa11d..a25b3ad 100644 (file)
@@ -66,6 +66,7 @@ struct config {
        int pg_timeout;
 
        char * dev;
+       char * sysfs_dir;
        char * udev_dir;
        char * selector;
        char * getuid;
index a5bad4c..3cd6041 100644 (file)
@@ -325,8 +325,10 @@ domap (struct multipath * mpp)
                        return DOMAP_RETRY;
                }
 
-               if (dm_map_present(mpp->alias))
+               if (dm_map_present(mpp->alias)) {
+                       condlog(3, "%s: map already present", mpp->alias);
                        break;
+               }
 
                r = dm_addmap(DM_DEVICE_CREATE, mpp->alias, DEFAULT_TARGET,
                              mpp->params, mpp->size, mpp->wwid);
index 3fc9247..e3d4cd5 100644 (file)
@@ -8,9 +8,8 @@
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <dirent.h>
 #include <errno.h>
-#include <sysfs/dlist.h>
-#include <sysfs/libsysfs.h>
 
 #include <checkers.h>
 
@@ -24,6 +23,7 @@
 #include "debug.h"
 #include "propsel.h"
 #include "sg_include.h"
+#include "sysfs.h"
 #include "discovery.h"
 
 struct path *
@@ -87,127 +87,117 @@ path_discover (vector pathvec, struct config * conf, char * devname, int flag)
 int
 path_discovery (vector pathvec, struct config * conf, int flag)
 {
-       struct dlist * ls;
-       struct sysfs_class * class;
-       struct sysfs_class_device * dev;
-       int r = 1;
+       DIR *blkdir;
+       struct dirent *blkdev;
+       struct stat statbuf;
+       char devpath[PATH_MAX];
+       char *devptr;
+       int r = 0;
 
-       if (!(class = sysfs_open_class("block")))
+       if (!(blkdir = opendir("/sys/block")))
                return 1;
 
-       if (!(ls = sysfs_get_class_devices(class)))
-               goto out;
-
-       r = 0;
-
-       dlist_for_each_data(ls, dev, struct sysfs_class_device)
-               r += path_discover(pathvec, conf, dev->name, flag);
-
-out:
-       sysfs_close_class(class);
-       return r;
-}
+       strcpy(devpath,"/sys/block");
+       while ((blkdev = readdir(blkdir)) != NULL) {
+               if ((strcmp(blkdev->d_name,".") == 0) ||
+                   (strcmp(blkdev->d_name,"..") == 0))
+                       continue;
 
-/*
- * the daemon can race udev upon path add,
- * not multipath(8), ran by udev
- */
-#if DAEMON
-#define WAIT_MAX_SECONDS 60
-#define WAIT_LOOP_PER_SECOND 5
+               devptr = devpath + 10;
+               *devptr = '\0';
+               strcat(devptr,"/");
+               strcat(devptr,blkdev->d_name);
+               if (stat(devpath, &statbuf) < 0)
+                       continue;
 
-static int
-wait_for_file (char * filename)
-{
-       int loop;
-       struct stat stats;
-       
-       loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
-       
-       while (--loop) {
-               if (stat(filename, &stats) == 0)
-                       return 0;
+               if (S_ISDIR(statbuf.st_mode) == 0)
+                       continue;
 
-               if (errno != ENOENT)
-                       return 1;
+               condlog(4, "Discover device %s", devpath);
 
-               usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
+               r += path_discover(pathvec, conf, blkdev->d_name, flag);
        }
-       return 1;
-}
-#else
-static int
-wait_for_file (char * filename)
-{
-       return 0;
+       closedir(blkdir);
+       condlog(4, "Discovery status %d", r);
+       return r;
 }
-#endif
 
-#define declare_sysfs_get_str(fname, fmt) \
+#define declare_sysfs_get_str(fname) \
 extern int \
-sysfs_get_##fname (char * sysfs_path, char * dev, char * buff, int len) \
+sysfs_get_##fname (struct sysfs_device * dev, char * buff, size_t len) \
 { \
-       struct sysfs_attribute * attr; \
-       char attr_path[SYSFS_PATH_SIZE]; \
+       char *attr; \
 \
-       if (safe_sprintf(attr_path, fmt, sysfs_path, dev)) \
+       attr = sysfs_attr_get_value(dev->devpath, #fname); \
+       if (!attr) \
                return 1; \
 \
-       if (wait_for_file(attr_path)) \
-               return 1; \
-\
-       if (!(attr = sysfs_open_attribute(attr_path))) \
-               return 1; \
-\
-       if (0 > sysfs_read_attribute(attr)) \
-               goto out; \
-\
-       if (attr->len < 2 || attr->len - 1 > len) \
-               goto out; \
-\
-       strncpy(buff, attr->value, attr->len - 1); \
-       strchop(buff); \
-       sysfs_close_attribute(attr); \
+       if (strlcpy(buff, attr, len) != strlen(attr)) \
+               return 2; \
        return 0; \
-out: \
-       sysfs_close_attribute(attr); \
-       return 1; \
 }
 
-declare_sysfs_get_str(devtype, "%s/block/%s/device/devtype");
-declare_sysfs_get_str(cutype, "%s/block/%s/device/cutype");
-declare_sysfs_get_str(vendor, "%s/block/%s/device/vendor");
-declare_sysfs_get_str(model, "%s/block/%s/device/model");
-declare_sysfs_get_str(rev, "%s/block/%s/device/rev");
-declare_sysfs_get_str(dev, "%s/block/%s/dev");
+declare_sysfs_get_str(devtype);
+declare_sysfs_get_str(cutype);
+declare_sysfs_get_str(vendor);
+declare_sysfs_get_str(model);
+declare_sysfs_get_str(rev);
 
 int
-sysfs_get_size (char * sysfs_path, char * dev, unsigned long long * size)
+sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len)
 {
-       struct sysfs_attribute * attr;
-       char attr_path[SYSFS_PATH_SIZE];
-       int r;
+       char *attr;
 
-       if (safe_sprintf(attr_path, "%s/block/%s/size", sysfs_path, dev))
+       attr = sysfs_attr_get_value(dev->devpath, "dev");
+       if (!attr) {
+               condlog(3, "%s: no 'dev' attribute in sysfs", dev->kernel);
                return 1;
+       }
+       if (strlcpy(buff, attr, len) != strlen(attr)) {
+               condlog(3, "%s: overflow in 'dev' attribute", dev->kernel);
+               return 2;
+       }
+       return 0;
+}
 
-       attr = sysfs_open_attribute(attr_path);
+int
+sysfs_get_size (struct sysfs_device * dev, unsigned long long * size)
+{
+       char *attr;
+       int r;
 
+       attr = sysfs_attr_get_value(dev->devpath, "size");
        if (!attr)
                return 1;
 
-       if (0 > sysfs_read_attribute(attr))
-               goto out;
-
-       r = sscanf(attr->value, "%llu\n", size);
-       sysfs_close_attribute(attr);
+       r = sscanf(attr, "%llu\n", size);
 
        if (r != 1)
                return 1;
 
        return 0;
-out:
-       sysfs_close_attribute(attr);
+}
+       
+int
+sysfs_get_fc_nodename (struct sysfs_device * dev, char * node,
+                      unsigned int host, unsigned int channel,
+                      unsigned int target)
+{
+       char attr_path[SYSFS_PATH_SIZE], *attr;
+
+       if (safe_sprintf(attr_path, 
+                        "/class/fc_transport/target%i:%i:%i",
+                        host, channel, target)) {
+               condlog(0, "attr_path too small");
+               return 1;
+       }
+
+       attr = sysfs_attr_get_value(attr_path, "node_name");
+       if (attr) {
+               strlcpy(node, attr, strlen(attr));
+               return 0;
+       }
+
        return 1;
 }
        
@@ -224,11 +214,6 @@ opennode (char * dev, int mode)
                return -1;
        }
 
-       if (wait_for_file(devpath)) {
-               condlog(3, "failed to open %s", devpath);
-               return -1;
-       }
-
        return open(devpath, mode);
 }
 
@@ -259,6 +244,7 @@ devt2devname (char *devname, char *devt)
                }
                if (r != 3)
                        continue;
+
                if ((major == tmpmaj) && (minor == tmpmin)) {
                        sprintf(block_path, "/sys/block/%s", dev);
                        break;
@@ -278,7 +264,6 @@ devt2devname (char *devname, char *devt)
                condlog(0, "sysfs entry %s is not a directory\n", block_path);
                return 1;
        }
-       strncpy(devname, dev, FILE_NAME_SIZE);
        return 0;
 }
 
@@ -357,79 +342,21 @@ get_serial (char * str, int maxlen, int fd)
 }
 
 static int
-sysfs_get_bus (char * sysfs_path, struct path * pp)
+scsi_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent)
 {
-       struct sysfs_device *sdev;
        char attr_path[FILE_NAME_SIZE];
-       char attr_buff[FILE_NAME_SIZE];
-
-       pp->bus = SYSFS_BUS_UNDEF;
 
-       /*
-        * This is ugly : we should be able to do a simple
-        * get_link("%s/block/%s/device/bus", ...) but it just
-        * won't work
-        */
-       if(safe_sprintf(attr_path, "%s/block/%s/device",
-                       sysfs_path, pp->dev)) {
-               condlog(0, "attr_path too small");
-               return 1;
-       }
-
-       if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff)))
-               return 1;
-
-#if DAEMON
-       int loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
-
-       while (loop--) {
-               sdev = sysfs_open_device_path(attr_buff);
-
-               if (strlen(sdev->bus))
-                       break;
-
-               sysfs_close_device(sdev);
-               usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
-       }
-#else
-       sdev = sysfs_open_device_path(attr_buff);
-#endif
-
-       if (!strncmp(sdev->bus, "scsi", 4))
-               pp->bus = SYSFS_BUS_SCSI;
-       else if (!strncmp(sdev->bus, "ide", 3))
-               pp->bus = SYSFS_BUS_IDE;
-       else if (!strncmp(sdev->bus, "ccw", 3))
-               pp->bus = SYSFS_BUS_CCW;
-       else
-               return 1;
-
-       sysfs_close_device(sdev);
-
-       return 0;
-}
-
-static int
-scsi_sysfs_pathinfo (struct path * pp)
-{
-       char attr_path[FILE_NAME_SIZE];
-       char attr_buff[FILE_NAME_SIZE];
-       struct sysfs_attribute * attr;
-
-       if (sysfs_get_vendor(sysfs_path, pp->dev,
-                            pp->vendor_id, SCSI_VENDOR_SIZE))
+       if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE))
                return 1;
 
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
 
-       if (sysfs_get_model(sysfs_path, pp->dev,
-                           pp->product_id, SCSI_PRODUCT_SIZE))
+       if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE))
                return 1;
 
        condlog(3, "%s: product = %s", pp->dev, pp->product_id);
 
-       if (sysfs_get_rev(sysfs_path, pp->dev,
-                         pp->rev, SCSI_REV_SIZE))
+       if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE))
                return 1;
 
        condlog(3, "%s: rev = %s", pp->dev, pp->rev);
@@ -442,15 +369,7 @@ scsi_sysfs_pathinfo (struct path * pp)
        /*
         * host / bus / target / lun
         */
-       if(safe_sprintf(attr_path, "%s/block/%s/device",
-                       sysfs_path, pp->dev)) {
-               condlog(0, "attr_path too small");
-               return 1;
-       }
-       if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff)))
-               return 1;
-       
-       basename(attr_buff, attr_path);
+       basename(parent->devpath, attr_path);
 
        sscanf(attr_path, "%i:%i:%i:%i",
                        &pp->sg_id.host_no,
@@ -467,35 +386,19 @@ scsi_sysfs_pathinfo (struct path * pp)
        /*
         * target node name
         */
-       if(safe_sprintf(attr_path,
-                       "%s/class/fc_transport/target%i:%i:%i/node_name",
-                       sysfs_path,
-                       pp->sg_id.host_no,
-                       pp->sg_id.channel,
-                       pp->sg_id.scsi_id)) {
-               condlog(0, "attr_path too small");
-               return 1;
+       if(!sysfs_get_fc_nodename(parent, pp->tgt_node_name,
+                                pp->sg_id.host_no,
+                                pp->sg_id.channel,
+                                pp->sg_id.scsi_id)) {
+               condlog(3, "%s: tgt_node_name = %s",
+                       pp->dev, pp->tgt_node_name);
        }
-       if (!(attr = sysfs_open_attribute(attr_path)))
-               return 0;
-
-       if (sysfs_read_attribute(attr))
-               goto err;
-
-       if (attr->len > 0)
-               strncpy(pp->tgt_node_name, attr->value, attr->len - 1);
-
-       condlog(3, "%s: tgt_node_name = %s",
-               pp->dev, pp->tgt_node_name);
 
        return 0;
-err:
-       sysfs_close_attribute(attr);
-       return 1;
 }
 
 static int
-ccw_sysfs_pathinfo (struct path * pp)
+ccw_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent)
 {
        char attr_path[FILE_NAME_SIZE];
        char attr_buff[FILE_NAME_SIZE];
@@ -504,8 +407,7 @@ ccw_sysfs_pathinfo (struct path * pp)
 
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
 
-       if (sysfs_get_devtype(sysfs_path, pp->dev,
-                             attr_buff, FILE_NAME_SIZE))
+       if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE))
                return 1;
 
        if (!strncmp(attr_buff, "3370", 4)) {
@@ -525,16 +427,8 @@ ccw_sysfs_pathinfo (struct path * pp)
 
        /*
         * host / bus / target / lun
-        */
-       if(safe_sprintf(attr_path, "%s/block/%s/device",
-                       sysfs_path, pp->dev)) {
-               condlog(0, "attr_path too small");
-               return 1;
-       }
-       if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff)))
-               return 1;
-       
-       basename(attr_buff, attr_path);
+        */     
+       basename(parent->devpath, attr_path);
        pp->sg_id.lun = 0;
        sscanf(attr_path, "%i.%i.%x",
                        &pp->sg_id.host_no,
@@ -551,20 +445,20 @@ ccw_sysfs_pathinfo (struct path * pp)
 }
 
 static int
-common_sysfs_pathinfo (struct path * pp)
+common_sysfs_pathinfo (struct path * pp, struct sysfs_device *dev)
 {
-       if (sysfs_get_bus(sysfs_path, pp))
-               return 1;
-
-       condlog(3, "%s: bus = %i", pp->dev, pp->bus);
+       char *attr;
 
-       if (sysfs_get_dev(sysfs_path, pp->dev,
-                         pp->dev_t, BLK_DEV_SIZE))
+       attr = sysfs_attr_get_value(dev->devpath, "dev");
+       if (!attr) {
+               condlog(3, "%s: no 'dev' attribute in sysfs", pp->dev);
                return 1;
+       }
+       strlcpy(pp->dev_t, attr, BLK_DEV_SIZE);
 
        condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t);
 
-       if (sysfs_get_size(sysfs_path, pp->dev, &pp->size))
+       if (sysfs_get_size(dev, &pp->size))
                return 1;
 
        condlog(3, "%s: size = %llu", pp->dev, pp->size);
@@ -572,19 +466,46 @@ common_sysfs_pathinfo (struct path * pp)
        return 0;
 }
 
+struct sysfs_device *sysfs_device_from_path(struct path *pp)
+{
+       char sysdev[FILE_NAME_SIZE];
+
+       strlcpy(sysdev,"/block/", FILE_NAME_SIZE);
+       strlcat(sysdev,pp->dev, FILE_NAME_SIZE);
+
+       return sysfs_device_get(sysdev);
+}
+
 extern int
 sysfs_pathinfo(struct path * pp)
 {
-       if (common_sysfs_pathinfo(pp))
+       struct sysfs_device *parent;
+
+       pp->sysdev = sysfs_device_from_path(pp);
+       if (!pp->sysdev) {
+               condlog(1, "%s: failed to get sysfs information", pp->dev);
                return 1;
+       }
+       
+       parent = sysfs_device_get_parent(pp->sysdev);
+
+       if (common_sysfs_pathinfo(pp, pp->sysdev))
+               return 1;
+
+       condlog(3, "%s: subsystem = %s", pp->dev, parent->subsystem);
+
+       if (!strncmp(parent->subsystem, "scsi",4))
+               pp->bus = SYSFS_BUS_SCSI;
+       if (!strncmp(parent->subsystem, "ccw",3))
+               pp->bus = SYSFS_BUS_CCW;
 
        if (pp->bus == SYSFS_BUS_UNDEF)
                return 0;
        else if (pp->bus == SYSFS_BUS_SCSI) {
-               if (scsi_sysfs_pathinfo(pp))
+               if (scsi_sysfs_pathinfo(pp, parent))
                        return 1;
        } else if (pp->bus == SYSFS_BUS_CCW) {
-               if (ccw_sysfs_pathinfo(pp))
+               if (ccw_sysfs_pathinfo(pp, parent))
                        return 1;
        }
        return 0;
index ab62a59..c7cf7e8 100644 (file)
 #define SCSI_COMMAND_TERMINATED 0x22
 #define SG_ERR_DRIVER_SENSE     0x08
 
-int sysfs_get_vendor (char * sysfs_path, char * dev, char * buff, int len);
-int sysfs_get_model (char * sysfs_path, char * dev, char * buff, int len);
-int sysfs_get_rev (char * sysfs_path, char * dev, char * buff, int len);
-int sysfs_get_dev (char * sysfs_path, char * dev, char * buff, int len);
-
-int sysfs_get_size (char * sysfs_path, char * dev, unsigned long long *);
+int sysfs_get_dev (struct sysfs_device * dev, char * buff, size_t len);
 int path_discovery (vector pathvec, struct config * conf, int flag);
 
 void basename (char *, char *);
diff --git a/libmultipath/list.h b/libmultipath/list.h
new file mode 100644 (file)
index 0000000..8626630
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copied from the Linux kernel source tree, version 2.6.0-test1.
+ *
+ * Licensed under the GPL v2 as per the whole kernel source tree.
+ *
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+       const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+       (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries. 
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry); 
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(struct list_head *head)
+{
+       return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+                                struct list_head *head)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+       struct list_head *at = head->next;
+
+       first->prev = head;
+       head->next = first;
+
+       last->next = at;
+       at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+                                   struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head);
+               INIT_LIST_HEAD(list);
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); \
+               pos = pos->next)
+
+/**
+ * __list_for_each     -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev  -       iterate over a list backwards
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+       for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe  -       iterate over a list safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member);      \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_entry((head)->prev, typeof(*pos), member);      \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop counter.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif /* _LIST_H */
index e50f37d..b1e7b26 100644 (file)
@@ -5,8 +5,8 @@
 #include <string.h>
 #include <libdevmapper.h>
 #include <stdarg.h>
-#include <sysfs/dlist.h>
-#include <sysfs/libsysfs.h>
+#include <sys/stat.h>
+#include <dirent.h>
 
 #include <checkers.h>
 
@@ -1096,35 +1096,46 @@ snprint_blacklist_except (char * buff, int len)
 extern int
 snprint_devices (char * buff, int len, struct vectors *vecs)
 {
-        struct dlist * ls;
-        struct sysfs_class * class;
-        struct sysfs_class_device * dev;
+       DIR *blkdir;
+       struct dirent *blkdev;
+       struct stat statbuf;
+       char devpath[PATH_MAX];
+       char *devptr;
        int threshold = MAX_LINE_LEN;
        int fwd = 0;
        int r;
 
        struct path * pp;
 
-        if (!(class = sysfs_open_class("block")))
-                return 0;
-
-        if (!(ls = sysfs_get_class_devices(class))) {
-                sysfs_close_class(class);
-                return 0;
-        }
+       if (!(blkdir = opendir("/sys/block")))
+               return 1;
 
        if ((len - fwd - threshold) <= 0)
                return len;
        fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n");
 
-        dlist_for_each_data(ls, dev, struct sysfs_class_device) {
+       strcpy(devpath,"/sys/block");
+       devptr = devpath + 10;
+       while ((blkdev = readdir(blkdir)) != NULL) {
+               if ((strcmp(blkdev->d_name,".") == 0) ||
+                   (strcmp(blkdev->d_name,"..") == 0))
+                       continue;
+
+               strcat(devptr,blkdev->d_name);
+               if (stat(devptr, &statbuf) < 0)
+                       continue;
+
+               if (S_ISDIR(statbuf.st_mode) == 0)
+                       continue;
+
                if ((len - fwd - threshold)  <= 0)
                        return len;
-               fwd += snprintf(buff + fwd, len - fwd, "    %s", dev->name);
-               pp = find_path_by_dev(vecs->pathvec, dev->name);
+
+               fwd += snprintf(buff + fwd, len - fwd, "    %s", devpath);
+               pp = find_path_by_dev(vecs->pathvec, devpath);
                if (!pp) {
                        r = filter_devnode(conf->blist_devnode,
-                                          conf->elist_devnode, dev->name);
+                                          conf->elist_devnode, devpath);
                        if (r > 0)
                                fwd += snprintf(buff + fwd, len - fwd,
                                                " (blacklisted)");
@@ -1133,8 +1144,8 @@ snprint_devices (char * buff, int len, struct vectors *vecs)
                                                " (whitelisted)");
                }
                fwd += snprintf(buff + fwd, len - fwd, "\n");
-        }
-        sysfs_close_class(class);
+       }
+       closedir(blkdir);
 
        if (fwd > len)
                return len;
index 46dcdee..75322aa 100644 (file)
@@ -9,6 +9,9 @@
 #define FILE_NAME_SIZE         256
 #define CALLOUT_MAX_SIZE       128
 #define BLK_DEV_SIZE           33
+#define PATH_SIZE              512
+#define NAME_SIZE              128
+
 
 #define SCSI_VENDOR_SIZE       9
 #define SCSI_PRODUCT_SIZE      17
@@ -86,9 +89,19 @@ struct scsi_dev {
        int host_no;
 };
 
+struct sysfs_device {
+       struct sysfs_device *parent;            /* parent device */
+       char devpath[PATH_SIZE];
+       char subsystem[NAME_SIZE];              /* $class, $bus, drivers, module */
+       char kernel[NAME_SIZE];                 /* device instance name */
+       char kernel_number[NAME_SIZE];
+       char driver[NAME_SIZE];                 /* device driver name */
+};
+
 struct path {
        char dev[FILE_NAME_SIZE];
        char dev_t[BLK_DEV_SIZE];
+       struct sysfs_device *sysdev;
        struct scsi_idlun scsi_id;
        struct sg_id sg_id;
        char wwid[WWID_SIZE];
@@ -200,6 +213,6 @@ struct path * first_path (struct multipath * mpp);
 int pathcountgr (struct pathgroup *, int);
 int pathcount (struct multipath *, int);
 
-char sysfs_path[FILE_NAME_SIZE];
+extern char sysfs_path[PATH_SIZE];
 
 #endif /* _STRUCTS_H */
index a4a996a..1cc6028 100644 (file)
@@ -13,6 +13,7 @@
 #include "dmparser.h"
 #include "config.h"
 #include "propsel.h"
+#include "sysfs.h"
 #include "discovery.h"
 #include "waiter.h"
 
@@ -373,10 +374,10 @@ verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec)
                /*
                 * see if path is in sysfs
                 */
-               if (!pp->dev || sysfs_get_dev(sysfs_path,
-                                 pp->dev, pp->dev_t, BLK_DEV_SIZE)) {
+               if (!pp->sysdev || sysfs_get_dev(pp->sysdev,
+                                                pp->dev_t, BLK_DEV_SIZE)) {
                        condlog(0, "%s: failed to access path %s", mpp->alias,
-                               pp->dev ? pp->dev : pp->dev_t);
+                               pp->sysdev ? pp->sysdev->devpath : pp->dev_t);
                        count++;
                        vector_del_slot(mpp->paths, i);
                        i--;
diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c
new file mode 100644 (file)
index 0000000..0983e15
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify it
+ *     under the terms of the GNU General Public License as published by the
+ *     Free Software Foundation version 2 of the License.
+ * 
+ *     This program is distributed in the hope that it will be useful, but
+ *     WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *     General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License along
+ *     with this program; if not, write to the Free Software Foundation, Inc.,
+ *     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "checkers.h"
+#include "vector.h"
+#include "structs.h"
+#include "sysfs.h"
+#include "list.h"
+#include "util.h"
+
+char sysfs_path[PATH_SIZE];
+
+/* attribute value cache */
+static LIST_HEAD(attr_list);
+struct sysfs_attr {
+       struct list_head node;
+       char path[PATH_SIZE];
+       char *value;                    /* points to value_local if value is cached */
+       char value_local[NAME_SIZE];
+};
+
+int sysfs_init(char *path, size_t len)
+{
+       if (path) {
+               strlcpy(sysfs_path, path, len);
+               remove_trailing_chars(sysfs_path, '/');
+       } else
+               strlcpy(sysfs_path, "/sys", len);
+       dbg("sysfs_path='%s'", sysfs_path);
+
+       INIT_LIST_HEAD(&attr_list);
+       return 0;
+}
+
+void sysfs_cleanup(void)
+{
+       struct sysfs_attr *attr_loop;
+       struct sysfs_attr *attr_temp;
+
+       list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) {
+               list_del(&attr_loop->node);
+               free(attr_loop);
+       }
+
+}
+
+void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath,
+                            const char *subsystem, const char *driver)
+{
+       char *pos;
+
+       strlcpy(dev->devpath, devpath, sizeof(dev->devpath));
+       if (subsystem != NULL)
+               strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem));
+       if (driver != NULL)
+               strlcpy(dev->driver, driver, sizeof(dev->driver));
+
+       /* set kernel name */
+       pos = strrchr(dev->devpath, '/');
+       if (pos == NULL)
+               return;
+       strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel));
+       dbg("kernel='%s'", dev->kernel);
+
+       /* some devices have '!' in their name, change that to '/' */
+       pos = dev->kernel;
+       while (pos[0] != '\0') {
+               if (pos[0] == '!')
+                       pos[0] = '/';
+               pos++;
+       }
+
+       /* get kernel number */
+       pos = &dev->kernel[strlen(dev->kernel)];
+       while (isdigit(pos[-1]))
+               pos--;
+       strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number));
+       dbg("kernel_number='%s'", dev->kernel_number);
+}
+
+int sysfs_resolve_link(char *devpath, size_t size)
+{
+       char link_path[PATH_SIZE];
+       char link_target[PATH_SIZE];
+       int len;
+       int i;
+       int back;
+
+       strlcpy(link_path, sysfs_path, sizeof(link_path));
+       strlcat(link_path, devpath, sizeof(link_path));
+       len = readlink(link_path, link_target, sizeof(link_target));
+       if (len <= 0)
+               return -1;
+       link_target[len] = '\0';
+       dbg("path link '%s' points to '%s'", devpath, link_target);
+
+       for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
+               ;
+       dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back);
+       for (i = 0; i <= back; i++) {
+               char *pos = strrchr(devpath, '/');
+
+               if (pos == NULL)
+                       return -1;
+               pos[0] = '\0';
+       }
+       dbg("after moving back '%s'", devpath);
+       strlcat(devpath, "/", size);
+       strlcat(devpath, &link_target[back * 3], size);
+       return 0;
+}
+
+struct sysfs_device *sysfs_device_get(const char *devpath)
+{
+       char path[PATH_SIZE];
+       char devpath_real[PATH_SIZE];
+       struct sysfs_device *dev;
+       struct stat statbuf;
+       char link_path[PATH_SIZE];
+       char link_target[PATH_SIZE];
+       int len;
+       char *pos;
+
+       dbg("open '%s'", devpath);
+       strlcpy(devpath_real, devpath, sizeof(devpath_real));
+       remove_trailing_chars(devpath_real, '/');
+
+       /* if we got a link, resolve it to the real device */
+       strlcpy(path, sysfs_path, sizeof(path));
+       strlcat(path, devpath_real, sizeof(path));
+       if (lstat(path, &statbuf) != 0) {
+               dbg("stat '%s' failed: %s", path, strerror(errno));
+               return NULL;
+       }
+       if (S_ISLNK(statbuf.st_mode)) {
+               if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0)
+                       return NULL;
+
+       }
+
+       /* it is a new device */
+       dbg("new device '%s'", devpath_real);
+       dev = malloc(sizeof(struct sysfs_device));
+       if (dev == NULL)
+               return NULL;
+       memset(dev, 0x00, sizeof(struct sysfs_device));
+
+       sysfs_device_set_values(dev, devpath_real, NULL, NULL);
+
+       /* get subsystem name */
+       strlcpy(link_path, sysfs_path, sizeof(link_path));
+       strlcat(link_path, dev->devpath, sizeof(link_path));
+       strlcat(link_path, "/subsystem", sizeof(link_path));
+       len = readlink(link_path, link_target, sizeof(link_target));
+       if (len > 0) {
+               /* get subsystem from "subsystem" link */
+               link_target[len] = '\0';
+               dbg("subsystem link '%s' points to '%s'", link_path, link_target);
+               pos = strrchr(link_target, '/');
+               if (pos != NULL)
+                       strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem));
+       } else if (strncmp(dev->devpath, "/class/", 7) == 0) {
+               /* get subsystem from class dir */
+               strlcpy(dev->subsystem, &dev->devpath[7], sizeof(dev->subsystem));
+               pos = strchr(dev->subsystem, '/');
+               if (pos != NULL)
+                       pos[0] = '\0';
+               else
+                       dev->subsystem[0] = '\0';
+       } else if (strncmp(dev->devpath, "/block/", 7) == 0) {
+               strlcpy(dev->subsystem, "block", sizeof(dev->subsystem));
+       } else if (strncmp(dev->devpath, "/devices/", 9) == 0) {
+               /* get subsystem from "bus" link */
+               strlcpy(link_path, sysfs_path, sizeof(link_path));
+               strlcat(link_path, dev->devpath, sizeof(link_path));
+               strlcat(link_path, "/bus", sizeof(link_path));
+               len = readlink(link_path, link_target, sizeof(link_target));
+               if (len > 0) {
+                       link_target[len] = '\0';
+                       dbg("bus link '%s' points to '%s'", link_path, link_target);
+                       pos = strrchr(link_target, '/');
+                       if (pos != NULL)
+                               strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem));
+               }
+       } else if (strstr(dev->devpath, "/drivers/") != NULL) {
+               strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem));
+       } else if (strncmp(dev->devpath, "/module/", 8) == 0) {
+               strlcpy(dev->subsystem, "module", sizeof(dev->subsystem));
+       }
+
+       /* get driver name */
+       strlcpy(link_path, sysfs_path, sizeof(link_path));
+       strlcat(link_path, dev->devpath, sizeof(link_path));
+       strlcat(link_path, "/driver", sizeof(link_path));
+       len = readlink(link_path, link_target, sizeof(link_target));
+       if (len > 0) {
+               link_target[len] = '\0';
+               dbg("driver link '%s' points to '%s'", link_path, link_target);
+               pos = strrchr(link_target, '/');
+               if (pos != NULL)
+                       strlcpy(dev->driver, &pos[1], sizeof(dev->driver));
+       }
+
+       return dev;
+}
+
+struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev)
+{
+       char parent_devpath[PATH_SIZE];
+       char *pos;
+
+       dbg("open '%s'", dev->devpath);
+
+       /* look if we already know the parent */
+       if (dev->parent != NULL)
+               return dev->parent;
+
+       /* requesting a parent is only valid for devices */
+       if ((strncmp(dev->devpath, "/devices/", 9) != 0) &&
+           (strncmp(dev->devpath, "/subsystem/", 11) != 0) &&
+           (strncmp(dev->devpath, "/class/", 7) != 0) &&
+           (strncmp(dev->devpath, "/block/", 7) != 0))
+               return NULL;
+
+       strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
+       dbg("'%s'", parent_devpath);
+
+       /* strip last element */
+       pos = strrchr(parent_devpath, '/');
+       if (pos == NULL || pos == parent_devpath)
+               return NULL;
+       pos[0] = '\0';
+
+       /* are we at the top level of /devices */
+       if (strcmp(parent_devpath, "/devices") == 0) {
+               dbg("/devices top level");
+               return NULL;
+       }
+
+       /* at the subsystems top level we want to follow the old-style "device" link */
+       if (strncmp(parent_devpath, "/subsystem", 10) == 0) {
+               pos = strrchr(parent_devpath, '/');
+               if (pos == &parent_devpath[10] || pos == parent_devpath || strcmp(pos, "/devices") == 0) {
+                       dbg("/subsystem top level, look for device link");
+                       goto device_link;
+               }
+       }
+       if (strncmp(parent_devpath, "/class", 6) == 0) {
+               pos = strrchr(parent_devpath, '/');
+               if (pos == &parent_devpath[6] || pos == parent_devpath) {
+                       dbg("/class top level, look for device link");
+                       goto device_link;
+               }
+       }
+       if (strcmp(parent_devpath, "/block") == 0) {
+               dbg("/block top level, look for device link");
+               goto device_link;
+       }
+
+       /* get parent and remember it */
+       dev->parent = sysfs_device_get(parent_devpath);
+       return dev->parent;
+
+device_link:
+       strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
+       strlcat(parent_devpath, "/device", sizeof(parent_devpath));
+       if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0)
+               return NULL;
+
+       /* get parent and remember it */
+       dev->parent = sysfs_device_get(parent_devpath);
+       return dev->parent;
+}
+
+struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem)
+{
+       struct sysfs_device *dev_parent;
+
+       dev_parent = sysfs_device_get_parent(dev);
+       while (dev_parent != NULL) {
+               if (strcmp(dev_parent->subsystem, subsystem) == 0)
+                       return dev_parent;
+               dev_parent = sysfs_device_get_parent(dev_parent);
+       }
+       return NULL;
+}
+
+char *sysfs_attr_get_value(const char *devpath, const char *attr_name)
+{
+       char path_full[PATH_SIZE];
+       const char *path;
+       char value[NAME_SIZE];
+       struct sysfs_attr *attr_loop;
+       struct sysfs_attr *attr;
+       struct stat statbuf;
+       int fd;
+       ssize_t size;
+       size_t sysfs_len;
+
+       dbg("open '%s'/'%s'", devpath, attr_name);
+       sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
+       path = &path_full[sysfs_len];
+       strlcat(path_full, devpath, sizeof(path_full));
+       strlcat(path_full, "/", sizeof(path_full));
+       strlcat(path_full, attr_name, sizeof(path_full));
+
+       /* look for attribute in cache */
+       list_for_each_entry(attr_loop, &attr_list, node) {
+               if (strcmp(attr_loop->path, path) == 0) {
+                       dbg("found in cache '%s'", attr_loop->path);
+                       attr = attr_loop;
+               }
+       }
+       if (!attr) {
+               /* store attribute in cache */
+               dbg("new uncached attribute '%s'", path_full);
+               attr = malloc(sizeof(struct sysfs_attr));
+               if (attr == NULL)
+                       return NULL;
+               memset(attr, 0x00, sizeof(struct sysfs_attr));
+               strlcpy(attr->path, path, sizeof(attr->path));
+               dbg("add to cache '%s'", path_full);
+               list_add(&attr->node, &attr_list);
+       } else {
+               /* clear old value */
+               memset(attr->value, 0x00, sizeof(attr->value));
+       }
+
+       if (lstat(path_full, &statbuf) != 0) {
+               dbg("stat '%s' failed: %s", path_full, strerror(errno));
+               goto out;
+       }
+
+       if (S_ISLNK(statbuf.st_mode)) {
+               /* links return the last element of the target path */
+               char link_target[PATH_SIZE];
+               int len;
+               const char *pos;
+
+               len = readlink(path_full, link_target, sizeof(link_target));
+               if (len > 0) {
+                       link_target[len] = '\0';
+                       pos = strrchr(link_target, '/');
+                       if (pos != NULL) {
+                               dbg("cache '%s' with link value '%s'", path_full, value);
+                               strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local));
+                               attr->value = attr->value_local;
+                       }
+               }
+               goto out;
+       }
+
+       /* skip directories */
+       if (S_ISDIR(statbuf.st_mode))
+               goto out;
+
+       /* skip non-readable files */
+       if ((statbuf.st_mode & S_IRUSR) == 0)
+               goto out;
+
+       /* read attribute value */
+       fd = open(path_full, O_RDONLY);
+       if (fd < 0) {
+               dbg("attribute '%s' does not exist", path_full);
+               goto out;
+       }
+       size = read(fd, value, sizeof(value));
+       close(fd);
+       if (size < 0)
+               goto out;
+       if (size == sizeof(value))
+               goto out;
+
+       /* got a valid value, store and return it */
+       value[size] = '\0';
+       remove_trailing_chars(value, '\n');
+       dbg("cache '%s' with attribute value '%s'", path_full, value);
+       strlcpy(attr->value_local, value, sizeof(attr->value_local));
+       attr->value = attr->value_local;
+
+out:
+       return attr->value;
+}
diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h
new file mode 100644 (file)
index 0000000..cf3af36
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * sysfs.h
+ */
+
+#ifndef _LIBMULTIPATH_SYSFS_H
+#define _LIBMULTIPATH_SYSFS_H
+
+#ifdef DEBUG
+# define dbg printf
+#else
+# define dbg(format, arg...) do {} while (0)
+#endif
+
+int sysfs_init(char *path, size_t len);
+void sysfs_cleanup(void);
+void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath,
+                            const char *subsystem, const char *driver);
+struct sysfs_device *sysfs_device_get(const char *devpath);
+struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev);
+struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem);
+char *sysfs_attr_get_value(const char *devpath, const char *attr_name);
+int sysfs_resolve_link(char *path, size_t size);
+
+#endif
index 911ec55..eaf2266 100644 (file)
@@ -103,3 +103,55 @@ get_word (char * sentence, char ** word)
        return skip + len;
 }
 
+size_t strlcpy(char *dst, const char *src, size_t size)
+{
+       size_t bytes = 0;
+       char *q = dst;
+       const char *p = src;
+       char ch;
+
+       while ((ch = *p++)) {
+               if (bytes+1 < size)
+                       *q++ = ch;
+               bytes++;
+       }
+
+       /* If size == 0 there is no space for a final null... */
+       if (size)
+               *q = '\0';
+       return bytes;
+}
+
+size_t strlcat(char *dst, const char *src, size_t size)
+{
+       size_t bytes = 0;
+       char *q = dst;
+       const char *p = src;
+       char ch;
+
+       while (bytes < size && *q) {
+               q++;
+               bytes++;
+       }
+       if (bytes == size)
+               return (bytes + strlen(src));
+
+       while ((ch = *p++)) {
+               if (bytes+1 < size)
+               *q++ = ch;
+               bytes++;
+       }
+
+       *q = '\0';
+       return bytes;
+}
+
+void remove_trailing_chars(char *path, char c)
+{
+       size_t len;
+
+       len = strlen(path);
+       while (len > 0 && path[len-1] == c)
+               path[--len] = '\0';
+}
+
index e86bae2..d0df8aa 100644 (file)
@@ -6,7 +6,9 @@ void strchop(char *);
 void basename (char * src, char * dst);
 int filepresent (char * run);
 int get_word (char * sentence, char ** word);
-
+size_t strlcpy(char *dst, const char *src, size_t size);
+size_t strlcat(char *dst, const char *src, size_t size);
+void remove_trailing_chars(char *path, char c);
 
 #define safe_sprintf(var, format, args...)     \
        snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
index ec3f73e..c4c70cd 100644 (file)
@@ -12,7 +12,7 @@ CFLAGS += -I$(multipathdir) -I$(checkersdir)
 ifeq ($(strip $(BUILD)),klibc)
        OBJS += $(libdm) $(libsysfs)
 else
-       LDFLAGS += -ldevmapper -lsysfs
+       LDFLAGS += -ldevmapper
 endif
 
 EXEC = multipath
index acc3137..815c307 100644 (file)
@@ -25,7 +25,6 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <ctype.h>
-#include <sysfs/libsysfs.h>
 
 #include <checkers.h>
 #include <vector.h>
@@ -37,6 +36,7 @@
 #include <structs.h>
 #include <structs_vec.h>
 #include <dmparser.h>
+#include <sysfs.h>
 #include <config.h>
 #include <blacklist.h>
 #include <discovery.h>
@@ -323,13 +323,13 @@ main (int argc, char *argv[])
        if (dm_prereq(DEFAULT_TARGET))
                exit(1);
 
-       if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
-               condlog(0, "multipath tools need sysfs mounted");
-               exit(1);
-       }
        if (load_config(DEFAULT_CONFIGFILE))
                exit(1);
 
+       if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) {
+               condlog(0, "multipath tools need sysfs mounted");
+               exit(1);
+       }
        while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:")) != EOF ) {
                switch(arg) {
                case 1: printf("optarg : %s\n",optarg);
@@ -419,6 +419,7 @@ main (int argc, char *argv[])
                condlog(3, "restart multipath configuration process");
        
 out:
+       sysfs_cleanup();
        free_config(conf);
        dm_lib_release();
        dm_lib_exit();
index 8ad25ee..5ae9e7b 100644 (file)
@@ -7,7 +7,7 @@ include ../Makefile.inc
 # basic flags setting
 #
 CFLAGS += -DDAEMON -I$(multipathdir) -I$(checkersdir)
-LDFLAGS = -lpthread -ldevmapper -lsysfs -lreadline -lncurses
+LDFLAGS = -lpthread -ldevmapper -lreadline -lncurses
 
 #
 # debuging stuff
index 4938e84..7bae02a 100644 (file)
@@ -13,6 +13,7 @@
 #include <blacklist.h>
 #include <debug.h>
 #include <print.h>
+#include <sysfs.h>
 
 #include "main.h"
 #include "cli.h"
@@ -312,6 +313,9 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
 {
        struct vectors * vecs = (struct vectors *)data;
        char * param = get_keyparam(v, MAP);
+       int minor;
+       char dev_path[PATH_SIZE];
+       struct sysfs_device *sysdev;
 
        condlog(2, "%s: add map (operator)", param);
 
@@ -321,7 +325,18 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
                condlog(2, "%s: map blacklisted", param);
                return 0;
        }
-       return ev_add_map(param, vecs);
+       minor = dm_get_minor(param);
+       if (minor < 0) {
+               condlog(2, "%s: not a device mapper table", param);
+               return 0;
+       }
+       sprintf(dev_path,"/block/dm-%d", minor);
+       sysdev = sysfs_device_get(dev_path);
+       if (!sysdev) {
+               condlog(2, "%s: not found in sysfs", param);
+               return 0;
+       }
+       return ev_add_map(sysdev, vecs);
 }
 
 int
index 94b0b95..a173da3 100644 (file)
 #include <fcntl.h>
 #include <errno.h>
 
-/*
- * libsysfs
- */
-#include <sysfs/libsysfs.h>
-#include <sysfs/dlist.h>
-
 /*
  * libcheckers
  */
@@ -40,6 +34,7 @@
 #include <structs_vec.h>
 #include <dmparser.h>
 #include <devmapper.h>
+#include <sysfs.h>
 #include <dict.h>
 #include <discovery.h>
 #include <debug.h>
@@ -208,31 +203,29 @@ flush_map(struct multipath * mpp, struct vectors * vecs)
 }
 
 static int
-uev_add_map (char * devname, struct vectors * vecs)
+uev_add_map (struct sysfs_device * dev, struct vectors * vecs)
 {
-       condlog(2, "%s: add map (uevent)", devname);
-       return ev_add_map(devname, vecs);
+       condlog(2, "%s: add map (uevent)", dev->kernel);
+       return ev_add_map(dev, vecs);
 }
 
 int
-ev_add_map (char * devname, struct vectors * vecs)
+ev_add_map (struct sysfs_device * dev, struct vectors * vecs)
 {
-       int major, minor;
-       char dev_t[BLK_DEV_SIZE];
        char * alias;
+       char *dev_t;
+       int major, minor;
        char * refwwid;
        struct multipath * mpp;
        int map_present;
        int r = 1;
 
-       /* libsysfs seems to forget to terminate the string... */
-       memset(dev_t, 0, BLK_DEV_SIZE);
-       if (sscanf(devname, "dm-%d", &minor) == 1 &&
-           !sysfs_get_dev(sysfs_path, devname, dev_t, BLK_DEV_SIZE) &&
-           sscanf(dev_t, "%d:%d", &major, &minor) == 2)
-               alias = dm_mapname(major, minor);
-       else
-               alias = STRDUP(devname);
+       dev_t = sysfs_attr_get_value(dev->devpath, "dev");
+
+       if (!dev_t || sscanf(dev_t, "%d:%d", &major, &minor) != 2)
+               return 1;
+
+       alias = dm_mapname(major, minor);
                
        if (!alias)
                return 1;
@@ -241,7 +234,6 @@ ev_add_map (char * devname, struct vectors * vecs)
 
        if (map_present && dm_type(alias, DEFAULT_TARGET) <= 0) {
                condlog(4, "%s: not a multipath map", alias);
-               FREE(alias);
                return 0;
        }
 
@@ -254,8 +246,7 @@ ev_add_map (char * devname, struct vectors * vecs)
                 * of uev_add_path
                 */
                condlog(0, "%s: devmap already registered",
-                       devname);
-               FREE(alias);
+                       dev->kernel);
                return 0;
        }
 
@@ -265,10 +256,10 @@ ev_add_map (char * devname, struct vectors * vecs)
        if (map_present && (mpp = add_map_without_path(vecs, minor, alias,
                                        start_waiter_thread))) {
                sync_map_state(mpp);
-               condlog(3, "%s: devmap %s added", alias, devname);
+               condlog(3, "%s: devmap %s added", alias, dev->kernel);
                return 0;
        }
-       refwwid = get_refwwid(devname, DEV_DEVMAP, vecs->pathvec);
+       refwwid = get_refwwid(dev->kernel, DEV_DEVMAP, vecs->pathvec);
 
        if (refwwid) {
                r = coalesce_paths(vecs, NULL, refwwid);
@@ -276,20 +267,19 @@ ev_add_map (char * devname, struct vectors * vecs)
        }
        
        if (!r)
-               condlog(3, "%s: devmap %s added", alias, devname);
+               condlog(3, "%s: devmap %s added", alias, dev->kernel);
        else
-               condlog(0, "%s: uev_add_map %s failed", alias, devname);
+               condlog(0, "%s: uev_add_map %s failed", alias, dev->kernel);
 
        FREE(refwwid);
-       FREE(alias);
        return r;
 }
 
 static int
-uev_remove_map (char * devname, struct vectors * vecs)
+uev_remove_map (struct sysfs_device * dev, struct vectors * vecs)
 {
-       condlog(2, "%s: remove map (uevent)", devname);
-       return ev_remove_map(devname, vecs);
+       condlog(2, "%s: remove map (uevent)", dev->kernel);
+       return ev_remove_map(dev->kernel, vecs);
 }
 
 int
@@ -310,13 +300,13 @@ ev_remove_map (char * devname, struct vectors * vecs)
 }
 
 static int
-uev_umount_map (char * devname, struct vectors * vecs)
+uev_umount_map (struct sysfs_device * dev, struct vectors * vecs)
 {
        struct multipath * mpp;
 
-       condlog(2, "%s: umount map (uevent)", devname);
+       condlog(2, "%s: umount map (uevent)", dev->kernel);
 
-       mpp = find_mp_by_str(vecs->mpvec, devname);
+       mpp = find_mp_by_str(vecs->mpvec, dev->kernel);
 
        if (!mpp)
                return 0;
@@ -331,10 +321,10 @@ uev_umount_map (char * devname, struct vectors * vecs)
 }
        
 static int
-uev_add_path (char * devname, struct vectors * vecs)
+uev_add_path (struct sysfs_device * dev, struct vectors * vecs)
 {
-       condlog(2, "%s: add path (uevent)", devname);
-       return (ev_add_path(devname, vecs) != 1)? 0 : 1;
+       condlog(2, "%s: add path (uevent)", dev->kernel);
+       return (ev_add_path(dev->kernel, vecs) != 1)? 0 : 1;
 }
 
 
@@ -450,10 +440,10 @@ out:
 }
 
 static int
-uev_remove_path (char * devname, struct vectors * vecs)
+uev_remove_path (struct sysfs_device * dev, struct vectors * vecs)
 {
-       condlog(2, "%s: remove path (uevent)", devname);
-       return ev_remove_path(devname, vecs);
+       condlog(2, "%s: remove path (uevent)", dev->kernel);
+       return ev_remove_path(dev->kernel, vecs);
 }
 
 int
@@ -636,7 +626,7 @@ int
 uev_trigger (struct uevent * uev, void * trigger_data)
 {
        int r = 0;
-       char devname[32];
+       struct sysfs_device *sysdev;
        struct vectors * vecs;
 
        vecs = (struct vectors *)trigger_data;
@@ -644,7 +634,7 @@ uev_trigger (struct uevent * uev, void * trigger_data)
        if (uev_discard(uev->devpath))
                return 0;
 
-       basename(uev->devpath, devname);
+       sysdev = sysfs_device_get(uev->devpath);
        lock(vecs->lock);
 
        /*
@@ -652,17 +642,17 @@ uev_trigger (struct uevent * uev, void * trigger_data)
         * Add events are ignored here as the tables
         * are not fully initialised then.
         */
-       if (!strncmp(devname, "dm-", 3)) {
+       if (!strncmp(sysdev->kernel, "dm-", 3)) {
                if (!strncmp(uev->action, "change", 6)) {
-                       r = uev_add_map(devname, vecs);
+                       r = uev_add_map(sysdev, vecs);
                        goto out;
                }
                if (!strncmp(uev->action, "remove", 6)) {
-                       r = uev_remove_map(devname, vecs);
+                       r = uev_remove_map(sysdev, vecs);
                        goto out;
                }
                if (!strncmp(uev->action, "umount", 6)) {
-                       r = uev_umount_map(devname, vecs);
+                       r = uev_umount_map(sysdev, vecs);
                        goto out;
                }
                goto out;
@@ -672,15 +662,15 @@ uev_trigger (struct uevent * uev, void * trigger_data)
         * path add/remove event
         */
        if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
-                          devname) > 0)
+                          sysdev->kernel) > 0)
                goto out;
 
        if (!strncmp(uev->action, "add", 3)) {
-               r = uev_add_path(devname, vecs);
+               r = uev_add_path(sysdev, vecs);
                goto out;
        }
        if (!strncmp(uev->action, "remove", 6)) {
-               r = uev_remove_path(devname, vecs);
+               r = uev_remove_path(sysdev, vecs);
                goto out;
        }
 
@@ -1278,7 +1268,7 @@ child (void * param)
        if (!vecs)
                exit(1);
 
-       if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
+       if (sysfs_init(conf->sysfs_dir, FILE_NAME_SIZE)) {
                condlog(0, "can not find sysfs mount point");
                exit(1);
        }
@@ -1315,6 +1305,8 @@ child (void * param)
        pthread_cancel(uevent_thr);
        pthread_cancel(uxlsnr_thr);
 
+       sysfs_cleanup();
+
        free_keys(keys);
        keys = NULL;
        free_handlers(handlers);
index d0cce3a..1a6dc55 100644 (file)
@@ -7,7 +7,7 @@
 int reconfigure (struct vectors *);
 int ev_add_path (char *, struct vectors *);
 int ev_remove_path (char *, struct vectors *);
-int ev_add_map (char *, struct vectors *);
+int ev_add_map (struct sysfs_device *, struct vectors *);
 int ev_remove_map (char *, struct vectors *);
 
 #endif /* MAIN_H */