Makefile.inc | 1 - devmap_name/Makefile | 5 +- kpartx/Makefile | 10 +- kpartx/devmapper.c | 29 ++ kpartx/devmapper.h | 1 + kpartx/dos.c | 7 +- kpartx/kpartx.c | 125 ++++++++-- kpartx/kpartx.h | 3 + kpartx/kpartx_id | 110 ++++++++ libcheckers/Makefile | 4 +- libmultipath/Makefile | 6 +- libmultipath/config.h | 1 + libmultipath/configure.c | 4 +- libmultipath/discovery.c | 422 ++++++++++++------------------- libmultipath/discovery.h | 7 +- libmultipath/list.h | 289 +++++++++++++++++++++ libmultipath/print.c | 47 ++-- libmultipath/structs.h | 15 +- libmultipath/structs_vec.c | 7 +- libmultipath/sysfs.c | 405 +++++++++++++++++++++++++++++ libmultipath/sysfs.h | 20 ++ libmultipath/uevent.c | 99 +++++--- libmultipath/util.c | 52 ++++ libmultipath/util.h | 4 +- multipath/Makefile | 10 +- multipath/main.c | 11 +- multipath/multipath.init.suse | 104 ++++++++ multipathd/71-multipath.rules | 27 ++ multipathd/Makefile | 7 +- multipathd/cli_handlers.c | 17 ++- multipathd/main.c | 92 +++---- multipathd/main.h | 2 +- multipathd/multipathd.init.suse | 155 +++++++++++ path_priority/pp_alua/Makefile | 3 +- path_priority/pp_balance_units/Makefile | 6 +- path_priority/pp_emc/Makefile | 2 +- path_priority/pp_hds_modular/Makefile | 2 +- path_priority/pp_netapp/Makefile | 2 +- path_priority/pp_random/Makefile | 2 +- path_priority/pp_tpc/Makefile | 2 +- 40 files changed, 1685 insertions(+), 432 deletions(-) diff --git a/Makefile.inc b/Makefile.inc index fe6cbdf..786565c 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -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 = diff --git a/devmap_name/Makefile b/devmap_name/Makefile index 8b0c678..57051d9 100644 --- a/devmap_name/Makefile +++ b/devmap_name/Makefile @@ -18,7 +18,7 @@ EXEC = devmap_name all: $(BUILD) prepare: - rm -f core *.o *.gz + rm -f core *.o glibc: prepare $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) @@ -34,7 +34,6 @@ install: $(EXEC) $(EXEC).8 uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) - rm $(DESTDIR)$(mandir)/$(EXEC).8 clean: - rm -f core *.o $(EXEC) *.gz + rm -f core *.o $(EXEC) diff --git a/kpartx/Makefile b/kpartx/Makefile index 522a6a0..9832467 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -23,7 +23,7 @@ EXEC = kpartx all: $(BUILD) prepare: - rm -f core *.o *.gz + rm -f core *.o glibc: prepare $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) @@ -34,15 +34,15 @@ klibc: prepare $(OBJS) $(MULTIPATHLIB)-$(BUILD).a: make -C $(multipathdir) BUILD=$(BUILD) -install: $(EXEC) $(EXEC).8 +install: $(EXEC) kpartx_id $(EXEC).8 install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) + install -m 755 $(EXEC) $(DESTDIR)$(bindir) + install -m 755 kpartx_id $(DESTDIR)$(bindir) install -d $(DESTDIR)$(mandir) install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) uninstall: rm -f $(DESTDIR)$(bindir)/$(EXEC) - rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz clean: - rm -f core *.o $(EXEC) *.gz + rm -f core *.o $(EXEC) diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 4b228ed..e5da022 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -219,3 +219,32 @@ out: dm_task_destroy(dmt); return uuid; } + +int +dm_devn (char * mapname, int *major, int *minor) +{ + int r = 1; + struct dm_task *dmt; + struct dm_info info; + + if (!(dmt = dm_task_create(DM_DEVICE_INFO))) + return 0; + + if (!dm_task_set_name(dmt, mapname)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + if (!dm_task_get_info(dmt, &info)) + goto out; + + *major = info.major; + *minor = info.minor; + + r = 0; +out: + dm_task_destroy(dmt); + return r; +} + diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h index e20e456..ccdbead 100644 --- a/kpartx/devmapper.h +++ b/kpartx/devmapper.h @@ -6,3 +6,4 @@ int dm_map_present (char *); char * dm_mapname(int major, int minor); dev_t dm_get_first_dep(char *devname); char * dm_mapuuid(int major, int minor); +int dm_devn (char * mapname, int *major, int *minor); diff --git a/kpartx/dos.c b/kpartx/dos.c index a707423..1691105 100644 --- a/kpartx/dos.c +++ b/kpartx/dos.c @@ -16,7 +16,7 @@ is_extended(int type) { } static int -read_extended_partition(int fd, struct partition *ep, +read_extended_partition(int fd, struct partition *ep, int en, struct slice *sp, int ns) { struct partition p; @@ -53,6 +53,7 @@ read_extended_partition(int fd, struct p if (n < ns) { sp[n].start = here + le32_to_cpu(p.start_sect); sp[n].size = le32_to_cpu(p.nr_sects); + sp[n].container = en + 1; n++; } else { fprintf(stderr, @@ -97,9 +98,7 @@ read_dos_pt(int fd, struct slice all, st break; } if (is_extended(p.sys_type)) { - n += read_extended_partition(fd, &p, sp+n, ns-n); - /* hide the extended partition itself */ - sp[i].size = 0; + n += read_extended_partition(fd, &p, i, sp+n, ns-n); } } return n; diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index b406b95..c48a59c 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -182,7 +182,7 @@ get_hotplug_device(void) int main(int argc, char **argv){ - int fd, i, j, k, n, op, off, arg; + int fd, i, j, k, m, n, op, off, arg, c, d; struct slice all; struct pt *ptp; enum action what = LIST; @@ -353,30 +353,47 @@ main(int argc, char **argv){ else continue; - /* - * test for overlap, as in the case of an extended partition - * zero their size to avoid mapping - */ - for (j=0; j slices[j].start && - slices[k].start < slices[j].start + - slices[j].size) - slices[j].size = 0; - } - } - switch(what) { case LIST: - for (j = 0; j < n; j++) { + for (j = 0, c = 0, m = 0; j < n; j++) { if (slices[j].size == 0) continue; + if (slices[j].container > 0) { + c++; + continue; + } + + slices[j].minor = m++; printf("%s%s%d : 0 %lu %s %lu\n", mapname, delim, j+1, (unsigned long) slices[j].size, device, (unsigned long) slices[j].start); } + /* Loop to resolve contained slices */ + d = c; + while (c) { + for (j = 0; j < n; j++) { + if (slices[j].size == 0) + continue; + if (slices[j].minor > 0) + continue; + if (slices[j].container == 0) + continue; + slices[j].minor = m++; + + printf("%s%s%d : 0 %lu /dev/dm-%d %lu\n", + mapname, delim, j+1, + (unsigned long) slices[j].size, + slices[j].minor, + (unsigned long) slices[j].start); + c--; + } + /* Terminate loop if nothing more to resolve */ + if (d == c) + break; + } + break; case DELETE: @@ -410,10 +427,16 @@ main(int argc, char **argv){ break; case ADD: - for (j=0; j 0) { + c++; + continue; + } + if (safe_sprintf(partname, "%s%s%d", mapname, delim, j+1)) { fprintf(stderr, "partname too small\n"); @@ -437,10 +460,74 @@ main(int argc, char **argv){ dm_simplecmd(DM_DEVICE_RESUME, partname); + dm_devn(partname, &slices[j].major, + &slices[j].minor); + if (verbose) - printf("add map %s : 0 %lu %s %s\n", - partname, slices[j].size, - DM_TARGET, params); + printf("add map %s (%d:%d): 0 %lu %s %s\n", + partname, slices[j].major, + slices[j].minor, slices[j].size, + DM_TARGET, params); + } + /* Loop to resolve contained slices */ + d = c; + while (c) { + for (j = 0; j < n; j++) { + int k = slices[j].container - 1; + + if (slices[j].size == 0) + continue; + + /* Skip all existing slices */ + if (slices[j].minor > 0) + continue; + + /* Skip all simple slices */ + if (k < 0) + continue; + + /* Check container slice */ + if (slices[k].size == 0) + fprintf(stderr, "Invalid slice %d\n", + k); + + if (safe_sprintf(partname, "%s%s%d", + mapname, delim, j+1)) { + fprintf(stderr, "partname too small\n"); + exit(1); + } + strip_slash(partname); + + if (safe_sprintf(params, "%d:%d %lu", + slices[k].major, + slices[k].minor, + (unsigned long)slices[j].start)) { + fprintf(stderr, "params too small\n"); + exit(1); + } + + op = (dm_map_present(partname) ? + DM_DEVICE_RELOAD : DM_DEVICE_CREATE); + + dm_addmap(op, partname, DM_TARGET, params, + slices[j].size, uuid, j+1); + + if (op == DM_DEVICE_RELOAD) + dm_simplecmd(DM_DEVICE_RESUME, + partname); + + dm_devn(partname, &slices[j].major, + &slices[j].minor); + + if (verbose) + printf("add map %s : 0 %lu %s %s\n", + partname, slices[j].size, + DM_TARGET, params); + c--; + } + /* Terminate loop */ + if (d == c) + break; } break; diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h index 6a715de..b49c543 100644 --- a/kpartx/kpartx.h +++ b/kpartx/kpartx.h @@ -22,6 +22,9 @@ struct slice { unsigned long start; unsigned long size; + int container; + int major; + int minor; }; typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns); diff --git a/kpartx/kpartx_id b/kpartx/kpartx_id new file mode 100644 index 0000000..c599e5d --- /dev/null +++ b/kpartx/kpartx_id @@ -0,0 +1,110 @@ +#!/bin/bash +# +# kpartx_id +# +# Generates ID information for device-mapper tables. +# +# Copyright (C) 2006 SUSE Linux Products GmbH +# Author: +# Hannes Reinecke +# +# +# 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 script generates ID information used to generate persistent symlinks. +# It relies on the UUID strings generated by the various programs; the name +# of the tables are of no consequence. +# +# Please note that dmraid does not provide the UUIDs (yet); a patch has been +# sent upstream but has not been accepted yet. +# + +DMSETUP=/sbin/dmsetup + +MAJOR=$1 +MINOR=$2 + +if [ -z "$MAJOR" -o -z "$MINOR" ]; then + echo "usage: $0 major minor" + exit 1; +fi + +# Device-mapper not installed; not an error +if [ ! -x $DMSETUP ] ; then + exit 0 +fi + +# Get the table info +tblinfo=$($DMSETUP info -c --noheadings -o name,uuid -j $MAJOR -m $MINOR) +if [ $? -ne 0 ] || [ -z "$tblinfo" ]; then + exit $? +fi + +set -- $(IFS=":"; echo $tblinfo) +tblname=$1 +tbluuid=$2 + +if [ -z "$tbluuid" ] ; then + exit 0 +fi + +# Table UUIDs are always '-'. +dmuuid=${tbluuid#*-} +dmtbl=${tbluuid%%-*} +dmpart=${dmtbl#part} +# kpartx types are 'part' +if [ "$dmpart" == "$dmtbl" ] ; then + dmpart= +else + dmtbl=part +fi + +# Set the name of the table. We're only interested in dmraid, +# multipath, and kpartx tables; everything else is ignored. +if [ "$dmtbl" == "part" ] ; then + # The name of the kpartx table is the name of the parent table + dmname=$($DMSETUP info -c --noheadings -o name -u $dmuuid) + # We need the dependencies of the parent table to figure out + # the type if the parent is a multipath table + case "$dmparent" in + mpath-*) + dmdeps=$($DMSETUP deps -u $dmuuid) + ;; + esac +elif [ "$dmtbl" == "mpath" ] ; then + dmname=$tblname + # We need the dependencies of the table to figure out the type + dmdeps=$($DMSETUP deps -u $tbluuid) +elif [ "$dmtbl" == "dmraid" ] ; then + dmname=$tblname +fi + +if [ -z "$dmname" ] ; then + exit 0 +fi + +echo "ID_DM_TABLE=$dmtbl" +echo "ID_DM_NAME=$dmname" +[ -n "$dmpart" ] && echo "ID_DM_PART=$dmpart" + +# Figure out the type of the map. For non-multipath maps it's +# always 'raid'. +if [ -n "$dmdeps" ] ; then + case "$dmdeps" in + *\(94,*) + echo "ID_DM_TYPE=dasd" + ;; + *\(9*) + echo "ID_DM_TYPE=raid" + ;; + *) + echo "ID_DM_TYPE=scsi" + ;; + esac +else + echo "ID_DM_TYPE=raid" +fi + +exit 0 diff --git a/libcheckers/Makefile b/libcheckers/Makefile index 6340a68..bdd423f 100644 --- a/libcheckers/Makefile +++ b/libcheckers/Makefile @@ -11,7 +11,7 @@ OBJS = libsg.o checkers.o readsector0.o all: $(BUILD) prepare: - @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o *.gz + @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o klibc: prepare $(OBJS) ar rs libcheckers-klibc.a *.o @@ -24,4 +24,4 @@ install: uninstall: clean: - rm -f core *.a *.o *.gz + rm -f core *.a *.o diff --git a/libmultipath/Makefile b/libmultipath/Makefile index ef561a8..a6ad89b 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -13,7 +13,7 @@ OBJS = memory.o parser.o vector.o devmap 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) @@ -34,7 +34,7 @@ endif all: $(BUILD) prepare: $(CLEAN) - @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o *.gz + @file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o @rm -f *-$(BUILD).a klibc: $(OBJS) @@ -48,4 +48,4 @@ install: uninstall: clean: - rm -f core *.a *.o *.gz + rm -f core *.a *.o diff --git a/libmultipath/config.h b/libmultipath/config.h index 7caa11d..a25b3ad 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -66,6 +66,7 @@ struct config { int pg_timeout; char * dev; + char * sysfs_dir; char * udev_dir; char * selector; char * getuid; diff --git a/libmultipath/configure.c b/libmultipath/configure.c index a5bad4c..3cd6041 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -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); diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index a196583..52e0621 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -8,9 +8,8 @@ #include #include #include +#include #include -#include -#include #include @@ -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 co 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; - - if (!(class = sysfs_open_class("block"))) + DIR *blkdir; + struct dirent *blkdev; + struct stat statbuf; + char devpath[PATH_MAX]; + char *devptr; + int r = 0; + + if (!(blkdir = opendir("/sys/block"))) return 1; - if (!(ls = sysfs_get_class_devices(class))) - goto out; + strcpy(devpath,"/sys/block"); + while ((blkdev = readdir(blkdir)) != NULL) { + if ((strcmp(blkdev->d_name,".") == 0) || + (strcmp(blkdev->d_name,"..") == 0)) + continue; - r = 0; + devptr = devpath + 10; + *devptr = '\0'; + strcat(devptr,"/"); + strcat(devptr,blkdev->d_name); + if (stat(devpath, &statbuf) < 0) + continue; - dlist_for_each_data(ls, dev, struct sysfs_class_device) - r += path_discover(pathvec, conf, dev->name, flag); + if (S_ISDIR(statbuf.st_mode) == 0) + continue; -out: - sysfs_close_class(class); - return r; -} + condlog(4, "Discover device %s", devpath); -/* - * 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 - -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 (errno != ENOENT) - return 1; - - 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,68 +214,52 @@ 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); } extern int devt2devname (char *devname, char *devt) { - struct dlist * ls; - char attr_path[FILE_NAME_SIZE]; + FILE *fd; + unsigned int tmpmaj, tmpmin, major, minor; + char dev[FILE_NAME_SIZE]; char block_path[FILE_NAME_SIZE]; - struct sysfs_attribute * attr = NULL; - struct sysfs_class * class; - struct sysfs_class_device * dev; + struct stat statbuf; - if(safe_sprintf(block_path, "%s/block", sysfs_path)) { - condlog(0, "block_path too small"); + if (sscanf(devt, "%u:%u", &major, &minor) != 2) { + condlog(0, "Invalid device number %s", devt); return 1; } - if (!(class = sysfs_open_class("block"))) - return 1; - if (!(ls = sysfs_get_class_devices(class))) - goto err; + if ((fd = fopen("/proc/partitions", "r")) < 0) { + condlog(0, "Cannot open /proc/partitions"); + return 1; + } + + while (!feof(fd)) { + if (fscanf(fd," %u %u %*d %s",&tmpmaj, &tmpmin, dev) != 3) + continue; - dlist_for_each_data(ls, dev, struct sysfs_class_device) { - if(safe_sprintf(attr_path, "%s/%s/dev", - block_path, dev->name)) { - condlog(0, "attr_path too small"); - goto err; - } - if (!(attr = sysfs_open_attribute(attr_path))) - goto err; - - if (sysfs_read_attribute(attr)) - goto err1; - - /* discard newline */ - if (attr->len > 1) attr->len--; - - if (strlen(devt) == attr->len && - strncmp(attr->value, devt, attr->len) == 0) { - if(safe_sprintf(attr_path, "%s/%s", - block_path, dev->name)) { - condlog(0, "attr_path too small"); - goto err1; - } - sysfs_get_name_from_path(attr_path, devname, - FILE_NAME_SIZE); - sysfs_close_attribute(attr); - sysfs_close_class(class); - return 0; + if ((major == tmpmaj) && (minor == tmpmin)) { + sprintf(block_path, "/sys/block/%s", dev); + break; } } -err1: - sysfs_close_attribute(attr); -err: - sysfs_close_class(class); - return 1; + fclose(fd); + + if (strncpy(block_path,"/sys/block", 10)) + return 1; + + if (stat(block_path, &statbuf) < 0) { + condlog(0, "No sysfs entry for %s\n", block_path); + return 1; + } + + if (S_ISDIR(statbuf.st_mode) == 0) { + condlog(0, "sysfs entry %s is not a directory\n", block_path); + return 1; + } + return 0; } static int @@ -363,79 +337,21 @@ get_serial (char * str, int maxlen, int } static int -sysfs_get_bus (char * sysfs_path, struct path * pp) -{ - 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) +scsi_sysfs_pathinfo (struct path * pp, struct sysfs_device * parent) { 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); @@ -448,15 +364,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, @@ -473,35 +381,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]; @@ -510,8 +402,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)) { @@ -531,16 +422,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, @@ -557,20 +440,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); @@ -578,19 +461,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; diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index ab62a59..c7cf7e8 100644 --- a/libmultipath/discovery.h +++ b/libmultipath/discovery.h @@ -24,12 +24,7 @@ #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 index 0000000..8626630 --- /dev/null +++ b/libmultipath/list.h @@ -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 */ diff --git a/libmultipath/print.c b/libmultipath/print.c index dc8af48..1d80e48 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -5,8 +5,8 @@ #include #include #include -#include -#include +#include +#include #include @@ -1096,35 +1096,46 @@ snprint_blacklist_except (char * buff, i 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, s " (whitelisted)"); } fwd += snprintf(buff + fwd, len - fwd, "\n"); - } - sysfs_close_class(class); + } + closedir(blkdir); if (fwd > len) return len; diff --git a/libmultipath/structs.h b/libmultipath/structs.h index 46dcdee..75322aa 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -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 multipa 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 */ diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index a4a996a..1cc6028 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -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, str /* * 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 index 0000000..e24aa34 --- /dev/null +++ b/libmultipath/sysfs.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2005-2006 Kay Sievers + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + return attr_loop->value; + } + } + + /* store attribute in cache (also negatives are kept 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); + + 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 index 0000000..4ac30db --- /dev/null +++ b/libmultipath/sysfs.h @@ -0,0 +1,20 @@ +/* + * sysfs.h + */ + +#ifndef _LIBMULTIPATH_SYSFS_H +#define _LIBMULTIPATH_SYSFS_H + +#define dbg(format, arg...) do {} while (0) + +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 diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index 6482698..a4028d8 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -26,12 +26,14 @@ #include #include #include +#include #include #include #include #include #include -#include +#include +#include #include #include #include @@ -105,6 +107,8 @@ int uevent_listen(int (*uev_trigger)(str { int sock; struct sockaddr_nl snl; + struct sockaddr_un sun; + socklen_t addrlen; int retval; int rcvbufsz = 128*1024; int rcvsz = 0; @@ -131,43 +135,72 @@ int uevent_listen(int (*uev_trigger)(str pthread_attr_setstacksize(&attr, 64 * 1024); pthread_create(&uevq_thr, &attr, uevq_thread, NULL); - memset(&snl, 0x00, sizeof(struct sockaddr_nl)); - snl.nl_family = AF_NETLINK; - snl.nl_pid = getpid(); - snl.nl_groups = 0xffffffff; - - sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); - if (sock == -1) { - condlog(0, "error getting socket, exit"); - return 1; - } - /* - * try to avoid dropping uevents, even so, this is not a guarantee, - * but it does help to change the netlink uevent socket's - * receive buffer threshold from the default value of 106,496 to - * the maximum value of 262,142. + * First check whether we have a udev socket */ - retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbufsz, - sizeof(rcvbufsz)); + memset(&sun, 0x00, sizeof(struct sockaddr_un)); + sun.sun_family = AF_LOCAL; + strcpy(&sun.sun_path[1], "/org/kernel/dm/multipath_event"); + addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun.sun_path+1) + 1; + + sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (sock >= 0) { + const int feature_on = 1; + + condlog(3, "reading events from udev socket."); + + /* the bind takes care of ensuring only one copy running */ + retval = bind(sock, (struct sockaddr *) &sun, addrlen); + if (retval < 0) { + condlog(0, "bind failed, exit"); + goto exit; + } - if (retval < 0) { - condlog(0, "error setting receive buffer size for socket, exit"); - exit(1); - } - retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsz, prcvszsz); + /* enable receiving of the sender credentials */ + setsockopt(sock, SOL_SOCKET, SO_PASSCRED, + &feature_on, sizeof(feature_on)); + + } else { + /* Fallback to read kernel netlink events */ + memset(&snl, 0x00, sizeof(struct sockaddr_nl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = getpid(); + snl.nl_groups = 0xffffffff; + + sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if (sock == -1) { + condlog(0, "error getting socket, exit"); + return 1; + } - if (retval < 0) { - condlog(0, "error setting receive buffer size for socket, exit"); - exit(1); - } - condlog(3, "receive buffer size for socket is %u.", rcvsz); + condlog(3, "reading events from kernel."); - retval = bind(sock, (struct sockaddr *) &snl, - sizeof(struct sockaddr_nl)); - if (retval < 0) { - condlog(0, "bind failed, exit"); - goto exit; + /* + * try to avoid dropping uevents, even so, this is not a guarantee, + * but it does help to change the netlink uevent socket's + * receive buffer threshold from the default value of 106,496 to + * the maximum value of 262,142. + */ + retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbufsz, + sizeof(rcvbufsz)); + + if (retval < 0) { + condlog(0, "error setting receive buffer size for socket, exit"); + exit(1); + } + retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsz, prcvszsz); + if (retval < 0) { + condlog(0, "error setting receive buffer size for socket, exit"); + exit(1); + } + condlog(3, "receive buffer size for socket is %u.", rcvsz); + + retval = bind(sock, (struct sockaddr *) &snl, + sizeof(struct sockaddr_nl)); + if (retval < 0) { + condlog(0, "bind failed, exit"); + goto exit; + } } while (1) { diff --git a/libmultipath/util.c b/libmultipath/util.c index 911ec55..eaf2266 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -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'; +} + diff --git a/libmultipath/util.h b/libmultipath/util.h index e86bae2..d0df8aa 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h @@ -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) diff --git a/multipath/Makefile b/multipath/Makefile index 947d481..a31afe2 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -12,7 +12,7 @@ CFLAGS += -I$(multipathdir) -I$(checkers ifeq ($(strip $(BUILD)),klibc) OBJS += $(libdm) $(libsysfs) else - LDFLAGS += -ldevmapper -lsysfs + LDFLAGS += -ldevmapper endif EXEC = multipath @@ -21,7 +21,7 @@ all: $(BUILD) prepare: make -C $(multipathdir) prepare - rm -f core *.o *.gz + rm -f core *.o glibc: prepare $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) @@ -37,17 +37,17 @@ $(MULTIPATHLIB)-$(BUILD).a: install: install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ install -d $(DESTDIR)/etc/udev/rules.d install -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/ install -d $(DESTDIR)$(mandir) install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) + install -d $(DESTDIR)$(man5dir) install -m 644 multipath.conf.5 $(DESTDIR)$(man5dir) uninstall: rm $(DESTDIR)/etc/udev/rules.d/multipath.rules rm $(DESTDIR)$(bindir)/$(EXEC) - rm $(DESTDIR)$(mandir)/$(EXEC).8 clean: - rm -f core *.o $(EXEC) *.gz + rm -f core *.o $(EXEC) diff --git a/multipath/main.c b/multipath/main.c index c3d0dac..e2d7f41 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -37,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -373,13 +373,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:t")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); @@ -472,6 +472,7 @@ main (int argc, char *argv[]) condlog(3, "restart multipath configuration process"); out: + sysfs_cleanup(); free_config(conf); dm_lib_release(); dm_lib_exit(); diff --git a/multipath/multipath.init.suse b/multipath/multipath.init.suse new file mode 100755 index 0000000..34a128c --- /dev/null +++ b/multipath/multipath.init.suse @@ -0,0 +1,104 @@ +#! /bin/sh +# Copyright (c) 2005 SuSE GmbH Nuernberg, Germany. +# +# Author: Hannes Reinecke +# +# init.d/boot.multipath +# +### BEGIN INIT INFO +# Provides: boot.multipath +# Required-Start: boot.device-mapper boot.udev +# Required-Stop: +# Default-Start: B +# Default-Stop: +# Description: Create multipath device targets +### END INIT INFO + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +PROGRAM=/sbin/multipath + +# Set the maximum number of open files +MAX_OPEN_FDS=4096 + +test -x $PROGRAM || exit 5 + +# Shell functions sourced from /etc/rc.status: +# rc_check check and set local and overall rc status +# rc_status check and set local and overall rc status +# rc_status -v ditto but be verbose in local rc status +# rc_status -v -r ditto and clear the local rc status +# rc_failed set local and overall rc status to failed +# rc_reset clear local rc status (overall remains) +# rc_exit exit appropriate to overall rc status +. /etc/rc.status + +# First reset status of this service +rc_reset + +# Return values acc. to LSB for all commands but status: +# 0 - success +# 1 - misc error +# 2 - invalid or excess args +# 3 - unimplemented feature (e.g. reload) +# 4 - insufficient privilege +# 5 - program not installed +# 6 - program not configured +# 7 - program is not running +# +# Note that starting an already running service, stopping +# or restarting a not-running service as well as the restart +# with force-reload (in case signalling is not supported) are +# considered a success. + +case "$1" in + start) + echo -n "Creating multipath targets" + # Check whether multipath daemon is already running + if /sbin/multipathd -k"list paths" > /dev/null 2>&1 ; then + echo -n " (multipathd running)" + rc_status -v + rc_exit + fi + + # Load prerequisite module + modprobe dm-multipath + + # Be a chicken and flush all existing maps + $PROGRAM -F + + # Clear /dev/disk/by-name/ prior to start-up; multipath will + # recreate them. + rm -f /dev/disk/by-name/* 2>&1 >/dev/null + + # Set the maximum number of open files + if [ -n "$MAX_OPEN_FDS" ] ; then + ulimit -n $MAX_OPEN_FDS + fi + + # Start the program directly as checkproc doesn't work here + $PROGRAM -v 0 + + # Create all partitions which might have been missing + /sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -a -p -part" + + # Remember status and be verbose + rc_status -v + sleep 1 + ;; + stop) + # Remove all partition mappings + if dmsetup ls | grep -q -- -part; then + /sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -d -p -part" + fi + + # And remove the multipath mappings themselves + for map in $(/sbin/dmsetup ls --target multipath | cut -f 1); do + /sbin/dmsetup remove $map + done + ;; + *) + echo "Usage: $0 {start|stop}" + exit 1 + ;; +esac +rc_exit diff --git a/multipathd/71-multipath.rules b/multipathd/71-multipath.rules new file mode 100644 index 0000000..756ebe7 --- /dev/null +++ b/multipathd/71-multipath.rules @@ -0,0 +1,27 @@ +# +# persistent links for device-mapper devices +# only hardware-backed device-mapper devices (ie multipath, dmraid, +# and kpartx) have meaningful persistent device names +# + +KERNEL!="dm-*", GOTO="multipath_end" +ACTION=="add|remove", GOTO="multipath_end" + +ACTION=="change", IMPORT{program}=="/sbin/kpartx_id %M %m" + +# Create persistent links for tables +ACTION=="change", ENV{ID_DM_TABLE}=="mpath|dmraid", ENV{ID_DM_TYPE}=="?*", \ + SYMLINK+="disk/by-id/$env{ID_DM_TYPE}-$env{ID_DM_NAME}" + +# Create dm tables for partitions +ACTION=="change", ENV{ID_DM_TABLE}=="mpath|dmraid", \ + RUN+="/sbin/kpartx -a -p _part /dev/mapper/$env{ID_DM_NAME}" + +# Create persistent links for partitions +ACTION=="change", ENV{ID_DM_TABLE}=="part", ENV{ID_DM_TYPE}=="?*", \ + SYMLINK+="disk/by-id/$env{ID_DM_TYPE}-$env{ID_DM_NAME}-part$env{ID_DM_PART}" + +# socket for uevents +RUN+="socket:/org/kernel/dm/multipath_event" +LABEL="multipath_end" + diff --git a/multipathd/Makefile b/multipathd/Makefile index da351dc..bbab8da 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -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 @@ -44,7 +44,7 @@ $(MULTIPATHLIB)-glibc.a: install: install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir) + install -m 755 $(EXEC) $(DESTDIR)$(bindir) install -d $(DESTDIR)$(rcdir) install -d $(DESTDIR)$(mandir) install -m 644 $(EXEC).8 $(DESTDIR)$(mandir) @@ -52,9 +52,8 @@ install: uninstall: rm -f $(DESTDIR)$(bindir)/$(EXEC) rm -f $(DESTDIR)$(rcdir)/$(EXEC) - rm -f $(DESTDIR)$(mandir)/$(EXEC).8 clean: $(MAKE) -C $(multipathdir) prepare DAEMON=1 - rm -f core *.o $(EXEC) *.gz + rm -f core *.o $(EXEC) diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index 4938e84..7bae02a 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "main.h" #include "cli.h" @@ -312,6 +313,9 @@ cli_add_map (void * v, char ** reply, in { 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, in 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 diff --git a/multipathd/main.c b/multipathd/main.c index 94b0b95..a173da3 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -14,12 +14,6 @@ #include /* - * libsysfs - */ -#include -#include - -/* * libcheckers */ #include @@ -40,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -208,31 +203,29 @@ flush_map(struct multipath * mpp, struct } 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 vecto 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 vecto * 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 vecto 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 vecto } 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 ve } 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 v } 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 * 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 * * 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 * * 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); diff --git a/multipathd/main.h b/multipathd/main.h index d0cce3a..1a6dc55 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -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 */ diff --git a/multipathd/multipathd.init.suse b/multipathd/multipathd.init.suse new file mode 100755 index 0000000..a297956 --- /dev/null +++ b/multipathd/multipathd.init.suse @@ -0,0 +1,155 @@ +#! /bin/sh +# Copyright (c) 1995-2001 SuSE GmbH Nuernberg, Germany. +# +# Author: Thorsten Kukuk +# +# init.d/routed +# +# and symbolic its link +# +# /usr/sbin/rcrouted +# +### BEGIN INIT INFO +# Provides: multipathd +# Required-Start: $syslog +# Required-Stop: +# Default-Start: 3 5 +# Default-Stop: 0 1 2 4 6 +# Description: Starts multipath daemon +### END INIT INFO + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +DAEMON=/sbin/multipathd +PIDFILE=/var/run/multipathd.pid + +# Set the maximum number of open files +MAX_OPEN_FDS=4096 + +test -x $DAEMON || exit 5 + +# Shell functions sourced from /etc/rc.status: +# rc_check check and set local and overall rc status +# rc_status check and set local and overall rc status +# rc_status -v ditto but be verbose in local rc status +# rc_status -v -r ditto and clear the local rc status +# rc_failed set local and overall rc status to failed +# rc_reset clear local rc status (overall remains) +# rc_exit exit appropriate to overall rc status +. /etc/rc.status + +# First reset status of this service +rc_reset + +# Return values acc. to LSB for all commands but status: +# 0 - success +# 1 - misc error +# 2 - invalid or excess args +# 3 - unimplemented feature (e.g. reload) +# 4 - insufficient privilege +# 5 - program not installed +# 6 - program not configured +# 7 - program is not running +# +# Note that starting an already running service, stopping +# or restarting a not-running service as well as the restart +# with force-reload (in case signalling is not supported) are +# considered a success. + +case "$1" in + start) + echo -n "Starting multipathd" + + modprobe dm-multipath + + # Set the maximum number of open files + if [ -n "$MAX_OPEN_FDS" ] ; then + ulimit -n $MAX_OPEN_FDS + fi + + if [ -f $PIDFILE ]; then + PID="$(cat $PIDFILE)" + PROCNAME="$(ps -o cmd --no-headers $PID)" + fi + + if [ "$PROCNAME" != "$DAEMON" ]; then + $DAEMON + fi + + # Remember status and be verbose + rc_status -v + sleep 1 + ;; + stop) + echo -n "Shutting down multipathd" + # Because of the way how multipathd sets up its own namespace + # and chroots to it, killproc cannot be used with this process. + # So implement a cruder version: + if [ -f $PIDFILE ]; then + PID="$(cat $PIDFILE)" + PROCNAME="$(ps -o cmd --no-headers $PID)" + fi + + if [ "$PROCNAME" == "$DAEMON" ]; then + kill -TERM $PID + fi + + # Remember status and be verbose + rc_status -v + ;; + try-restart) + ## Stop the service and if this succeeds (i.e. the + ## service was running before), start it again. + $0 status >/dev/null && $0 restart + + # Remember status and be quiet + rc_status + ;; + restart|force-reload) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + reload) + ## Like force-reload, but if daemon does not support + ## signalling, do nothing (!) + + # If it does not support reload: + exit 3 + ;; + status) + echo -n "Checking for multipathd: " + + # Status has a slightly different for the status command: + # 0 - service running + # 1 - service dead, but /var/run/ pid file exists + # 2 - service dead, but /var/lock/ lock file exists + # 3 - service not running + + if [ -f $PIDFILE ]; then + PID="$(cat $PIDFILE)" + PROCNAME="$(ps -o cmd --no-headers $PID)" + if [ "$PROCNAME" == "$DAEMON" ]; then + (exit 0) + else + (exit 1) + fi + else + (exit 3) + fi + + rc_status -v + ;; + probe) + ## Optional: Probe for the necessity of a reload, + ## give out the argument which is required for a reload. + ;; + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" + exit 1 + ;; +esac +rc_exit diff --git a/path_priority/pp_alua/Makefile b/path_priority/pp_alua/Makefile index 6c58529..ce78455 100644 --- a/path_priority/pp_alua/Makefile +++ b/path_priority/pp_alua/Makefile @@ -41,10 +41,9 @@ install: $(EXEC) $(EXEC).8 uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) - rm $(DESTDIR)$(mandir)/$(EXEC).8 clean: - rm -f *.o *.gz $(EXEC) + rm -f *.o $(EXEC) main.o: main.c rtpg.h spc3.h diff --git a/path_priority/pp_balance_units/Makefile b/path_priority/pp_balance_units/Makefile index bed7fb0..b0fee28 100644 --- a/path_priority/pp_balance_units/Makefile +++ b/path_priority/pp_balance_units/Makefile @@ -22,7 +22,7 @@ EXEC = mpath_prio_balance_units all: $(BUILD) prepare: - rm -f core *.o *.gz + rm -f core *.o glibc: prepare $(OBJS) $(CC) -o $(EXEC) $(OBJS) $(LDFLAGS) @@ -35,10 +35,10 @@ $(MULTIPATHLIB)-$(BUILD).a: install: install -d $(DESTDIR)$(bindir) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/ + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/ uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) clean: - rm -f core *.o $(EXEC) *.gz + rm -f core *.o $(EXEC) diff --git a/path_priority/pp_emc/Makefile b/path_priority/pp_emc/Makefile index 651bdcd..8c6e922 100644 --- a/path_priority/pp_emc/Makefile +++ b/path_priority/pp_emc/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_hds_modular/Makefile b/path_priority/pp_hds_modular/Makefile index a0249a5..1c276e6 100644 --- a/path_priority/pp_hds_modular/Makefile +++ b/path_priority/pp_hds_modular/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_netapp/Makefile b/path_priority/pp_netapp/Makefile index 9e7d3a3..2d571b0 100644 --- a/path_priority/pp_netapp/Makefile +++ b/path_priority/pp_netapp/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_random/Makefile b/path_priority/pp_random/Makefile index 85f42a2..ca7974e 100644 --- a/path_priority/pp_random/Makefile +++ b/path_priority/pp_random/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC) diff --git a/path_priority/pp_tpc/Makefile b/path_priority/pp_tpc/Makefile index 86841dd..624f76d 100644 --- a/path_priority/pp_tpc/Makefile +++ b/path_priority/pp_tpc/Makefile @@ -14,7 +14,7 @@ klibc: $(OBJS) $(CC) -static -o $(EXEC) $(OBJS) install: $(EXEC) - install -s -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) + install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC) uninstall: rm $(DESTDIR)$(bindir)/$(EXEC)