Imported Upstream version 0.5.0 upstream/0.5.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:13 +0000 (13:50 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:13 +0000 (13:50 +0900)
116 files changed:
.gitignore
Makefile
Makefile.inc
kpartx/Makefile
kpartx/byteorder.h
kpartx/dasd.c
kpartx/dasd.h
kpartx/devmapper.c
kpartx/devmapper.h
kpartx/dos.c
kpartx/gpt.c
kpartx/kpartx.8
kpartx/kpartx.c
kpartx/kpartx.h
kpartx/kpartx.rules
kpartx/kpartx_id
kpartx/lopart.c
kpartx/ps3.c [new file with mode: 0644]
libmpathpersist/mpath_persist.c
libmpathpersist/mpath_persist.h
libmpathpersist/mpath_persistent_reserve_out.3
libmpathpersist/mpath_pr_ioctl.c
libmpathpersist/mpath_updatepr.c
libmultipath/Makefile
libmultipath/alias.c
libmultipath/alias.h
libmultipath/blacklist.c
libmultipath/blacklist.h
libmultipath/callout.c [new file with mode: 0644]
libmultipath/callout.h [new file with mode: 0644]
libmultipath/checkers.c
libmultipath/checkers.h
libmultipath/checkers/emc_clariion.c
libmultipath/checkers/hp_sw.c
libmultipath/checkers/libsg.c
libmultipath/checkers/rdac.c
libmultipath/checkers/tur.c
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/configure.h
libmultipath/debug.c
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/devmapper.h
libmultipath/dict.c
libmultipath/discovery.c
libmultipath/discovery.h
libmultipath/dmparser.c
libmultipath/file.c
libmultipath/hwtable.c
libmultipath/lock.c
libmultipath/lock.h
libmultipath/log.c
libmultipath/log.h
libmultipath/log_pthread.c
libmultipath/log_pthread.h
libmultipath/parser.c
libmultipath/print.c
libmultipath/prio.c
libmultipath/prio.h
libmultipath/prioritizers/alua.c
libmultipath/prioritizers/datacore.c
libmultipath/prioritizers/emc.c
libmultipath/prioritizers/hds.c
libmultipath/prioritizers/hp_sw.c
libmultipath/prioritizers/iet.c
libmultipath/prioritizers/ontap.c
libmultipath/prioritizers/rdac.c
libmultipath/prioritizers/weightedpath.c
libmultipath/propsel.c
libmultipath/propsel.h
libmultipath/regex.c [deleted file]
libmultipath/regex.h [deleted file]
libmultipath/structs.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/structs_vec.h
libmultipath/sysfs.c
libmultipath/sysfs.h
libmultipath/uevent.c
libmultipath/uevent.h
libmultipath/util.c
libmultipath/util.h
libmultipath/uxsock.c
libmultipath/vector.c
libmultipath/vector.h
libmultipath/version.h
libmultipath/waiter.c
libmultipath/wwids.c
libmultipath/wwids.h
mpathpersist/Makefile
mpathpersist/main.c
multipath.conf.annotated
multipath.conf.defaults
multipath/Makefile
multipath/main.c
multipath/multipath.8
multipath/multipath.conf.5
multipath/multipath.init.suse
multipath/multipath.rules [deleted file]
multipathd/Makefile
multipathd/cli.c
multipathd/cli.h
multipathd/cli_handlers.c
multipathd/cli_handlers.h
multipathd/main.c
multipathd/main.h
multipathd/multipathd.8
multipathd/multipathd.init.redhat
multipathd/multipathd.init.suse
multipathd/multipathd.service
multipathd/multipathd.socket [new file with mode: 0644]
multipathd/uxclnt.c
multipathd/uxlsnr.c
multipathd/uxlsnr.h

index 9b3f663..7f25d0e 100644 (file)
@@ -5,6 +5,8 @@
 *.so.0
 *.a
 *.gz
-kpartx
-multipath
-multipathd
+kpartx/kpartx
+multipath/multipath
+multipathd/multipathd
+mpathpersist/mpathpersist
+.nfs*
index 5b0c61a..baf7753 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -66,6 +66,13 @@ install:     recurse_install
 
 uninstall:     recurse_uninstall
 
+.PHONY:        TAGS
+TAGS:
+       etags -a libmultipath/*.c
+       etags -a libmultipath/*.h
+       etags -a multipathd/*.c
+       etags -a multipathd/*.h
+
 release:
        sed -e "s/__VERSION__/${VERSION}/" \
        multipath-tools.spec.in > multipath-tools.spec
index b0c68f4..f445160 100644 (file)
@@ -21,6 +21,12 @@ ifndef LIB
        endif
 endif
 
+ifndef SYSTEMD
+       ifeq ($(shell systemctl --version > /dev/null 2>&1 && echo 1), 1)
+               SYSTEMD = $(shell systemctl --version 2> /dev/null |  sed -n 's/systemd \([0-9]*\)/\1/p')
+       endif
+endif
+
 prefix      = 
 exec_prefix = $(prefix)
 bindir      = $(exec_prefix)/sbin
@@ -32,10 +38,10 @@ man3dir      = $(prefix)/usr/share/man/man3
 rcdir      = $(prefix)/etc/init.d
 syslibdir   = $(prefix)/$(LIB)
 libdir     = $(prefix)/$(LIB)/multipath
-unitdir     = $(prefix)/lib/systemd/system
+unitdir     = $(prefix)/usr/lib/systemd/system
 mpathpersistdir = $(TOPDIR)/libmpathpersist
 
-GZIP        = /bin/gzip -9 -c
+GZIP        = gzip -9 -c
 INSTALL_PROGRAM = install
 
 ifndef RPM_OPT_FLAGS
index 1287053..4ba38ba 100644 (file)
@@ -14,7 +14,7 @@ endif
 
 LDFLAGS = -ldevmapper
 OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o sun.o \
-       gpt.o mac.o crc32.o lopart.o xstrncpy.o devmapper.o
+       gpt.o mac.o ps3.o crc32.o lopart.o xstrncpy.o devmapper.o
 EXEC = kpartx
 
 all: $(EXEC)
@@ -36,6 +36,7 @@ install: $(EXEC) $(EXEC).8
 uninstall:
        rm -f $(DESTDIR)$(bindir)/$(EXEC)
        rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz
+       rm -f $(DESTDIR)$(libudevdir)/kpartx_id
 
 clean:
        rm -f core *.o $(EXEC) *.gz
index 21962d6..199c66b 100644 (file)
 #  define le16_to_cpu(x) (x)
 #  define be16_to_cpu(x) bswap_16(x)
 #  define le32_to_cpu(x) (x)
+#  define le64_to_cpu(x) (x)
 #  define be32_to_cpu(x) bswap_32(x)
+#  define be64_to_cpu(x) bswap_64(x)
 #elif BYTE_ORDER == BIG_ENDIAN
 #  define le16_to_cpu(x) bswap_16(x)
 #  define be16_to_cpu(x) (x)
 #  define le32_to_cpu(x) bswap_32(x)
+#  define le64_to_cpu(x) bswap_64(x)
 #  define be32_to_cpu(x) (x)
+#  define be64_to_cpu(x) (x)
 #else
 #  error unsupported
 #endif
index dcdf678..1fcf778 100644 (file)
@@ -46,6 +46,8 @@ unsigned long long sectors512(unsigned long long sectors, int blocksize)
        return sectors * (blocksize >> 9);
 }
 
+typedef unsigned int __attribute__((__may_alias__)) label_ints_t;
+
 /*
  */
 int 
@@ -169,7 +171,7 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
                /*
                 * VM style CMS1 labeled disk
                 */
-               unsigned int *label = (unsigned int *) &vlabel;
+               label_ints_t *label = (label_ints_t *) &vlabel;
 
                blocksize = label[4];
                if (label[14] != 0) {
index 0ed7c80..42f94db 100644 (file)
@@ -68,7 +68,7 @@ typedef struct volume_label
        char res2[4];           /* reserved                                  */
        char lvtoc[14];         /* owner code for LVTOC                      */
        char res3[28];          /* reserved                                  */
-       char ldl_version;       /* version number, valid for ldl format      */
+       uint8_t ldl_version;    /* version number, valid for ldl format      */
        uint64_t formatted_blocks; /* valid when ldl_version >= f2           */
 } __attribute__ ((packed)) volume_label_t;
 
index 4baebd9..24a43ee 100644 (file)
@@ -78,7 +78,7 @@ dm_simplecmd (int task, const char *name, int no_flush, uint32_t *cookie) {
        if (no_flush)
                dm_task_no_flush(dmt);
 
-       if (udev_wait_flag && !dm_task_set_cookie(dmt, cookie, 0))
+       if (udev_wait_flag && !dm_task_set_cookie(dmt, cookie, (udev_sync)? 0 : DM_UDEV_DISABLE_LIBRARY_FALLBACK))
                goto out;
        r = dm_task_run(dmt);
 
@@ -128,7 +128,7 @@ dm_addmap (int task, const char *name, const char *target,
 
        dm_task_no_open_count(dmt);
 
-       if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, cookie, 0))
+       if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, cookie, (udev_sync)? 0 : DM_UDEV_DISABLE_LIBRARY_FALLBACK))
                goto addout;
        r = dm_task_run (dmt);
 
index 8e350a0..0edc063 100644 (file)
@@ -2,6 +2,8 @@
 #define MINOR(dev)      ((dev & 0xff) | ((dev >> 12) & 0xfff00))
 #define MKDEV(ma,mi)    ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12))
 
+extern int udev_sync;
+
 int dm_prereq (char *, int, int, int);
 int dm_simplecmd (int, const char *, int, uint32_t *);
 int dm_addmap (int, const char *, const char *, const char *, uint64_t,
index 1691105..0e57f0e 100644 (file)
@@ -26,7 +26,9 @@ read_extended_partition(int fd, struct partition *ep, int en,
        int moretodo = 1;
        int i, n=0;
 
-       next = start = le32_to_cpu(ep->start_sect);
+       int sector_size_mul = get_sector_size(fd)/512;
+
+       next = start = sector_size_mul * le32_to_cpu(ep->start_sect);
 
        while (moretodo) {
                here = next;
@@ -45,14 +47,14 @@ read_extended_partition(int fd, struct partition *ep, int en,
                        memcpy(&p, bp + 0x1be + i * sizeof (p), sizeof (p));
                        if (is_extended(p.sys_type)) {
                                if (p.nr_sects && !moretodo) {
-                                       next = start + le32_to_cpu(p.start_sect);
+                                       next = start + sector_size_mul * le32_to_cpu(p.start_sect);
                                        moretodo = 1;
                                }
                                continue;
                        }
                        if (n < ns) {
-                               sp[n].start = here + le32_to_cpu(p.start_sect);
-                               sp[n].size = le32_to_cpu(p.nr_sects);
+                               sp[n].start = here + sector_size_mul * le32_to_cpu(p.start_sect);
+                               sp[n].size = sector_size_mul * le32_to_cpu(p.nr_sects);
                                sp[n].container = en + 1;
                                n++;
                        } else {
@@ -77,6 +79,7 @@ read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) {
        unsigned long offset = all.start;
        int i, n=4;
        unsigned char *bp;
+       int sector_size_mul = get_sector_size(fd)/512;
 
        bp = (unsigned char *)getblock(fd, offset);
        if (bp == NULL)
@@ -90,14 +93,16 @@ read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) {
                if (is_gpt(p.sys_type))
                        return 0;
                if (i < ns) {
-                       sp[i].start =  le32_to_cpu(p.start_sect);
-                       sp[i].size = le32_to_cpu(p.nr_sects);
+                       sp[i].start =  sector_size_mul * le32_to_cpu(p.start_sect);
+                       sp[i].size = sector_size_mul * le32_to_cpu(p.nr_sects);
                } else {
                        fprintf(stderr,
                                "dos_partition: too many slices\n");
                        break;
                }
                if (is_extended(p.sys_type)) {
+                       sp[i].size = sector_size_mul * 2; /* extended partitions only get two
+                                          sectors mapped for LILO to install */
                        n += read_extended_partition(fd, &p, i, sp+n, ns-n);
                }
        }
index 3082cae..5a54970 100644 (file)
@@ -38,6 +38,7 @@
 #include <byteswap.h>
 #include <linux/fs.h>
 #include "crc32.h"
+#include "kpartx.h"
 
 #if BYTE_ORDER == LITTLE_ENDIAN
 #  define __le16_to_cpu(x) (x)
@@ -116,25 +117,6 @@ is_pmbr_valid(legacy_mbr *mbr)
 
 
 /************************************************************
- * get_sector_size
- * Requires:
- *  - filedes is an open file descriptor, suitable for reading
- * Modifies: nothing
- * Returns:
- *  sector size, or 512.
- ************************************************************/
-static int
-get_sector_size(int filedes)
-{
-       int rc, sector_size = 512;
-
-       rc = ioctl(filedes, BLKSSZGET, &sector_size);
-       if (rc)
-               sector_size = 512;
-       return sector_size;
-}
-
-/************************************************************
  * _get_num_sectors
  * Requires:
  *  - filedes is an open file descriptor, suitable for reading
@@ -637,6 +619,7 @@ read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns)
        uint32_t i;
        int n = 0;
         int last_used_index=-1;
+       int sector_size_mul = get_sector_size(fd)/512;
 
        if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) {
                if (gpt)
@@ -652,9 +635,11 @@ read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns)
                        sp[n].size = 0;
                        n++;
                } else {
-                       sp[n].start = __le64_to_cpu(ptes[i].starting_lba);
-                       sp[n].size  = __le64_to_cpu(ptes[i].ending_lba) -
-                               __le64_to_cpu(ptes[i].starting_lba) + 1;
+                       sp[n].start = sector_size_mul *
+                                     __le64_to_cpu(ptes[i].starting_lba);
+                       sp[n].size  = sector_size_mul *
+                                     (__le64_to_cpu(ptes[i].ending_lba) -
+                                      __le64_to_cpu(ptes[i].starting_lba) + 1);
                         last_used_index=n;
                        n++;
                }
index 8a37d4f..021ddc0 100644 (file)
@@ -18,7 +18,7 @@ creation and deletion.
 Add partition mappings
 .TP
 .B \-r
-Readonly partition mappings
+Read-only partition mappings
 .TP
 .B \-d
 Delete partition mappings
@@ -27,7 +27,7 @@ Delete partition mappings
 Update partition mappings
 .TP
 .B \-l
-List partition mappings that would be added -a
+List partition mappings that would be added \-a
 .TP
 .B \-p
 set device name-partition number delimiter
@@ -46,7 +46,7 @@ Sync mode. Don't return until the partitions are created
 .SH EXAMPLE
 To mount all the partitions in a raw disk image:
 .IP
-kpartx -av disk.img
+kpartx \-av disk.img
 .PP
 This will output lines such as:
 .IP
@@ -62,7 +62,7 @@ fsck /dev/mapper/loop3p1
 .PP
 When you're done, you need to remove the devices:
 .IP
-kpartx -d disk.img
+kpartx \-d disk.img
 .SH "SEE ALSO"
 .BR multipath (8)
 .BR multipathd (8)
index b5e0a32..9a9a5eb 100644 (file)
@@ -26,6 +26,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <stdint.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <ctype.h>
@@ -56,6 +57,7 @@ struct pt {
 } pts[MAXTYPES];
 
 int ptct = 0;
+int udev_sync = 0;
 
 static void
 addpts(char *t, ptreader f)
@@ -80,6 +82,7 @@ initpts(void)
        addpts("dasd", read_dasd_pt);
        addpts("mac", read_mac_pt);
        addpts("sun", read_sun_pt);
+       addpts("ps3", read_ps3_pt);
 }
 
 static char short_opts[] = "rladfgvp:t:su";
@@ -202,10 +205,8 @@ main(int argc, char **argv){
        char * delim = NULL;
        char *uuid = NULL;
        char *mapname = NULL;
-       int loopro = 0;
        int hotplug = 0;
        int loopcreated = 0;
-       int sync = 0;
        struct stat buf;
        uint32_t cookie = 0;
 
@@ -267,7 +268,7 @@ main(int argc, char **argv){
                        what = DELETE;
                        break;
                case 's':
-                       sync = 1;
+                       udev_sync = 1;
                        break;
                case 'u':
                        what = UPDATE;
@@ -278,7 +279,7 @@ main(int argc, char **argv){
        }
 
 #ifdef LIBDM_API_COOKIE
-       if (!sync)
+       if (!udev_sync)
                dm_udev_set_sync_support(0);
 #endif
 
@@ -314,7 +315,7 @@ main(int argc, char **argv){
                if (!loopdev) {
                        loopdev = find_unused_loop_device();
 
-                       if (set_loop(loopdev, device, 0, &loopro)) {
+                       if (set_loop(loopdev, device, 0, &ro)) {
                                fprintf(stderr, "can't set up loop\n");
                                exit (1);
                        }
@@ -347,7 +348,7 @@ main(int argc, char **argv){
        if (delim == NULL) {
                delim = malloc(DELIM_SIZE);
                memset(delim, 0, DELIM_SIZE);
-               set_delimiter(device, delim);
+               set_delimiter(mapname, delim);
        }
 
        fd = open(device, O_RDONLY);
@@ -515,7 +516,6 @@ main(int argc, char **argv){
                        d = c;
                        while (c) {
                                for (j = 0; j < n; j++) {
-                                       uint64_t start;
                                        int k = slices[j].container - 1;
 
                                        if (slices[j].size == 0)
@@ -541,11 +541,9 @@ main(int argc, char **argv){
                                        }
                                        strip_slash(partname);
 
-                                       start = slices[j].start - slices[k].start;
-                                       if (safe_sprintf(params, "%d:%d %" PRIu64,
-                                                        slices[k].major,
-                                                        slices[k].minor,
-                                                        start)) {
+                                       if (safe_sprintf(params, "%s %" PRIu64,
+                                                        device,
+                                                        slices[j].start)) {
                                                fprintf(stderr, "params too small\n");
                                                exit(1);
                                        }
@@ -697,3 +695,14 @@ getblock (int fd, unsigned int secnr) {
 
        return bp->block;
 }
+
+int
+get_sector_size(int filedes)
+{
+       int rc, sector_size = 512;
+
+       rc = ioctl(filedes, BLKSSZGET, &sector_size);
+       if (rc)
+               sector_size = 512;
+       return sector_size;
+}
index 43ae3f8..a55c211 100644 (file)
@@ -2,6 +2,7 @@
 #define _KPARTX_H
 
 #include <stdint.h>
+#include <sys/ioctl.h>
 
 /*
  * For each partition type there is a routine that takes
 #define safe_sprintf(var, format, args...)     \
        snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
 
+#ifndef BLKSSZGET
+#define BLKSSZGET  _IO(0x12,104)       /* get block device sector size */
+#endif
+
+int
+get_sector_size(int filedes);
+
 /*
  * units: 512 byte sectors
  */
@@ -39,6 +47,7 @@ extern ptreader read_gpt_pt;
 extern ptreader read_dasd_pt;
 extern ptreader read_mac_pt;
 extern ptreader read_sun_pt;
+extern ptreader read_ps3_pt;
 
 char *getblock(int fd, unsigned int secnr);
 
index 64863a0..5c0d0ff 100644 (file)
@@ -8,21 +8,47 @@ KERNEL!="dm-*", GOTO="kpartx_end"
 ACTION=="remove", GOTO="kpartx_end"
 
 ENV{DM_TABLE_STATE}!="LIVE", GOTO="kpartx_end"
+ENV{DM_DEPS}=="0", GOTO="kpartx_end"
 
 ENV{DM_UUID}=="?*", IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}"
 
+ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0", \
+       GOTO="blkid_end"
+ENV{DM_UUID}=="mpath-*", IMPORT{program}="/sbin/blkid -o udev -p $tempnode"
+ENV{DM_PART}=="?*", IMPORT{program}="/sbin/blkid -o udev -p $tempnode"
+LABEL="blkid_end"
+
 OPTIONS="link_priority=50"
 
 # Create persistent links for multipath tables
 ENV{DM_UUID}=="mpath-*", \
        SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
+ENV{DM_MPATH}=="?*", ENV{DM_PART}!="?*", \
+       SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_MPATH}"
+ENV{DM_WWN}=="?*", ENV{DM_PART}!="?*", \
+       SYMLINK+="disk/by-id/wwn-$env{DM_WWN}"
 
 # Create persistent links for partitions
 ENV{DM_PART}=="?*", \
         SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}-part$env{DM_PART}"
+ENV{DM_MPATH}=="?*", ENV{DM_PART}=="?*", \
+       SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_MPATH}-part$env{DM_PART}"
+ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \
+       SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}"
+
+# Create persistent by-label/by-uuid links
+ENV{ID_FS_USAGE}=="?*", IMPORT{db}="ID_FS_USAGE"
+ENV{ID_FS_UUID_ENC}=="?*", IMPORT{db}="ID_FS_UUID_ENC"
+ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", \
+       SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
+ENV{ID_FS_LABEL_ENC}=="?*", IMPORT{db}="ID_FS_LABEL_ENC"
+ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", \
+       SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
 
 # Create dm tables for partitions
-ENV{DM_STATE}=="ACTIVE", ENV{DM_UUID}=="mpath-*", \
+ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0", \
+       GOTO="kpartx_end"
+ENV{DM_STATE}!="SUSPENDED", ENV{DM_UUID}=="mpath-*", \
         RUN+="/sbin/kpartx -u -p -part /dev/$name"
 
 LABEL="kpartx_end"
index 81f32bf..517b856 100644 (file)
@@ -55,6 +55,9 @@ 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)
     echo "DM_NAME=$dmname"
+    if [ "$dmname" != ${dmuuid#mpath-} ] ; then
+       echo "DM_MPATH=${dmuuid#mpath-}"
+    fi
     # We need the dependencies of the parent table to figure out
     # the type if the parent is a multipath table
     case "$dmuuid" in
@@ -63,7 +66,10 @@ if [ "$dmtbl" = "part" ] ; then
            ;;
     esac
 elif [ "$dmtbl" = "mpath" ] ; then
-    dmname=$tblname
+    if [ -n "$DM_NAME" -a "$DM_NAME" != "$dmuuid" ] ; then
+       echo "DM_MPATH=$dmuuid"
+    fi
+    dmname="$dmuuid"
     # We need the dependencies of the table to figure out the type
     dmdeps=$($DMSETUP deps -u $UUID)
 elif [ "$dmtbl" = "dmraid" ] ; then
@@ -77,13 +83,17 @@ fi
 if [ -n "$dmdeps" ] ; then
     case "$dmdeps" in
        *\(94,*)
-            echo "DM_TYPE=dasd"
+            echo "DM_TYPE=ccw"
+           ;;
+       *\(104,* | *\(105,* | *\(106,* | *\(107,* | *\(108,* | *\(109,* | *\(110,* | *\(112,*)
+           echo "DM_TYPE=cciss"
            ;;
        *\(9*)
             echo "DM_TYPE=raid"
            ;;
        *)
             echo "DM_TYPE=scsi"
+           echo "DM_WWN=0x${dmname#?}"
            ;;
     esac
 else
index 79d8328..6f83048 100644 (file)
 #include <sys/stat.h>
 #include <sys/mman.h>
 #include <sysmacros.h>
-
-#if defined(__hppa__) || defined(__powerpc64__) || defined (__alpha__) \
- || defined (__x86_64__)
-typedef unsigned long __kernel_old_dev_t;
-#elif defined(__powerpc__) || defined(__ia64__) || (defined(__sparc__) && defined (__arch64__))
-typedef unsigned int __kernel_old_dev_t;
-#else
-typedef unsigned short __kernel_old_dev_t;
-#endif
-
-#define dev_t __kernel_old_dev_t
-
+#include <asm/posix_types.h>
 #include <linux/loop.h>
 
 #include "lopart.h"
 #include "xstrncpy.h"
 
+#ifndef LOOP_CTL_GET_FREE
+#define LOOP_CTL_GET_FREE       0x4C82
+#endif
+
 #if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \
         && !defined (__s390x__)
 #define int2ptr(x)     ((void *) ((int) x))
@@ -151,14 +144,23 @@ find_unused_loop_device (void)
 
        char dev[20];
        char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
-       int i, j, fd, somedev = 0, someloop = 0, loop_known = 0;
+       int i, j, fd, first = 0, somedev = 0, someloop = 0, loop_known = 0;
        struct stat statbuf;
        struct loop_info loopinfo;
        FILE *procdev;
 
+       if (stat("/dev/loop-control", &statbuf) == 0 &&
+           S_ISCHR(statbuf.st_mode)) {
+               fd = open("/dev/loop-control", O_RDWR);
+               if (fd >= 0)
+                       first = ioctl(fd, LOOP_CTL_GET_FREE);
+               close(fd);
+               if (first < 0)
+                       first = 0;
+       }
        for (j = 0; j < SIZE(loop_formats); j++) {
 
-           for(i = 0; i < 256; i++) {
+           for(i = first; i < 256; i++) {
                sprintf(dev, loop_formats[j], i);
 
                if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
@@ -216,13 +218,13 @@ find_unused_loop_device (void)
                fprintf(stderr,
                    "mount: Could not find any loop device, and, according to %s,\n"
                    "       this kernel does not know about the loop device.\n"
-                   "       (If so, then recompile or `insmod loop.o'.)",
+                   "       (If so, then recompile or `modprobe loop'.)",
                      PROC_DEVICES);
 
            else
                fprintf(stderr,
                    "mount: Could not find any loop device. Maybe this kernel does not know\n"
-                   "       about the loop device (then recompile or `insmod loop.o'), or\n"
+                   "       about the loop device (then recompile or `modprobe loop'), or\n"
                    "       maybe /dev/loop# has the wrong major number?");
 
        } else
@@ -241,7 +243,7 @@ set_loop (const char *device, const char *file, int offset, int *loopro)
 
        if ((ffd = open (file, mode)) < 0) {
 
-               if (!*loopro && errno == EROFS)
+               if (!*loopro && (errno == EROFS || errno == EACCES))
                        ffd = open (file, mode = O_RDONLY);
 
                if (ffd < 0) {
@@ -286,6 +288,7 @@ set_loop (const char *device, const char *file, int offset, int *loopro)
 extern int 
 del_loop (const char *device)
 {
+       int retries = 3;
        int fd;
 
        if ((fd = open (device, O_RDONLY)) < 0) {
@@ -295,10 +298,17 @@ del_loop (const char *device)
                return 1;
        }
 
-       if (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
-               perror ("ioctl: LOOP_CLR_FD");
-               close (fd);
-               return 1;
+       while (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
+               if (errno != EBUSY || retries-- <= 0) {
+                       perror ("ioctl: LOOP_CLR_FD");
+                       close (fd);
+                       return 1;
+               }
+               fprintf(stderr,
+                       "loop: device %s still in use, retrying delete\n",
+                       device);
+               sleep(1);
+               continue;
        }
 
        close (fd);
diff --git a/kpartx/ps3.c b/kpartx/ps3.c
new file mode 100644 (file)
index 0000000..d1e5d64
--- /dev/null
@@ -0,0 +1,77 @@
+#include "kpartx.h"
+#include "byteorder.h"
+#include <sys/types.h>
+#include <string.h>
+
+#define SECTOR_SIZE            512
+#define MAX_ACL_ENTRIES                8
+#define MAX_PARTITIONS         8
+
+#define MAGIC1                 0x0FACE0FFULL
+#define MAGIC2                 0xDEADFACEULL
+
+struct p_acl_entry {
+       u_int64_t laid;
+       u_int64_t rights;
+};
+
+struct d_partition {
+       u_int64_t p_start;
+       u_int64_t p_size;
+       struct p_acl_entry p_acl[MAX_ACL_ENTRIES];
+};
+
+struct disklabel {
+       u_int8_t d_res1[16];
+       u_int64_t d_magic1;
+       u_int64_t d_magic2;
+       u_int64_t d_res2;
+       u_int64_t d_res3;
+       struct d_partition d_partitions[MAX_PARTITIONS];
+       u_int8_t d_pad[0x600 - MAX_PARTITIONS * sizeof(struct d_partition)- 0x30];
+};
+
+static int
+read_disklabel(int fd, struct disklabel *label) {
+       unsigned char *data;
+       int i;
+
+       for (i = 0; i < sizeof(struct disklabel) / SECTOR_SIZE; i++) {
+               data = (unsigned char *) getblock(fd, i);
+               if (!data)
+                       return 0;
+
+               memcpy((unsigned char *) label + i * SECTOR_SIZE, data, SECTOR_SIZE);
+       }
+
+       return 1;
+}
+
+int
+read_ps3_pt(int fd, struct slice all, struct slice *sp, int ns) {
+       struct disklabel label;
+        int n = 0;
+       int i;
+
+       if (!read_disklabel(fd, &label))
+               return -1;
+
+       if ((be64_to_cpu(label.d_magic1) != MAGIC1) ||
+           (be64_to_cpu(label.d_magic2) != MAGIC2))
+               return -1;
+
+       for (i = 0; i < MAX_PARTITIONS; i++) {
+               if (label.d_partitions[i].p_start && label.d_partitions[i].p_size) {
+                       sp[n].start =  be64_to_cpu(label.d_partitions[i].p_start);
+                       sp[n].size =  be64_to_cpu(label.d_partitions[i].p_size);
+                       n++;
+               }
+       }
+
+       return n;
+}
+
+
+
+
+
index 3041089..bd30125 100644 (file)
@@ -1,4 +1,3 @@
-#include "mpath_persist.h"
 #include <libdevmapper.h>
 #include <defaults.h>
 #include <sys/stat.h>
@@ -8,6 +7,7 @@
 #include <checkers.h>
 #include <structs.h>
 #include <structs_vec.h>
+#include <libudev.h>
 
 #include <prio.h>
 #include <unistd.h>
@@ -20,6 +20,7 @@
 #include <ctype.h>
 #include <propsel.h>
 
+#include "mpath_persist.h"
 #include "mpathpr.h"
 #include "mpath_pr_ioctl.h"
 
@@ -32,9 +33,9 @@
 
 
 int
-mpath_lib_init (void)
+mpath_lib_init (struct udev *udev)
 {
-       if (load_config(DEFAULT_CONFIGFILE)){
+       if (load_config(DEFAULT_CONFIGFILE, udev)){
                condlog(0, "Failed to initialize multipath config.");
                return 1;
        }
index 42294e9..a5e7868 100644 (file)
@@ -174,7 +174,7 @@ struct prout_param_descriptor {     /* PROUT parameter descriptor */
  *
  * RETURNS: 0->Success, 1->Failed.
  */
-extern int mpath_lib_init (void );
+extern int mpath_lib_init (struct udev *udev);
 
 
 /*
@@ -194,15 +194,15 @@ extern int mpath_lib_exit (void );
  *
  * @fd:        The file descriptor of a multipath device. Input argument.
  * @rq_servact: PRIN command service action. Input argument
- * @resp: The response from PRIN service action. The resp is a struct specified below. The caller should
+ * @resp: The response from PRIN service action. The resp is a struct specified above. The caller should
  *     manage the memory allocation of this struct
  * @noisy: Turn on debugging trace: Input argument. 0->Disable, 1->Enable
  * @verbose: Set verbosity level. Input argument. value:[0-3]. 0->disabled, 3->Max verbose
  *
  * RESTRICTIONS:
  *
- * RETURNS: MPATH_PR_SUCCESS if PR command successful else returns any of the PR status (specified
- *      above).
+ * RETURNS: MPATH_PR_SUCCESS if PR command successful else returns any of the status specified
+ *       above in RETURN_STATUS.
  *
  */
 extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp,
@@ -217,7 +217,7 @@ extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp
  * @rq_scope: Persistent reservation scope. The value should be always LU_SCOPE (0h).
  * @rq_type: Persistent reservation type. The valid values of persistent reservation types are
  *     5h (Write exclusive - registrants only)
- *     8h (Exclusive access - registrants only)
+ *     6h (Exclusive access - registrants only)
  *     7h (Write exclusive - All registrants)
  *     8h (Exclusive access - All registrants).
  * @paramp: PROUT command parameter data. The paramp is a struct which describes PROUT
index 44f950b..f7f84ff 100644 (file)
@@ -26,7 +26,7 @@ sends PR OUT command to the DM device and gets the response.
 .I rq_type
 .B Persistent reservation type. The valid values of persistent reservation types are
       5h (Write exclusive - registrants only)
-      8h (Exclusive access - registrants only)
+      6h (Exclusive access - registrants only)
       7h (Write exclusive - All registrants)
       8h (Exclusive access - All registrants).
 .br
index de3292e..c85fd10 100644 (file)
@@ -10,8 +10,9 @@
 #include <string.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
-#include "mpath_pr_ioctl.h" 
-#include <mpath_persist.h> 
+#include <libudev.h>
+#include "mpath_pr_ioctl.h"
+#include <mpath_persist.h>
 
 #include <debug.h>
 
index 2982947..8597d40 100644 (file)
@@ -14,6 +14,7 @@
 #include <debug.h>
 #include "memory.h"
 #include "../libmultipath/uxsock.h"
+#include "../libmultipath/defaults.h"
 
 unsigned long mem_allocated;    /* Total memory used in Bytes */
 
@@ -25,7 +26,7 @@ int update_prflag(char * arg1, char * arg2, int noisy)
        size_t len;
        int ret = 0;
 
-       fd = ux_socket_connect("/var/run/multipathd.sock");
+       fd = ux_socket_connect(DEFAULT_SOCKET);
        if (fd == -1) {
                condlog (0, "ux socket connect error");
                return 1 ;
index 0395602..6364364 100644 (file)
@@ -8,14 +8,17 @@ SONAME=0
 DEVLIB = libmultipath.so
 LIBS = $(DEVLIB).$(SONAME)
 LIBDEPS = -lpthread -ldl -ldevmapper -ludev
+ifdef SYSTEMD
+       LIBDEPS += -lsystemd-daemon
+endif
 
-OBJS = memory.o parser.o vector.o devmapper.o \
+OBJS = memory.o parser.o vector.o devmapper.o callout.o \
        hwtable.o blacklist.o util.o dmparser.o config.o \
        structs.o discovery.o propsel.o dict.o \
-       pgpolicies.o debug.o regex.o defaults.o uevent.o \
+       pgpolicies.o debug.o defaults.o uevent.o \
        switchgroup.o uxsock.o print.o alias.o log_pthread.o \
        log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \
-       lock.o waiter.o file.o wwids.o
+       lock.o waiter.o file.o wwids.o prioritizers/alua_rtpg.o
 
 LIBDM_API_FLUSH = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_no_flush' /usr/include/libdevmapper.h)
 
@@ -35,6 +38,9 @@ ifneq ($(strip $(LIBUDEV_API_RECVBUF)),0)
        CFLAGS += -DLIBUDEV_API_RECVBUF
 endif
 
+ifdef SYSTEMD
+       CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
+endif
 
 all: $(LIBS)
 
index e1f3073..ab15185 100644 (file)
@@ -13,6 +13,9 @@
 #include "uxsock.h"
 #include "alias.h"
 #include "file.h"
+#include "vector.h"
+#include "checkers.h"
+#include "structs.h"
 
 
 /*
@@ -43,11 +46,11 @@ format_devname(char *name, int id, int len, char *prefix)
        memset(name,0, len);
        strcpy(name, prefix);
        for (pos = len - 1; pos >= prefix_len; pos--) {
+               id--;
                name[pos] = 'a' + id % 26;
                if (id < 26)
                        break;
                id /= 26;
-               id--;
        }
        memmove(name + prefix_len, name + pos, len - pos);
        name[prefix_len + len - pos] = '\0';
@@ -63,13 +66,22 @@ scan_devname(char *alias, char *prefix)
        if (!prefix || strncmp(alias, prefix, strlen(prefix)))
                return -1;
 
+       if (strlen(alias) == strlen(prefix))
+               return -1;      
+
+       if (strlen(alias) > strlen(prefix) + 7)
+               /* id of 'aaaaaaaa' overflows int */
+               return -1;
+
        c = alias + strlen(prefix);
        while (*c != '\0' && *c != ' ' && *c != '\t') {
+               if (*c < 'a' || *c > 'z')
+                       return -1;
                i = *c - 'a';
                n = ( n * 26 ) + i;
+               if (n < 0)
+                       return -1;
                c++;
-               if (*c < 'a' || *c > 'z')
-                       break;
                n++;
        }
 
@@ -81,7 +93,9 @@ lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
 {
        char buf[LINE_MAX];
        unsigned int line_nr = 0;
-       int id = 0;
+       int id = 1;
+       int biggest_id = 1;
+       int smallest_bigger_id = INT_MAX;
 
        *map_alias = NULL;
 
@@ -97,9 +111,13 @@ lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
                if (!alias) /* blank line */
                        continue;
                curr_id = scan_devname(alias, prefix);
-               if (curr_id >= id)
-                       id = curr_id + 1;
-               wwid = strtok(NULL, "");
+               if (curr_id == id)
+                       id++;
+               if (curr_id > biggest_id)
+                       biggest_id = curr_id;
+               if (curr_id > id && curr_id < smallest_bigger_id)
+                       smallest_bigger_id = curr_id;
+               wwid = strtok(NULL, " \t");
                if (!wwid){
                        condlog(3,
                                "Ignoring malformed line %u in bindings file",
@@ -113,31 +131,37 @@ lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
                        if (*map_alias == NULL)
                                condlog(0, "Cannot copy alias from bindings "
                                        "file : %s", strerror(errno));
-                       return id;
+                       return 0;
                }
        }
        condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
-       return id;
+       if (id < 0) {
+               condlog(0, "no more available user_friendly_names");
+               return 0;
+       }
+       if (id < smallest_bigger_id)
+               return id;
+       return biggest_id + 1;
 }
 
 static int
-rlookup_binding(FILE *f, char **map_wwid, char *map_alias)
+rlookup_binding(FILE *f, char *buff, char *map_alias)
 {
-       char buf[LINE_MAX];
+       char line[LINE_MAX];
        unsigned int line_nr = 0;
        int id = 0;
 
-       *map_wwid = NULL;
+       buff[0] = '\0';
 
-       while (fgets(buf, LINE_MAX, f)) {
+       while (fgets(line, LINE_MAX, f)) {
                char *c, *alias, *wwid;
                int curr_id;
 
                line_nr++;
-               c = strpbrk(buf, "#\n\r");
+               c = strpbrk(line, "#\n\r");
                if (c)
                        *c = '\0';
-               alias = strtok(buf, " \t");
+               alias = strtok(line, " \t");
                if (!alias) /* blank line */
                        continue;
                curr_id = scan_devname(alias, NULL); /* TBD: Why this call? */
@@ -150,13 +174,16 @@ rlookup_binding(FILE *f, char **map_wwid, char *map_alias)
                                line_nr);
                        continue;
                }
+               if (strlen(wwid) > WWID_SIZE - 1) {
+                       condlog(3,
+                               "Ignoring too large wwid at %u in bindings file", line_nr);
+                       continue;
+               }
                if (strcmp(alias, map_alias) == 0){
                        condlog(3, "Found matching alias [%s] in bindings file."
                                "\nSetting wwid to %s", alias, wwid);
-                       *map_wwid = strdup(wwid);
-                       if (*map_wwid == NULL)
-                               condlog(0, "Cannot copy alias from bindings "
-                                       "file : %s", strerror(errno));
+                       strncpy(buff, wwid, WWID_SIZE);
+                       buff[WWID_SIZE - 1] = '\0';
                        return id;
                }
        }
@@ -248,43 +275,42 @@ get_user_friendly_alias(char *wwid, char *file, char *prefix,
                return NULL;
        }
 
-       if (!alias && can_write && !bindings_read_only)
+       if (!alias && can_write && !bindings_read_only && id)
                alias = allocate_binding(fd, wwid, id, prefix);
 
        fclose(f);
        return alias;
 }
 
-char *
-get_user_friendly_wwid(char *alias, char *file)
+int
+get_user_friendly_wwid(char *alias, char *buff, char *file)
 {
-       char *wwid;
-       int fd, id, unused;
+       int fd, unused;
        FILE *f;
 
        if (!alias || *alias == '\0') {
                condlog(3, "Cannot find binding for empty alias");
-               return NULL;
+               return -1;
        }
 
        fd = open_file(file, &unused, BINDINGS_FILE_HEADER);
        if (fd < 0)
-               return NULL;
+               return -1;
 
        f = fdopen(fd, "r");
        if (!f) {
                condlog(0, "cannot fdopen on bindings file descriptor : %s",
                        strerror(errno));
                close(fd);
-               return NULL;
+               return -1;
        }
 
-       id = rlookup_binding(f, &wwid, alias);
-       if (id < 0) {
+       rlookup_binding(f, buff, alias);
+       if (!strlen(buff)) {
                fclose(f);
-               return NULL;
+               return -1;
        }
 
        fclose(f);
-       return wwid;
+       return 0;
 }
index c625090..8ddd0b5 100644 (file)
@@ -9,4 +9,4 @@
 
 char *get_user_friendly_alias(char *wwid, char *file, char *prefix,
                              int bindings_readonly);
-char *get_user_friendly_wwid(char *alias, char *file);
+int get_user_friendly_wwid(char *alias, char *buff, char *file);
index f369517..79ddcde 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (c) 2004, 2005 Christophe Varoqui
  */
 #include <stdio.h>
+#include <libudev.h>
 
 #include "checkers.h"
 #include "memory.h"
@@ -96,51 +97,7 @@ set_ble_device (vector blist, char * vendor, char * product, int origin)
 }
 
 int
-setup_default_blist (struct config * conf)
-{
-       struct blentry * ble;
-       struct hwentry *hwe;
-       char * str;
-       int i;
-
-       str = STRDUP("^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*");
-       if (!str)
-               return 1;
-       if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
-               return 1;
-
-       str = STRDUP("^hd[a-z]");
-       if (!str)
-               return 1;
-       if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
-               return 1;
-
-       str = STRDUP("^dcssblk[0-9]*");
-       if (!str)
-               return 1;
-       if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
-               return 1;
-
-       vector_foreach_slot (conf->hwtable, hwe, i) {
-               if (hwe->bl_product) {
-                       if (alloc_ble_device(conf->blist_device))
-                               return 1;
-                       ble = VECTOR_SLOT(conf->blist_device,
-                                         VECTOR_SIZE(conf->blist_device) -1);
-                       if (set_ble_device(conf->blist_device,
-                                          STRDUP(hwe->vendor),
-                                          STRDUP(hwe->bl_product),
-                                          ORIGIN_DEFAULT)) {
-                               FREE(ble);
-                               return 1;
-                       }
-               }
-       }
-       return 0;
-}
-
-int
-_blacklist_exceptions (vector elist, char * str)
+_blacklist_exceptions (vector elist, const char * str)
 {
        int i;
        struct blentry * ele;
@@ -153,7 +110,7 @@ _blacklist_exceptions (vector elist, char * str)
 }
 
 int
-_blacklist (vector blist, char * str)
+_blacklist (vector blist, const char * str)
 {
        int i;
        struct blentry * ble;
@@ -193,16 +150,73 @@ _blacklist_device (vector blist, char * vendor, char * product)
        return 0;
 }
 
-#define LOG_BLIST(M) \
-       if (vendor && product)                                           \
-               condlog(3, "%s: (%s:%s) %s", dev, vendor, product, (M)); \
-       else if (wwid)                                                   \
-               condlog(3, "%s: (%s) %s", dev, wwid, (M));               \
-       else                                                             \
-               condlog(3, "%s: %s", dev, (M))
+int
+setup_default_blist (struct config * conf)
+{
+       struct blentry * ble;
+       struct hwentry *hwe;
+       char * str;
+       int i;
+
+       str = STRDUP("^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*");
+       if (!str)
+               return 1;
+       if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
+               return 1;
+
+       str = STRDUP("^(td|hd)[a-z]");
+       if (!str)
+               return 1;
+       if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
+               return 1;
+
+       str = STRDUP("^dcssblk[0-9]*");
+       if (!str)
+               return 1;
+       if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
+               return 1;
+
+       str = STRDUP("(ID_SCSI_VPD|ID_WWN)");
+       if (!str)
+               return 1;
+       if (store_ble(conf->elist_property, str, ORIGIN_DEFAULT))
+               return 1;
+
+       vector_foreach_slot (conf->hwtable, hwe, i) {
+               if (hwe->bl_product) {
+                       if (_blacklist_device(conf->blist_device, hwe->vendor,
+                                             hwe->bl_product))
+                               continue;
+                       if (alloc_ble_device(conf->blist_device))
+                               return 1;
+                       ble = VECTOR_SLOT(conf->blist_device,
+                                         VECTOR_SIZE(conf->blist_device) -1);
+                       if (set_ble_device(conf->blist_device,
+                                          STRDUP(hwe->vendor),
+                                          STRDUP(hwe->bl_product),
+                                          ORIGIN_DEFAULT)) {
+                               FREE(ble);
+                               return 1;
+                       }
+               }
+       }
+       return 0;
+}
+
+#define LOG_BLIST(M,S)                                                 \
+       if (vendor && product)                                          \
+               condlog(3, "%s: (%s:%s) %s %s",                         \
+                       dev, vendor, product, (M), (S));                \
+       else if (wwid)                                                  \
+               condlog(3, "%s: %s %s %s", dev, (M), wwid, (S));        \
+       else if (env)                                                   \
+               condlog(3, "%s: %s %s %s", dev, (M), env, (S));         \
+       else                                                            \
+               condlog(3, "%s: %s %s", dev, (M), (S))
 
 void
-log_filter (char *dev, char *vendor, char *product, char *wwid, int r)
+log_filter (const char *dev, char *vendor, char *product, char *wwid,
+           const char *env, int r)
 {
        /*
         * Try to sort from most likely to least.
@@ -211,22 +225,31 @@ log_filter (char *dev, char *vendor, char *product, char *wwid, int r)
        case MATCH_NOTHING:
                break;
        case MATCH_DEVICE_BLIST:
-               LOG_BLIST("vendor/product blacklisted");
+               LOG_BLIST("vendor/product", "blacklisted");
                break;
        case MATCH_WWID_BLIST:
-               LOG_BLIST("wwid blacklisted");
+               LOG_BLIST("wwid", "blacklisted");
                break;
        case MATCH_DEVNODE_BLIST:
-               LOG_BLIST("device node name blacklisted");
+               LOG_BLIST("device node name", "blacklisted");
+               break;
+       case MATCH_PROPERTY_BLIST:
+               LOG_BLIST("udev property", "blacklisted");
                break;
        case MATCH_DEVICE_BLIST_EXCEPT:
-               LOG_BLIST("vendor/product whitelisted");
+               LOG_BLIST("vendor/product", "whitelisted");
                break;
        case MATCH_WWID_BLIST_EXCEPT:
-               LOG_BLIST("wwid whitelisted");
+               LOG_BLIST("wwid", "whitelisted");
                break;
        case MATCH_DEVNODE_BLIST_EXCEPT:
-               LOG_BLIST("device node name whitelisted");
+               LOG_BLIST("device node name", "whitelisted");
+               break;
+       case MATCH_PROPERTY_BLIST_EXCEPT:
+               LOG_BLIST("udev property", "whitelisted");
+               break;
+       case MATCH_PROPERTY_BLIST_MISSING:
+               LOG_BLIST("blacklisted,", "udev property missing");
                break;
        }
 }
@@ -247,7 +270,7 @@ int
 filter_device (vector blist, vector elist, char * vendor, char * product)
 {
        int r = _filter_device(blist, elist, vendor, product);
-       log_filter(NULL, vendor, product, NULL, r);
+       log_filter(NULL, vendor, product, NULL, NULL, r);
        return r;
 }
 
@@ -267,7 +290,7 @@ int
 filter_devnode (vector blist, vector elist, char * dev)
 {
        int r = _filter_devnode(blist, elist, dev);
-       log_filter(dev, NULL, NULL, NULL, r);
+       log_filter(dev, NULL, NULL, NULL, NULL, r);
        return r;
 }
 
@@ -287,7 +310,7 @@ int
 filter_wwid (vector blist, vector elist, char * wwid)
 {
        int r = _filter_wwid(blist, elist, wwid);
-       log_filter(NULL, NULL, NULL, wwid, r);
+       log_filter(NULL, NULL, NULL, wwid, NULL, r);
        return r;
 }
 
@@ -311,10 +334,55 @@ int
 filter_path (struct config * conf, struct path * pp)
 {
        int r=_filter_path(conf, pp);
-       log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, r);
+       log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, NULL, r);
        return r;
 }
 
+int
+_filter_property (struct config *conf, const char *env)
+{
+       if (_blacklist_exceptions(conf->elist_property, env))
+               return MATCH_PROPERTY_BLIST_EXCEPT;
+       if (_blacklist(conf->blist_property, env))
+               return MATCH_PROPERTY_BLIST;
+
+       return 0;
+}
+
+int
+filter_property(struct config * conf, struct udev_device * udev)
+{
+       const char *devname = udev_device_get_sysname(udev);
+       struct udev_list_entry *list_entry;
+       int r;
+
+       if (!udev)
+               return 0;
+
+       udev_list_entry_foreach(list_entry,
+                               udev_device_get_properties_list_entry(udev)) {
+               const char *env;
+
+               env = udev_list_entry_get_name(list_entry);
+               if (!env)
+                       continue;
+
+               r = _filter_property(conf, env);
+               if (r) {
+                       log_filter(devname, NULL, NULL, NULL, env, r);
+                       return r;
+               }
+       }
+
+       /*
+        * This is the inverse of the 'normal' matching;
+        * the environment variable _has_ to match.
+        */
+       log_filter(devname, NULL, NULL, NULL, NULL,
+                  MATCH_PROPERTY_BLIST_MISSING);
+       return MATCH_PROPERTY_BLIST_MISSING;
+}
+
 void
 free_blacklist (vector blist)
 {
index cdbebef..0e90e9a 100644 (file)
@@ -1,15 +1,19 @@
 #ifndef _BLACKLIST_H
 #define _BLACKLIST_H
 
+#include <libudev.h>
 #include "regex.h"
 
-#define MATCH_NOTHING       0
-#define MATCH_WWID_BLIST    1
-#define MATCH_DEVICE_BLIST  2
-#define MATCH_DEVNODE_BLIST 3
-#define MATCH_WWID_BLIST_EXCEPT    -MATCH_WWID_BLIST
-#define MATCH_DEVICE_BLIST_EXCEPT  -MATCH_DEVICE_BLIST
-#define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST
+#define MATCH_NOTHING        0
+#define MATCH_WWID_BLIST     1
+#define MATCH_DEVICE_BLIST   2
+#define MATCH_DEVNODE_BLIST  3
+#define MATCH_PROPERTY_BLIST 4
+#define MATCH_PROPERTY_BLIST_MISSING 5
+#define MATCH_WWID_BLIST_EXCEPT     -MATCH_WWID_BLIST
+#define MATCH_DEVICE_BLIST_EXCEPT   -MATCH_DEVICE_BLIST
+#define MATCH_DEVNODE_BLIST_EXCEPT  -MATCH_DEVNODE_BLIST
+#define MATCH_PROPERTY_BLIST_EXCEPT -MATCH_PROPERTY_BLIST
 
 struct blentry {
        char * str;
@@ -31,6 +35,7 @@ int filter_devnode (vector, vector, char *);
 int filter_wwid (vector, vector, char *);
 int filter_device (vector, vector, char *, char *);
 int filter_path (struct config *, struct path *);
+int filter_property(struct config *, struct udev_device *);
 int store_ble (vector, char *, int);
 int set_ble_device (vector, char *, char *, int);
 void free_blacklist (vector);
diff --git a/libmultipath/callout.c b/libmultipath/callout.c
new file mode 100644 (file)
index 0000000..c35c7c0
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Source: copy of the udev package source file
+ *
+ * Copyrights of the source file apply
+ * Copyright (c) 2004 Christophe Varoqui
+ */
+#include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include "checkers.h"
+#include "vector.h"
+#include "structs.h"
+#include "util.h"
+#include "debug.h"
+
+int execute_program(char *path, char *value, int len)
+{
+       int retval;
+       int count;
+       int status;
+       int fds[2], null_fd;
+       pid_t pid;
+       char *pos;
+       char arg[CALLOUT_MAX_SIZE];
+       int argc = sizeof(arg) / 2;
+       char *argv[argc + 1];
+       int i;
+
+       i = 0;
+
+       if (strchr(path, ' ')) {
+               strlcpy(arg, path, sizeof(arg));
+               pos = arg;
+               while (pos != NULL && i < argc) {
+                       if (pos[0] == '\'') {
+                               /* don't separate if in apostrophes */
+                               pos++;
+                               argv[i] = strsep(&pos, "\'");
+                               while (pos[0] == ' ')
+                                       pos++;
+                       } else {
+                               argv[i] = strsep(&pos, " ");
+                       }
+                       i++;
+               }
+       } else {
+               argv[i++] = path;
+       }
+       argv[i] =  NULL;
+
+       retval = pipe(fds);
+
+       if (retval != 0) {
+               condlog(0, "error creating pipe for callout: %s", strerror(errno));
+               return -1;
+       }
+
+       pid = fork();
+
+       switch(pid) {
+       case 0:
+               /* child */
+               close(STDOUT_FILENO);
+
+               /* dup write side of pipe to STDOUT */
+               if (dup(fds[1]) < 0)
+                       return -1;
+
+               /* Ignore writes to stderr */
+               null_fd = open("/dev/null", O_WRONLY);
+               if (null_fd > 0) {
+                       close(STDERR_FILENO);
+                       retval = dup(null_fd);
+                       close(null_fd);
+               }
+
+               retval = execv(argv[0], argv);
+               condlog(0, "error execing %s : %s", argv[0], strerror(errno));
+               exit(-1);
+       case -1:
+               condlog(0, "fork failed: %s", strerror(errno));
+               close(fds[0]);
+               close(fds[1]);
+               return -1;
+       default:
+               /* parent reads from fds[0] */
+               close(fds[1]);
+               retval = 0;
+               i = 0;
+               while (1) {
+                       count = read(fds[0], value + i, len - i-1);
+                       if (count <= 0)
+                               break;
+
+                       i += count;
+                       if (i >= len-1) {
+                               condlog(0, "not enough space for response from %s", argv[0]);
+                               retval = -1;
+                               break;
+                       }
+               }
+
+               if (count < 0) {
+                       condlog(0, "no response from %s", argv[0]);
+                       retval = -1;
+               }
+
+               if (i > 0 && value[i-1] == '\n')
+                       i--;
+               value[i] = '\0';
+
+               wait(&status);
+               close(fds[0]);
+
+               retval = -1;
+               if (WIFEXITED(status)) {
+                       status = WEXITSTATUS(status);
+                       if (status == 0)
+                               retval = 0;
+                       else
+                               condlog(0, "%s exitted with %d", argv[0], status);
+               }
+               else if (WIFSIGNALED(status))
+                       condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status));
+               else
+                       condlog(0, "%s terminated abnormally", argv[0]);
+       }
+       return retval;
+}
+
+extern int
+apply_format (char * string, char * cmd, struct path * pp)
+{
+       char * pos;
+       char * dst;
+       char * p;
+       char * q;
+       int len;
+       int myfree;
+
+       if (!string)
+               return 1;
+
+       if (!cmd)
+               return 1;
+
+       dst = cmd;
+       p = dst;
+       pos = strchr(string, '%');
+       myfree = CALLOUT_MAX_SIZE;
+
+       if (!pos) {
+               strcpy(dst, string);
+               return 0;
+       }
+
+       len = (int) (pos - string) + 1;
+       myfree -= len;
+
+       if (myfree < 2)
+               return 1;
+
+       snprintf(p, len, "%s", string);
+       p += len - 1;
+       pos++;
+
+       switch (*pos) {
+       case 'n':
+               len = strlen(pp->dev) + 1;
+               myfree -= len;
+
+               if (myfree < 2)
+                       return 1;
+
+               snprintf(p, len, "%s", pp->dev);
+               for (q = p; q < p + len; q++) {
+                       if (q && *q == '!')
+                               *q = '/';
+               }
+               p += len - 1;
+               break;
+       case 'd':
+               len = strlen(pp->dev_t) + 1;
+               myfree -= len;
+
+               if (myfree < 2)
+                       return 1;
+
+               snprintf(p, len, "%s", pp->dev_t);
+               p += len - 1;
+               break;
+       default:
+               break;
+       }
+       pos++;
+
+       if (!*pos)
+               return 0;
+
+       len = strlen(pos) + 1;
+       myfree -= len;
+
+       if (myfree < 2)
+               return 1;
+
+       snprintf(p, len, "%s", pos);
+       condlog(3, "reformated callout = %s", dst);
+       return 0;
+}
diff --git a/libmultipath/callout.h b/libmultipath/callout.h
new file mode 100644 (file)
index 0000000..ab648e8
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _CALLOUT_H
+#define _CALLOUT_H
+
+int execute_program(char *, char *, int);
+int apply_format (char *, char *, struct path *);
+
+#endif /* _CALLOUT_H */
index 01dafdd..4a4cd7c 100644 (file)
@@ -16,7 +16,9 @@ char *checker_state_names[] = {
       "up",
       "shaky",
       "ghost",
-      "pending"
+      "pending",
+      "timeout",
+      "removed",
 };
 
 static LIST_HEAD(checkers);
index 5a96165..e62b52f 100644 (file)
  * PATH_PENDING:
  * - Use: All async checkers
  * - Description: Indicates a check IO is in flight.
+ *
+ * PATH_TIMEOUT:
+ * - Use: Only tur checker
+ * - Description: Command timed out
+ *
+ * PATH REMOVED:
+ * - Use: All checkers
+ * - Description: Device has been removed from the system
  */
 enum path_check_state {
        PATH_WILD,
@@ -55,6 +63,8 @@ enum path_check_state {
        PATH_SHAKY,
        PATH_GHOST,
        PATH_PENDING,
+       PATH_TIMEOUT,
+       PATH_REMOVED,
        PATH_MAX_STATE
 };
 
index b42d267..a797734 100644 (file)
@@ -15,6 +15,7 @@
 #include "../libmultipath/sg_include.h"
 #include "libsg.h"
 #include "checkers.h"
+#include "debug.h"
 
 #define INQUIRY_CMD     0x12
 #define INQUIRY_CMDLEN  6
@@ -113,7 +114,7 @@ int libcheck_check (struct checker * c)
        io_hdr.dxferp = sense_buffer;
        io_hdr.cmdp = inqCmdBlk;
        io_hdr.sbp = sb;
-       io_hdr.timeout = c->timeout;
+       io_hdr.timeout = c->timeout * 1000;
        io_hdr.pack_id = 0;
        if (ioctl(c->fd, SG_IO, &io_hdr) < 0) {
                MSG(c, "emc_clariion_checker: sending query command failed");
@@ -199,7 +200,7 @@ int libcheck_check (struct checker * c)
                                 * 02/04/03 not 05/25/01 on read.
                                 */
                                SET_INACTIVE_SNAP(c);
-                               MSG(c, "emc_clariion_checker: Active "
+                               condlog(3, "emc_clariion_checker: Active "
                                        "path to inactive snapshot WWN %s.",
                                        wwnstr);
                        } else
@@ -220,7 +221,7 @@ int libcheck_check (struct checker * c)
        } else {
                if (IS_INACTIVE_SNAP(c)) {
                        hexadecimal_to_ascii(ct->wwn, wwnstr);
-                       MSG(c, "emc_clariion_checker: Passive "
+                       condlog(3, "emc_clariion_checker: Passive "
                                "path to inactive snapshot WWN %s.",
                                wwnstr);
                        ret = PATH_DOWN;
index b50ac0c..fe5e0f9 100644 (file)
@@ -70,7 +70,7 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
        io_hdr.dxferp = resp;
        io_hdr.cmdp = inqCmdBlk;
        io_hdr.sbp = sense_b;
-       io_hdr.timeout = timeout;
+       io_hdr.timeout = timeout * 1000;
 
        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
                return 1;
@@ -111,7 +111,7 @@ do_tur (int fd, unsigned int timeout)
        io_hdr.dxfer_direction = SG_DXFER_NONE;
        io_hdr.cmdp = turCmdBlk;
        io_hdr.sbp = sense_buffer;
-       io_hdr.timeout = timeout;
+       io_hdr.timeout = timeout * 1000;
        io_hdr.pack_id = 0;
 
        if (ioctl(fd, SG_IO, &io_hdr) < 0)
index 5a989d3..0d3af1f 100644 (file)
@@ -53,7 +53,7 @@ sg_read (int sg_fd, unsigned char * buff, int buff_len,
        io_hdr.dxferp = buff;
        io_hdr.mx_sb_len = sense_len;
        io_hdr.sbp = sense;
-       io_hdr.timeout = timeout;
+       io_hdr.timeout = timeout * 1000;
        io_hdr.pack_id = (int)start_block;
        if (diop && *diop)
        io_hdr.flags |= SG_FLAG_DIRECT_IO;
index 5f24f55..e0b2ea4 100644 (file)
 #define MSG_RDAC_UP    "rdac checker reports path is up"
 #define MSG_RDAC_DOWN  "rdac checker reports path is down"
 #define MSG_RDAC_GHOST "rdac checker reports path is ghost"
+#define MSG_RDAC_DOWN_TYPE(STR) MSG_RDAC_DOWN": "STR
+
+#define RTPG_UNAVAILABLE       0x3
+#define RTPG_OFFLINE           0xE
+#define RTPG_TRANSITIONING     0xF
+
+#define RTPG_UNAVAIL_NON_RESPONSIVE    0x2
+#define RTPG_UNAVAIL_IN_RESET          0x3
+#define RTPG_UNAVAIL_CFW_DL1           0x4
+#define RTPG_UNAVAIL_CFW_DL2           0x5
+#define RTPG_UNAVAIL_QUIESCED          0x6
+#define RTPG_UNAVAIL_SERVICE_MODE      0x7
 
 struct control_mode_page {
        unsigned char header[8];
@@ -74,7 +86,7 @@ int libcheck_init (struct checker * c)
        io_hdr.dxferp = &current;
        io_hdr.cmdp = cmd;
        io_hdr.sbp = sense_b;
-       io_hdr.timeout = c->timeout;
+       io_hdr.timeout = c->timeout * 1000;
 
        if (ioctl(c->fd, SG_IO, &io_hdr) < 0)
                goto out;
@@ -150,7 +162,7 @@ retry:
        io_hdr.dxferp = resp;
        io_hdr.cmdp = inqCmdBlk;
        io_hdr.sbp = sense_b;
-       io_hdr.timeout = timeout;
+       io_hdr.timeout = timeout * 1000;
 
        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
                return 1;
@@ -199,22 +211,64 @@ struct volume_access_inq
        char PQ_PDT;
        char dontcare0[7];
        char avtcvp;
-       char dontcare1;
-       char asym_access_state_cur;
+       char vol_ppp;
+       char aas_cur;
        char vendor_specific_cur;
-       char dontcare2[36];
+       char aas_alt;
+       char vendor_specific_alt;
+       char dontcare1[34];
 };
 
+const char
+*checker_msg_string(struct volume_access_inq *inq)
+{
+       /* lun not connected */
+       if (((inq->PQ_PDT & 0xE0) == 0x20) || (inq->PQ_PDT & 0x7f))
+               return MSG_RDAC_DOWN_TYPE("lun not connected");
+
+       /* if no tpg data is available, give the generic path down message */
+       if (!(inq->avtcvp & 0x10))
+               return MSG_RDAC_DOWN;
+
+       /* controller is booting up */
+       if (((inq->aas_cur & 0x0F) == RTPG_TRANSITIONING) &&
+               (inq->aas_alt & 0x0F) != RTPG_TRANSITIONING)
+               return MSG_RDAC_DOWN_TYPE("ctlr is in startup sequence");
+
+       /* if not unavailable, give generic message */
+       if ((inq->aas_cur & 0x0F) != RTPG_UNAVAILABLE)
+               return MSG_RDAC_DOWN;
+
+       /* target port group unavailable */
+       switch (inq->vendor_specific_cur) {
+       case RTPG_UNAVAIL_NON_RESPONSIVE:
+               return MSG_RDAC_DOWN_TYPE("non-responsive to queries");
+       case RTPG_UNAVAIL_IN_RESET:
+               return MSG_RDAC_DOWN_TYPE("ctlr held in reset");
+       case RTPG_UNAVAIL_CFW_DL1:
+       case RTPG_UNAVAIL_CFW_DL2:
+               return MSG_RDAC_DOWN_TYPE("ctlr firmware downloading");
+       case RTPG_UNAVAIL_QUIESCED:
+               return MSG_RDAC_DOWN_TYPE("ctlr quiesced by admin request");
+       case RTPG_UNAVAIL_SERVICE_MODE:
+               return MSG_RDAC_DOWN_TYPE("ctlr is in service mode");
+       default:
+               return MSG_RDAC_DOWN_TYPE("ctlr is unavailable");
+       }
+}
+
 extern int
 libcheck_check (struct checker * c)
 {
        struct volume_access_inq inq;
-       int ret;
+       int ret, inqfail;
 
+       inqfail = 0;
        memset(&inq, 0, sizeof(struct volume_access_inq));
        if (0 != do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq),
                        c->timeout)) {
                ret = PATH_DOWN;
+               inqfail = 1;
                goto done;
        } else if (((inq.PQ_PDT & 0xE0) == 0x20) || (inq.PQ_PDT & 0x7f)) {
                /* LUN not connected*/
@@ -222,12 +276,27 @@ libcheck_check (struct checker * c)
                goto done;
        }
 
-       /* check if controller is in service mode */
-       if ((inq.avtcvp & 0x10) &&
-           ((inq.asym_access_state_cur & 0x0F) == 0x3) &&
-           (inq.vendor_specific_cur == 0x7)) {
-               ret = PATH_DOWN;
-               goto done;
+       /* If TPGDE bit set, evaluate TPG information */
+       if ((inq.avtcvp & 0x10)) {
+               switch (inq.aas_cur & 0x0F) {
+               /* Never use the path if it reports unavailable */
+               case RTPG_UNAVAILABLE:
+                       ret = PATH_DOWN;
+                       goto done;
+               /*
+                * If both controllers report transitioning, it
+                * means mode select or STPG is being processed.
+                *
+                * If this controller alone is transitioning, it's
+                * booting and we shouldn't use it yet.
+                */
+               case RTPG_TRANSITIONING:
+                       if ((inq.aas_alt & 0xF) != RTPG_TRANSITIONING) {
+                               ret = PATH_DOWN;
+                               goto done;
+                       }
+                       break;
+               }
        }
 
        /* If owner set or ioship mode is enabled return PATH_UP always */
@@ -239,7 +308,8 @@ libcheck_check (struct checker * c)
 done:
        switch (ret) {
        case PATH_DOWN:
-               MSG(c, MSG_RDAC_DOWN);
+               MSG(c, (inqfail) ? MSG_RDAC_DOWN_TYPE("inquiry failed") :
+                       checker_msg_string(&inq));
                break;
        case PATH_UP:
                MSG(c, MSG_RDAC_UP);
index 9c141aa..bd7372d 100644 (file)
@@ -116,7 +116,7 @@ tur_check(int fd, unsigned int timeout, char *msg)
        io_hdr.dxfer_direction = SG_DXFER_NONE;
        io_hdr.cmdp = turCmdBlk;
        io_hdr.sbp = sense_buffer;
-       io_hdr.timeout = timeout;
+       io_hdr.timeout = timeout * 1000;
        io_hdr.pack_id = 0;
        if (ioctl(fd, SG_IO, &io_hdr) < 0) {
                TUR_MSG(msg, MSG_TUR_DOWN);
@@ -297,8 +297,7 @@ libcheck_check (struct checker * c)
                                pthread_cancel(ct->thread);
                                ct->running = 0;
                                MSG(c, MSG_TUR_TIMEOUT);
-                               tur_status = PATH_DOWN;
-                               ct->state = PATH_UNCHECKED;
+                               tur_status = PATH_TIMEOUT;
                        } else {
                                condlog(3, "%d:%d: tur checker not finished",
                                        TUR_DEVT(ct));
@@ -317,9 +316,9 @@ libcheck_check (struct checker * c)
                if (ct->thread) {
                        /* pthread cancel failed. continue in sync mode */
                        pthread_mutex_unlock(&ct->lock);
-                       condlog(3, "%d:%d: tur thread not responding, "
-                               "using sync mode", TUR_DEVT(ct));
-                       return tur_check(c->fd, c->timeout, c->message);
+                       condlog(3, "%d:%d: tur thread not responding",
+                               TUR_DEVT(ct));
+                       return PATH_TIMEOUT;
                }
                /* Start new TUR checker */
                ct->state = PATH_UNCHECKED;
index 0a56cd1..e13c307 100644 (file)
 static int
 hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2)
 {
-       if (hwe1->vendor && hwe2->vendor && strcmp(hwe1->vendor, hwe2->vendor))
+       if ((hwe2->vendor && !hwe1->vendor) ||
+           (hwe1->vendor && (!hwe2->vendor ||
+                             strcmp(hwe1->vendor, hwe2->vendor))))
                return 1;
 
-       if (hwe1->product && hwe2->product && strcmp(hwe1->product, hwe2->product))
+       if ((hwe2->product && !hwe1->product) ||
+           (hwe1->product && (!hwe2->product ||
+                             strcmp(hwe1->product, hwe2->product))))
                return 1;
 
-       if (hwe1->revision && hwe2->revision && strcmp(hwe1->revision, hwe2->revision))
+       if ((hwe2->revision && !hwe1->revision) ||
+           (hwe1->revision && (!hwe2->revision ||
+                             strcmp(hwe1->revision, hwe2->revision))))
                return 1;
 
        return 0;
@@ -161,6 +167,9 @@ free_hwe (struct hwentry * hwe)
        if (hwe->revision)
                FREE(hwe->revision);
 
+       if (hwe->getuid)
+               FREE(hwe->getuid);
+
        if (hwe->uid_attribute)
                FREE(hwe->uid_attribute);
 
@@ -218,6 +227,9 @@ free_mpe (struct mpentry * mpe)
        if (mpe->selector)
                FREE(mpe->selector);
 
+       if (mpe->getuid)
+               FREE(mpe->getuid);
+
        if (mpe->uid_attribute)
                FREE(mpe->uid_attribute);
 
@@ -306,6 +318,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
        merge_str(vendor);
        merge_str(product);
        merge_str(revision);
+       merge_str(getuid);
        merge_str(uid_attribute);
        merge_str(features);
        merge_str(hwhandler);
@@ -321,11 +334,21 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
        merge_num(no_path_retry);
        merge_num(minio);
        merge_num(minio_rq);
-       merge_num(pg_timeout);
        merge_num(flush_on_last_del);
        merge_num(fast_io_fail);
        merge_num(dev_loss);
        merge_num(user_friendly_names);
+       merge_num(retain_hwhandler);
+       merge_num(detect_prio);
+
+       /*
+        * Make sure features is consistent with
+        * no_path_retry
+        */
+       if (dst->no_path_retry == NO_PATH_RETRY_FAIL)
+               remove_feature(&dst->features, "queue_if_no_path");
+       else if (dst->no_path_retry != NO_PATH_RETRY_UNDEF)
+               add_feature(&dst->features, "queue_if_no_path");
 
        return 0;
 }
@@ -353,6 +376,9 @@ store_hwe (vector hwtable, struct hwentry * dhwe)
        if (dhwe->uid_attribute && !(hwe->uid_attribute = set_param_str(dhwe->uid_attribute)))
                goto out;
 
+       if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid)))
+               goto out;
+
        if (dhwe->features && !(hwe->features = set_param_str(dhwe->features)))
                goto out;
 
@@ -380,11 +406,12 @@ store_hwe (vector hwtable, struct hwentry * dhwe)
        hwe->no_path_retry = dhwe->no_path_retry;
        hwe->minio = dhwe->minio;
        hwe->minio_rq = dhwe->minio_rq;
-       hwe->pg_timeout = dhwe->pg_timeout;
        hwe->flush_on_last_del = dhwe->flush_on_last_del;
        hwe->fast_io_fail = dhwe->fast_io_fail;
        hwe->dev_loss = dhwe->dev_loss;
        hwe->user_friendly_names = dhwe->user_friendly_names;
+       hwe->retain_hwhandler = dhwe->retain_hwhandler;
+       hwe->detect_prio = dhwe->detect_prio;
 
        if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product)))
                goto out;
@@ -399,12 +426,13 @@ out:
        return 1;
 }
 
-static int
+static void
 factorize_hwtable (vector hw, int n)
 {
        struct hwentry *hwe1, *hwe2;
        int i, j;
 
+restart:
        vector_foreach_slot(hw, hwe1, i) {
                if (i == n)
                        break;
@@ -414,9 +442,21 @@ factorize_hwtable (vector hw, int n)
                                continue;
                        /* dup */
                        merge_hwe(hwe2, hwe1);
+                       if (hwe_strmatch(hwe2, hwe1) == 0) {
+                               vector_del_slot(hw, i);
+                               free_hwe(hwe1);
+                               n -= 1;
+                               /*
+                                * Play safe here; we have modified
+                                * the original vector so the outer
+                                * vector_foreach_slot() might
+                                * become confused.
+                                */
+                               goto restart;
+                       }
                }
        }
-       return 0;
+       return;
 }
 
 struct config *
@@ -434,9 +474,6 @@ free_config (struct config * conf)
        if (conf->dev)
                FREE(conf->dev);
 
-       if (conf->udev)
-               udev_unref(conf->udev);
-
        if (conf->multipath_dir)
                FREE(conf->multipath_dir);
 
@@ -446,6 +483,9 @@ free_config (struct config * conf)
        if (conf->uid_attribute)
                FREE(conf->uid_attribute);
 
+       if (conf->getuid)
+               FREE(conf->getuid);
+
        if (conf->features)
                FREE(conf->features);
 
@@ -473,10 +513,12 @@ free_config (struct config * conf)
 
        free_blacklist(conf->blist_devnode);
        free_blacklist(conf->blist_wwid);
+       free_blacklist(conf->blist_property);
        free_blacklist_device(conf->blist_device);
 
        free_blacklist(conf->elist_devnode);
        free_blacklist(conf->elist_wwid);
+       free_blacklist(conf->elist_property);
        free_blacklist_device(conf->elist_device);
 
        free_mptable(conf->mptable);
@@ -486,12 +528,12 @@ free_config (struct config * conf)
 }
 
 int
-load_config (char * file)
+load_config (char * file, struct udev *udev)
 {
        if (!conf)
                conf = alloc_config();
 
-       if (!conf)
+       if (!conf || !udev)
                return 1;
 
        /*
@@ -500,8 +542,7 @@ load_config (char * file)
        if (!conf->verbosity)
                conf->verbosity = DEFAULT_VERBOSITY;
 
-       conf->udev = udev_new();
-       conf->dmrq = dm_drv_get_rq();
+       conf->udev = udev;
        conf->dev_type = DEV_NONE;
        conf->minio = DEFAULT_MINIO;
        conf->minio_rq = DEFAULT_MINIO_RQ;
@@ -516,6 +557,10 @@ load_config (char * file)
        conf->reassign_maps = DEFAULT_REASSIGN_MAPS;
        conf->checkint = DEFAULT_CHECKINT;
        conf->max_checkint = MAX_CHECKINT(conf->checkint);
+       conf->pgfailback = DEFAULT_FAILBACK;
+       conf->fast_io_fail = DEFAULT_FAST_IO_FAIL;
+       conf->retain_hwhandler = DEFAULT_RETAIN_HWHANDLER;
+       conf->detect_prio = DEFAULT_DETECT_PRIO;
 
        /*
         * preload default hwtable
@@ -575,8 +620,12 @@ load_config (char * file)
                if (!conf->blist_device)
                        goto out;
        }
-       if (setup_default_blist(conf))
-               goto out;
+       if (conf->blist_property == NULL) {
+               conf->blist_property = vector_alloc();
+
+               if (!conf->blist_property)
+                       goto out;
+       }
 
        if (conf->elist_devnode == NULL) {
                conf->elist_devnode = vector_alloc();
@@ -598,6 +647,15 @@ load_config (char * file)
                        goto out;
        }
 
+       if (conf->elist_property == NULL) {
+               conf->elist_property = vector_alloc();
+
+               if (!conf->elist_property)
+                       goto out;
+       }
+       if (setup_default_blist(conf))
+               goto out;
+
        if (conf->mptable == NULL) {
                conf->mptable = vector_alloc();
                if (!conf->mptable)
index 16f530f..445525b 100644 (file)
@@ -11,6 +11,7 @@
  * In kernel, fast_io_fail == 0 means immediate failure on rport delete.
  * OTOH '0' means not-configured in various places in multipath-tools.
  */
+#define MP_FAST_IO_FAIL_UNSET (0)
 #define MP_FAST_IO_FAIL_OFF (-1)
 #define MP_FAST_IO_FAIL_ZERO (-2)
 
@@ -26,6 +27,7 @@ struct hwentry {
        char * product;
        char * revision;
        char * uid_attribute;
+       char * getuid;
        char * features;
        char * hwhandler;
        char * selector;
@@ -40,11 +42,12 @@ struct hwentry {
        int no_path_retry;
        int minio;
        int minio_rq;
-       int pg_timeout;
        int flush_on_last_del;
        int fast_io_fail;
        unsigned int dev_loss;
        int user_friendly_names;
+       int retain_hwhandler;
+       int detect_prio;
        char * bl_product;
 };
 
@@ -52,6 +55,7 @@ struct mpentry {
        char * wwid;
        char * alias;
        char * uid_attribute;
+       char * getuid;
        char * selector;
        char * features;
 
@@ -64,7 +68,6 @@ struct mpentry {
        int no_path_retry;
        int minio;
        int minio_rq;
-       int pg_timeout;
        int flush_on_last_del;
        int attribute_flags;
        int user_friendly_names;
@@ -74,7 +77,6 @@ struct mpentry {
 };
 
 struct config {
-       int dmrq;
        int verbosity;
        int dry_run;
        int list;
@@ -91,12 +93,14 @@ struct config {
        int no_path_retry;
        int user_friendly_names;
        int bindings_read_only;
-       int pg_timeout;
        int max_fds;
        int force_reload;
        int queue_without_daemon;
        int checker_timeout;
        int daemon;
+#ifdef USE_SYSTEMD
+       int watchdog;
+#endif
        int flush_on_last_del;
        int attribute_flags;
        int fast_io_fail;
@@ -108,12 +112,16 @@ struct config {
        mode_t mode;
        uint32_t cookie;
        int reassign_maps;
+       int retain_hwhandler;
+       int detect_prio;
+       unsigned int version[3];
 
        char * dev;
        struct udev * udev;
        char * multipath_dir;
        char * selector;
        char * uid_attribute;
+       char * getuid;
        char * features;
        char * hwhandler;
        char * bindings_file;
@@ -131,9 +139,11 @@ struct config {
        vector blist_devnode;
        vector blist_wwid;
        vector blist_device;
+       vector blist_property;
        vector elist_devnode;
        vector elist_wwid;
        vector elist_device;
+       vector elist_property;
 };
 
 struct config * conf;
@@ -152,7 +162,7 @@ void free_mptable (vector mptable);
 
 int store_hwe (vector hwtable, struct hwentry *);
 
-int load_config (char * file);
+int load_config (char * file, struct udev * udev);
 struct config * alloc_config (void);
 void free_config (struct config * conf);
 
index 1bb45a3..8c09791 100644 (file)
@@ -69,13 +69,13 @@ setup_map (struct multipath * mpp, char * params, int params_size)
        select_rr_weight(mpp);
        select_minio(mpp);
        select_no_path_retry(mpp);
-       select_pg_timeout(mpp);
        select_mode(mpp);
        select_uid(mpp);
        select_gid(mpp);
        select_fast_io_fail(mpp);
        select_dev_loss(mpp);
        select_reservation_key(mpp);
+       select_retain_hwhandler(mpp);
 
        sysfs_set_scsi_tmo(mpp);
        /*
@@ -209,7 +209,7 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
                        mpp->alias);
                return;
        }
-       if (!mpp->no_path_retry && !mpp->pg_timeout &&
+       if (!mpp->no_path_retry &&
            (strlen(cmpp->features) != strlen(mpp->features) ||
             strcmp(cmpp->features, mpp->features))) {
                mpp->action =  ACT_RELOAD;
@@ -217,8 +217,10 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
                        mpp->alias);
                return;
        }
-       if (!cmpp->selector || strncmp(cmpp->hwhandler, mpp->hwhandler,
-                   strlen(mpp->hwhandler))) {
+       if (mpp->retain_hwhandler != RETAIN_HWHANDLER_ON &&
+            (strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) ||
+            strncmp(cmpp->hwhandler, mpp->hwhandler,
+                   strlen(mpp->hwhandler)))) {
                mpp->action = ACT_RELOAD;
                condlog(3, "%s: set ACT_RELOAD (hwhandler change)",
                        mpp->alias);
@@ -381,24 +383,17 @@ domap (struct multipath * mpp, char * params)
 
                r = dm_addmap_create(mpp, params);
 
-               if (!r)
-                       r = dm_addmap_create_ro(mpp, params);
-
                lock_multipath(mpp, 0);
                break;
 
        case ACT_RELOAD:
                r = dm_addmap_reload(mpp, params);
-               if (!r)
-                       r = dm_addmap_reload_ro(mpp, params);
                if (r)
                        r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias);
                break;
 
        case ACT_RESIZE:
                r = dm_addmap_reload(mpp, params);
-               if (!r)
-                       r = dm_addmap_reload_ro(mpp, params);
                if (r)
                        r = dm_simplecmd_flush(DM_DEVICE_RESUME, mpp->alias, 1);
                break;
@@ -516,7 +511,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
                /* 1. if path has no unique id or wwid blacklisted */
                if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 ||
                    filter_path(conf, pp1) > 0) {
-                       orphan_path(pp1);
+                       orphan_path(pp1, "wwid blacklisted");
                        continue;
                }
 
@@ -526,7 +521,7 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
 
                /* 3. if path has disappeared */
                if (!pp1->size) {
-                       orphan_path(pp1);
+                       orphan_path(pp1, "invalid size");
                        continue;
                }
 
@@ -624,12 +619,6 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
                                                    "queue_if_no_path");
                        }
                }
-               if (mpp->pg_timeout != PGTIMEOUT_UNDEF) {
-                       if (mpp->pg_timeout == -PGTIMEOUT_NONE)
-                               dm_set_pg_timeout(mpp->alias,  0);
-                       else
-                               dm_set_pg_timeout(mpp->alias, mpp->pg_timeout);
-               }
 
                if (!conf->daemon && mpp->action != ACT_NOTHING)
                        print_multipath_topology(mpp, conf->verbosity);
@@ -673,21 +662,32 @@ coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_r
        return 0;
 }
 
-extern char *
-get_refwwid (char * dev, enum devtypes dev_type, vector pathvec)
+/*
+ * returns:
+ * 0 - success
+ * 1 - failure
+ * 2 - blacklist
+ */
+extern int
+get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid)
 {
+       int ret = 1;
        struct path * pp;
        char buff[FILE_NAME_SIZE];
        char * refwwid = NULL, tmpwwid[WWID_SIZE];
 
+       if (!wwid)
+               return 1;
+       *wwid = NULL;
+
        if (dev_type == DEV_NONE)
-               return NULL;
+               return 1;
 
        if (dev_type == DEV_DEVNODE) {
                if (basenamecpy(dev, buff, FILE_NAME_SIZE) == 0) {
                        condlog(1, "basename failed for '%s' (%s)",
                                dev, buff);
-                       return NULL;
+                       return 1;
                }
 
                pp = find_path_by_dev(pathvec, buff);
@@ -696,14 +696,16 @@ get_refwwid (char * dev, enum devtypes dev_type, vector pathvec)
 
                        if (!udevice) {
                                condlog(2, "%s: can't get udev device", buff);
-                               return NULL;
+                               return 1;
                        }
-                       pp = store_pathinfo(pathvec, conf->hwtable, udevice,
-                                           DI_SYSFS | DI_WWID);
+                       ret = store_pathinfo(pathvec, conf->hwtable, udevice,
+                                            DI_SYSFS | DI_WWID, &pp);
                        udev_device_unref(udevice);
                        if (!pp) {
-                               condlog(0, "%s can't store path info", buff);
-                               return NULL;
+                               if (ret == 1)
+                                       condlog(0, "%s can't store path info",
+                                               buff);
+                               return ret;
                        }
                }
                refwwid = pp->wwid;
@@ -718,14 +720,16 @@ get_refwwid (char * dev, enum devtypes dev_type, vector pathvec)
 
                        if (!udevice) {
                                condlog(2, "%s: can't get udev device", dev);
-                               return NULL;
+                               return 1;
                        }
-                       pp = store_pathinfo(pathvec, conf->hwtable, udevice,
-                                           DI_SYSFS | DI_WWID);
+                       ret = store_pathinfo(pathvec, conf->hwtable, udevice,
+                                            DI_SYSFS | DI_WWID, &pp);
                        udev_device_unref(udevice);
                        if (!pp) {
-                               condlog(0, "%s can't store path info", buff);
-                               return NULL;
+                               if (ret == 1)
+                                       condlog(0, "%s can't store path info",
+                                               buff);
+                               return ret;
                        }
                }
                refwwid = pp->wwid;
@@ -735,17 +739,17 @@ get_refwwid (char * dev, enum devtypes dev_type, vector pathvec)
 
                if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) {
                        refwwid = tmpwwid;
-                       goto out;
+                       goto check;
                }
 
                /*
                 * may be a binding
                 */
-               refwwid = get_user_friendly_wwid(dev,
-                                                conf->bindings_file);
-
-               if (refwwid)
-                       return refwwid;
+               if (get_user_friendly_wwid(dev, tmpwwid,
+                                          conf->bindings_file) == 0) {
+                       refwwid = tmpwwid;
+                       goto check;
+               }
 
                /*
                 * or may be an alias
@@ -757,22 +761,40 @@ get_refwwid (char * dev, enum devtypes dev_type, vector pathvec)
                 */
                if (!refwwid)
                        refwwid = dev;
+
+check:
+               if (refwwid && strlen(refwwid)) {
+                       if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
+                                       refwwid) > 0)
+                       return 2;
+               }
        }
 out:
-       if (refwwid && strlen(refwwid))
-               return STRDUP(refwwid);
+       if (refwwid && strlen(refwwid)) {
+               *wwid = STRDUP(refwwid);
+               return 0;
+       }
 
-       return NULL;
+       return 1;
 }
 
-extern int reload_map(struct vectors *vecs, struct multipath *mpp)
+extern int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh)
 {
-       char params[PARAMS_SIZE];
-       int r;
+       char params[PARAMS_SIZE] = {0};
+       struct path *pp;
+       int i, r;
 
        update_mpp_paths(mpp, vecs->pathvec);
-
-       params[0] = '\0';
+       if (refresh) {
+               vector_foreach_slot (mpp->paths, pp, i) {
+                       r = pathinfo(pp, conf->hwtable, DI_PRIO);
+                       if (r) {
+                               condlog(2, "%s: failed to refresh pathinfo",
+                                       mpp->alias);
+                               return 1;
+                       }
+               }
+       }
        if (setup_map(mpp, params, PARAMS_SIZE)) {
                condlog(0, "%s: failed to setup map", mpp->alias);
                return 1;
@@ -791,12 +813,6 @@ extern int reload_map(struct vectors *vecs, struct multipath *mpp)
                else
                        dm_queue_if_no_path(mpp->alias, 1);
        }
-       if (mpp->pg_timeout != PGTIMEOUT_UNDEF) {
-               if (mpp->pg_timeout == -PGTIMEOUT_NONE)
-                       dm_set_pg_timeout(mpp->alias,  0);
-               else
-                       dm_set_pg_timeout(mpp->alias, mpp->pg_timeout);
-       }
 
        return 0;
 }
index 6c1c493..650f080 100644 (file)
@@ -27,6 +27,6 @@ int setup_map (struct multipath * mpp, char * params, int params_size );
 int domap (struct multipath * mpp, char * params);
 int reinstate_paths (struct multipath *mpp);
 int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload);
-char * get_refwwid (char * dev, enum devtypes dev_type, vector pathvec);
-int reload_map(struct vectors *vecs, struct multipath *mpp);
+int get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid);
+int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh);
 
index d30517d..bad78a8 100644 (file)
@@ -21,15 +21,18 @@ void dlog (int sink, int prio, const char * fmt, ...)
        thres = (conf) ? conf->verbosity : 0;
 
        if (prio <= thres) {
-               if (!sink) {
-                       time_t t = time(NULL);
-                       struct tm *tb = localtime(&t);
-                       char buff[16];
+               if (sink < 1) {
+                       if (sink == 0) {
+                               time_t t = time(NULL);
+                               struct tm *tb = localtime(&t);
+                               char buff[16];
 
-                       strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb);
-                       buff[sizeof(buff)-1] = '\0';
+                               strftime(buff, sizeof(buff),
+                                        "%b %d %H:%M:%S", tb);
+                               buff[sizeof(buff)-1] = '\0';
 
-                       fprintf(stdout, "%s | ", buff);
+                               fprintf(stdout, "%s | ", buff);
+                       }
                        vfprintf(stdout, fmt, ap);
                }
                else
index 74b236f..b83d9fb 100644 (file)
@@ -1,7 +1,7 @@
 #define DEFAULT_UID_ATTRIBUTE  "ID_SERIAL"
 #define DEFAULT_UDEVDIR                "/dev"
 #define DEFAULT_MULTIPATHDIR   "/" LIB_STRING "/multipath"
-#define DEFAULT_SELECTOR       "round-robin 0"
+#define DEFAULT_SELECTOR       "service-time 0"
 #define DEFAULT_ALIAS_PREFIX   "mpath"
 #define DEFAULT_FEATURES       "0"
 #define DEFAULT_HWHANDLER      "0"
 #define DEFAULT_USER_FRIENDLY_NAMES    0
 #define DEFAULT_VERBOSITY      2
 #define DEFAULT_REASSIGN_MAPS  1
+#define DEFAULT_FAST_IO_FAIL   5
+#define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_OFF
+#define DEFAULT_DETECT_PRIO DETECT_PRIO_OFF
 
 #define DEFAULT_CHECKINT       5
 #define MAX_CHECKINT(a)                (a << 2)
 
 #define MAX_DEV_LOSS_TMO       0x7FFFFFFF
 #define DEFAULT_PIDFILE                "/var/run/multipathd.pid"
-#define DEFAULT_SOCKET         "/var/run/multipathd.sock"
+#define DEFAULT_SOCKET         "/org/kernel/linux/storage/multipathd"
 #define DEFAULT_CONFIGFILE     "/etc/multipath.conf"
 #define DEFAULT_BINDINGS_FILE  "/etc/multipath/bindings"
 #define DEFAULT_WWIDS_FILE     "/etc/multipath/wwids"
index de7d446..6eb2d96 100644 (file)
@@ -71,15 +71,17 @@ dm_write_log (int level, const char *file, int line, const char *f, ...)
                return;
 
        va_start(ap, f);
-       if (!logsink) {
-               time_t t = time(NULL);
-               struct tm *tb = localtime(&t);
-               char buff[16];
+       if (logsink < 1) {
+               if (logsink == 0) {
+                       time_t t = time(NULL);
+                       struct tm *tb = localtime(&t);
+                       char buff[16];
 
-               strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb);
-               buff[sizeof(buff)-1] = '\0';
+                       strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb);
+                       buff[sizeof(buff)-1] = '\0';
 
-               fprintf(stdout, "%s | ", buff);
+                       fprintf(stdout, "%s | ", buff);
+               }
                fprintf(stdout, "libdevmapper: %s(%i): ", file, line);
                vfprintf(stdout, f, ap);
                fprintf(stdout, "\n");
@@ -98,12 +100,6 @@ dm_init(void) {
        dm_log_init_verbose(conf ? conf->verbosity + 3 : 0);
 }
 
-#define VERSION_GE(v, minv) ( \
- (v[0] > minv[0]) || \
- ((v[0] == minv[0]) && (v[1] > minv[1])) || \
- ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2])) \
-)
-
 static int
 dm_lib_prereq (void)
 {
@@ -126,7 +122,7 @@ dm_lib_prereq (void)
        return 1;
 }
 
-static int
+int
 dm_drv_version (unsigned int * version, char * str)
 {
        int r = 2;
@@ -135,6 +131,10 @@ dm_drv_version (unsigned int * version, char * str)
        struct dm_versions *last_target;
        unsigned int *v;
 
+       version[0] = 0;
+       version[1] = 0;
+       version[2] = 0;
+
        if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
                return 1;
 
@@ -169,28 +169,6 @@ out:
        return r;
 }
 
-int
-dm_drv_get_rq (void)
-{
-       unsigned int minv_dmrq[3] = {1, 1, 0};
-       unsigned int version[3] = {0, 0, 0};
-        unsigned int * v = version;
-
-       if (dm_drv_version(v, TGT_MPATH)) {
-               /* in doubt return least capable */
-               return 0;
-       }
-
-       /* test request based multipath capability */
-       if VERSION_GE(v, minv_dmrq) {
-               condlog(3, "activate request-based multipathing mode "
-                          "(driver >= v%u.%u.%u)",
-                       minv_dmrq[0], minv_dmrq[1], minv_dmrq[2]);
-               return 1;
-       }
-       return 0;
-}
-
 static int
 dm_drv_prereq (void)
 {
@@ -243,7 +221,7 @@ dm_simplecmd (int task, const char *name, int no_flush, int need_sync) {
                dm_task_no_flush(dmt);          /* for DM_DEVICE_SUSPEND/RESUME */
 #endif
 
-       if (udev_wait_flag && !dm_task_set_cookie(dmt, &conf->cookie, 0))
+       if (udev_wait_flag && !dm_task_set_cookie(dmt, &conf->cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0))
                goto out;
        r = dm_task_run (dmt);
 
@@ -281,10 +259,10 @@ dm_addmap (int task, const char *target, struct multipath *mpp, char * params,
        if (ro)
                dm_task_set_ro(dmt);
 
-       if (use_uuid && mpp->wwid){
+       if (use_uuid && strlen(mpp->wwid) > 0){
                prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(mpp->wwid) + 1);
                if (!prefixed_uuid) {
-                       condlog(0, "cannot create prefixed uuid : %s\n",
+                       condlog(0, "cannot create prefixed uuid : %s",
                                strerror(errno));
                        goto addout;
                }
@@ -302,13 +280,13 @@ dm_addmap (int task, const char *target, struct multipath *mpp, char * params,
        if (mpp->attribute_flags & (1 << ATTR_GID) &&
            !dm_task_set_gid(dmt, mpp->gid))
                goto freeout;
-       condlog(4, "%s: addmap [0 %llu %s %s]\n", mpp->alias, mpp->size,
+       condlog(4, "%s: addmap [0 %llu %s %s]", mpp->alias, mpp->size,
                target, params);
 
        dm_task_no_open_count(dmt);
 
        if (task == DM_DEVICE_CREATE &&
-           !dm_task_set_cookie(dmt, &conf->cookie, 0))
+           !dm_task_set_cookie(dmt, &conf->cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0))
                goto freeout;
        r = dm_task_run (dmt);
 
@@ -322,42 +300,39 @@ dm_addmap (int task, const char *target, struct multipath *mpp, char * params,
        return r;
 }
 
-static int
-_dm_addmap_create (struct multipath *mpp, char * params, int ro) {
-       int r;
-       r = dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, 1, ro);
-       /*
-        * DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD.
-        * Failing the second part leaves an empty map. Clean it up.
-        */
-       if (!r && dm_map_present(mpp->alias)) {
-               condlog(3, "%s: failed to load map (a path might be in use)",
-                       mpp->alias);
-               dm_flush_map_nosync(mpp->alias);
+extern int
+dm_addmap_create (struct multipath *mpp, char * params) {
+       int ro;
+
+       for (ro = 0; ro <= 1; ro++) {
+               int err;
+
+               if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, 1, ro))
+                       return 1;
+               /*
+                * DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD.
+                * Failing the second part leaves an empty map. Clean it up.
+                */
+               err = errno;
+               if (dm_map_present(mpp->alias)) {
+                       condlog(3, "%s: failed to load map (a path might be in use)", mpp->alias);
+                       dm_flush_map_nosync(mpp->alias);
+               }
+               if (err != EROFS)
+                       break;
        }
-       return r;
+       return 0;
 }
 
 #define ADDMAP_RW 0
 #define ADDMAP_RO 1
 
 extern int
-dm_addmap_create (struct multipath *mpp, char *params) {
-       return _dm_addmap_create(mpp, params, ADDMAP_RW);
-}
-
-extern int
-dm_addmap_create_ro (struct multipath *mpp, char *params) {
-       return _dm_addmap_create(mpp, params, ADDMAP_RO);
-}
-
-extern int
 dm_addmap_reload (struct multipath *mpp, char *params) {
-       return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW);
-}
-
-extern int
-dm_addmap_reload_ro (struct multipath *mpp, char *params) {
+       if (dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RW))
+               return 1;
+       if (errno != EROFS)
+               return 0;
        return dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, 0, ADDMAP_RO);
 }
 
@@ -390,7 +365,7 @@ out:
 }
 
 extern int
-dm_get_map(char * name, unsigned long long * size, char * outparams)
+dm_get_map(const char * name, unsigned long long * size, char * outparams)
 {
        int r = 1;
        struct dm_task *dmt;
@@ -707,6 +682,43 @@ _dm_flush_map (const char * mapname, int need_sync)
 }
 
 extern int
+dm_suspend_and_flush_map (const char * mapname)
+{
+       int s = 0, queue_if_no_path = 0;
+       unsigned long long mapsize;
+       char params[PARAMS_SIZE] = {0};
+
+       if (!dm_map_present(mapname))
+               return 0;
+
+       if (dm_type(mapname, TGT_MPATH) <= 0)
+               return 0; /* nothing to do */
+
+       if (!dm_get_map(mapname, &mapsize, params)) {
+               if (strstr(params, "queue_if_no_path"))
+                       queue_if_no_path = 1;
+       }
+
+       if (queue_if_no_path)
+               s = dm_queue_if_no_path((char *)mapname, 0);
+       /* Leave queue_if_no_path alone if unset failed */
+       if (s)
+               queue_if_no_path = 0;
+       else
+               s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 0);
+
+       if (!dm_flush_map(mapname)) {
+               condlog(4, "multipath map %s removed", mapname);
+               return 0;
+       }
+       condlog(2, "failed to remove multipath map %s", mapname);
+       dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname);
+       if (queue_if_no_path)
+               s = dm_queue_if_no_path((char *)mapname, 1);
+       return 1;
+}
+
+extern int
 dm_flush_maps (void)
 {
        int r = 0;
@@ -729,7 +741,7 @@ dm_flush_maps (void)
                goto out;
 
        do {
-               r |= dm_flush_map(names->name);
+               r |= dm_suspend_and_flush_map(names->name);
                next = names->next;
                names = (void *) names + next;
        } while (next);
@@ -806,16 +818,6 @@ dm_queue_if_no_path(char *mapname, int enable)
        return dm_message(mapname, message);
 }
 
-int
-dm_set_pg_timeout(char *mapname, int timeout_val)
-{
-       char message[24];
-
-       if (snprintf(message, 24, "set_pg_timeout %d", timeout_val) >= 24)
-               return 1;
-       return dm_message(mapname, message);
-}
-
 static int
 dm_groupmsg (char * msg, char * mapname, int index)
 {
@@ -917,51 +919,6 @@ out:
        return r;
 }
 
-extern char *
-dm_get_name(char *uuid)
-{
-       struct dm_task *dmt;
-       struct dm_info info;
-       char *prefixed_uuid, *name = NULL;
-       const char *nametmp;
-
-       dmt = dm_task_create(DM_DEVICE_INFO);
-       if (!dmt)
-               return NULL;
-
-       prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(uuid) + 1);
-       if (!prefixed_uuid) {
-               condlog(0, "cannot create prefixed uuid : %s\n",
-                       strerror(errno));
-               goto freeout;
-       }
-       sprintf(prefixed_uuid, UUID_PREFIX "%s", uuid);
-       if (!dm_task_set_uuid(dmt, prefixed_uuid))
-               goto freeout;
-
-       if (!dm_task_run(dmt))
-               goto freeout;
-
-       if (!dm_task_get_info(dmt, &info) || !info.exists)
-               goto freeout;
-
-       nametmp = dm_task_get_name(dmt);
-       if (nametmp && strlen(nametmp)) {
-               name = MALLOC(strlen(nametmp) + 1);
-               if (name)
-                       strcpy(name, nametmp);
-       } else {
-               condlog(2, "%s: no device-mapper name found", uuid);
-       }
-
-freeout:
-       if (prefixed_uuid)
-               FREE(prefixed_uuid);
-       dm_task_destroy(dmt);
-
-       return name;
-}
-
 int
 dm_geteventnr (char *name)
 {
@@ -1268,7 +1225,7 @@ dm_rename (char * old, char * new)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_set_cookie(dmt, &conf->cookie, 0))
+       if (!dm_task_set_cookie(dmt, &conf->cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0))
                goto out;
        if (!dm_task_run(dmt))
                goto out;
@@ -1366,12 +1323,14 @@ int dm_reassign(const char *mapname)
        int r = 0, i;
 
        if (dm_dev_t(mapname, &dev_t[0], 32)) {
-               condlog(3, "%s: failed to get device number\n", mapname);
+               condlog(3, "%s: failed to get device number", mapname);
                return 1;
        }
 
-       if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
+       if (!(dmt = dm_task_create(DM_DEVICE_DEPS))) {
+               condlog(3, "%s: couldn't make dm task", mapname);
                return 0;
+       }
 
        if (!dm_task_set_name(dmt, mapname))
                goto out;
index 0c2e03f..58cd718 100644 (file)
@@ -8,25 +8,23 @@
 
 void dm_init(void);
 int dm_prereq (void);
-int dm_drv_get_rq (void);
+int dm_drv_version (unsigned int * version, char * str);
 int dm_simplecmd_flush (int, const char *, int);
 int dm_simplecmd_noflush (int, const char *);
 int dm_addmap_create (struct multipath *mpp, char *params);
-int dm_addmap_create_ro (struct multipath *mpp, char *params);
 int dm_addmap_reload (struct multipath *mpp, char *params);
-int dm_addmap_reload_ro (struct multipath *mpp, char *params);
 int dm_map_present (const char *);
-int dm_get_map(char *, unsigned long long *, char *);
+int dm_get_map(const char *, unsigned long long *, char *);
 int dm_get_status(char *, char *);
 int dm_type(const char *, char *);
 int _dm_flush_map (const char *, int);
 #define dm_flush_map(mapname) _dm_flush_map(mapname, 1)
 #define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0)
+int dm_suspend_and_flush_map(const char * mapname);
 int dm_flush_maps (void);
 int dm_fail_path(char * mapname, char * path);
 int dm_reinstate_path(char * mapname, char * path);
 int dm_queue_if_no_path(char *mapname, int enable);
-int dm_set_pg_timeout(char *mapname, int timeout_val);
 int dm_switchgroup(char * mapname, int index);
 int dm_enablegroup(char * mapname, int index);
 int dm_disablegroup(char * mapname, int index);
@@ -39,11 +37,16 @@ int dm_remove_partmaps (const char * mapname, int need_sync);
 int dm_get_uuid(char *name, char *uuid);
 int dm_get_info (char * mapname, struct dm_info ** dmi);
 int dm_rename (char * old, char * new);
-char * dm_get_name(char * uuid);
 int dm_reassign(const char * mapname);
 int dm_reassign_table(const char *name, char *old, char *new);
 int dm_setgeometry(struct multipath *mpp);
 void udev_wait(unsigned int c);
 void udev_set_sync_support(int c);
 
+#define VERSION_GE(v, minv) ( \
+ (v[0] > minv[0]) || \
+ ((v[0] == minv[0]) && (v[1] > minv[1])) || \
+ ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2])) \
+)
+
 #endif /* _DEVMAPPER_H */
index e0535a6..9db4725 100644 (file)
@@ -47,7 +47,7 @@ def_fast_io_fail_handler(vector strvec)
                conf->fast_io_fail = MP_FAST_IO_FAIL_OFF;
        else if (sscanf(buff, "%d", &conf->fast_io_fail) != 1 ||
                 conf->fast_io_fail < MP_FAST_IO_FAIL_ZERO)
-               conf->fast_io_fail = 0;
+               conf->fast_io_fail = MP_FAST_IO_FAIL_UNSET;
        else if (conf->fast_io_fail == 0)
                conf->fast_io_fail = MP_FAST_IO_FAIL_ZERO;
 
@@ -161,6 +161,17 @@ def_uid_attribute_handler(vector strvec)
 }
 
 static int
+def_getuid_callout_handler(vector strvec)
+{
+       conf->getuid = set_value(strvec);
+
+       if (!conf->getuid)
+               return 1;
+
+       return 0;
+}
+
+static int
 def_prio_handler(vector strvec)
 {
        conf->prio_name = set_value(strvec);
@@ -279,18 +290,27 @@ static int
 max_fds_handler(vector strvec)
 {
        char * buff;
-       int r = 0;
+       int r = 0, max_fds;
 
        buff = set_value(strvec);
 
        if (!buff)
                return 1;
 
+       r = get_sys_max_fds(&max_fds);
+       if (r) {
+               /* Assume safe limit */
+               max_fds = 4096;
+       }
        if (strlen(buff) == 3 &&
            !strcmp(buff, "max"))
-               r = get_sys_max_fds(&conf->max_fds);
+               conf->max_fds = max_fds;
        else
                conf->max_fds = atoi(buff);
+
+       if (conf->max_fds > max_fds)
+               conf->max_fds = max_fds;
+
        FREE(buff);
 
        return r;
@@ -438,14 +458,11 @@ def_queue_without_daemon(vector strvec)
        if (!buff)
                return 1;
 
-       if (!strncmp(buff, "off", 3) || !strncmp(buff, "no", 2) ||
-           !strncmp(buff, "0", 1))
-               conf->queue_without_daemon = QUE_NO_DAEMON_OFF;
-       else if (!strncmp(buff, "on", 2) || !strncmp(buff, "yes", 3) ||
+       if (!strncmp(buff, "on", 2) || !strncmp(buff, "yes", 3) ||
                 !strncmp(buff, "1", 1))
                conf->queue_without_daemon = QUE_NO_DAEMON_ON;
        else
-               conf->queue_without_daemon = QUE_NO_DAEMON_UNDEF;
+               conf->queue_without_daemon = QUE_NO_DAEMON_OFF;
 
        free(buff);
        return 0;
@@ -473,7 +490,6 @@ def_checker_timeout_handler(vector strvec)
 static int
 def_pg_timeout_handler(vector strvec)
 {
-       int pg_timeout;
        char * buff;
 
        buff = set_value(strvec);
@@ -481,16 +497,7 @@ def_pg_timeout_handler(vector strvec)
        if (!buff)
                return 1;
 
-       if (strlen(buff) == 4 && !strcmp(buff, "none"))
-               conf->pg_timeout = -PGTIMEOUT_NONE;
-       else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) {
-               if (pg_timeout == 0)
-                       conf->pg_timeout = -PGTIMEOUT_NONE;
-               else
-                       conf->pg_timeout = pg_timeout;
-       }
-       else
-               conf->pg_timeout = PGTIMEOUT_UNDEF;
+       /* Deprecated; device-mapper support has been removed */
 
        FREE(buff);
        return 0;
@@ -508,7 +515,7 @@ def_flush_on_last_del_handler(vector strvec)
        if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) ||
            (strlen(buff) == 1 && strcmp(buff, "0") == 0))
                conf->flush_on_last_del = FLUSH_DISABLED;
-       if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
+       else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
            (strlen(buff) == 1 && strcmp(buff, "1") == 0))
                conf->flush_on_last_del = FLUSH_ENABLED;
        else
@@ -629,6 +636,52 @@ wwids_file_handler(vector strvec)
        return 0;
 }
 
+static int
+def_retain_hwhandler_handler(vector strvec)
+{
+       char * buff;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
+           (strlen(buff) == 1 && !strcmp(buff, "0")))
+               conf->retain_hwhandler = RETAIN_HWHANDLER_OFF;
+       else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
+                (strlen(buff) == 1 && !strcmp(buff, "1")))
+               conf->retain_hwhandler = RETAIN_HWHANDLER_ON;
+       else
+               conf->retain_hwhandler = RETAIN_HWHANDLER_UNDEF;
+
+       FREE(buff);
+       return 0;
+}
+
+static int
+def_detect_prio_handler(vector strvec)
+{
+       char * buff;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
+           (strlen(buff) == 1 && !strcmp(buff, "0")))
+               conf->detect_prio = DETECT_PRIO_OFF;
+       else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
+                (strlen(buff) == 1 && !strcmp(buff, "1")))
+               conf->detect_prio = DETECT_PRIO_ON;
+       else
+               conf->detect_prio = DETECT_PRIO_UNDEF;
+
+       FREE(buff);
+       return 0;
+}
+
 /*
  * blacklist block handlers
  */
@@ -638,8 +691,10 @@ blacklist_handler(vector strvec)
        conf->blist_devnode = vector_alloc();
        conf->blist_wwid = vector_alloc();
        conf->blist_device = vector_alloc();
+       conf->blist_property = vector_alloc();
 
-       if (!conf->blist_devnode || !conf->blist_wwid || !conf->blist_device)
+       if (!conf->blist_devnode || !conf->blist_wwid ||
+           !conf->blist_device || !conf->blist_property)
                return 1;
 
        return 0;
@@ -651,8 +706,10 @@ blacklist_exceptions_handler(vector strvec)
        conf->elist_devnode = vector_alloc();
        conf->elist_wwid = vector_alloc();
        conf->elist_device = vector_alloc();
+       conf->elist_property = vector_alloc();
 
-       if (!conf->elist_devnode || !conf->elist_wwid || !conf->elist_device)
+       if (!conf->elist_devnode || !conf->elist_wwid ||
+           !conf->elist_device || !conf->elist_property)
                return 1;
 
        return 0;
@@ -711,6 +768,32 @@ ble_except_wwid_handler(vector strvec)
 }
 
 static int
+ble_property_handler(vector strvec)
+{
+       char * buff;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       return store_ble(conf->blist_property, buff, ORIGIN_CONFIG);
+}
+
+static int
+ble_except_property_handler(vector strvec)
+{
+       char * buff;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       return store_ble(conf->elist_property, buff, ORIGIN_CONFIG);
+}
+
+static int
 ble_device_handler(vector strvec)
 {
        return alloc_ble_device(conf->blist_device);
@@ -882,7 +965,7 @@ hw_fast_io_fail_handler(vector strvec)
                hwe->fast_io_fail = MP_FAST_IO_FAIL_OFF;
        else if (sscanf(buff, "%d", &hwe->fast_io_fail) != 1 ||
                 hwe->fast_io_fail < MP_FAST_IO_FAIL_ZERO)
-               hwe->fast_io_fail = 0;
+               hwe->fast_io_fail = MP_FAST_IO_FAIL_UNSET;
        else if (hwe->fast_io_fail == 0)
                hwe->fast_io_fail = MP_FAST_IO_FAIL_ZERO;
 
@@ -940,6 +1023,19 @@ hw_uid_attribute_handler(vector strvec)
 }
 
 static int
+hw_getuid_callout_handler(vector strvec)
+{
+       struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+
+       hwe->getuid = set_value(strvec);
+
+       if (!hwe->getuid)
+               return 1;
+
+       return 0;
+}
+
+static int
 hw_selector_handler(vector strvec)
 {
        struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
@@ -1171,28 +1267,14 @@ hw_minio_rq_handler(vector strvec)
 static int
 hw_pg_timeout_handler(vector strvec)
 {
-       int pg_timeout;
-       struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
        char *buff;
 
-       if (!hwe)
-               return 1;
-
        buff = set_value(strvec);
 
        if (!buff)
                return 1;
 
-       if (strlen(buff) == 4 && !strcmp(buff, "none"))
-               hwe->pg_timeout = -PGTIMEOUT_NONE;
-       else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) {
-               if (pg_timeout == 0)
-                       hwe->pg_timeout = -PGTIMEOUT_NONE;
-               else
-                       hwe->pg_timeout = pg_timeout;
-       }
-       else
-               hwe->pg_timeout = PGTIMEOUT_UNDEF;
+       /* Deprecated; device-mapper support has been removed */
 
        FREE(buff);
        return 0;
@@ -1214,7 +1296,7 @@ hw_flush_on_last_del_handler(vector strvec)
        if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) ||
            (strlen(buff) == 1 && strcmp(buff, "0") == 0))
                hwe->flush_on_last_del = FLUSH_DISABLED;
-       if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
+       else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
            (strlen(buff) == 1 && strcmp(buff, "1") == 0))
                hwe->flush_on_last_del = FLUSH_ENABLED;
        else
@@ -1250,6 +1332,60 @@ hw_names_handler(vector strvec)
        return 0;
 }
 
+static int
+hw_retain_hwhandler_handler(vector strvec)
+{
+       struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
+       char * buff;
+
+       if (!hwe)
+               return 1;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
+           (strlen(buff) == 1 && !strcmp(buff, "0")))
+               hwe->retain_hwhandler = RETAIN_HWHANDLER_OFF;
+       else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
+                (strlen(buff) == 1 && !strcmp(buff, "1")))
+               hwe->retain_hwhandler = RETAIN_HWHANDLER_ON;
+       else
+               hwe->user_friendly_names = RETAIN_HWHANDLER_UNDEF;
+
+       FREE(buff);
+       return 0;
+}
+
+static int
+hw_detect_prio_handler(vector strvec)
+{
+       struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
+       char * buff;
+
+       if (!hwe)
+               return 1;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
+           (strlen(buff) == 1 && !strcmp(buff, "0")))
+               hwe->detect_prio = DETECT_PRIO_OFF;
+       else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
+                (strlen(buff) == 1 && !strcmp(buff, "1")))
+               hwe->detect_prio = DETECT_PRIO_ON;
+       else
+               hwe->detect_prio = DETECT_PRIO_UNDEF;
+
+       FREE(buff);
+       return 0;
+}
+
 /*
  * multipaths block handlers
  */
@@ -1551,27 +1687,14 @@ mp_minio_rq_handler(vector strvec)
 static int
 mp_pg_timeout_handler(vector strvec)
 {
-       int pg_timeout;
-       struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
        char *buff;
 
-       if (!mpe)
-               return 1;
-
        buff = set_value(strvec);
 
        if (!buff)
                return 1;
-       if (strlen(buff) == 4 && !strcmp(buff, "none"))
-               mpe->pg_timeout = -PGTIMEOUT_NONE;
-       else if (sscanf(buff, "%d", &pg_timeout) == 1 && pg_timeout >= 0) {
-               if (pg_timeout == 0)
-                       mpe->pg_timeout = -PGTIMEOUT_NONE;
-               else
-                       mpe->pg_timeout = pg_timeout;
-       }
-       else
-               mpe->pg_timeout = PGTIMEOUT_UNDEF;
+
+       /* Deprecated; device-mapper support has been removed */
 
        FREE(buff);
        return 0;
@@ -1609,7 +1732,7 @@ mp_flush_on_last_del_handler(vector strvec)
        if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) ||
            (strlen(buff) == 1 && strcmp(buff, "0") == 0))
                mpe->flush_on_last_del = FLUSH_DISABLED;
-       if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
+       else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
            (strlen(buff) == 1 && strcmp(buff, "1") == 0))
                mpe->flush_on_last_del = FLUSH_ENABLED;
        else
@@ -1622,7 +1745,7 @@ mp_flush_on_last_del_handler(vector strvec)
 static int
 mp_prio_handler(vector strvec)
 {
-       struct mpentry * mpe = VECTOR_LAST_SLOT(conf->hwtable);
+       struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
 
        if (!mpe)
                return 1;
@@ -1757,7 +1880,7 @@ snprint_mp_path_grouping_policy (char * buff, int len, void * data)
                return 0;
        get_pgpolicy_name(str, POLICY_NAME_SIZE, mpe->pgpolicy);
 
-       return snprintf(buff, len, "%s", str);
+       return snprintf(buff, len, "\"%s\"", str);
 }
 
 static int
@@ -1776,18 +1899,17 @@ snprint_mp_failback (char * buff, int len, void * data)
 {
        struct mpentry * mpe = (struct mpentry *)data;
 
-       if (!mpe->pgfailback)
+       if (mpe->pgfailback == FAILBACK_UNDEF ||
+           mpe->pgfailback == DEFAULT_FAILBACK)
                return 0;
 
        switch(mpe->pgfailback) {
-       case  FAILBACK_UNDEF:
-               break;
        case -FAILBACK_MANUAL:
-               return snprintf(buff, len, "manual");
+               return snprintf(buff, len, "\"manual\"");
        case -FAILBACK_IMMEDIATE:
-               return snprintf(buff, len, "immediate");
+               return snprintf(buff, len, "\"immediate\"");
        case -FAILBACK_FOLLOWOVER:
-               return snprintf(buff, len, "followover");
+               return snprintf(buff, len, "\"followover\"");
        default:
                return snprintf(buff, len, "%i", mpe->pgfailback);
        }
@@ -1832,9 +1954,9 @@ snprint_mp_rr_weight (char * buff, int len, void * data)
        if (!mpe->rr_weight)
                return 0;
        if (mpe->rr_weight == RR_WEIGHT_PRIO)
-               return snprintf(buff, len, "priorities");
+               return snprintf(buff, len, "\"priorities\"");
        if (mpe->rr_weight == RR_WEIGHT_NONE)
-               return snprintf(buff, len, "uniform");
+               return snprintf(buff, len, "\"uniform\"");
 
        return 0;
 }
@@ -1851,9 +1973,9 @@ snprint_mp_no_path_retry (char * buff, int len, void * data)
        case NO_PATH_RETRY_UNDEF:
                break;
        case NO_PATH_RETRY_FAIL:
-               return snprintf(buff, len, "fail");
+               return snprintf(buff, len, "\"fail\"");
        case NO_PATH_RETRY_QUEUE:
-               return snprintf(buff, len, "queue");
+               return snprintf(buff, len, "\"queue\"");
        default:
                return snprintf(buff, len, "%i",
                                mpe->no_path_retry);
@@ -1886,16 +2008,6 @@ snprint_mp_rr_min_io_rq (char * buff, int len, void * data)
 static int
 snprint_mp_pg_timeout (char * buff, int len, void * data)
 {
-       struct mpentry * mpe = (struct mpentry *)data;
-
-       switch (mpe->pg_timeout) {
-       case PGTIMEOUT_UNDEF:
-               break;
-       case -PGTIMEOUT_NONE:
-               return snprintf(buff, len, "none");
-       default:
-               return snprintf(buff, len, "%i", mpe->pg_timeout);
-       }
        return 0;
 }
 
@@ -1910,7 +2022,7 @@ snprint_mp_features (char * buff, int len, void * data)
            !strcmp(mpe->features, conf->features))
                return 0;
 
-       return snprintf(buff, len, "%s", mpe->features);
+       return snprintf(buff, len, "\"%s\"", mpe->features);
 }
 
 static int
@@ -1920,9 +2032,9 @@ snprint_mp_flush_on_last_del (char * buff, int len, void * data)
 
        switch (mpe->flush_on_last_del) {
        case FLUSH_DISABLED:
-               return snprintf(buff, len, "no");
+               return snprintf(buff, len, "\"no\"");
        case FLUSH_ENABLED:
-               return snprintf(buff, len, "yes");
+               return snprintf(buff, len, "\"yes\"");
        }
        return 0;
 }
@@ -1935,7 +2047,7 @@ snprint_mp_prio(char * buff, int len, void * data)
        if (!mpe->prio_name)
                return 0;
 
-       return snprintf(buff, len, "%s", mpe->prio_name);
+       return snprintf(buff, len, "\"%s\"", mpe->prio_name);
 }
 
 static int
@@ -1946,14 +2058,28 @@ snprint_mp_prio_args(char * buff, int len, void * data)
        if (!mpe->prio_args)
                return 0;
 
-       return snprintf(buff, len, "%s", mpe->prio_args);
+       return snprintf(buff, len, "\"%s\"", mpe->prio_args);
 }
 
 static int
 snprint_mp_reservation_key (char * buff, int len, void * data)
 {
+       int i;
+       unsigned char *keyp;
+       uint64_t prkey = 0;
        struct mpentry * mpe = (struct mpentry *)data;
-       return snprintf(buff, len, "%s" , mpe->reservation_key);
+
+       if (!mpe->reservation_key)
+               return 0;
+       keyp = (unsigned char *)mpe->reservation_key;
+       for (i = 0; i < 8; i++) {
+               if (i > 0)
+                       prkey <<= 8;
+               prkey |= *keyp;
+               keyp++;
+       }
+
+       return snprintf(buff, len, "0x%" PRIx64, prkey);
 }
 
        static int
@@ -1964,21 +2090,21 @@ snprint_mp_user_friendly_names (char * buff, int len, void * data)
        if (mpe->user_friendly_names == USER_FRIENDLY_NAMES_UNDEF)
                return 0;
        else if (mpe->user_friendly_names == USER_FRIENDLY_NAMES_OFF)
-               return snprintf(buff, len, "no");
+               return snprintf(buff, len, "\"no\"");
        else
-               return snprintf(buff, len, "yes");
+               return snprintf(buff, len, "\"yes\"");
 }
 
 static int
 snprint_hw_fast_io_fail(char * buff, int len, void * data)
 {
        struct hwentry * hwe = (struct hwentry *)data;
-       if (!hwe->fast_io_fail)
+       if (hwe->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
                return 0;
        if (hwe->fast_io_fail == conf->fast_io_fail)
                return 0;
        if (hwe->fast_io_fail == MP_FAST_IO_FAIL_OFF)
-               return snprintf(buff, len, "off");
+               return snprintf(buff, len, "\"off\"");
        if (hwe->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
                return snprintf(buff, len, "0");
        return snprintf(buff, len, "%d", hwe->fast_io_fail);
@@ -1993,7 +2119,7 @@ snprint_hw_dev_loss(char * buff, int len, void * data)
        if (hwe->dev_loss == conf->dev_loss)
                return 0;
        if (hwe->dev_loss >= MAX_DEV_LOSS_TMO)
-               return snprintf(buff, len, "infinity");
+               return snprintf(buff, len, "\"infinity\"");
 
        return snprintf(buff, len, "%u", hwe->dev_loss);
 }
@@ -2054,6 +2180,17 @@ snprint_hw_uid_attribute (char * buff, int len, void * data)
 }
 
 static int
+snprint_hw_getuid_callout (char * buff, int len, void * data)
+{
+       struct hwentry * hwe = (struct hwentry *)data;
+
+       if (!hwe->getuid)
+               return 0;
+
+       return snprintf(buff, len, "\"%s\"", hwe->getuid);
+}
+
+static int
 snprint_hw_prio (char * buff, int len, void * data)
 {
        struct hwentry * hwe = (struct hwentry *)data;
@@ -2061,7 +2198,7 @@ snprint_hw_prio (char * buff, int len, void * data)
        if (!hwe->prio_name)
                return 0;
 
-       return snprintf(buff, len, "%s", hwe->prio_name);
+       return snprintf(buff, len, "\"%s\"", hwe->prio_name);
 }
 
 static int
@@ -2131,7 +2268,7 @@ snprint_hw_path_grouping_policy (char * buff, int len, void * data)
 
        get_pgpolicy_name(str, POLICY_NAME_SIZE, hwe->pgpolicy);
 
-       return snprintf(buff, len, "%s", str);
+       return snprintf(buff, len, "\"%s\"", str);
 }
 
 static int
@@ -2139,18 +2276,17 @@ snprint_hw_failback (char * buff, int len, void * data)
 {
        struct hwentry * hwe = (struct hwentry *)data;
 
-       if (!hwe->pgfailback)
+       if (hwe->pgfailback == FAILBACK_UNDEF ||
+           hwe->pgfailback == DEFAULT_FAILBACK)
                return 0;
 
        switch(hwe->pgfailback) {
-       case  FAILBACK_UNDEF:
-               break;
        case -FAILBACK_MANUAL:
-               return snprintf(buff, len, "manual");
+               return snprintf(buff, len, "\"manual\"");
        case -FAILBACK_IMMEDIATE:
-               return snprintf(buff, len, "immediate");
+               return snprintf(buff, len, "\"immediate\"");
        case -FAILBACK_FOLLOWOVER:
-               return snprintf(buff, len, "followover");
+               return snprintf(buff, len, "\"followover\"");
        default:
                return snprintf(buff, len, "%i", hwe->pgfailback);
        }
@@ -2165,9 +2301,9 @@ snprint_hw_rr_weight (char * buff, int len, void * data)
        if (!hwe->rr_weight)
                return 0;
        if (hwe->rr_weight == RR_WEIGHT_PRIO)
-               return snprintf(buff, len, "priorities");
+               return snprintf(buff, len, "\"priorities\"");
        if (hwe->rr_weight == RR_WEIGHT_NONE)
-               return snprintf(buff, len, "uniform");
+               return snprintf(buff, len, "\"uniform\"");
 
        return 0;
 }
@@ -2184,9 +2320,9 @@ snprint_hw_no_path_retry (char * buff, int len, void * data)
        case NO_PATH_RETRY_UNDEF:
                break;
        case NO_PATH_RETRY_FAIL:
-               return snprintf(buff, len, "fail");
+               return snprintf(buff, len, "\"fail\"");
        case NO_PATH_RETRY_QUEUE:
-               return snprintf(buff, len, "queue");
+               return snprintf(buff, len, "\"queue\"");
        default:
                return snprintf(buff, len, "%i",
                                hwe->no_path_retry);
@@ -2219,19 +2355,6 @@ snprint_hw_rr_min_io_rq (char * buff, int len, void * data)
 static int
 snprint_hw_pg_timeout (char * buff, int len, void * data)
 {
-       struct hwentry * hwe = (struct hwentry *)data;
-
-       if (!hwe->pg_timeout)
-               return 0;
-
-       switch (hwe->pg_timeout) {
-       case PGTIMEOUT_UNDEF:
-               break;
-       case -PGTIMEOUT_NONE:
-               return snprintf(buff, len, "none");
-       default:
-               return snprintf(buff, len, "%i", hwe->pg_timeout);
-       }
        return 0;
 }
 
@@ -2242,9 +2365,9 @@ snprint_hw_flush_on_last_del (char * buff, int len, void * data)
 
        switch (hwe->flush_on_last_del) {
        case FLUSH_DISABLED:
-               return snprintf(buff, len, "no");
+               return snprintf(buff, len, "\"no\"");
        case FLUSH_ENABLED:
-               return snprintf(buff, len, "yes");
+               return snprintf(buff, len, "\"yes\"");
        }
        return 0;
 }
@@ -2257,7 +2380,7 @@ snprint_hw_path_checker (char * buff, int len, void * data)
        if (!hwe->checker_name)
                return 0;
 
-       return snprintf(buff, len, "%s", hwe->checker_name);
+       return snprintf(buff, len, "\"%s\"", hwe->checker_name);
 }
 
        static int
@@ -2268,9 +2391,35 @@ snprint_hw_user_friendly_names (char * buff, int len, void * data)
        if (hwe->user_friendly_names == USER_FRIENDLY_NAMES_UNDEF)
                return 0;
        else if (hwe->user_friendly_names == USER_FRIENDLY_NAMES_OFF)
-               return snprintf(buff, len, "no");
+               return snprintf(buff, len, "\"no\"");
        else
-               return snprintf(buff, len, "yes");
+               return snprintf(buff, len, "\"yes\"");
+}
+
+static int
+snprint_hw_retain_hwhandler_handler(char * buff, int len, void * data)
+{
+       struct hwentry * hwe = (struct hwentry *)data;
+
+       if (hwe->retain_hwhandler == RETAIN_HWHANDLER_ON)
+               return snprintf(buff, len, "\"yes\"");
+       else if (hwe->retain_hwhandler == RETAIN_HWHANDLER_OFF)
+               return snprintf(buff, len, "\"no\"");
+       else
+               return 0;
+}
+
+static int
+snprint_detect_prio(char * buff, int len, void * data)
+{
+       struct hwentry * hwe = (struct hwentry *)data;
+
+       if (hwe->detect_prio == DETECT_PRIO_ON)
+               return snprintf(buff, len, "\"yes\"");
+       else if (hwe->detect_prio == DETECT_PRIO_OFF)
+               return snprintf(buff, len, "\"no\"");
+       else
+               return 0;
 }
 
 static int
@@ -2282,10 +2431,10 @@ snprint_def_polling_interval (char * buff, int len, void * data)
 static int
 snprint_def_fast_io_fail(char * buff, int len, void * data)
 {
-       if (!conf->fast_io_fail)
+       if (conf->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
                return 0;
        if (conf->fast_io_fail == MP_FAST_IO_FAIL_OFF)
-               return snprintf(buff, len, "off");
+               return snprintf(buff, len, "\"off\"");
        if (conf->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
                return snprintf(buff, len, "0");
        return snprintf(buff, len, "%d", conf->fast_io_fail);
@@ -2297,7 +2446,7 @@ snprint_def_dev_loss(char * buff, int len, void * data)
        if (!conf->dev_loss)
                return 0;
        if (conf->dev_loss >= MAX_DEV_LOSS_TMO)
-               return snprintf(buff, len, "infinity");
+               return snprintf(buff, len, "\"infinity\"");
        return snprintf(buff, len, "%u", conf->dev_loss);
 }
 
@@ -2310,17 +2459,13 @@ snprint_def_verbosity (char * buff, int len, void * data)
 static int
 snprint_def_max_polling_interval (char * buff, int len, void * data)
 {
-       if (conf->max_checkint == MAX_CHECKINT(conf->checkint))
-               return 0;
        return snprintf(buff, len, "%i", conf->max_checkint);
 }
 
 static int
 snprint_reassign_maps (char * buff, int len, void * data)
 {
-       if (conf->reassign_maps == DEFAULT_REASSIGN_MAPS)
-               return 0;
-       return snprintf(buff, len, "%s",
+       return snprintf(buff, len, "\"%s\"",
                        conf->reassign_maps?"yes":"no");
 }
 
@@ -2353,7 +2498,7 @@ snprint_def_path_grouping_policy (char * buff, int len, void * data)
 
        get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy);
 
-       return snprintf(buff, len, "%s", str);
+       return snprintf(buff, len, "\"%s\"", str);
 }
 
 static int
@@ -2366,12 +2511,21 @@ snprint_def_uid_attribute (char * buff, int len, void * data)
 }
 
 static int
+snprint_def_getuid_callout (char * buff, int len, void * data)
+{
+       if (!conf->getuid)
+               return 0;
+
+       return snprintf(buff, len, "\"%s\"", conf->getuid);
+}
+
+static int
 snprint_def_prio (char * buff, int len, void * data)
 {
        if (!conf->prio_name)
-               return snprintf(buff, len, "%s", DEFAULT_PRIO);
+               return snprintf(buff, len, "\"%s\"", DEFAULT_PRIO);
 
-       return snprintf(buff, len, "%s", conf->prio_name);
+       return snprintf(buff, len, "\"%s\"", conf->prio_name);
 }
 
 static int
@@ -2396,27 +2550,23 @@ static int
 snprint_def_path_checker (char * buff, int len, void * data)
 {
        if (!conf->checker_name)
-               return snprintf(buff, len, "%s", DEFAULT_CHECKER);
+               return snprintf(buff, len, "\"%s\"", DEFAULT_CHECKER);
 
-       return snprintf(buff, len, "%s", conf->checker_name);
+       return snprintf(buff, len, "\"%s\"", conf->checker_name);
 }
 
 static int
 snprint_def_failback (char * buff, int len, void * data)
 {
-       int pgfailback = conf->pgfailback;
-       if (!pgfailback)
-               pgfailback = DEFAULT_FAILBACK;
-
        switch(conf->pgfailback) {
        case  FAILBACK_UNDEF:
-               break;
+               return snprintf(buff, len, "\"undef\"");
        case -FAILBACK_MANUAL:
-               return snprintf(buff, len, "manual");
+               return snprintf(buff, len, "\"manual\"");
        case -FAILBACK_IMMEDIATE:
-               return snprintf(buff, len, "immediate");
+               return snprintf(buff, len, "\"immediate\"");
        case -FAILBACK_FOLLOWOVER:
-               return snprintf(buff, len, "followover");
+               return snprintf(buff, len, "\"followover\"");
        default:
                return snprintf(buff, len, "%i", conf->pgfailback);
        }
@@ -2444,10 +2594,16 @@ snprint_def_rr_min_io_rq (char * buff, int len, void * data)
 static int
 snprint_max_fds (char * buff, int len, void * data)
 {
+       int r = 0, max_fds;
+
        if (!conf->max_fds)
                return 0;
 
-       return snprintf(buff, len, "%d", conf->max_fds);
+       r = get_sys_max_fds(&max_fds);
+       if (!r && conf->max_fds >= max_fds)
+               return snprintf(buff, len, "\"max\"");
+       else
+               return snprintf(buff, len, "%d", conf->max_fds);
 }
 
 static int
@@ -2478,9 +2634,9 @@ static int
 snprint_def_rr_weight (char * buff, int len, void * data)
 {
        if (!conf->rr_weight || conf->rr_weight == RR_WEIGHT_NONE)
-               return snprintf(buff, len, "uniform");
+               return snprintf(buff, len, "\"uniform\"");
        if (conf->rr_weight == RR_WEIGHT_PRIO)
-               return snprintf(buff, len, "priorities");
+               return snprintf(buff, len, "\"priorities\"");
 
        return 0;
 }
@@ -2492,9 +2648,9 @@ snprint_def_no_path_retry (char * buff, int len, void * data)
        case NO_PATH_RETRY_UNDEF:
                break;
        case NO_PATH_RETRY_FAIL:
-               return snprintf(buff, len, "fail");
+               return snprintf(buff, len, "\"fail\"");
        case NO_PATH_RETRY_QUEUE:
-               return snprintf(buff, len, "queue");
+               return snprintf(buff, len, "\"queue\"");
        default:
                return snprintf(buff, len, "%i",
                                conf->no_path_retry);
@@ -2507,10 +2663,11 @@ snprint_def_queue_without_daemon (char * buff, int len, void * data)
 {
        switch (conf->queue_without_daemon) {
        case QUE_NO_DAEMON_OFF:
-               return snprintf(buff, len, "no");
+               return snprintf(buff, len, "\"no\"");
        case QUE_NO_DAEMON_ON:
-       case QUE_NO_DAEMON_UNDEF:
-               return snprintf(buff, len, "yes");
+               return snprintf(buff, len, "\"yes\"");
+       case QUE_NO_DAEMON_FORCE:
+               return snprintf(buff, len, "\"forced\"");
        }
        return 0;
 }
@@ -2527,13 +2684,6 @@ snprint_def_checker_timeout (char *buff, int len, void *data)
 static int
 snprint_def_pg_timeout (char * buff, int len, void * data)
 {
-       switch (conf->pg_timeout) {
-       case PGTIMEOUT_UNDEF:
-       case -PGTIMEOUT_NONE:
-               return snprintf(buff, len, "none");
-       default:
-               return snprintf(buff, len, "%i", conf->pg_timeout);
-       }
        return 0;
 }
 
@@ -2543,10 +2693,10 @@ snprint_def_flush_on_last_del (char * buff, int len, void * data)
        switch (conf->flush_on_last_del) {
        case FLUSH_UNDEF:
        case FLUSH_DISABLED:
-               return snprintf(buff, len, "no");
+               return snprintf(buff, len, "\"no\"");
        case FLUSH_ENABLED:
        case FLUSH_IN_PROGRESS:
-               return snprintf(buff, len, "yes");
+               return snprintf(buff, len, "\"yes\"");
        }
        return 0;
 }
@@ -2563,9 +2713,9 @@ static int
 snprint_def_user_friendly_names (char * buff, int len, void * data)
 {
        if (conf->user_friendly_names  == USER_FRIENDLY_NAMES_ON)
-               return snprintf(buff, len, "yes");
+               return snprintf(buff, len, "\"yes\"");
        else
-               return snprintf(buff, len, "no");
+               return snprintf(buff, len, "\"no\"");
 }
 
 static int
@@ -2581,7 +2731,7 @@ snprint_def_bindings_file (char * buff, int len, void * data)
 {
        if (conf->bindings_file == NULL)
                return 0;
-       return snprintf(buff, len, "%s", conf->bindings_file);
+       return snprintf(buff, len, "\"%s\"", conf->bindings_file);
 }
 
 static int
@@ -2595,7 +2745,38 @@ snprint_def_wwids_file (char * buff, int len, void * data)
 static int
 snprint_def_reservation_key(char * buff, int len, void * data)
 {
-       return snprintf(buff, len, "%s", conf->reservation_key);
+       int i;
+       unsigned char *keyp;
+       uint64_t prkey = 0;
+
+       if (!conf->reservation_key)
+               return 0;
+       keyp = (unsigned char *)conf->reservation_key;
+       for (i = 0; i < 8; i++) {
+               if (i > 0)
+                       prkey <<= 8;
+               prkey |= *keyp;
+               keyp++;
+       }
+       return snprintf(buff, len, "0x%" PRIx64, prkey);
+}
+
+static int
+snprint_def_retain_hwhandler_handler(char * buff, int len, void * data)
+{
+       if (conf->retain_hwhandler == RETAIN_HWHANDLER_ON)
+               return snprintf(buff, len, "yes");
+       else
+               return snprintf(buff, len, "no");
+}
+
+static int
+snprint_def_detect_prio(char * buff, int len, void * data)
+{
+       if (conf->detect_prio == DETECT_PRIO_ON)
+               return snprintf(buff, len, "yes");
+       else
+               return snprintf(buff, len, "no");
 }
 
 static int
@@ -2636,6 +2817,7 @@ init_keywords(void)
        install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
        install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_path_grouping_policy);
        install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute);
+       install_keyword("getuid_callout", &def_getuid_callout_handler, &snprint_def_getuid_callout);
        install_keyword("prio", &def_prio_handler, &snprint_def_prio);
        install_keyword("prio_args", &def_prio_args_handler, &snprint_def_prio_args);
        install_keyword("features", &def_features_handler, &snprint_def_features);
@@ -2662,15 +2844,19 @@ init_keywords(void)
        install_keyword("wwids_file", &wwids_file_handler, &snprint_def_wwids_file);
        install_keyword("log_checker_err", &def_log_checker_err_handler, &snprint_def_log_checker_err);
        install_keyword("reservation_key", &def_reservation_key_handler, &snprint_def_reservation_key);
+       install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler_handler);
+       install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio);
        __deprecated install_keyword("default_selector", &def_selector_handler, NULL);
        __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
        __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
+       __deprecated install_keyword("default_getuid_callout", &def_getuid_callout_handler, NULL);
        __deprecated install_keyword("default_features", &def_features_handler, NULL);
        __deprecated install_keyword("default_path_checker", &def_path_checker_handler, NULL);
 
        install_keyword_root("blacklist", &blacklist_handler);
        install_keyword_multi("devnode", &ble_devnode_handler, &snprint_ble_simple);
        install_keyword_multi("wwid", &ble_wwid_handler, &snprint_ble_simple);
+       install_keyword_multi("property", &ble_property_handler, &snprint_ble_simple);
        install_keyword_multi("device", &ble_device_handler, NULL);
        install_sublevel();
        install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor);
@@ -2679,6 +2865,7 @@ init_keywords(void)
        install_keyword_root("blacklist_exceptions", &blacklist_exceptions_handler);
        install_keyword_multi("devnode", &ble_except_devnode_handler, &snprint_ble_simple);
        install_keyword_multi("wwid", &ble_except_wwid_handler, &snprint_ble_simple);
+       install_keyword_multi("property", &ble_except_property_handler, &snprint_ble_simple);
        install_keyword_multi("device", &ble_except_device_handler, NULL);
        install_sublevel();
        install_keyword("vendor", &ble_except_vendor_handler, &snprint_bled_vendor);
@@ -2705,6 +2892,7 @@ init_keywords(void)
        install_keyword("product_blacklist", &bl_product_handler, &snprint_hw_bl_product);
        install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_path_grouping_policy);
        install_keyword("uid_attribute", &hw_uid_attribute_handler, &snprint_hw_uid_attribute);
+       install_keyword("getuid_callout", &hw_getuid_callout_handler, &snprint_hw_getuid_callout);
        install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector);
        install_keyword("path_checker", &hw_path_checker_handler, &snprint_hw_path_checker);
        install_keyword("checker", &hw_path_checker_handler, NULL);
@@ -2723,6 +2911,8 @@ init_keywords(void)
        install_keyword("fast_io_fail_tmo", &hw_fast_io_fail_handler, &snprint_hw_fast_io_fail);
        install_keyword("dev_loss_tmo", &hw_dev_loss_handler, &snprint_hw_dev_loss);
        install_keyword("user_friendly_names", &hw_names_handler, &snprint_hw_user_friendly_names);
+       install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler_handler);
+       install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_detect_prio);
        install_sublevel_end();
 
        install_keyword_root("multipaths", &multipaths_handler);
index 33e44b6..228ffd3 100644 (file)
@@ -20,6 +20,7 @@
 #include "structs.h"
 #include "config.h"
 #include "blacklist.h"
+#include "callout.h"
 #include "debug.h"
 #include "propsel.h"
 #include "sg_include.h"
 #include "prio.h"
 #include "defaults.h"
 
-struct path *
+int
 store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice,
-               int flag)
+               int flag, struct path **pp_ptr)
 {
+       int err = 1;
        struct path * pp;
        const char * devname;
 
+       if (pp_ptr)
+               *pp_ptr = NULL;
+
        devname = udev_device_get_sysname(udevice);
        if (!devname)
-               return NULL;
+               return 1;
 
        pp = alloc_path();
 
        if (!pp)
-               return NULL;
+               return 1;
 
        if(safe_sprintf(pp->dev, "%s", devname)) {
                condlog(0, "pp->dev too small");
                goto out;
        }
        pp->udev = udev_device_ref(udevice);
-       if (pathinfo(pp, hwtable, flag))
+       err = pathinfo(pp, hwtable,
+                      (conf->dry_run == 3)? flag : (flag | DI_BLACKLIST));
+       if (err)
                goto out;
 
-       if (store_path(pathvec, pp))
+       err = store_path(pathvec, pp);
+       if (err)
                goto out;
 
-       return pp;
 out:
-       free_path(pp);
-       return NULL;
+       if (err)
+               free_path(pp);
+       else if (pp_ptr)
+               *pp_ptr = pp;
+       return err;
 }
 
 static int
@@ -72,15 +82,20 @@ path_discover (vector pathvec, struct config * conf,
        if (!devname)
                return 0;
 
+       if (filter_property(conf, udevice) > 0)
+               return 0;
+
        if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
                           (char *)devname) > 0)
                return 0;
 
        pp = find_path_by_dev(pathvec, (char *)devname);
        if (!pp) {
-               pp = store_pathinfo(pathvec, conf->hwtable,
-                                   udevice, flag);
-               return (pp ? 0 : 1);
+               if (store_pathinfo(pathvec, conf->hwtable,
+                                  udevice, flag, NULL) != 1)
+                       return 0;
+               else
+                       return 1;
        }
        return pathinfo(pp, conf->hwtable, flag);
 }
@@ -103,6 +118,7 @@ path_discovery (vector pathvec, struct config * conf, int flag)
 
        udev_list_entry_foreach(entry,
                                udev_enumerate_get_list_entry(udev_iter)) {
+               const char *devtype;
                devpath = udev_list_entry_get_name(entry);
                condlog(4, "Discover device %s", devpath);
                udevice = udev_device_new_from_syspath(conf->udev, devpath);
@@ -111,8 +127,11 @@ path_discovery (vector pathvec, struct config * conf, int flag)
                        r++;
                        continue;
                }
-               if(!strncmp(udev_device_get_devtype(udevice), "disk", 4))
-                       r += path_discover(pathvec, conf, udevice, flag);
+               devtype = udev_device_get_devtype(udevice);
+               if(devtype && !strncmp(devtype, "disk", 4)) {
+                       r += path_discover(pathvec, conf,
+                                                  udevice, flag);
+               }
                udev_device_unref(udevice);
        }
        udev_enumerate_unref(udev_iter);
@@ -121,35 +140,35 @@ path_discovery (vector pathvec, struct config * conf, int flag)
 }
 
 #define declare_sysfs_get_str(fname)                                   \
-extern int                                                             \
+extern ssize_t                                                         \
 sysfs_get_##fname (struct udev_device * udev, char * buff, size_t len) \
 {                                                                      \
        const char * attr;                                              \
        const char * devname;                                           \
                                                                        \
+       if (!udev)                                                      \
+               return -ENOSYS;                                         \
+                                                                       \
        devname = udev_device_get_sysname(udev);                        \
                                                                        \
        attr = udev_device_get_sysattr_value(udev, #fname);             \
        if (!attr) {                                                    \
                condlog(3, "%s: attribute %s not found in sysfs",       \
                        devname, #fname);                               \
-               return 1;                                               \
+               return -ENXIO;                                          \
        }                                                               \
-       if (strlen(attr) > len) {                                       \
+       if (strchop(attr) > len) {                                      \
                condlog(3, "%s: overflow in attribute %s",              \
                        devname, #fname);                               \
-               return 2;                                               \
+               return -EINVAL;                                         \
        }                                                               \
-       strlcpy(buff, attr, len);                                       \
-       return 0;                                                       \
+       return strlcpy(buff, attr, len);                                \
 }
 
 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);
-declare_sysfs_get_str(state);
 declare_sysfs_get_str(dev);
 
 int
@@ -162,7 +181,7 @@ sysfs_get_timeout(struct path *pp, unsigned int *timeout)
        unsigned int t;
 
        if (!pp->udev || pp->bus != SYSFS_BUS_SCSI)
-               return 1;
+               return -ENOSYS;
 
        parent = pp->udev;
        while (parent) {
@@ -174,7 +193,7 @@ sysfs_get_timeout(struct path *pp, unsigned int *timeout)
        }
        if (!attr) {
                condlog(3, "%s: No timeout value in sysfs", pp->dev);
-               return 1;
+               return -ENXIO;
        }
 
        r = sscanf(attr, "%u\n", &t);
@@ -182,10 +201,10 @@ sysfs_get_timeout(struct path *pp, unsigned int *timeout)
        if (r != 1) {
                condlog(3, "%s: Cannot parse timeout attribute '%s'",
                        pp->dev, attr);
-               return 1;
+               return -EINVAL;
        }
 
-       *timeout = t * 1000;
+       *timeout = t;
 
        return 0;
 }
@@ -193,8 +212,9 @@ sysfs_get_timeout(struct path *pp, unsigned int *timeout)
 int
 sysfs_get_tgt_nodename (struct path *pp, char * node)
 {
-       const char *targetid, *value;
+       const char *tgtname, *value;
        struct udev_device *parent, *tgtdev;
+       int host, channel, tgtid = -1;
 
        parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_device");
        if (!parent)
@@ -202,45 +222,73 @@ sysfs_get_tgt_nodename (struct path *pp, char * node)
        /* Check for SAS */
        value = udev_device_get_sysattr_value(parent, "sas_address");
        if (value) {
-               strncpy(node, value, NODE_NAME_SIZE);
-               return 0;
+               tgtdev = udev_device_get_parent(parent);
+               while (tgtdev) {
+                       tgtname = udev_device_get_sysname(tgtdev);
+                       if (sscanf(tgtname, "end_device-%d:%d",
+                                  &host, &tgtid) == 2)
+                               break;
+                       tgtdev = udev_device_get_parent(tgtdev);
+                       tgtid = -1;
+               }
+               if (tgtid >= 0) {
+                       pp->sg_id.proto_id = SCSI_PROTOCOL_SAS;
+                       pp->sg_id.transport_id = tgtid;
+                       strncpy(node, value, NODE_NAME_SIZE);
+                       return 0;
+               }
        }
 
        parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_target");
        if (!parent)
                return 1;
-       tgtdev = udev_device_new_from_subsystem_sysname(conf->udev, "fc_transport", udev_device_get_sysname(parent));
-       /* Check if it's FibreChannel */
-       if (tgtdev) {
-               const char *value;
-
-               value = udev_device_get_sysattr_value(tgtdev, "node_name");
-               if (value) {
-                       strncpy(node, value, NODE_NAME_SIZE);
-                       udev_device_unref(tgtdev);
-                       return 0;
+       /* Check for FibreChannel */
+       tgtdev = udev_device_get_parent(parent);
+       value = udev_device_get_sysname(tgtdev);
+       if (sscanf(value, "rport-%d:%d-%d",
+                  &host, &channel, &tgtid) == 3) {
+               tgtdev = udev_device_new_from_subsystem_sysname(conf->udev,
+                               "fc_remote_ports", value);
+               if (tgtdev) {
+                       condlog(3, "SCSI target %d:%d:%d -> "
+                               "FC rport %d:%d-%d",
+                               pp->sg_id.host_no, pp->sg_id.channel,
+                               pp->sg_id.scsi_id, host, channel,
+                               tgtid);
+                       value = udev_device_get_sysattr_value(tgtdev,
+                                                             "node_name");
+                       if (value) {
+                               pp->sg_id.proto_id = SCSI_PROTOCOL_FCP;
+                               pp->sg_id.transport_id = tgtid;
+                               strncpy(node, value, NODE_NAME_SIZE);
+                               udev_device_unref(tgtdev);
+                               return 0;
+                       } else
+                               udev_device_unref(tgtdev);
                }
-               else
-                       udev_device_unref(tgtdev);
        }
 
        /* Check for iSCSI */
        parent = pp->udev;
-       targetid = NULL;
+       tgtname = NULL;
        while (parent) {
-               targetid = udev_device_get_sysname(parent);
-               if (!strncmp(targetid , "session", 6))
+               tgtname = udev_device_get_sysname(parent);
+               if (tgtname && sscanf(tgtname , "session%d", &tgtid) == 1)
                        break;
                parent = udev_device_get_parent(parent);
-               targetid = NULL;
+               tgtname = NULL;
+               tgtid = -1;
        }
-       if (parent) {
-               tgtdev = udev_device_new_from_subsystem_sysname(conf->udev, "iscsi_session", targetid);
+       if (parent && tgtname) {
+               tgtdev = udev_device_new_from_subsystem_sysname(conf->udev,
+                               "iscsi_session", tgtname);
                if (tgtdev) {
                        const char *value;
 
-                       value = udev_device_get_sysattr_value(tgtdev, "targetname");
+                       value = udev_device_get_sysattr_value(tgtdev, "tgtname");
                        if (value) {
+                               pp->sg_id.proto_id = SCSI_PROTOCOL_ISCSI;
+                               pp->sg_id.transport_id = tgtid;
                                strncpy(node, value, NODE_NAME_SIZE);
                                udev_device_unref(tgtdev);
                                return 0;
@@ -249,70 +297,202 @@ sysfs_get_tgt_nodename (struct path *pp, char * node)
                                udev_device_unref(tgtdev);
                }
        }
+       /* Check for libata */
+       parent = pp->udev;
+       tgtname = NULL;
+       while (parent) {
+               tgtname = udev_device_get_sysname(parent);
+               if (tgtname && sscanf(tgtname, "ata%d", &tgtid) == 1)
+                       break;
+               parent = udev_device_get_parent(parent);
+               tgtname = NULL;
+       }
+       if (tgtname) {
+               pp->sg_id.proto_id = SCSI_PROTOCOL_ATA;
+               pp->sg_id.transport_id = tgtid;
+               snprintf(node, NODE_NAME_SIZE, "ata-%d.00", tgtid);
+               return 0;
+       }
+       pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
        return 1;
 }
 
 static void
 sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
 {
-       struct udev_device *parent = pp->udev;
        struct udev_device *rport_dev = NULL;
-       char value[11];
-       const char *rport_id = NULL;
-
-       while (parent) {
-               rport_id = udev_device_get_sysname(parent);
-               if (!strncmp(rport_id, "rport-", 6))
-                       break;
-               parent = udev_device_get_parent(parent);
-               rport_id = NULL;
-       }
-       if (!parent || !rport_id) {
-               condlog(0, "%s: rport id not found", pp->dev);
-               return;
-       }
-       rport_dev = udev_device_new_from_subsystem_sysname(conf->udev, "fc_remote_ports", rport_id);
+       char value[16];
+       char rport_id[32];
+       unsigned long long tmo = 0;
+       int ret;
+
+       sprintf(rport_id, "rport-%d:%d-%d",
+               pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
+       rport_dev = udev_device_new_from_subsystem_sysname(conf->udev,
+                               "fc_remote_ports", rport_id);
        if (!rport_dev) {
-               condlog(3, "%s: No fc_remote_port device for '%s'", pp->dev,
+               condlog(1, "%s: No fc_remote_port device for '%s'", pp->dev,
                        rport_id);
                return;
        }
        condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
                pp->sg_id.channel, pp->sg_id.scsi_id, rport_id);
 
-       snprintf(value, 11, "%u", mpp->dev_loss);
-       if (mpp->dev_loss &&
-           sysfs_attr_set_value(rport_dev, "dev_loss_tmo", value, 11) <= 0) {
-               if ((!mpp->fast_io_fail ||
-                    mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
-                   && mpp->dev_loss > 600) {
-                       condlog(3, "%s: limiting dev_loss_tmo to 600, since "
-                               "fast_io_fail is not set", mpp->alias);
-                       snprintf(value, 11, "%u", 600);
-                       if (sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
-                                                value, 11) <= 0)
-                               condlog(0, "%s failed to set dev_loss_tmo",
-                                       mpp->alias);
+       /*
+        * This is tricky.
+        * dev_loss_tmo will be limited to 600 if fast_io_fail
+        * is _not_ set.
+        * fast_io_fail will be limited by the current dev_loss_tmo
+        * setting.
+        * So to get everything right we first need to increase
+        * dev_loss_tmo to the fast_io_fail setting (if present),
+        * then set fast_io_fail, and _then_ set dev_loss_tmo
+        * to the correct value.
+        */
+       memset(value, 0, 16);
+       if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
+           mpp->fast_io_fail != MP_FAST_IO_FAIL_ZERO &&
+           mpp->fast_io_fail != MP_FAST_IO_FAIL_OFF) {
+               /* Check if we need to temporarily increase dev_loss_tmo */
+               ret = sysfs_attr_get_value(rport_dev, "dev_loss_tmo",
+                                          value, 16);
+               if (ret <= 0) {
+                       condlog(0, "%s: failed to read dev_loss_tmo value, "
+                               "error %d", rport_id, -ret);
+                       goto out;
+               }
+               if (sscanf(value, "%llu\n", &tmo) != 1) {
+                       condlog(0, "%s: Cannot parse dev_loss_tmo "
+                               "attribute '%s'", rport_id, value);
                        goto out;
                }
+               if (mpp->fast_io_fail >= tmo) {
+                       snprintf(value, 16, "%u", mpp->fast_io_fail + 1);
+               }
+       } else if (mpp->dev_loss > 600) {
+               condlog(3, "%s: limiting dev_loss_tmo to 600, since "
+                       "fast_io_fail is not set", rport_id);
+               snprintf(value, 16, "%u", 600);
+       } else {
+               snprintf(value, 16, "%u", mpp->dev_loss);
        }
-       if (mpp->fast_io_fail){
+       if (strlen(value)) {
+               ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
+                                          value, strlen(value));
+               if (ret <= 0) {
+                       if (ret == -EBUSY)
+                               condlog(3, "%s: rport blocked", rport_id);
+                       else
+                               condlog(0, "%s: failed to set dev_loss_tmo to %s, error %d",
+                                       rport_id, value, -ret);
+                       goto out;
+               }
+       }
+       if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
                if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
                        sprintf(value, "off");
                else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
                        sprintf(value, "0");
                else
-                       snprintf(value, 11, "%u", mpp->fast_io_fail);
-               if (sysfs_attr_set_value(rport_dev, "fast_io_fail_tmo",
-                                        value, 11) <= 0) {
-                       condlog(0, "%s failed to set fast_io_fail_tmo",
-                               mpp->alias);
+                       snprintf(value, 16, "%u", mpp->fast_io_fail);
+               ret = sysfs_attr_set_value(rport_dev, "fast_io_fail_tmo",
+                                          value, strlen(value));
+               if (ret <= 0) {
+                       if (ret == -EBUSY)
+                               condlog(3, "%s: rport blocked", rport_id);
+                       else
+                               condlog(0, "%s: failed to set fast_io_fail_tmo to %s, error %d",
+                                       rport_id, value, -ret);
+               }
+       }
+       if (tmo > 0) {
+               snprintf(value, 16, "%u", mpp->dev_loss);
+               ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
+                                          value, strlen(value));
+               if (ret <= 0) {
+                       if (ret == -EBUSY)
+                               condlog(3, "%s: rport blocked", rport_id);
+                       else
+                               condlog(0, "%s: failed to set dev_loss_tmo to %s, error %d",
+                                       rport_id, value, -ret);
                }
        }
 out:
        udev_device_unref(rport_dev);
 }
 
+static void
+sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
+{
+       struct udev_device *session_dev = NULL;
+       char session_id[64];
+       char value[11];
+
+       sprintf(session_id, "session%d", pp->sg_id.transport_id);
+       session_dev = udev_device_new_from_subsystem_sysname(conf->udev,
+                               "iscsi_session", session_id);
+       if (!session_dev) {
+               condlog(1, "%s: No iscsi session for '%s'", pp->dev,
+                       session_id);
+               return;
+       }
+       condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
+               pp->sg_id.channel, pp->sg_id.scsi_id, session_id);
+
+       if (mpp->dev_loss) {
+               condlog(3, "%s: ignoring dev_loss_tmo on iSCSI", pp->dev);
+       }
+       if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
+               if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) {
+                       condlog(3, "%s: can't switch off fast_io_fail_tmo "
+                               "on iSCSI", pp->dev);
+               } else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) {
+                       condlog(3, "%s: can't set fast_io_fail_tmo to '0'"
+                               "on iSCSI", pp->dev);
+               } else {
+                       snprintf(value, 11, "%u", mpp->fast_io_fail);
+                       if (sysfs_attr_set_value(session_dev, "recovery_tmo",
+                                                value, 11) <= 0) {
+                               condlog(3, "%s: Failed to set recovery_tmo, "
+                                       " error %d", pp->dev, errno);
+                       }
+               }
+       }
+       udev_device_unref(session_dev);
+       return;
+}
+
+static void
+sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp)
+{
+       struct udev_device *sas_dev = NULL;
+       char end_dev_id[64];
+       char value[11];
+
+       sprintf(end_dev_id, "end_device-%d:%d",
+               pp->sg_id.host_no, pp->sg_id.transport_id);
+       sas_dev = udev_device_new_from_subsystem_sysname(conf->udev,
+                               "sas_end_device", end_dev_id);
+       if (!sas_dev) {
+               condlog(1, "%s: No SAS end device for '%s'", pp->dev,
+                       end_dev_id);
+               return;
+       }
+       condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
+               pp->sg_id.channel, pp->sg_id.scsi_id, end_dev_id);
+
+       if (mpp->dev_loss) {
+               snprintf(value, 11, "%u", mpp->dev_loss);
+               if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout",
+                                        value, 11) <= 0)
+                       condlog(3, "%s: failed to update "
+                               "I_T Nexus loss timeout, error %d",
+                               pp->dev, errno);
+       }
+       udev_device_unref(sas_dev);
+       return;
+}
+
 int
 sysfs_set_scsi_tmo (struct multipath *mpp)
 {
@@ -340,11 +520,16 @@ sysfs_set_scsi_tmo (struct multipath *mpp)
                        mpp->alias, mpp->fast_io_fail);
                mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF;
        }
-       if (!mpp->dev_loss && !mpp->fast_io_fail)
+       if (!mpp->dev_loss && mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
                return 0;
 
        vector_foreach_slot(mpp->paths, pp, i) {
-               sysfs_set_rport_tmo(mpp, pp);
+               if (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP)
+                       sysfs_set_rport_tmo(mpp, pp);
+               if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI)
+                       sysfs_set_session_tmo(mpp, pp);
+               if (pp->sg_id.proto_id == SCSI_PROTOCOL_SAS)
+                       sysfs_set_nexus_loss_tmo(mpp, pp);
        }
        return 0;
 }
@@ -449,7 +634,8 @@ scsi_sysfs_pathinfo (struct path * pp)
 
        parent = pp->udev;
        while (parent) {
-               if (!strncmp(udev_device_get_subsystem(parent), "scsi", 4)) {
+               const char *subsys = udev_device_get_subsystem(parent);
+               if (subsys && !strncmp(subsys, "scsi", 4)) {
                        attr_path = udev_device_get_sysname(parent);
                        if (!attr_path)
                                break;
@@ -465,17 +651,17 @@ scsi_sysfs_pathinfo (struct path * pp)
        if (!attr_path || pp->sg_id.host_no == -1)
                return 1;
 
-       if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE))
+       if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0)
                return 1;
 
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
 
-       if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE))
+       if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0)
                return 1;
 
        condlog(3, "%s: product = %s", pp->dev, pp->product_id);
 
-       if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE))
+       if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) <= 0)
                return 1;
 
        condlog(3, "%s: rev = %s", pp->dev, pp->rev);
@@ -515,7 +701,8 @@ ccw_sysfs_pathinfo (struct path * pp)
 
        parent = pp->udev;
        while (parent) {
-               if (!strncmp(udev_device_get_subsystem(parent), "ccw", 3))
+               const char *subsys = udev_device_get_subsystem(parent);
+               if (subsys && !strncmp(subsys, "ccw", 3))
                        break;
                parent = udev_device_get_parent(parent);
        }
@@ -526,7 +713,7 @@ ccw_sysfs_pathinfo (struct path * pp)
 
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
 
-       if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE))
+       if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE) <= 0)
                return 1;
 
        if (!strncmp(attr_buff, "3370", 4)) {
@@ -571,7 +758,8 @@ cciss_sysfs_pathinfo (struct path * pp)
 
        parent = pp->udev;
        while (parent) {
-               if (!strncmp(udev_device_get_subsystem(parent), "cciss", 5)) {
+               const char *subsys = udev_device_get_subsystem(parent);
+               if (subsys && !strncmp(subsys, "cciss", 5)) {
                        attr_path = udev_device_get_sysname(parent);
                        if (!attr_path)
                                break;
@@ -585,17 +773,17 @@ cciss_sysfs_pathinfo (struct path * pp)
        if (!attr_path || pp->sg_id.host_no == -1)
                return 1;
 
-       if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE))
+       if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0)
                return 1;
 
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
 
-       if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE))
+       if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0)
                return 1;
 
        condlog(3, "%s: product = %s", pp->dev, pp->product_id);
 
-       if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE))
+       if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) <= 0)
                return 1;
 
        condlog(3, "%s: rev = %s", pp->dev, pp->rev);
@@ -622,11 +810,14 @@ cciss_sysfs_pathinfo (struct path * pp)
 static int
 common_sysfs_pathinfo (struct path * pp)
 {
+       if (!pp)
+               return 1;
+
        if (!pp->udev) {
                condlog(4, "%s: udev not initialised", pp->dev);
                return 1;
        }
-       if (sysfs_get_dev(pp->udev, pp->dev_t, BLK_DEV_SIZE)) {
+       if (sysfs_get_dev(pp->udev, pp->dev_t, BLK_DEV_SIZE) <= 0) {
                condlog(3, "%s: no 'dev' attribute in sysfs", pp->dev);
                return 1;
        }
@@ -646,33 +837,44 @@ path_offline (struct path * pp)
 {
        struct udev_device * parent;
        char buff[SCSI_STATE_SIZE];
+       int err;
 
        if (pp->bus != SYSFS_BUS_SCSI)
                return PATH_UP;
 
        parent = pp->udev;
        while (parent) {
-               if (!strncmp(udev_device_get_subsystem(parent), "scsi", 4))
+               const char *subsys = udev_device_get_subsystem(parent);
+               if (subsys && !strncmp(subsys, "scsi", 4))
                        break;
                parent = udev_device_get_parent(parent);
        }
 
        if (!parent) {
                condlog(1, "%s: failed to get sysfs information", pp->dev);
-               return PATH_WILD;
+               return PATH_REMOVED;
+       }
+
+       memset(buff, 0x0, SCSI_STATE_SIZE);
+       err = sysfs_attr_get_value(parent, "state", buff, SCSI_STATE_SIZE);
+       if (err <= 0) {
+               if (err == -ENXIO)
+                       return PATH_REMOVED;
+               else
+                       return PATH_DOWN;
        }
 
-       if (sysfs_get_state(parent, buff, SCSI_STATE_SIZE))
-               return PATH_WILD;
 
        condlog(3, "%s: path state = %s", pp->dev, buff);
 
-       if (!strncmp(buff, "offline", 7)) {
+       if (!strncmp(buff, "offline", 7) ||
+           !strncmp(buff, "quiesce", 7) ||
+           !strncmp(buff, "transport-offline", 17)) {
                pp->offline = 1;
                return PATH_DOWN;
        }
        pp->offline = 0;
-       if (!strncmp(buff, "blocked", 7))
+       if (!strncmp(buff, "blocked", 7) || !strncmp(buff, "quiesce", 7))
                return PATH_PENDING;
        else if (!strncmp(buff, "running", 7))
                return PATH_UP;
@@ -680,7 +882,7 @@ path_offline (struct path * pp)
        return PATH_DOWN;
 }
 
-extern int
+int
 sysfs_pathinfo(struct path * pp)
 {
        if (common_sysfs_pathinfo(pp))
@@ -761,11 +963,13 @@ get_state (struct path * pp, int daemon)
        checker_clear_message(c);
        if (daemon)
                checker_set_async(c);
-       if (!conf->checker_timeout)
-               sysfs_get_timeout(pp, &(c->timeout));
+       if (!conf->checker_timeout &&
+           sysfs_get_timeout(pp, &(c->timeout)) <= 0)
+               c->timeout = DEF_TIMEOUT;
        state = checker_check(c);
        condlog(3, "%s: state = %s", pp->dev, checker_state_name(state));
-       if (state == PATH_DOWN && strlen(checker_message(c)))
+       if (state != PATH_UP && state != PATH_GHOST &&
+           strlen(checker_message(c)))
                condlog(3, "%s: checker msg is \"%s\"",
                        pp->dev, checker_message(c));
        return state;
@@ -777,21 +981,25 @@ get_prio (struct path * pp)
        if (!pp)
                return 0;
 
-       if (!pp->prio) {
+       struct prio * p = &pp->prio;
+
+       if (!prio_selected(p)) {
+               select_detect_prio(pp);
                select_prio(pp);
-               if (!pp->prio) {
+               if (!prio_selected(p)) {
                        condlog(3, "%s: no prio selected", pp->dev);
+                       pp->priority = PRIO_UNDEF;
                        return 1;
                }
        }
-       pp->priority = prio_getprio(pp->prio, pp);
+       pp->priority = prio_getprio(p, pp);
        if (pp->priority < 0) {
-               condlog(3, "%s: %s prio error", pp->dev, prio_name(pp->prio));
+               condlog(3, "%s: %s prio error", pp->dev, prio_name(p));
                pp->priority = PRIO_UNDEF;
                return 1;
        }
        condlog(3, "%s: %s prio = %u",
-               pp->dev, prio_name(pp->prio), pp->priority);
+               pp->dev, prio_name(p), pp->priority);
        return 0;
 }
 
@@ -799,9 +1007,9 @@ static int
 get_uid (struct path * pp)
 {
        char *c;
-       const char *value;
+       const char *origin;
 
-       if (!pp->uid_attribute)
+       if (!pp->uid_attribute && !pp->getuid)
                select_getuid(pp);
 
        if (!pp->udev) {
@@ -810,23 +1018,41 @@ get_uid (struct path * pp)
        }
 
        memset(pp->wwid, 0, WWID_SIZE);
-       value = udev_device_get_property_value(pp->udev, pp->uid_attribute);
-       if ((!value || strlen(value) == 0) && conf->dry_run == 2)
-               value = getenv(pp->uid_attribute);
-       if (value && strlen(value)) {
-               size_t len = WWID_SIZE;
-
-               if (strlen(value) + 1 > WWID_SIZE) {
-                       condlog(0, "%s: wwid overflow", pp->dev);
-               } else {
-                       len = strlen(value);
+       if (pp->getuid) {
+               char buff[CALLOUT_MAX_SIZE];
+
+               /* Use 'getuid' callout, deprecated */
+               condlog(1, "%s: using deprecated getuid callout", pp->dev);
+               if (apply_format(pp->getuid, &buff[0], pp)) {
+                       condlog(0, "error formatting uid callout command");
+                       memset(pp->wwid, 0, WWID_SIZE);
+               } else if (execute_program(buff, pp->wwid, WWID_SIZE)) {
+                       condlog(3, "error calling out %s", buff);
+                       memset(pp->wwid, 0, WWID_SIZE);
                }
-               strncpy(pp->wwid, value, len);
+               origin = "callout";
        } else {
-               condlog(3, "%s: no %s attribute", pp->dev,
-                       pp->uid_attribute);
-       }
+               const char *value;
 
+               value = udev_device_get_property_value(pp->udev,
+                                                      pp->uid_attribute);
+               if ((!value || strlen(value) == 0) && conf->dry_run == 2)
+                       value = getenv(pp->uid_attribute);
+               if (value && strlen(value)) {
+                       size_t len = WWID_SIZE;
+
+                       if (strlen(value) + 1 > WWID_SIZE) {
+                               condlog(0, "%s: wwid overflow", pp->dev);
+                       } else {
+                               len = strlen(value);
+                       }
+                       strncpy(pp->wwid, value, len);
+               } else {
+                       condlog(3, "%s: no %s attribute", pp->dev,
+                               pp->uid_attribute);
+               }
+               origin = "udev";
+       }
        /* Strip any trailing blanks */
        c = strchr(pp->wwid, '\0');
        c--;
@@ -834,8 +1060,8 @@ get_uid (struct path * pp)
                *c = '\0';
                c--;
        }
-       condlog(3, "%s: uid = %s (udev)", pp->dev,
-               *pp->wwid == '\0' ? "<empty>" : pp->wwid);
+       condlog(3, "%s: uid = %s (%s)", pp->dev,
+               *pp->wwid == '\0' ? "<empty>" : pp->wwid, origin);
        return 0;
 }
 
@@ -844,6 +1070,9 @@ pathinfo (struct path *pp, vector hwtable, int mask)
 {
        int path_state;
 
+       if (!pp)
+               return 1;
+
        condlog(3, "%s: mask = 0x%x", pp->dev, mask);
 
        /*
@@ -852,13 +1081,22 @@ pathinfo (struct path *pp, vector hwtable, int mask)
        if (mask & DI_SYSFS && sysfs_pathinfo(pp))
                return 1;
 
+       if (mask & DI_BLACKLIST && mask & DI_SYSFS) {
+               if (filter_device(conf->blist_device, conf->elist_device,
+                                 pp->vendor_id, pp->product_id) > 0) {
+                       return 2;
+               }
+       }
+
        path_state = path_offline(pp);
+       if (path_state == PATH_REMOVED)
+               goto blank;
 
        /*
         * fetch info not available through sysfs
         */
        if (pp->fd < 0)
-               pp->fd = open(udev_device_get_devnode(pp->udev), O_RDWR);
+               pp->fd = open(udev_device_get_devnode(pp->udev), O_RDONLY);
 
        if (pp->fd < 0) {
                condlog(4, "Couldn't open node for %s: %s",
@@ -883,9 +1121,23 @@ pathinfo (struct path *pp, vector hwtable, int mask)
                        if (pp->state == PATH_UNCHECKED ||
                            pp->state == PATH_WILD)
                                goto blank;
+                       if (pp->state == PATH_TIMEOUT)
+                               pp->state = PATH_DOWN;
                } else {
                        condlog(3, "%s: path inaccessible", pp->dev);
                        pp->chkrstate = pp->state = path_state;
+                       if (path_state == PATH_PENDING ||
+                           path_state == PATH_DOWN)
+                               pp->priority = 0;
+               }
+       }
+
+       if ((mask & DI_WWID) && !strlen(pp->wwid))
+               get_uid(pp);
+       if (mask & DI_BLACKLIST && mask & DI_WWID) {
+               if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
+                               pp->wwid) > 0) {
+                       return 2;
                }
        }
 
@@ -898,14 +1150,9 @@ pathinfo (struct path *pp, vector hwtable, int mask)
                        if (!strlen(pp->wwid))
                                get_uid(pp);
                        get_prio(pp);
-               } else {
-                       pp->priority = PRIO_UNDEF;
                }
        }
 
-       if (path_state == PATH_UP && (mask & DI_WWID) && !strlen(pp->wwid))
-               get_uid(pp);
-
        return 0;
 
 blank:
index 17108c7..3d2d0ce 100644 (file)
@@ -14,7 +14,7 @@
 #endif
 
 #ifndef DEF_TIMEOUT
-#define DEF_TIMEOUT    300000
+#define DEF_TIMEOUT    30
 #endif
 
 /*
 
 struct config;
 
-int sysfs_get_dev (struct udev_device *udev, char * buff, size_t len);
+ssize_t sysfs_get_dev (struct udev_device *udev, char * buff, size_t len);
 int path_discovery (vector pathvec, struct config * conf, int flag);
 
 int do_tur (char *);
 int path_offline (struct path *);
 int get_state (struct path * pp, int daemon);
 int pathinfo (struct path *, vector hwtable, int mask);
-struct path * store_pathinfo (vector pathvec, vector hwtable,
-                             struct udev_device *udevice, int flag);
+int store_pathinfo (vector pathvec, vector hwtable,
+                   struct udev_device *udevice, int flag,
+                   struct path **pp_ptr);
 int sysfs_set_scsi_tmo (struct multipath *mpp);
 int sysfs_get_timeout(struct path *pp, unsigned int *timeout);
 
@@ -46,14 +47,16 @@ enum discovery_mode {
        __DI_SERIAL,
        __DI_CHECKER,
        __DI_PRIO,
-       __DI_WWID
+       __DI_WWID,
+       __DI_BLACKLIST,
 };
 
 #define DI_SYSFS       (1 << __DI_SYSFS)
 #define DI_SERIAL      (1 << __DI_SERIAL)
 #define DI_CHECKER     (1 << __DI_CHECKER)
 #define DI_PRIO                (1 << __DI_PRIO)
-#define DI_WWID        (1 << __DI_WWID)
+#define DI_WWID                (1 << __DI_WWID)
+#define DI_BLACKLIST   (1 << __DI_BLACKLIST)
 
 #define DI_ALL         (DI_SYSFS  | DI_SERIAL | DI_CHECKER | DI_PRIO | \
                         DI_WWID)
index 5848ec5..2562ba1 100644 (file)
@@ -56,6 +56,7 @@ assemble_map (struct multipath * mp, char * params, int len)
        int nr_priority_groups, initial_pg_nr;
        char * p, * f;
        char no_path_retry[] = "queue_if_no_path";
+       char retain_hwhandler[] = "retain_attached_hw_handler";
        struct pathgroup * pgp;
        struct path * pp;
 
@@ -81,6 +82,8 @@ assemble_map (struct multipath * mp, char * params, int len)
        } else {
                add_feature(&f, no_path_retry);
        }
+       if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON)
+               add_feature(&f, retain_hwhandler);
 
        shift = snprintf(p, freechar, "%s %s %i %i",
                         f, mp->hwhandler,
@@ -100,7 +103,7 @@ assemble_map (struct multipath * mp, char * params, int len)
                shift = snprintf(p, freechar, " %s %i 1", mp->selector,
                                 VECTOR_SIZE(pgp->paths));
                if (shift >= freechar) {
-                       condlog(0, "%s: params too small\n", mp->alias);
+                       condlog(0, "%s: params too small", mp->alias);
                        return 1;
                }
                p += shift;
@@ -113,7 +116,7 @@ assemble_map (struct multipath * mp, char * params, int len)
                            && pp->priority > 0)
                                tmp_minio = minio * pp->priority;
                        if (!strlen(pp->dev_t) ) {
-                               condlog(0, "dev_t not set for '%s'\n", pp->dev);
+                               condlog(0, "dev_t not set for '%s'", pp->dev);
                                return 1;
                        }
                        shift = snprintf(p, freechar, " %s %d",
@@ -232,13 +235,16 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
        num_pg = atoi(word);
        FREE(word);
 
-       if (num_pg > 0 && !mpp->pg) {
-               mpp->pg = vector_alloc();
-
-               if (!mpp->pg)
-                       return 1;
-       } else
+       if (num_pg > 0) {
+               if (!mpp->pg) {
+                       mpp->pg = vector_alloc();
+                       if (!mpp->pg)
+                               return 1;
+               }
+       } else {
+               free_pgvec(mpp->pg, KEEP_PATHS);
                mpp->pg = NULL;
+       }
 
        /*
         * first pg to try
@@ -273,7 +279,6 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                        num_pg_args = atoi(word);
 
                        if (merge_words(&mpp->selector, word, 1)) {
-                               FREE(word);
                                goto out1;
                        }
                        FREE(word);
index 5ee46dc..74cde64 100644 (file)
@@ -98,7 +98,7 @@ lock_file(int fd, char *file_name)
        sigaddset(&set, SIGALRM);
 
        sigaction(SIGALRM, &act, &oldact);
-       sigprocmask(SIG_UNBLOCK, &set, &oldset);
+       pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
 
        alarm(FILE_TIMEOUT);
        err = fcntl(fd, F_SETLKW, &lock);
@@ -112,7 +112,7 @@ lock_file(int fd, char *file_name)
                        condlog(0, "%s is locked. Giving up.", file_name);
        }
 
-       sigprocmask(SIG_SETMASK, &oldset, NULL);
+       pthread_sigmask(SIG_SETMASK, &oldset, NULL);
        sigaction(SIGALRM, &oldact, NULL);
        return err;
 }
index 668ccec..bb73a40 100644 (file)
@@ -28,13 +28,10 @@ static struct hwentry default_hw[] = {
                .product       = "Compellent Vol",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -50,13 +47,10 @@ static struct hwentry default_hw[] = {
                .product       = "Xserve RAID ",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DEFAULT_CHECKER,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -72,13 +66,10 @@ static struct hwentry default_hw[] = {
                .product       = "VV",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DEFAULT_CHECKER,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -88,13 +79,10 @@ static struct hwentry default_hw[] = {
                .product       = "HSG80",
                .features      = "1 queue_if_no_path",
                .hwhandler     = "1 hp_sw",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = HP_SW,
                .prio_name     = PRIO_HP_SW,
                .prio_args     = NULL,
@@ -104,13 +92,10 @@ static struct hwentry default_hw[] = {
                .product       = "A6189A",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 12,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DIRECTIO,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -121,7 +106,6 @@ static struct hwentry default_hw[] = {
                .product       = "(MSA|HSV)1.0.*",
                .features      = "1 queue_if_no_path",
                .hwhandler     = "1 hp_sw",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -137,7 +121,6 @@ static struct hwentry default_hw[] = {
                .product       = "MSA VOLUME",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -150,10 +133,9 @@ static struct hwentry default_hw[] = {
        {
                /* EVA 3000/5000 with new firmware, EVA 4000/6000/8000 */
                .vendor        = "(COMPAQ|HP)",
-               .product       = "HSV1[01]1|HSV2[01]0|HSV300|HSV4[05]0",
+               .product       = "HSV1[01]1|HSV2[01]0|HSV3[046]0|HSV4[05]0",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -169,7 +151,6 @@ static struct hwentry default_hw[] = {
                .product       = "MSA2[02]12fc|MSA2012i",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -185,7 +166,6 @@ static struct hwentry default_hw[] = {
                .product       = "MSA2012sa|MSA23(12|24)(fc|i|sa)|MSA2000s VOLUME",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -202,7 +182,6 @@ static struct hwentry default_hw[] = {
                .product       = "HSVX700",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 alua",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -219,13 +198,10 @@ static struct hwentry default_hw[] = {
                .product       = "LOGICAL VOLUME.*",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 12,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -236,13 +212,11 @@ static struct hwentry default_hw[] = {
                .product       = "P2000 G3 FC|P2000G3 FC/iSCSI|P2000 G3 SAS|P2000 G3 iSCSI",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 18,
                .minio         = 100,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_ALUA,
                .prio_args     = NULL,
@@ -258,13 +232,10 @@ static struct hwentry default_hw[] = {
                .product       = "SAN DataDirector",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DIRECTIO,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -280,13 +251,10 @@ static struct hwentry default_hw[] = {
                .product       = "SYMMETRIX",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 6,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -297,13 +265,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "LUNZ",
                .features      = "1 queue_if_no_path",
                .hwhandler     = "1 emc",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = (300 / DEFAULT_CHECKINT),
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = EMC_CLARIION,
                .prio_name     = PRIO_EMC,
                .prio_args     = NULL,
@@ -314,13 +279,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "LUNZ",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 5,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -336,13 +298,10 @@ static struct hwentry default_hw[] = {
                .product       = "CentricStor",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_SERIAL,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DIRECTIO,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -352,12 +311,10 @@ static struct hwentry default_hw[] = {
                .product       = "ETERNUS_DX(L|400|8000)",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 10,
-               .minio         = DEFAULT_MINIO,
                .checker_name  = TUR,
                .prio_name     = PRIO_ALUA,
                .prio_args     = NULL,
@@ -373,13 +330,10 @@ static struct hwentry default_hw[] = {
                .product       = "OPEN-.*",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -389,13 +343,10 @@ static struct hwentry default_hw[] = {
                .product       = "DF.*",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_HDS,
                .prio_args     = NULL,
@@ -411,13 +362,10 @@ static struct hwentry default_hw[] = {
                .product       = "ProFibre 4000R",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DIRECTIO,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -429,13 +377,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = "1 queue_if_no_path",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 300,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -447,13 +392,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = "1 queue_if_no_path",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 300,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -465,13 +407,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = "1 queue_if_no_path",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 300,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -483,13 +422,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -500,13 +436,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = "2 pg_init_retries 50",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 15,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -518,13 +451,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -536,13 +466,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -554,13 +481,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -572,13 +496,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -589,13 +510,10 @@ static struct hwentry default_hw[] = {
                .product       = "^3542",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_SERIAL,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -606,13 +524,10 @@ static struct hwentry default_hw[] = {
                .product       = "^2105800",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_SERIAL,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -623,13 +538,10 @@ static struct hwentry default_hw[] = {
                .product       = "^2105F20",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_SERIAL,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -640,13 +552,10 @@ static struct hwentry default_hw[] = {
                .product       = "^1750500",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_ALUA,
                .prio_args     = NULL,
@@ -657,13 +566,10 @@ static struct hwentry default_hw[] = {
                .product       = "^2107900",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -674,13 +580,10 @@ static struct hwentry default_hw[] = {
                .product       = "^2145",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_ALUA,
                .prio_args     = NULL,
@@ -693,13 +596,10 @@ static struct hwentry default_hw[] = {
                .uid_attribute = "ID_UID",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DIRECTIO,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -712,13 +612,10 @@ static struct hwentry default_hw[] = {
                .uid_attribute = "ID_UID",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DIRECTIO,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -729,13 +626,10 @@ static struct hwentry default_hw[] = {
                .product       = "^IPR.*",
                .features      = "1 queue_if_no_path",
                .hwhandler     = "1 alua",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_ALUA,
                .prio_args     = NULL,
@@ -746,13 +640,11 @@ static struct hwentry default_hw[] = {
                .product       = "1820N00",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
                .minio         = 100,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_ALUA,
                .prio_args     = NULL,
@@ -763,13 +655,11 @@ static struct hwentry default_hw[] = {
                .product       = "2810XIV",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = 15,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
                .minio         = 15,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -786,13 +676,10 @@ static struct hwentry default_hw[] = {
                .product       = "VDASD",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = (300 / DEFAULT_CHECKINT),
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DIRECTIO,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -803,13 +690,10 @@ static struct hwentry default_hw[] = {
                .product       = "3303      NVDISK",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = FAILOVER,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = (300 / DEFAULT_CHECKINT),
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -820,13 +704,10 @@ static struct hwentry default_hw[] = {
                .product       = "NVDISK",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 alua",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = (300 / DEFAULT_CHECKINT),
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_ALUA,
                .prio_args     = NULL,
@@ -838,13 +719,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = "2 pg_init_retries 50",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 15,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -856,13 +734,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = "2 pg_init_retries 50",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 15,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -874,13 +749,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = "2 pg_init_retries 50",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 15,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -892,13 +764,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = "2 pg_init_retries 50",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 15,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -914,16 +783,19 @@ static struct hwentry default_hw[] = {
                .product       = "LUN.*",
                .features      = "3 queue_if_no_path pg_init_retries 50",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .flush_on_last_del = FLUSH_ENABLED,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
                .minio         = 128,
+               .dev_loss      = MAX_DEV_LOSS_TMO,
                .checker_name  = TUR,
                .prio_name     = PRIO_ONTAP,
                .prio_args     = NULL,
+               .retain_hwhandler = RETAIN_HWHANDLER_ON,
+               .user_friendly_names = USER_FRIENDLY_NAMES_OFF,
+               .detect_prio   = DETECT_PRIO_ON,
        },
        /*
         * NEXENTA/COMSTAR controller family
@@ -936,7 +808,6 @@ static struct hwentry default_hw[] = {
                .product       = "COMSTAR",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_SERIAL,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -957,7 +828,6 @@ static struct hwentry default_hw[] = {
                .product       = "Nseries.*",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -978,13 +848,10 @@ static struct hwentry default_hw[] = {
                .product       = "Axiom.*",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_ALUA,
                .prio_args     = NULL,
@@ -1001,13 +868,10 @@ static struct hwentry default_hw[] = {
                .product       = "TP9[13]00",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DIRECTIO,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -1018,13 +882,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -1035,13 +896,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = "2 pg_init_retries 50",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = 15,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -1052,13 +910,10 @@ static struct hwentry default_hw[] = {
                .product       = "DISK ARRAY",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 alua",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_ALUA,
                .prio_args     = NULL,
@@ -1075,13 +930,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = TUR,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -1097,29 +949,36 @@ static struct hwentry default_hw[] = {
                .product       = "(StorEdge 3510|T4)",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DIRECTIO,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
        },
        {
+               .vendor        = "SUN",
+               .product       = "STK6580_6780",
+               .features      = DEFAULT_FEATURES,
+               .hwhandler     = "1 rdac",
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .rr_weight     = RR_WEIGHT_NONE,
+               .no_path_retry = NO_PATH_RETRY_UNDEF,
+               .checker_name  = TUR,
+               .prio_name     = PRIO_RDAC,
+               .prio_args     = NULL,
+       },
+       {
                .vendor        = "EUROLOGC",
                .product       = "FC2502",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_UNDEF,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = DEFAULT_CHECKER,
                .prio_name     = DEFAULT_PRIO,
                .prio_args     = NULL,
@@ -1135,7 +994,6 @@ static struct hwentry default_hw[] = {
                .product       = "RAIGE VOLUME",
                .features      = "1 queue_if_no_path",
                .hwhandler     = DEFAULT_HWHANDLER,
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = MULTIBUS,
                .pgfailback    = FAILBACK_UNDEF,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -1151,13 +1009,10 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
@@ -1169,7 +1024,20 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .rr_weight     = RR_WEIGHT_NONE,
+               .no_path_retry = NO_PATH_RETRY_QUEUE,
+               .checker_name  = RDAC,
+               .prio_name     = PRIO_RDAC,
+               .prio_args     = NULL,
+       },
+       /* StorageTek 6180 */
+       {
+               .vendor        = "SUN",
+               .product       = "SUN_6180",
+               .features      = DEFAULT_FEATURES,
+               .hwhandler     = "1 rdac",
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
@@ -1178,25 +1046,27 @@ static struct hwentry default_hw[] = {
                .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
-               .prio_args     = NULL,
        },
-       /* LSI/Engenio/NetApp E-Series RDAC storage */
+       /* LSI/Engenio/NetApp E-Series RDAC storage
+        *
+        * Maintainer : Sean Stewart
+        * Mail : sean.stewart@netapp.com
+        */
        {
-               .vendor        = "(LSI|ENGENIO)",
+               .vendor        = "(NETAPP|LSI|ENGENIO)",
                .product       = "INF-01-00",
                .bl_product    = "Universal Xport",
                .features      = "2 pg_init_retries 50",
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
-               .no_path_retry = 15,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
+               .no_path_retry = 30,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
+               .detect_prio   = DETECT_PRIO_ON,
+               .retain_hwhandler = RETAIN_HWHANDLER_ON,
        },
        {
                .vendor        = "STK",
@@ -1204,17 +1074,53 @@ static struct hwentry default_hw[] = {
                .bl_product    = "Universal Xport",
                .features      = DEFAULT_FEATURES,
                .hwhandler     = "1 rdac",
-               .selector      = DEFAULT_SELECTOR,
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .rr_weight     = RR_WEIGHT_NONE,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
-               .minio         = DEFAULT_MINIO,
-               .minio_rq      = DEFAULT_MINIO_RQ,
                .checker_name  = RDAC,
                .prio_name     = PRIO_RDAC,
                .prio_args     = NULL,
        },
+       {
+               .vendor        = "Intel",
+               .product       = "Multi-Flex",
+               .features      = DEFAULT_FEATURES,
+               .hwhandler     = "1 alua",
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .rr_weight     = RR_WEIGHT_NONE,
+               .no_path_retry = NO_PATH_RETRY_QUEUE,
+               .checker_name  = TUR,
+               .prio_name     = PRIO_ALUA,
+               .prio_args     = NULL,
+       },
+       {
+               .vendor        = "DataCore",
+               .product       = "SANmelody",
+               .features      = DEFAULT_FEATURES,
+               .hwhandler     = DEFAULT_HWHANDLER,
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .rr_weight     = RR_WEIGHT_NONE,
+               .no_path_retry = NO_PATH_RETRY_QUEUE,
+               .checker_name  = TUR,
+               .prio_name     = PRIO_ALUA,
+               .prio_args     = NULL,
+       },
+       {
+               .vendor        = "DataCore",
+               .product       = "Virtual Disk",
+               .features      = DEFAULT_FEATURES,
+               .hwhandler     = DEFAULT_HWHANDLER,
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .rr_weight     = RR_WEIGHT_NONE,
+               .no_path_retry = NO_PATH_RETRY_QUEUE,
+               .checker_name  = TUR,
+               .prio_name     = PRIO_ALUA,
+               .prio_args     = NULL,
+       },
        /*
         * EOL
         */
index 4439a51..6ce9a74 100644 (file)
@@ -1,16 +1,7 @@
 #include <pthread.h>
-#include <signal.h>
 #include "lock.h"
 #include <stdio.h>
 
-void block_signal (int signum, sigset_t *old)
-{
-       sigset_t set;
-       sigemptyset(&set);
-       sigaddset(&set, signum);
-       pthread_sigmask(SIG_BLOCK, &set, old);
-}
-
 void cleanup_lock (void * data)
 {
        unlock ((*(struct mutex_lock *)data));
index 6897a74..04ef78d 100644 (file)
@@ -29,6 +29,5 @@ struct mutex_lock {
 #endif
 
 void cleanup_lock (void * data);
-void block_signal(int signum, sigset_t *old);
 
 #endif /* _LOCK_H */
index 57b7696..ab92e2a 100644 (file)
@@ -8,6 +8,7 @@
 #include <stdarg.h>
 #include <string.h>
 #include <syslog.h>
+#include <time.h>
 
 #include "memory.h"
 #include "log.h"
@@ -96,6 +97,13 @@ void log_close (void)
        return;
 }
 
+void log_reset (char *program_name)
+{
+       closelog();
+       tzset();
+       openlog(program_name, 0, LOG_DAEMON);
+}
+
 int log_enqueue (int prio, const char * fmt, va_list ap)
 {
        int len, fwd;
index 6634c83..984f047 100644 (file)
@@ -33,6 +33,7 @@ struct logarea * la;
 
 int log_init (char * progname, int size);
 void log_close (void);
+void log_reset (char * progname);
 int log_enqueue (int prio, const char * fmt, va_list ap);
 int log_dequeue (void *);
 void log_syslog (void *);
index d701ba1..47d75a1 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <syslog.h>
 #include <pthread.h>
 #include <sys/mman.h>
 
 #include "log.h"
 #include "lock.h"
 
+pthread_t log_thr;
+
+pthread_mutex_t logq_lock;
+pthread_mutex_t logev_lock;
+pthread_cond_t logev_cond;
+
+int logq_running;
+
 void log_safe (int prio, const char * fmt, va_list ap)
 {
-       sigset_t old;
-
-       block_signal(SIGUSR1, &old);
-       block_signal(SIGHUP, NULL);
+       if (log_thr == (pthread_t)0) {
+               syslog(prio, fmt, ap);
+               return;
+       }
 
-       pthread_mutex_lock(logq_lock);
+       pthread_mutex_lock(&logq_lock);
        log_enqueue(prio, fmt, ap);
-       pthread_mutex_unlock(logq_lock);
+       pthread_mutex_unlock(&logq_lock);
+
+       pthread_mutex_lock(&logev_lock);
+       pthread_cond_signal(&logev_cond);
+       pthread_mutex_unlock(&logev_lock);
+}
 
-       pthread_mutex_lock(logev_lock);
-       pthread_cond_signal(logev_cond);
-       pthread_mutex_unlock(logev_lock);
+void log_thread_flush (void)
+{
+       int empty;
 
-       pthread_sigmask(SIG_SETMASK, &old, NULL);
+       do {
+               pthread_mutex_lock(&logq_lock);
+               empty = log_dequeue(la->buff);
+               pthread_mutex_unlock(&logq_lock);
+               if (!empty)
+                       log_syslog(la->buff);
+       } while (empty == 0);
 }
 
 static void flush_logqueue (void)
@@ -36,9 +56,9 @@ static void flush_logqueue (void)
        int empty;
 
        do {
-               pthread_mutex_lock(logq_lock);
+               pthread_mutex_lock(&logq_lock);
                empty = log_dequeue(la->buff);
-               pthread_mutex_unlock(logq_lock);
+               pthread_mutex_unlock(&logq_lock);
                if (!empty)
                        log_syslog(la->buff);
        } while (empty == 0);
@@ -46,15 +66,23 @@ static void flush_logqueue (void)
 
 static void * log_thread (void * et)
 {
+       int running;
+
+       pthread_mutex_lock(&logev_lock);
+       logq_running = 1;
+       pthread_mutex_unlock(&logev_lock);
+
        mlockall(MCL_CURRENT | MCL_FUTURE);
        logdbg(stderr,"enter log_thread\n");
 
        while (1) {
-               pthread_mutex_lock(logev_lock);
-               pthread_cond_wait(logev_cond, logev_lock);
-               pthread_mutex_unlock(logev_lock);
-
-               flush_logqueue();
+               pthread_mutex_lock(&logev_lock);
+               pthread_cond_wait(&logev_cond, &logev_lock);
+               running = logq_running;
+               pthread_mutex_unlock(&logev_lock);
+               if (!running)
+                       break;
+               log_thread_flush();
        }
        return NULL;
 }
@@ -63,19 +91,18 @@ void log_thread_start (pthread_attr_t *attr)
 {
        logdbg(stderr,"enter log_thread_start\n");
 
-       logq_lock = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t));
-       logev_lock = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t));
-       logev_cond = (pthread_cond_t *) malloc(sizeof(pthread_cond_t));
-
-       pthread_mutex_init(logq_lock, NULL);
-       pthread_mutex_init(logev_lock, NULL);
-       pthread_cond_init(logev_cond, NULL);
+       pthread_mutex_init(&logq_lock, NULL);
+       pthread_mutex_init(&logev_lock, NULL);
+       pthread_cond_init(&logev_cond, NULL);
 
        if (log_init("multipathd", 0)) {
                fprintf(stderr,"can't initialize log buffer\n");
                exit(1);
        }
-       pthread_create(&log_thr, attr, log_thread, NULL);
+       if (pthread_create(&log_thr, attr, log_thread, NULL)) {
+               fprintf(stderr,"can't start log thread\n");
+               exit(1);
+       }
 
        return;
 }
@@ -84,22 +111,23 @@ void log_thread_stop (void)
 {
        logdbg(stderr,"enter log_thread_stop\n");
 
-       pthread_mutex_lock(logq_lock);
+       pthread_mutex_lock(&logev_lock);
+       logq_running = 0;
+       pthread_cond_signal(&logev_cond);
+       pthread_mutex_unlock(&logev_lock);
+
+       pthread_mutex_lock(&logq_lock);
        pthread_cancel(log_thr);
-       pthread_mutex_unlock(logq_lock);
+       pthread_mutex_unlock(&logq_lock);
        pthread_join(log_thr, NULL);
+       log_thr = (pthread_t)0;
 
        flush_logqueue();
 
-       pthread_mutex_destroy(logq_lock);
-       pthread_mutex_destroy(logev_lock);
-       pthread_cond_destroy(logev_cond);
-
-       free(logq_lock);
-       logq_lock = NULL;
-       free(logev_lock);
-       logev_lock = NULL;
-       free(logev_cond);
-       logev_cond = NULL;
-       free_logarea();
+       pthread_mutex_destroy(&logq_lock);
+       pthread_mutex_destroy(&logev_lock);
+       pthread_cond_destroy(&logev_cond);
+
+       log_close();
 }
+
index 77780d8..e5c6499 100644 (file)
@@ -3,14 +3,17 @@
 
 #include <pthread.h>
 
-pthread_t log_thr;
+extern pthread_t log_thr;
 
-pthread_mutex_t *logq_lock;
-pthread_mutex_t *logev_lock;
-pthread_cond_t *logev_cond;
+extern pthread_mutex_t logq_lock;
+extern pthread_mutex_t logev_lock;
+extern pthread_cond_t logev_cond;
+
+extern int logq_running;
 
 void log_safe(int prio, const char * fmt, va_list ap);
 void log_thread_start(pthread_attr_t *attr);
 void log_thread_stop(void);
+void log_thread_flush(void);
 
 #endif /* _LOG_PTHREAD_H */
index 79c2d22..526c45b 100644 (file)
@@ -311,8 +311,10 @@ read_value_block(void)
 
        buf = (char *) MALLOC(MAXBUF);
 
-       if (!buf)
+       if (!buf) {
+               vector_free(elements);
                return NULL;
+       }
 
        while (read_line(buf, MAXBUF)) {
                vec = alloc_strvec(buf);
@@ -323,21 +325,20 @@ read_value_block(void)
                                break;
                        }
 
-                       if (VECTOR_SIZE(vec))
-                               for (i = 0; i < VECTOR_SIZE(vec); i++) {
-                                       str = VECTOR_SLOT(vec, i);
-                                       dup = (char *) MALLOC(strlen(str) + 1);
-                                       if (!dup)
-                                               goto out;
-                                       memcpy(dup, str, strlen(str));
-
-                                       if (!vector_alloc_slot(elements)) {
-                                               free_strvec(vec);
-                                               goto out1;
-                                       }
+                       for (i = 0; i < VECTOR_SIZE(vec); i++) {
+                               str = VECTOR_SLOT(vec, i);
+                               dup = (char *) MALLOC(strlen(str) + 1);
+                               if (!dup)
+                                       goto out;
+                               memcpy(dup, str, strlen(str));
 
-                                       vector_set_slot(elements, dup);
+                               if (!vector_alloc_slot(elements)) {
+                                       free_strvec(vec);
+                                       goto out1;
                                }
+
+                               vector_set_slot(elements, dup);
+                       }
                        free_strvec(vec);
                }
                memset(buf, 0, MAXBUF);
@@ -348,6 +349,7 @@ out1:
        FREE(dup);
 out:
        FREE(buf);
+       vector_free(elements);
        return NULL;
 }
 
index a29b885..3c526c2 100644 (file)
@@ -8,6 +8,8 @@
 #include <sys/stat.h>
 #include <dirent.h>
 #include <unistd.h>
+#include <string.h>
+#include <errno.h>
 
 #include "checkers.h"
 #include "vector.h"
@@ -24,6 +26,7 @@
 #include "switchgroup.h"
 #include "devmapper.h"
 #include "uevent.h"
+#include "debug.h"
 
 #define MAX(x,y) (x > y) ? x : y
 #define TAIL     (line + len - 1 - c)
@@ -332,6 +335,10 @@ snprint_chk_state (char * buff, size_t len, struct path * pp)
                return snprintf(buff, len, "shaky");
        case PATH_GHOST:
                return snprintf(buff, len, "ghost");
+       case PATH_PENDING:
+               return snprintf(buff, len, "i/o pending");
+       case PATH_TIMEOUT:
+               return snprintf(buff, len, "i/o timeout");
        default:
                return snprintf(buff, len, "undef");
        }
@@ -381,7 +388,6 @@ snprint_pg_selector (char * buff, size_t len, struct pathgroup * pgp)
 static int
 snprint_pg_pri (char * buff, size_t len, struct pathgroup * pgp)
 {
-       int avg_priority = 0;
        /*
         * path group priority is not updated for every path prio change,
         * but only on switch group code path.
@@ -389,9 +395,7 @@ snprint_pg_pri (char * buff, size_t len, struct pathgroup * pgp)
         * Printing is another reason to update.
         */
        path_group_prio_update(pgp);
-       if (pgp->enabled_paths)
-               avg_priority = pgp->priority / pgp->enabled_paths;
-       return snprint_int(buff, len, avg_priority);
+       return snprint_int(buff, len, pgp->priority);
 }
 
 static int
@@ -422,6 +426,16 @@ snprint_path_serial (char * buff, size_t len, struct path * pp)
 }
 
 static int
+snprint_path_mpp (char * buff, size_t len, struct path * pp)
+{
+       if (!pp->mpp)
+               return snprintf(buff, len, "[orphan]");
+       if (!pp->mpp->alias)
+               return snprintf(buff, len, "[unknown]");
+       return snprint_str(buff, len, pp->mpp->alias);
+}
+
+static int
 snprint_path_checker (char * buff, size_t len, struct path * pp)
 {
        struct checker * c = &pp->checker;
@@ -464,6 +478,7 @@ struct path_data pd[] = {
        {'p', "pri",           0, snprint_pri},
        {'S', "size",          0, snprint_path_size},
        {'z', "serial",        0, snprint_path_serial},
+       {'m', "multipath",     0, snprint_path_mpp},
        {0, NULL, 0 , NULL}
 };
 
@@ -757,11 +772,30 @@ snprint_pathgroup (char * line, int len, char * format,
 extern void
 print_multipath_topology (struct multipath * mpp, int verbosity)
 {
-       char buff[MAX_LINE_LEN * MAX_LINES] = {};
+       int resize;
+       char *buff = NULL;
+       char *old = NULL;
+       int len, maxlen = MAX_LINE_LEN * MAX_LINES;
+
+       buff = MALLOC(maxlen);
+       do {
+               if (!buff) {
+                       if (old)
+                               FREE(old);
+                       condlog(0, "couldn't allocate memory for list: %s\n",
+                               strerror(errno));
+                       return;
+               }
 
-       memset(&buff[0], 0, MAX_LINE_LEN * MAX_LINES);
-       snprint_multipath_topology(&buff[0], MAX_LINE_LEN * MAX_LINES,
-                                  mpp, verbosity);
+               len = snprint_multipath_topology(buff, maxlen, mpp, verbosity);
+               resize = (len == maxlen - 1);
+
+               if (resize) {
+                       maxlen *= 2;
+                       old = buff;
+                       buff = REALLOC(buff, maxlen);
+               }
+       } while (resize);
        printf("%s", buff);
 }
 
@@ -1055,6 +1089,19 @@ snprint_blacklist_report (char * buff, int len)
 
        if ((len - fwd - threshold) <= 0)
                return len;
+       fwd += snprintf(buff + fwd, len - fwd, "udev property rules:\n"
+                                              "- blacklist:\n");
+       if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_property))
+               return len;
+
+       if ((len - fwd - threshold) <= 0)
+               return len;
+       fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
+       if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_property) == 0)
+               return len;
+
+       if ((len - fwd - threshold) <= 0)
+               return len;
        fwd += snprintf(buff + fwd, len - fwd, "wwid rules:\n"
                                               "- blacklist:\n");
        if (snprint_blacklist_group(buff, len, &fwd, &conf->blist_wwid) == 0)
@@ -1120,6 +1167,15 @@ snprint_blacklist (char * buff, int len)
                if (fwd > len)
                        return len;
        }
+       vector_foreach_slot (conf->blist_property, ble, i) {
+               kw = find_keyword(rootkw->sub, "property");
+               if (!kw)
+                       return 0;
+               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
+                                      kw, ble);
+               if (fwd > len)
+                       return len;
+       }
        rootkw = find_keyword(rootkw->sub, "device");
        if (!rootkw)
                return 0;
@@ -1188,6 +1244,15 @@ snprint_blacklist_except (char * buff, int len)
                if (fwd > len)
                        return len;
        }
+       vector_foreach_slot (conf->elist_property, ele, i) {
+               kw = find_keyword(rootkw->sub, "property");
+               if (!kw)
+                       return 0;
+               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
+                                      kw, ele);
+               if (fwd > len)
+                       return len;
+       }
        rootkw = find_keyword(rootkw->sub, "device");
        if (!rootkw)
                return 0;
index 61c19b7..05a8cf1 100644 (file)
@@ -22,13 +22,23 @@ static struct prio * alloc_prio (void)
        struct prio *p;
 
        p = MALLOC(sizeof(struct prio));
-       if (p)
+       if (p) {
                INIT_LIST_HEAD(&p->node);
+               p->refcount = 1;
+       }
        return p;
 }
 
 void free_prio (struct prio * p)
 {
+       if (!p)
+               return;
+       p->refcount--;
+       if (p->refcount) {
+               condlog(3, "%s prioritizer refcount %d",
+                       p->name, p->refcount);
+               return;
+       }
        condlog(3, "unloading %s prioritizer", p->name);
        list_del(&p->node);
        if (p->handle) {
@@ -54,6 +64,9 @@ struct prio * prio_lookup (char * name)
 {
        struct prio * p;
 
+       if (!name || !strlen(name))
+               return NULL;
+
        list_for_each_entry(p, &prioritizers, node) {
                if (!strncmp(name, p->name, PRIO_NAME_LEN))
                        return p;
@@ -76,6 +89,7 @@ struct prio * add_prio (char * name)
        p = alloc_prio();
        if (!p)
                return NULL;
+       snprintf(p->name, PRIO_NAME_LEN, "%s", name);
        snprintf(libname, LIB_PRIO_NAMELEN, "%s/libprio%s.so",
                 conf->multipath_dir, name);
        if (stat(libname,&stbuf) < 0) {
@@ -97,7 +111,6 @@ struct prio * add_prio (char * name)
                condlog(0, "A dynamic linking error occurred: (%s)", errstr);
        if (!p->getprio)
                goto out;
-       snprintf(p->name, PRIO_NAME_LEN, "%s", name);
        list_add(&p->node, &prioritizers);
        return p;
 out:
@@ -110,6 +123,13 @@ int prio_getprio (struct prio * p, struct path * pp)
        return p->getprio(pp, p->args);
 }
 
+int prio_selected (struct prio * p)
+{
+       if (!p || !p->getprio)
+               return 0;
+       return (p->getprio) ? 1 : 0;
+}
+
 char * prio_name (struct prio * p)
 {
        return p->name;
@@ -119,3 +139,33 @@ char * prio_args (struct prio * p)
 {
        return p->args;
 }
+
+void prio_get (struct prio * dst, char * name, char * args)
+{
+       struct prio * src = prio_lookup(name);
+
+       if (!src) {
+               dst->getprio = NULL;
+               return;
+       }
+
+       strncpy(dst->name, src->name, PRIO_NAME_LEN);
+       if (args)
+               strncpy(dst->args, args, PRIO_ARGS_LEN);
+       dst->getprio = src->getprio;
+       dst->handle = NULL;
+
+       src->refcount++;
+}
+
+void prio_put (struct prio * dst)
+{
+       struct prio * src;
+
+       if (!dst)
+               return;
+
+       src = prio_lookup(dst->name);
+       memset(dst, 0x0, sizeof(struct prio));
+       free_prio(src);
+}
index 36929fb..4eeb216 100644 (file)
@@ -6,7 +6,10 @@
  */
 #include "checkers.h"
 #include "vector.h"
-#include "structs.h"
+
+/* forward declaration to avoid circular dependency */
+struct path;
+
 #include "list.h"
 #include "memory.h"
 
@@ -41,6 +44,7 @@
 
 struct prio {
        void *handle;
+       int refcount;
        struct list_head node;
        char name[PRIO_NAME_LEN];
        char args[PRIO_ARGS_LEN];
@@ -52,6 +56,9 @@ void cleanup_prio (void);
 struct prio * add_prio (char *);
 struct prio * prio_lookup (char *);
 int prio_getprio (struct prio *, struct path *);
+void prio_get (struct prio *, char *, char *);
+void prio_put (struct prio *);
+int prio_selected (struct prio *);
 char * prio_name (struct prio *);
 char * prio_args (struct prio *);
 int prio_set_args (struct prio *, char *);
index 4da3ee7..4165ec6 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <debug.h>
 #include <prio.h>
+#include <structs.h>
 
 #include "alua.h"
 
@@ -107,7 +108,7 @@ int getprio (struct path * pp, char * args)
                        default:
                                rc = 0;
                }
-               if (priopath)
+               if (priopath && aas != AAS_OPTIMIZED)
                        rc += 80;
        } else {
                switch(-rc) {
index 2c16c6c..e3e6a51 100644 (file)
@@ -24,6 +24,7 @@
 #include <sg_include.h>
 #include <debug.h>
 #include <prio.h>
+#include <structs.h>
 
 #define INQ_REPLY_LEN 255
 #define INQ_CMD_CODE 0x12
index 20d727e..87e9a8d 100644 (file)
@@ -5,6 +5,7 @@
 #include <sg_include.h>
 #include <debug.h>
 #include <prio.h>
+#include <structs.h>
 
 #define INQUIRY_CMD     0x12
 #define INQUIRY_CMDLEN  6
index 4789340..f748707 100644 (file)
@@ -75,6 +75,7 @@
 #include <sg_include.h>
 #include <debug.h>
 #include <prio.h>
+#include <structs.h>
 
 #define INQ_REPLY_LEN 255
 #define INQ_CMD_CODE 0x12
@@ -86,7 +87,7 @@
 int hds_modular_prio (const char *dev, int fd)
 {
        int k;
-       char vendor[8];
+       char vendor[9];
        char product[32];
        char serial[32];
        char ldev[32];
@@ -104,6 +105,7 @@ int hds_modular_prio (const char *dev, int fd)
        }
 
        memset (&io_hdr, 0, sizeof (sg_io_hdr_t));
+       memset (inqBuff, 0, INQ_REPLY_LEN);
        io_hdr.interface_id = 'S';
        io_hdr.cmd_len = sizeof (inqCmdBlk);
        io_hdr.mx_sb_len = sizeof (sense_buffer);
@@ -123,7 +125,7 @@ int hds_modular_prio (const char *dev, int fd)
                return -1;
        }
 
-       snprintf (vendor, 8, "%.8s", inqBuffp + 8);
+       snprintf (vendor, 9, "%.8s", inqBuffp + 8);
        snprintf (product, 17, "%.16s", inqBuffp + 16);
        snprintf (serial, 5, "%.4s", inqBuffp + 40);
        snprintf (ldev, 5, "%.4s", inqBuffp + 44);
index 2de460f..c24baad 100644 (file)
@@ -15,6 +15,7 @@
 #include <sg_include.h>
 #include <debug.h>
 #include <prio.h>
+#include <structs.h>
 
 #define TUR_CMD_LEN            6
 #define SCSI_CHECK_CONDITION   0x2
index 2570b61..94406df 100644 (file)
@@ -8,6 +8,7 @@
 #include <prio.h>
 #include <debug.h>
 #include <unistd.h>
+#include <structs.h>
 
 //
 // This prioritizer suits iSCSI needs, makes it possible to prefer one path.
index 6e6e3d3..026d45d 100644 (file)
@@ -22,6 +22,7 @@
 #include <sg_include.h>
 #include <debug.h>
 #include <prio.h>
+#include <structs.h>
 
 #define INQUIRY_CMD    0x12
 #define INQUIRY_CMDLEN 6
@@ -79,6 +80,7 @@ static int send_gva(const char *dev, int fd, unsigned char pg,
        int ret = -1;
 
        memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
+       memset(results, 0, *results_size);
        io_hdr.interface_id = 'S';
        io_hdr.cmd_len = sizeof (cdb);
        io_hdr.mx_sb_len = sizeof (sb);
index 41ea887..441b3b0 100644 (file)
@@ -5,6 +5,7 @@
 #include <sg_include.h>
 #include <debug.h>
 #include <prio.h>
+#include <structs.h>
 
 #define INQUIRY_CMD     0x12
 #define INQUIRY_CMDLEN  6
@@ -21,6 +22,7 @@ int rdac_prio(const char *dev, int fd)
        int ret = 0;
 
        memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
+       memset(sense_buffer, 0, 256);
        io_hdr.interface_id = 'S';
        io_hdr.cmd_len = sizeof (inqCmdBlk);
        io_hdr.mx_sb_len = sizeof (sb);
index d6c81f0..54c9039 100644 (file)
@@ -60,6 +60,10 @@ int prio_path_weight(struct path *pp, char *prio_args)
 
        regex = get_next_string(&temp, split_char);
 
+       /* Return default priority if the argument is not parseable */
+       if (!regex)
+               return priority;
+
        if (!strcmp(regex, HBTL)) {
                sprintf(path, "%d:%d:%d:%d", pp->sg_id.host_no,
                        pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun);
@@ -67,7 +71,7 @@ int prio_path_weight(struct path *pp, char *prio_args)
                strcpy(path, pp->dev);
        } else {
                condlog(0, "%s: %s - Invalid arguments", pp->dev,
-                       pp->prio->name);
+                       pp->prio.name);
                return priority;
        }
 
index 6ac4caa..7b7944d 100644 (file)
@@ -17,6 +17,7 @@
 #include "devmapper.h"
 #include "prio.h"
 #include "discovery.h"
+#include "prioritizers/alua_rtpg.h"
 #include <inttypes.h>
 
 pgpolicyfn *pgpolicies[] = {
@@ -262,8 +263,6 @@ select_alias (struct multipath * mp)
                                        conf->bindings_file, mp->alias_prefix, conf->bindings_read_only);
                }
                if (mp->alias == NULL)
-                       mp->alias = dm_get_name(mp->wwid);
-               if (mp->alias == NULL)
                        mp->alias = STRDUP(mp->wwid);
        }
 
@@ -282,8 +281,11 @@ select_features (struct multipath * mp)
        } else if (mp->hwe && mp->hwe->features) {
                mp->features = STRDUP(mp->hwe->features);
                origin = "controller setting";
-       } else {
+       } else if (conf->features) {
                mp->features = STRDUP(conf->features);
+               origin = "config file default";
+       } else {
+               mp->features = set_default(DEFAULT_FEATURES);
                origin = "internal default";
        }
        condlog(3, "%s: features = %s (%s)",
@@ -343,11 +345,11 @@ select_checker(struct path *pp)
                pp->dev, checker_name(c));
 out:
        if (conf->checker_timeout) {
-               c->timeout = conf->checker_timeout * 1000;
-               condlog(3, "%s: checker timeout = %u ms (config file default)",
+               c->timeout = conf->checker_timeout;
+               condlog(3, "%s: checker timeout = %u s (config file default)",
                                pp->dev, c->timeout);
        }
-       else if (pp->udev && sysfs_get_timeout(pp, &c->timeout) == 0)
+       else if (sysfs_get_timeout(pp, &c->timeout) > 0)
                condlog(3, "%s: checker timeout = %u ms (sysfs setting)",
                                pp->dev, c->timeout);
        else {
@@ -367,36 +369,72 @@ select_getuid (struct path * pp)
                        pp->dev, pp->uid_attribute);
                return 0;
        }
+       if (pp->hwe && pp->hwe->getuid) {
+               pp->getuid = pp->hwe->getuid;
+               condlog(3, "%s: getuid = %s (deprecated) (controller setting)",
+                       pp->dev, pp->getuid);
+               return 0;
+       }
        if (conf->uid_attribute) {
                pp->uid_attribute = conf->uid_attribute;
                condlog(3, "%s: uid_attribute = %s (config file default)",
                        pp->dev, pp->uid_attribute);
                return 0;
        }
+       if (conf->getuid) {
+               pp->getuid = conf->getuid;
+               condlog(3, "%s: getuid = %s (deprecated) (config file default)",
+                       pp->dev, pp->getuid);
+               return 0;
+       }
        pp->uid_attribute = STRDUP(DEFAULT_UID_ATTRIBUTE);
        condlog(3, "%s: uid_attribute = %s (internal default)",
                pp->dev, pp->uid_attribute);
        return 0;
 }
 
+void
+detect_prio(struct path * pp)
+{
+       int ret;
+       struct prio *p = &pp->prio;
+
+       if (get_target_port_group_support(pp->fd) <= 0)
+               return;
+       ret = get_target_port_group(pp->fd);
+       if (ret < 0)
+               return;
+       if (get_asymmetric_access_state(pp->fd, ret) < 0)
+               return;
+       prio_get(p, PRIO_ALUA, DEFAULT_PRIO_ARGS);
+}
+
 extern int
 select_prio (struct path * pp)
 {
        struct mpentry * mpe;
+       struct prio * p = &pp->prio;
+
+       if (pp->detect_prio == DETECT_PRIO_ON) {
+               detect_prio(pp);
+               if (prio_selected(p)) {
+                       condlog(3, "%s: prio = %s (detected setting)",
+                               pp->dev, prio_name(p));
+                       return 0;
+               }
+       }
 
        if ((mpe = find_mpe(pp->wwid))) {
                if (mpe->prio_name) {
-                       pp->prio = prio_lookup(mpe->prio_name);
-                       prio_set_args(pp->prio, mpe->prio_args);
+                       prio_get(p, mpe->prio_name, mpe->prio_args);
                        condlog(3, "%s: prio = %s (LUN setting)",
-                               pp->dev, pp->prio->name);
+                               pp->dev, prio_name(p));
                        return 0;
                }
        }
 
        if (pp->hwe && pp->hwe->prio_name) {
-               pp->prio = prio_lookup(pp->hwe->prio_name);
-               prio_set_args(pp->prio, pp->hwe->prio_args);
+               prio_get(p, pp->hwe->prio_name, pp->hwe->prio_args);
                condlog(3, "%s: prio = %s (controller setting)",
                        pp->dev, pp->hwe->prio_name);
                condlog(3, "%s: prio args = %s (controller setting)",
@@ -404,19 +442,17 @@ select_prio (struct path * pp)
                return 0;
        }
        if (conf->prio_name) {
-               pp->prio = prio_lookup(conf->prio_name);
-               prio_set_args(pp->prio, conf->prio_args);
+               prio_get(p, conf->prio_name, conf->prio_args);
                condlog(3, "%s: prio = %s (config file default)",
                        pp->dev, conf->prio_name);
                condlog(3, "%s: prio args = %s (config file default)",
                        pp->dev, conf->prio_args);
                return 0;
        }
-       pp->prio = prio_lookup(DEFAULT_PRIO);
-       prio_set_args(pp->prio, DEFAULT_PRIO_ARGS);
+       prio_get(p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
        condlog(3, "%s: prio = %s (internal default)",
                pp->dev, DEFAULT_PRIO);
-       condlog(3, "%s: prio = %s (internal default)",
+       condlog(3, "%s: prio args = %s (internal default)",
                pp->dev, DEFAULT_PRIO_ARGS);
        return 0;
 }
@@ -513,73 +549,40 @@ select_minio_bio (struct multipath * mp)
 extern int
 select_minio (struct multipath * mp)
 {
-       if (conf->dmrq)
+       unsigned int minv_dmrq[3] = {1, 1, 0};
+
+       if (VERSION_GE(conf->version, minv_dmrq))
                return select_minio_rq(mp);
        else
                return select_minio_bio(mp);
 }
 
 extern int
-select_pg_timeout(struct multipath *mp)
-{
-       if (mp->mpe && mp->mpe->pg_timeout != PGTIMEOUT_UNDEF) {
-               mp->pg_timeout = mp->mpe->pg_timeout;
-               if (mp->pg_timeout > 0)
-                       condlog(3, "%s: pg_timeout = %d (multipath setting)",
-                               mp->alias, mp->pg_timeout);
-               else
-                       condlog(3, "%s: pg_timeout = NONE (multipath setting)",
-                               mp->alias);
-               return 0;
-       }
-       if (mp->hwe && mp->hwe->pg_timeout != PGTIMEOUT_UNDEF) {
-               mp->pg_timeout = mp->hwe->pg_timeout;
-               if (mp->pg_timeout > 0)
-                       condlog(3, "%s: pg_timeout = %d (controller setting)",
-                               mp->alias, mp->pg_timeout);
-               else
-                       condlog(3, "%s: pg_timeout = NONE (controller setting)",
-                               mp->alias);
-               return 0;
-       }
-       if (conf->pg_timeout != PGTIMEOUT_UNDEF) {
-               mp->pg_timeout = conf->pg_timeout;
-               if (mp->pg_timeout > 0)
-                       condlog(3, "%s: pg_timeout = %d (config file default)",
-                               mp->alias, mp->pg_timeout);
-               else
-                       condlog(3,
-                               "%s: pg_timeout = NONE (config file default)",
-                               mp->alias);
-               return 0;
-       }
-       mp->pg_timeout = PGTIMEOUT_UNDEF;
-       condlog(3, "%s: pg_timeout = NONE (internal default)", mp->alias);
-       return 0;
-}
-
-extern int
 select_fast_io_fail(struct multipath *mp)
 {
-       if (mp->hwe && mp->hwe->fast_io_fail) {
+       if (mp->hwe && mp->hwe->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
                mp->fast_io_fail = mp->hwe->fast_io_fail;
                if (mp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
-                       condlog(3, "%s: fast_io_fail_tmo = off (controller default)", mp->alias);
+                       condlog(3, "%s: fast_io_fail_tmo = off "
+                               "(controller setting)", mp->alias);
                else
-                       condlog(3, "%s: fast_io_fail_tmo = %d (controller default)", mp->alias,
+                       condlog(3, "%s: fast_io_fail_tmo = %d "
+                               "(controller setting)", mp->alias,
                                mp->fast_io_fail == MP_FAST_IO_FAIL_ZERO ? 0 : mp->fast_io_fail);
                return 0;
        }
-       if (conf->fast_io_fail) {
+       if (conf->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
                mp->fast_io_fail = conf->fast_io_fail;
                if (mp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
-                       condlog(3, "%s: fast_io_fail_tmo = off (config file default)", mp->alias);
+                       condlog(3, "%s: fast_io_fail_tmo = off "
+                               "(config file default)", mp->alias);
                else
-                       condlog(3, "%s: fast_io_fail_tmo = %d (config file default)", mp->alias,
+                       condlog(3, "%s: fast_io_fail_tmo = %d "
+                               "(config file default)", mp->alias,
                                mp->fast_io_fail == MP_FAST_IO_FAIL_ZERO ? 0 : mp->fast_io_fail);
                return 0;
        }
-       mp->fast_io_fail = 0;
+       mp->fast_io_fail = MP_FAST_IO_FAIL_UNSET;
        return 0;
 }
 
@@ -609,24 +612,25 @@ select_flush_on_last_del(struct multipath *mp)
                return 0;
        if (mp->mpe && mp->mpe->flush_on_last_del != FLUSH_UNDEF) {
                mp->flush_on_last_del = mp->mpe->flush_on_last_del;
-               condlog(3, "flush_on_last_del = %i (multipath setting)",
-                               mp->flush_on_last_del);
+               condlog(3, "%s: flush_on_last_del = %i (multipath setting)",
+                       mp->alias, mp->flush_on_last_del);
                return 0;
        }
        if (mp->hwe && mp->hwe->flush_on_last_del != FLUSH_UNDEF) {
                mp->flush_on_last_del = mp->hwe->flush_on_last_del;
-               condlog(3, "flush_on_last_del = %i (controler setting)",
-                               mp->flush_on_last_del);
+               condlog(3, "%s: flush_on_last_del = %i (controler setting)",
+                       mp->alias, mp->flush_on_last_del);
                return 0;
        }
        if (conf->flush_on_last_del != FLUSH_UNDEF) {
                mp->flush_on_last_del = conf->flush_on_last_del;
-               condlog(3, "flush_on_last_del = %i (config file default)",
-                               mp->flush_on_last_del);
+               condlog(3, "%s: flush_on_last_del = %i (config file default)",
+                       mp->alias, mp->flush_on_last_del);
                return 0;
        }
        mp->flush_on_last_del = FLUSH_UNDEF;
-       condlog(3, "flush_on_last_del = DISABLED (internal default)");
+       condlog(3, "%s: flush_on_last_del = DISABLED (internal default)",
+               mp->alias);
        return 0;
 }
 
@@ -674,3 +678,46 @@ select_reservation_key (struct multipath * mp)
        return 0;
 }
 
+extern int
+select_retain_hwhandler (struct multipath * mp)
+{
+       unsigned int minv_dm_retain[3] = {1, 5, 0};
+
+       if (!VERSION_GE(conf->version, minv_dm_retain)) {
+               mp->retain_hwhandler = RETAIN_HWHANDLER_OFF;
+               condlog(3, "%s: retain_attached_hw_handler disabled (requires kernel version >= 1.5.0)", mp->alias);
+               return 0;
+       }
+
+       if (mp->hwe && mp->hwe->retain_hwhandler) {
+               mp->retain_hwhandler = mp->hwe->retain_hwhandler;
+               condlog(3, "%s: retain_attached_hw_handler = %d (controller default)", mp->alias, mp->retain_hwhandler);
+               return 0;
+       }
+       if (conf->retain_hwhandler) {
+               mp->retain_hwhandler = conf->retain_hwhandler;
+               condlog(3, "%s: retain_attached_hw_handler = %d (config file default)", mp->alias, mp->retain_hwhandler);
+               return 0;
+       }
+       mp->retain_hwhandler = 0;
+       condlog(3, "%s: retain_attached_hw_handler = %d (compiled in default)", mp->alias, mp->retain_hwhandler);
+       return 0;
+}
+
+extern int
+select_detect_prio (struct path * pp)
+{
+       if (pp->hwe && pp->hwe->detect_prio) {
+               pp->detect_prio = pp->hwe->detect_prio;
+               condlog(3, "%s: detect_prio = %d (controller default)", pp->dev, pp->detect_prio);
+               return 0;
+       }
+       if (conf->detect_prio) {
+               pp->detect_prio = conf->detect_prio;
+               condlog(3, "%s: detect_prio = %d (config file default)", pp->dev, pp->detect_prio);
+               return 0;
+       }
+       pp->detect_prio = 0;
+       condlog(3, "%s: detect_prio = %d (compiled in default)", pp->dev, pp->detect_prio);
+       return 0;
+}
index 4e931d4..05c6a4e 100644 (file)
@@ -9,7 +9,6 @@ int select_checker(struct path *pp);
 int select_getuid (struct path * pp);
 int select_prio (struct path * pp);
 int select_no_path_retry(struct multipath *mp);
-int select_pg_timeout(struct multipath *mp);
 int select_flush_on_last_del(struct multipath *mp);
 int select_minio(struct multipath *mp);
 int select_mode(struct multipath *mp);
@@ -18,3 +17,5 @@ int select_gid(struct multipath *mp);
 int select_fast_io_fail(struct multipath *mp);
 int select_dev_loss(struct multipath *mp);
 int select_reservation_key(struct multipath *mp);
+int select_retain_hwhandler (struct multipath * mp);
+int select_detect_prio(struct path * pp);
diff --git a/libmultipath/regex.c b/libmultipath/regex.c
deleted file mode 100644 (file)
index 0e13c62..0000000
+++ /dev/null
@@ -1,4032 +0,0 @@
-/* Extended regular expression matching and search library,
-   version 0.12.
-   (Implements POSIX draft P10003.2/D11.2, except for
-   internationalization features.)
-
-   Copyright (C) 1993 Free Software Foundation, Inc.
-
-   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; either version 2, or (at your option)
-   any later version.
-
-   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <sys/types.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifndef bcmp
-#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
-#endif
-#ifndef bcopy
-#define bcopy(s, d, n)  memcpy ((d), (s), (n))
-#endif
-#ifndef bzero
-#define bzero(s, n)     memset ((s), 0, (n))
-#endif
-
-/* Define the syntax stuff for \<, \>, etc.  */
-
-#ifndef Sword
-#define Sword 1
-#endif
-
-#define CHAR_SET_SIZE 256
-
-static char re_syntax_table[CHAR_SET_SIZE];
-
-static void init_syntax_once(void)
-{
-       register int c;
-       static int done = 0;
-
-       if (done)
-               return;
-
-       bzero(re_syntax_table, sizeof re_syntax_table);
-
-       for (c = 'a'; c <= 'z'; c++)
-               re_syntax_table[c] = Sword;
-
-       for (c = 'A'; c <= 'Z'; c++)
-               re_syntax_table[c] = Sword;
-
-       for (c = '0'; c <= '9'; c++)
-               re_syntax_table[c] = Sword;
-
-       re_syntax_table['_'] = Sword;
-
-       done = 1;
-}
-
-#define SYNTAX(c) re_syntax_table[c]
-
-#include "regex.h"
-#include <ctype.h>
-
-#ifdef isblank
-#define ISBLANK(c) (isascii (c) && isblank (c))
-#else
-#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
-#endif
-#ifdef isgraph
-#define ISGRAPH(c) (isascii (c) && isgraph (c))
-#else
-#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
-#endif
-
-#define ISPRINT(c) (isascii (c) && isprint (c))
-#define ISDIGIT(c) (isascii (c) && isdigit (c))
-#define ISALNUM(c) (isascii (c) && isalnum (c))
-#define ISALPHA(c) (isascii (c) && isalpha (c))
-#define ISCNTRL(c) (isascii (c) && iscntrl (c))
-#define ISLOWER(c) (isascii (c) && islower (c))
-#define ISPUNCT(c) (isascii (c) && ispunct (c))
-#define ISSPACE(c) (isascii (c) && isspace (c))
-#define ISUPPER(c) (isascii (c) && isupper (c))
-#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
-
-#undef SIGN_EXTEND_CHAR
-#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
-
-#ifndef alloca
-#ifdef __GNUC__
-#define alloca __builtin_alloca
-#endif                         /* not __GNUC__ */
-#endif                         /* not alloca */
-
-#define REGEX_ALLOCATE alloca
-
-/* Assumes a `char *destination' variable.  */
-#define REGEX_REALLOCATE(source, osize, nsize)                         \
-       (destination = (char *) alloca (nsize),                         \
-        bcopy (source, destination, osize),                            \
-        destination)
-
-/* True if `size1' is non-NULL and PTR is pointing anywhere inside
-   `string1' or just past its end.  This works if PTR is NULL, which is
-   a good thing.  */
-#define FIRST_STRING_P(ptr)                                            \
-       (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
-
-/* (Re)Allocate N items of type T using malloc, or fail.  */
-#define TALLOC(n, t)        ((t *) malloc ((n) * sizeof (t)))
-#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
-#define REGEX_TALLOC(n, t)   ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
-
-#define BYTEWIDTH 8            /* In bits.  */
-
-#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
-
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-typedef char boolean;
-#define false 0
-#define true 1
-
-typedef enum {
-       no_op = 0,
-       exactn = 1,
-       anychar,
-       charset,
-       charset_not,
-       start_memory,
-       stop_memory,
-       duplicate,
-       begline,
-       endline,
-       begbuf,
-       endbuf,
-       jump,
-       jump_past_alt,
-       on_failure_jump,
-       on_failure_keep_string_jump,
-       pop_failure_jump,
-       maybe_pop_jump,
-       dummy_failure_jump,
-       push_dummy_failure,
-       succeed_n,
-       jump_n,
-       set_number_at,
-       wordchar,
-       notwordchar,
-       wordbeg,
-       wordend,
-       wordbound,
-       notwordbound
-} re_opcode_t;
-
-#define STORE_NUMBER(destination, number)                              \
-  do {                                                                 \
-    (destination)[0] = (number) & 0377;                                        \
-    (destination)[1] = (number) >> 8;                                  \
-  } while (0)
-
-#define STORE_NUMBER_AND_INCR(destination, number)                     \
-  do {                                                                 \
-    STORE_NUMBER (destination, number);                                        \
-    (destination) += 2;                                                        \
-  } while (0)
-
-#define EXTRACT_NUMBER(destination, source)                            \
-  do {                                                                 \
-    (destination) = *(source) & 0377;                                  \
-    (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8;          \
-  } while (0)
-
-#define EXTRACT_NUMBER_AND_INCR(destination, source)                   \
-  do {                                                                 \
-    EXTRACT_NUMBER (destination, source);                              \
-    (source) += 2;                                                     \
-  } while (0)
-
-#undef assert
-#define assert(e)
-
-#define DEBUG_STATEMENT(e)
-#define DEBUG_PRINT1(x)
-#define DEBUG_PRINT2(x1, x2)
-#define DEBUG_PRINT3(x1, x2, x3)
-#define DEBUG_PRINT4(x1, x2, x3, x4)
-#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
-#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
-
-reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
-reg_syntax_t re_set_syntax(syntax)
-reg_syntax_t syntax;
-{
-       reg_syntax_t ret = re_syntax_options;
-
-       re_syntax_options = syntax;
-       return ret;
-}
-
-/* This table gives an error message for each of the error codes listed
-   in regex.h.  Obviously the order here has to be same as there.  */
-
-static const char *re_error_msg[] = { NULL,    /* REG_NOERROR */
-       "No match",             /* REG_NOMATCH */
-       "Invalid regular expression",   /* REG_BADPAT */
-       "Invalid collation character",  /* REG_ECOLLATE */
-       "Invalid character class name", /* REG_ECTYPE */
-       "Trailing backslash",   /* REG_EESCAPE */
-       "Invalid back reference",       /* REG_ESUBREG */
-       "Unmatched [ or [^",    /* REG_EBRACK */
-       "Unmatched ( or \\(",   /* REG_EPAREN */
-       "Unmatched \\{",        /* REG_EBRACE */
-       "Invalid content of \\{\\}",    /* REG_BADBR */
-       "Invalid range end",    /* REG_ERANGE */
-       "Memory exhausted",     /* REG_ESPACE */
-       "Invalid preceding regular expression", /* REG_BADRPT */
-       "Premature end of regular expression",  /* REG_EEND */
-       "Regular expression too big",   /* REG_ESIZE */
-       "Unmatched ) or \\)",   /* REG_ERPAREN */
-};
-
-/* Subroutine declarations and macros for regex_compile.  */
-
-static reg_errcode_t regex_compile (const char *pattern, size_t size,
-                                   reg_syntax_t syntax,
-                                   struct re_pattern_buffer * bufp);
-
-static void store_op1 (re_opcode_t op, unsigned char *loc, int arg);
-
-static void store_op2 (re_opcode_t op, unsigned char *loc, int arg1, int arg2);
-
-static void insert_op1 (re_opcode_t op, unsigned char *loc, int arg,
-                       unsigned char *end);
-
-static void insert_op2 (re_opcode_t op, unsigned char *loc, int arg1, int arg2,
-                       unsigned char *end);
-
-static boolean at_begline_loc_p (const char *pattern, const char *p,
-                                reg_syntax_t syntax);
-
-static boolean at_endline_loc_p (const char *p, const char *pend,
-                                reg_syntax_t syntax);
-
-static reg_errcode_t compile_range (const char **p_ptr, const char *pend,
-                                   char *translate, reg_syntax_t syntax,
-                                   unsigned char *b);
-
-/* Fetch the next character in the uncompiled pattern---translating it
-   if necessary.  Also cast from a signed character in the constant
-   string passed to us by the user to an unsigned char that we can use
-   as an array index (in, e.g., `translate').  */
-#define PATFETCH(c)                                                    \
-  do {if (p == pend) return REG_EEND;                                  \
-    c = (unsigned char) *p++;                                          \
-    if (translate) c = translate[c];                                   \
-  } while (0)
-
-/* Fetch the next character in the uncompiled pattern, with no
-   translation.  */
-#define PATFETCH_RAW(c)                                                        \
-  do {if (p == pend) return REG_EEND;                                  \
-    c = (unsigned char) *p++;                                          \
-  } while (0)
-
-/* Go backwards one character in the pattern.  */
-#define PATUNFETCH p--
-
-
-/* If `translate' is non-null, return translate[D], else just D.  We
-   cast the subscript to translate because some data is declared as
-   `char *', to avoid warnings when a string constant is passed.  But
-   when we use a character as a subscript we must make it unsigned.  */
-#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
-
-
-/* Macros for outputting the compiled pattern into `buffer'.  */
-
-/* If the buffer isn't allocated when it comes in, use this.  */
-#define INIT_BUF_SIZE  32
-
-/* Make sure we have at least N more bytes of space in buffer.  */
-#define GET_BUFFER_SPACE(n)                                            \
-    while (b - bufp->buffer + (n) > bufp->allocated)                   \
-      EXTEND_BUFFER ()
-
-/* Make sure we have one more byte of buffer space and then add C to it.  */
-#define BUF_PUSH(c)                                                    \
-  do {                                                                 \
-    GET_BUFFER_SPACE (1);                                              \
-    *b++ = (unsigned char) (c);                                                \
-  } while (0)
-
-
-/* Ensure we have two more bytes of buffer space and then append C1 and C2.  */
-#define BUF_PUSH_2(c1, c2)                                             \
-  do {                                                                 \
-    GET_BUFFER_SPACE (2);                                              \
-    *b++ = (unsigned char) (c1);                                       \
-    *b++ = (unsigned char) (c2);                                       \
-  } while (0)
-
-
-/* As with BUF_PUSH_2, except for three bytes.  */
-#define BUF_PUSH_3(c1, c2, c3)                                         \
-  do {                                                                 \
-    GET_BUFFER_SPACE (3);                                              \
-    *b++ = (unsigned char) (c1);                                       \
-    *b++ = (unsigned char) (c2);                                       \
-    *b++ = (unsigned char) (c3);                                       \
-  } while (0)
-
-
-/* Store a jump with opcode OP at LOC to location TO.  We store a
-   relative address offset by the three bytes the jump itself occupies.  */
-#define STORE_JUMP(op, loc, to) \
-  store_op1 (op, loc, (int)((to) - (loc) - 3))
-
-/* Likewise, for a two-argument jump.  */
-#define STORE_JUMP2(op, loc, to, arg) \
-  store_op2 (op, loc, (int)((to) - (loc) - 3), arg)
-
-/* Like `STORE_JUMP', but for inserting.  Assume `b' is the buffer end.  */
-#define INSERT_JUMP(op, loc, to) \
-  insert_op1 (op, loc, (int)((to) - (loc) - 3), b)
-
-/* Like `STORE_JUMP2', but for inserting.  Assume `b' is the buffer end.  */
-#define INSERT_JUMP2(op, loc, to, arg) \
-  insert_op2 (op, loc, (int)((to) - (loc) - 3), arg, b)
-
-
-/* This is not an arbitrary limit: the arguments which represent offsets
-   into the pattern are two bytes long.  So if 2^16 bytes turns out to
-   be too small, many things would have to change.  */
-#define MAX_BUF_SIZE (1L << 16)
-#define REALLOC realloc
-
-/* Extend the buffer by twice its current size via realloc and
-   reset the pointers that pointed into the old block to point to the
-   correct places in the new one.  If extending the buffer results in it
-   being larger than MAX_BUF_SIZE, then flag memory exhausted.  */
-#define EXTEND_BUFFER()                                                        \
-  do {                                                                 \
-    unsigned char *old_buffer = bufp->buffer;                          \
-    if (bufp->allocated == MAX_BUF_SIZE)                               \
-      return REG_ESIZE;                                                        \
-    bufp->allocated <<= 1;                                             \
-    if (bufp->allocated > MAX_BUF_SIZE)                                        \
-      bufp->allocated = MAX_BUF_SIZE;                                  \
-    bufp->buffer = (unsigned char *) REALLOC(bufp->buffer, bufp->allocated);\
-    if (bufp->buffer == NULL)                                          \
-      return REG_ESPACE;                                               \
-    /* If the buffer moved, move all the pointers into it.  */         \
-    if (old_buffer != bufp->buffer)                                    \
-      {                                                                        \
-       b = (b - old_buffer) + bufp->buffer;                            \
-       begalt = (begalt - old_buffer) + bufp->buffer;                  \
-       if (fixup_alt_jump)                                             \
-         fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
-       if (laststart)                                                  \
-         laststart = (laststart - old_buffer) + bufp->buffer;          \
-       if (pending_exact)                                              \
-         pending_exact = (pending_exact - old_buffer) + bufp->buffer;  \
-      }                                                                        \
-  } while (0)
-
-
-/* Since we have one byte reserved for the register number argument to
-   {start,stop}_memory, the maximum number of groups we can report
-   things about is what fits in that byte.  */
-#define MAX_REGNUM 255
-
-/* But patterns can have more than `MAX_REGNUM' registers.  We just
-   ignore the excess.  */
-typedef unsigned regnum_t;
-
-
-/* Macros for the compile stack.  */
-
-/* Since offsets can go either forwards or backwards, this type needs to
-   be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.  */
-/* int may be not enough when sizeof(int) == 2                           */
-typedef long pattern_offset_t;
-
-typedef struct {
-       pattern_offset_t begalt_offset;
-       pattern_offset_t fixup_alt_jump;
-       pattern_offset_t inner_group_offset;
-       pattern_offset_t laststart_offset;
-       regnum_t regnum;
-} compile_stack_elt_t;
-
-
-typedef struct {
-       compile_stack_elt_t *stack;
-       unsigned size;
-       unsigned avail;         /* Offset of next open position.  */
-} compile_stack_type;
-
-
-#define INIT_COMPILE_STACK_SIZE 32
-
-#define COMPILE_STACK_EMPTY  (compile_stack.avail == 0)
-#define COMPILE_STACK_FULL  (compile_stack.avail == compile_stack.size)
-
-/* The next available element.  */
-#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
-
-
-/* Set the bit for character C in a list.  */
-#define SET_LIST_BIT(c)                               \
-  (b[((unsigned char) (c)) / BYTEWIDTH]               \
-   |= 1 << (((unsigned char) c) % BYTEWIDTH))
-
-
-/* Get the next unsigned number in the uncompiled pattern.  */
-#define GET_UNSIGNED_NUMBER(num)                                       \
-  { if (p != pend)                                                     \
-     {                                                                 \
-       PATFETCH (c);                                                   \
-       while (ISDIGIT (c))                                             \
-        {                                                              \
-          if (num < 0)                                                 \
-             num = 0;                                                  \
-          num = num * 10 + c - '0';                                    \
-          if (p == pend)                                               \
-             break;                                                    \
-          PATFETCH (c);                                                \
-        }                                                              \
-       }                                                               \
-    }
-
-#define CHAR_CLASS_MAX_LENGTH  6       /* Namely, `xdigit'.  */
-
-#define IS_CHAR_CLASS(string)                                          \
-   (STREQ (string, "alpha") || STREQ (string, "upper")                 \
-    || STREQ (string, "lower") || STREQ (string, "digit")              \
-    || STREQ (string, "alnum") || STREQ (string, "xdigit")             \
-    || STREQ (string, "space") || STREQ (string, "print")              \
-    || STREQ (string, "punct") || STREQ (string, "graph")              \
-    || STREQ (string, "cntrl") || STREQ (string, "blank"))
-
-static boolean group_in_compile_stack (compile_stack_type
-                                      compile_stack, regnum_t regnum);
-
-/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
-   Returns one of error codes defined in `regex.h', or zero for success  */
-
-static reg_errcode_t regex_compile(pattern, size, syntax, bufp)
-const char *pattern;
-size_t size;
-reg_syntax_t syntax;
-struct re_pattern_buffer *bufp;
-{
-       /* We fetch characters from PATTERN here.  Even though PATTERN is
-          `char *' (i.e., signed), we declare these variables as unsigned, so
-          they can be reliably used as array indices.  */
-       register unsigned char c, c1;
-
-       /* A random tempory spot in PATTERN.  */
-       const char *p1;
-
-       /* Points to the end of the buffer, where we should append.  */
-       register unsigned char *b;
-
-       /* Keeps track of unclosed groups.  */
-       compile_stack_type compile_stack;
-
-       /* Points to the current (ending) position in the pattern.  */
-       const char *p = pattern;
-       const char *pend = pattern + size;
-
-       /* How to translate the characters in the pattern.  */
-       char *translate = bufp->translate;
-
-       /* Address of the count-byte of the most recently inserted `exactn'
-          command.  This makes it possible to tell if a new exact-match
-          character can be added to that command or if the character requires
-          a new `exactn' command.  */
-       unsigned char *pending_exact = 0;
-
-       /* Address of start of the most recently finished expression.
-          This tells, e.g., postfix * where to find the start of its
-          operand.  Reset at the beginning of groups and alternatives.  */
-       unsigned char *laststart = 0;
-
-       /* Address of beginning of regexp, or inside of last group.  */
-       unsigned char *begalt;
-
-       /* Place in the uncompiled pattern (i.e., the {) to
-          which to go back if the interval is invalid.  */
-       const char *beg_interval;
-
-       /* Address of the place where a forward jump should go to the end of
-          the containing expression.  Each alternative of an `or' -- except the
-          last -- ends with a forward jump of this sort.  */
-       unsigned char *fixup_alt_jump = 0;
-
-       /* Counts open-groups as they are encountered.  Remembered for the
-          matching close-group on the compile stack, so the same register
-          number is put in the stop_memory as the start_memory.  */
-       regnum_t regnum = 0;
-
-       /* Initialize the compile stack.  */
-       compile_stack.stack =
-           TALLOC(INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
-       if (compile_stack.stack == NULL)
-               return REG_ESPACE;
-
-       compile_stack.size = INIT_COMPILE_STACK_SIZE;
-       compile_stack.avail = 0;
-
-       /* Initialize the pattern buffer.  */
-       bufp->syntax = syntax;
-       bufp->fastmap_accurate = 0;
-       bufp->not_bol = bufp->not_eol = 0;
-
-       /* Set `used' to zero, so that if we return an error, the pattern
-          printer (for debugging) will think there's no pattern.  We reset it
-          at the end.  */
-       bufp->used = 0;
-
-       /* Always count groups, whether or not bufp->no_sub is set.  */
-       bufp->re_nsub = 0;
-
-       /* Initialize the syntax table.  */
-       init_syntax_once();
-
-       if (bufp->allocated == 0) {
-               if (bufp->buffer) {
-                       RETALLOC(bufp->buffer, INIT_BUF_SIZE,
-                                unsigned char);
-               } else { /* Caller did not allocate a buffer. Do it for them. */
-                       bufp->buffer =
-                           TALLOC(INIT_BUF_SIZE, unsigned char);
-               }
-               if (!bufp->buffer)
-                       return REG_ESPACE;
-
-               bufp->allocated = INIT_BUF_SIZE;
-       }
-
-       begalt = b = bufp->buffer;
-
-       /* Loop through the uncompiled pattern until we're at the end.  */
-       while (p != pend) {
-               PATFETCH(c);
-
-               switch (c) {
-               case '^':
-               {
-                       if (p == pattern + 1 ||
-                           syntax & RE_CONTEXT_INDEP_ANCHORS ||
-                           at_begline_loc_p(pattern, p, syntax))
-                               BUF_PUSH(begline);
-                       else
-                               goto normal_char;
-               }
-               break;
-
-               case '$':
-               {
-                       if (p == pend ||
-                           syntax & RE_CONTEXT_INDEP_ANCHORS ||
-                           at_endline_loc_p(p, pend, syntax))
-                               BUF_PUSH(endline);
-                       else
-                               goto normal_char;
-               }
-               break;
-
-               case '+':
-
-               case '?':
-               if ((syntax & RE_BK_PLUS_QM) ||
-                   (syntax & RE_LIMITED_OPS))
-                       goto normal_char;
-               handle_plus:
-
-               case '*':
-               /* If there is no previous pattern... */
-               if (!laststart) {
-                       if (syntax & RE_CONTEXT_INVALID_OPS)
-                               return REG_BADRPT;
-                       else if (!(syntax & RE_CONTEXT_INDEP_OPS))
-                               goto normal_char;
-               }
-
-               {
-                       /* Are we optimizing this jump?  */
-                       boolean keep_string_p = false;
-
-                       /* 1 means zero (many) matches is allowed.  */
-                       char zero_times_ok = 0, many_times_ok = 0;
-
-                       for (;;) {
-                               zero_times_ok |= c != '+';
-                               many_times_ok |= c != '?';
-
-                               if (p == pend)
-                                       break;
-
-                               PATFETCH(c);
-
-                               if (c == '*' || (!(syntax & RE_BK_PLUS_QM) &&
-                                   (c == '+' || c == '?')));
-
-                               else if (syntax & RE_BK_PLUS_QM && c == '\\') {
-                                       if (p == pend)
-                                               return REG_EESCAPE;
-
-                                       PATFETCH(c1);
-                                       if (!(c1 == '+' || c1 == '?')) {
-                                               PATUNFETCH;
-                                               PATUNFETCH;
-                                               break;
-                                       }
-
-                                       c = c1;
-                               } else {
-                                       PATUNFETCH;
-                                       break;
-                               }
-                       }
-
-                       if (!laststart)
-                               break;
-
-                       if (many_times_ok) {
-                               assert(p - 1 > pattern);
-
-                               /* Allocate the space for the jump.  */
-                               GET_BUFFER_SPACE(3);
-
-                               if (TRANSLATE(*(p - 2)) == TRANSLATE('.') &&
-                                   zero_times_ok && p < pend &&
-                                   TRANSLATE(*p) == TRANSLATE('\n') &&
-                                   !(syntax & RE_DOT_NEWLINE)) {
-                                       /* We have .*\n.  */
-                                       STORE_JUMP(jump, b, laststart);
-                                       keep_string_p = true;
-                               } else
-                                       STORE_JUMP(maybe_pop_jump, b,
-                                                  laststart - 3);
-
-                                       b += 3;
-                               }
-
-                               GET_BUFFER_SPACE(3);
-                               INSERT_JUMP(keep_string_p ?
-                                           on_failure_keep_string_jump :
-                                           on_failure_jump, laststart,
-                                           b + 3);
-                               pending_exact = 0;
-                               b += 3;
-
-                               if (!zero_times_ok) {
-                                       GET_BUFFER_SPACE(3);
-                                       INSERT_JUMP(dummy_failure_jump,
-                                                   laststart,
-                                                   laststart + 6);
-                                       b += 3;
-                               }
-                       }
-                       break;
-
-
-               case '.':
-               laststart = b;
-               BUF_PUSH(anychar);
-               break;
-
-               case '[':
-               {
-                       boolean had_char_class = false;
-
-                       if (p == pend)
-                               return REG_EBRACK;
-
-                       GET_BUFFER_SPACE(34);
-
-                       laststart = b;
-
-                       /* We test `*p == '^' twice, instead of using an if
-                          statement, so we only need one BUF_PUSH.  */
-                       BUF_PUSH(*p == '^' ? charset_not : charset);
-                       if (*p == '^')
-                               p++;
-
-                       p1 = p;
-
-                       /* Push the number of bytes in the bitmap.  */
-                       BUF_PUSH((1 << BYTEWIDTH) / BYTEWIDTH);
-
-                       /* Clear the whole map.  */
-                       bzero(b, (1 << BYTEWIDTH) / BYTEWIDTH);
-
-                       if ((re_opcode_t) b[-2] == charset_not
-                           && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
-                               SET_LIST_BIT('\n');
-
-                       /* Read in characters and ranges, setting map bits.  */
-                       for (;;) {
-                               if (p == pend)
-                                       return REG_EBRACK;
-
-                               PATFETCH(c);
-
-                               if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) &&
-                                   c == '\\') {
-                                       if (p == pend)
-                                               return REG_EESCAPE;
-
-                                       PATFETCH(c1);
-                                       SET_LIST_BIT(c1);
-                                       continue;
-                               }
-
-                               if (c == ']' && p != p1 + 1)
-                                       break;
-
-                               if (had_char_class && c == '-' && *p != ']')
-                                       return REG_ERANGE;
-
-                               if (c == '-' && !(p - 2 >= pattern &&
-                                   p[-2] == '[') && !(p - 3 >= pattern &&
-                                   p[-3] == '[' && p[-2] == '^') &&
-                                   *p != ']') {
-                                       reg_errcode_t ret =
-                                           compile_range(&p, pend, translate,
-                                                         syntax, b);
-                                       if (ret != REG_NOERROR)
-                                               return ret;
-                               }
-
-                               else if (p[0] == '-' && p[1] != ']') {
-                                       reg_errcode_t ret;
-
-                                       /* Move past the `-'.  */
-                                       PATFETCH(c1);
-
-                                       ret = compile_range(&p, pend, translate,
-                                                           syntax, b);
-                                       if (ret != REG_NOERROR)
-                                               return ret;
-                               }
-
-                               else if (syntax & RE_CHAR_CLASSES &&
-                                        c == '[' && *p == ':') {
-                                       char str[CHAR_CLASS_MAX_LENGTH + 1];
-
-                                       PATFETCH(c);
-                                       c1 = 0;
-
-                                       /* If pattern is `[[:'.  */
-                                       if (p == pend)
-                                               return REG_EBRACK;
-
-                                       for (;;) {
-                                               PATFETCH(c);
-                                               if (c == ':' || c == ']' ||
-                                                   p == pend || c1 ==
-                                                   CHAR_CLASS_MAX_LENGTH)
-                                                       break;
-                                               str[c1++] = c;
-                                       }
-                                       str[c1] = '\0';
-
-                                       if (c == ':' && *p == ']') {
-                                               int ch;
-                                               boolean is_alnum =
-                                                   STREQ(str, "alnum");
-                                               boolean is_alpha =
-                                                   STREQ(str, "alpha");
-                                               boolean is_blank =
-                                                   STREQ(str, "blank");
-                                               boolean is_cntrl =
-                                                   STREQ(str, "cntrl");
-                                               boolean is_digit =
-                                                   STREQ(str, "digit");
-                                               boolean is_graph =
-                                                   STREQ(str, "graph");
-                                               boolean is_lower =
-                                                   STREQ(str, "lower");
-                                               boolean is_print =
-                                                   STREQ(str, "print");
-                                               boolean is_punct =
-                                                   STREQ(str, "punct");
-                                               boolean is_space =
-                                                   STREQ(str, "space");
-                                               boolean is_upper =
-                                                   STREQ(str, "upper");
-                                               boolean is_xdigit =
-                                                   STREQ(str, "xdigit");
-
-                                               if (!IS_CHAR_CLASS(str))
-                                                       return REG_ECTYPE;
-
-                                               PATFETCH(c);
-
-                                               if (p == pend)
-                                                       return REG_EBRACK;
-
-                                               for (ch = 0; ch < 1 <<
-                                                    BYTEWIDTH; ch++) {
-                                                       if ((is_alnum &&
-                                                            ISALNUM(ch)) ||
-                                                           (is_alpha &&
-                                                            ISALPHA(ch)) ||
-                                                           (is_blank &&
-                                                            ISBLANK(ch)) ||
-                                                           (is_cntrl &&
-                                                            ISCNTRL(ch)) ||
-                                                           (is_digit &&
-                                                            ISDIGIT(ch)) ||
-                                                           (is_graph &&
-                                                            ISGRAPH(ch)) ||
-                                                           (is_lower &&
-                                                            ISLOWER(ch)) ||
-                                                           (is_print &&
-                                                            ISPRINT(ch)) ||
-                                                           (is_punct &&
-                                                            ISPUNCT(ch)) ||
-                                                           (is_space &&
-                                                            ISSPACE(ch)) ||
-                                                           (is_upper &&
-                                                            ISUPPER(ch)) ||
-                                                           (is_xdigit &&
-                                                            ISXDIGIT(ch)))
-                                                               SET_LIST_BIT(ch);
-                                               }
-                                               had_char_class =
-                                                   true;
-                                       } else {
-                                               c1++;
-                                               while (c1--)
-                                                       PATUNFETCH;
-                                               SET_LIST_BIT('[');
-                                               SET_LIST_BIT(':');
-                                               had_char_class = false;
-                                       }
-                               } else {
-                                       had_char_class = false;
-                                       SET_LIST_BIT(c);
-                               }
-                       }
-
-                       while ((int) b[-1] > 0
-                              && b[b[-1] - 1] == 0)
-                               b[-1]--;
-                       b += b[-1];
-               }
-               break;
-
-               case '(':
-               if (syntax & RE_NO_BK_PARENS)
-                       goto handle_open;
-               else
-                       goto normal_char;
-
-
-               case ')':
-               if (syntax & RE_NO_BK_PARENS)
-                       goto handle_close;
-               else
-                       goto normal_char;
-
-
-               case '\n':
-               if (syntax & RE_NEWLINE_ALT)
-                       goto handle_alt;
-               else
-                       goto normal_char;
-
-
-               case '|':
-               if (syntax & RE_NO_BK_VBAR)
-                       goto handle_alt;
-               else
-                       goto normal_char;
-
-
-               case '{':
-               if (syntax & RE_INTERVALS
-                   && syntax & RE_NO_BK_BRACES)
-                       goto handle_interval;
-               else
-                       goto normal_char;
-
-
-               case '\\':
-               if (p == pend)
-                       return REG_EESCAPE;
-
-               PATFETCH_RAW(c);
-
-               switch (c) {
-                       case '(':
-                       if (syntax & RE_NO_BK_PARENS)
-                               goto normal_backslash;
-
-                     handle_open:
-                       bufp->re_nsub++;
-                       regnum++;
-
-                       if (COMPILE_STACK_FULL) {
-                               RETALLOC(compile_stack.stack,
-                                        compile_stack.size << 1,
-                                        compile_stack_elt_t);
-                               if (compile_stack.stack == NULL)
-                                       return REG_ESPACE;
-
-                               compile_stack.size <<= 1;
-                       }
-
-                       COMPILE_STACK_TOP.begalt_offset =
-                           begalt - bufp->buffer;
-                       COMPILE_STACK_TOP.fixup_alt_jump =
-                           fixup_alt_jump ? fixup_alt_jump -
-                           bufp->buffer + 1 : 0;
-                       COMPILE_STACK_TOP.laststart_offset =
-                           b - bufp->buffer;
-                       COMPILE_STACK_TOP.regnum = regnum;
-
-                       if (regnum <= MAX_REGNUM) {
-                               COMPILE_STACK_TOP.inner_group_offset =
-                                   b - bufp->buffer + 2;
-                               BUF_PUSH_3(start_memory, regnum, 0);
-                       }
-
-                       compile_stack.avail++;
-
-                       fixup_alt_jump = 0;
-                       laststart = 0;
-                       begalt = b;
-                       pending_exact = 0;
-                       break;
-
-                       case ')':
-                       if (syntax & RE_NO_BK_PARENS)
-                               goto normal_backslash;
-
-                       if (COMPILE_STACK_EMPTY) {
-                               if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
-                                       goto normal_backslash;
-                               else
-                                       return REG_ERPAREN;
-                       }
-
-                     handle_close:
-                       if (fixup_alt_jump) {
-                               BUF_PUSH(push_dummy_failure);
-                               STORE_JUMP(jump_past_alt,
-                                          fixup_alt_jump, b - 1);
-                       }
-
-                       if (COMPILE_STACK_EMPTY) {
-                               if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
-                                       goto normal_char;
-                               else
-                                       return REG_ERPAREN;
-                       }
-
-                       assert(compile_stack.avail != 0);
-                       {
-                               regnum_t this_group_regnum;
-
-                               compile_stack.avail--;
-                               begalt = bufp->buffer +
-                                           COMPILE_STACK_TOP.begalt_offset;
-                               fixup_alt_jump =
-                                           COMPILE_STACK_TOP.fixup_alt_jump ?
-                                           bufp->buffer + COMPILE_STACK_TOP.
-                                           fixup_alt_jump - 1 : 0;
-                               laststart = bufp->buffer +
-                                           COMPILE_STACK_TOP.laststart_offset;
-                               this_group_regnum = COMPILE_STACK_TOP.regnum;
-                               pending_exact = 0;
-
-                               if (this_group_regnum <= MAX_REGNUM) {
-                                       unsigned char
-                                       *inner_group_loc = bufp->buffer +
-                                               COMPILE_STACK_TOP.
-                                               inner_group_offset;
-
-                                       *inner_group_loc = regnum -
-                                               this_group_regnum;
-                                       BUF_PUSH_3(stop_memory,
-                                                  this_group_regnum,
-                                                  regnum - this_group_regnum);
-                               }
-                       }
-                       break;
-
-
-                       case '|':       /* `\|'.  */
-                       if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
-                               goto normal_backslash;
-                     handle_alt:
-                       if (syntax & RE_LIMITED_OPS)
-                               goto normal_char;
-
-                       GET_BUFFER_SPACE(3);
-                       INSERT_JUMP(on_failure_jump, begalt, b + 6);
-                       pending_exact = 0;
-                       b += 3;
-
-                       if (fixup_alt_jump)
-                               STORE_JUMP(jump_past_alt, fixup_alt_jump, b);
-
-                       fixup_alt_jump = b;
-                       GET_BUFFER_SPACE(3);
-                       b += 3;
-
-                       laststart = 0;
-                       begalt = b;
-                       break;
-
-
-                       case '{':
-                       /* If \{ is a literal.  */
-                       if (!(syntax & RE_INTERVALS) || ((syntax & RE_INTERVALS)
-                           && (syntax & RE_NO_BK_BRACES))
-                           || (p - 2 == pattern && p == pend))
-                               goto normal_backslash;
-
-                     handle_interval:
-                       {
-                               int lower_bound = -1, upper_bound = -1;
-                               beg_interval = p - 1;
-
-                               if (p == pend) {
-                                       if (syntax & RE_NO_BK_BRACES)
-                                               goto unfetch_interval;
-                                       else
-                                               return REG_EBRACE;
-                               }
-
-                               GET_UNSIGNED_NUMBER(lower_bound);
-
-                               if (c == ',') {
-                                       GET_UNSIGNED_NUMBER(upper_bound);
-                                       if (upper_bound < 0)
-                                               upper_bound = RE_DUP_MAX;
-                               } else
-                                       upper_bound = lower_bound;
-
-                               if (lower_bound < 0 || upper_bound > RE_DUP_MAX
-                                   || lower_bound > upper_bound) {
-                                       if (syntax & RE_NO_BK_BRACES)
-                                               goto unfetch_interval;
-                                       else
-                                               return REG_BADBR;
-                               }
-
-                               if (!(syntax & RE_NO_BK_BRACES)) {
-                                       if (c != '\\')
-                                               return REG_EBRACE;
-
-                                       PATFETCH(c);
-                               }
-
-                               if (c != '}') {
-                                       if (syntax & RE_NO_BK_BRACES)
-                                               goto unfetch_interval;
-                                       else
-                                               return REG_BADBR;
-                               }
-
-                               if (!laststart) {
-                                       if (syntax & RE_CONTEXT_INVALID_OPS)
-                                               return REG_BADRPT;
-                                       else if (syntax & RE_CONTEXT_INDEP_OPS)
-                                               laststart = b;
-                                       else
-                                               goto unfetch_interval;
-                               }
-
-                               if (upper_bound == 0) {
-                                       GET_BUFFER_SPACE(3);
-                                       INSERT_JUMP(jump, laststart, b + 3);
-                                       b += 3;
-                               }
-
-                               else {
-                                       unsigned nbytes =
-                                           10 + (upper_bound > 1) * 10;
-
-                                       GET_BUFFER_SPACE(nbytes);
-
-                                       INSERT_JUMP2(succeed_n, laststart,
-                                                    b + 5 + (upper_bound >
-                                                     1) * 5, lower_bound);
-                                       b += 5;
-
-                                       insert_op2(set_number_at, laststart, 5,
-                                                  lower_bound, b);
-                                       b += 5;
-
-                                       if (upper_bound > 1) {
-                                               STORE_JUMP2(jump_n, b,
-                                                           laststart + 5,
-                                                           upper_bound - 1);
-                                               b += 5;
-
-                                               insert_op2(set_number_at,
-                                                          laststart,
-                                                          b - laststart,
-                                                          upper_bound - 1, b);
-                                               b += 5;
-                                       }
-                               }
-                               pending_exact = 0;
-                               beg_interval = NULL;
-                       }
-                       break;
-
-                     unfetch_interval:
-                       assert(beg_interval);
-                       p = beg_interval;
-                       beg_interval = NULL;
-
-                       /* normal_char and normal_backslash need `c'.  */
-                       PATFETCH(c);
-
-                       if (!(syntax & RE_NO_BK_BRACES)) {
-                               if (p > pattern && p[-1] == '\\')
-                                       goto normal_backslash;
-                       }
-                       goto normal_char;
-
-                       case 'w':
-                               if (re_syntax_options & RE_NO_GNU_OPS)
-                                       goto normal_char;
-                               laststart = b;
-                               BUF_PUSH(wordchar);
-                               break;
-
-
-                       case 'W':
-                               if (re_syntax_options & RE_NO_GNU_OPS)
-                                       goto normal_char;
-                               laststart = b;
-                               BUF_PUSH(notwordchar);
-                               break;
-
-
-                       case '<':
-                               if (re_syntax_options & RE_NO_GNU_OPS)
-                                       goto normal_char;
-                               BUF_PUSH(wordbeg);
-                               break;
-
-                       case '>':
-                               if (re_syntax_options & RE_NO_GNU_OPS)
-                                       goto normal_char;
-                               BUF_PUSH(wordend);
-                               break;
-
-                       case 'b':
-                               if (re_syntax_options & RE_NO_GNU_OPS)
-                                       goto normal_char;
-                               BUF_PUSH(wordbound);
-                               break;
-
-                       case 'B':
-                               if (re_syntax_options & RE_NO_GNU_OPS)
-                                       goto normal_char;
-                               BUF_PUSH(notwordbound);
-                               break;
-
-                       case '`':
-                               if (re_syntax_options & RE_NO_GNU_OPS)
-                                       goto normal_char;
-                               BUF_PUSH(begbuf);
-                               break;
-
-                       case '\'':
-                               if (re_syntax_options & RE_NO_GNU_OPS)
-                                       goto normal_char;
-                               BUF_PUSH(endbuf);
-                               break;
-
-                       case '1':
-                       case '2':
-                       case '3':
-                       case '4':
-                       case '5':
-                       case '6':
-                       case '7':
-                       case '8':
-                       case '9':
-                               if (syntax & RE_NO_BK_REFS)
-                                       goto normal_char;
-
-                               c1 = c - '0';
-
-                               if (c1 > regnum)
-                                       return REG_ESUBREG;
-
-                               /* Can't back reference to a subexpression if inside of it.  */
-                               if (group_in_compile_stack
-                                   (compile_stack, (regnum_t) c1))
-                                       goto normal_char;
-
-                               laststart = b;
-                               BUF_PUSH_2(duplicate, c1);
-                               break;
-
-
-                       case '+':
-                       case '?':
-                               if (syntax & RE_BK_PLUS_QM)
-                                       goto handle_plus;
-                               else
-                                       goto normal_backslash;
-
-                       default:
-                             normal_backslash:
-                               /* You might think it would be useful for \ to mean
-                                  not to translate; but if we don't translate it
-                                  it will never match anything.  */
-                               c = TRANSLATE(c);
-                               goto normal_char;
-                       }
-                       break;
-
-
-               default:
-                       /* Expects the character in `c'.  */
-                     normal_char:
-                       /* If no exactn currently being built.  */
-                       if (!pending_exact
-                           /* If last exactn not at current position.  */
-                           || pending_exact + *pending_exact + 1 != b
-                           /* We have only one byte following the exactn for the count.  */
-                           || *pending_exact == (1 << BYTEWIDTH) - 1
-                           /* If followed by a repetition operator.  */
-                           || *p == '*' || *p == '^'
-                           || ((syntax & RE_BK_PLUS_QM)
-                               ? *p == '\\' && (p[1] == '+'
-                                                || p[1] == '?')
-                               : (*p == '+' || *p == '?'))
-                           || ((syntax & RE_INTERVALS)
-                               && ((syntax & RE_NO_BK_BRACES)
-                                   ? *p == '{'
-                                   : (p[0] == '\\' && p[1] == '{')))) {
-                               /* Start building a new exactn.  */
-
-                               laststart = b;
-
-                               BUF_PUSH_2(exactn, 0);
-                               pending_exact = b - 1;
-                       }
-
-                       BUF_PUSH(c);
-                       (*pending_exact)++;
-                       break;
-               }               /* switch (c) */
-       }                       /* while p != pend */
-
-
-       /* Through the pattern now.  */
-
-       if (fixup_alt_jump)
-               STORE_JUMP(jump_past_alt, fixup_alt_jump, b);
-
-       if (!COMPILE_STACK_EMPTY)
-               return REG_EPAREN;
-
-       free(compile_stack.stack);
-
-       /* We have succeeded; set the length of the buffer.  */
-       bufp->used = b - bufp->buffer;
-
-       return REG_NOERROR;
-}                              /* regex_compile */
-
-/* Subroutines for `regex_compile'.  */
-
-/* Store OP at LOC followed by two-byte integer parameter ARG.  */
-
-static void store_op1(op, loc, arg)
-re_opcode_t op;
-unsigned char *loc;
-int arg;
-{
-       *loc = (unsigned char) op;
-       STORE_NUMBER(loc + 1, arg);
-}
-
-
-/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2.  */
-
-static void store_op2(op, loc, arg1, arg2)
-re_opcode_t op;
-unsigned char *loc;
-int arg1, arg2;
-{
-       *loc = (unsigned char) op;
-       STORE_NUMBER(loc + 1, arg1);
-       STORE_NUMBER(loc + 3, arg2);
-}
-
-
-/* Copy the bytes from LOC to END to open up three bytes of space at LOC
-   for OP followed by two-byte integer parameter ARG.  */
-
-static void insert_op1(op, loc, arg, end)
-re_opcode_t op;
-unsigned char *loc;
-int arg;
-unsigned char *end;
-{
-       register unsigned char *pfrom = end;
-       register unsigned char *pto = end + 3;
-
-       while (pfrom != loc)
-               *--pto = *--pfrom;
-
-       store_op1(op, loc, arg);
-}
-
-
-/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2.  */
-
-static void insert_op2(op, loc, arg1, arg2, end)
-re_opcode_t op;
-unsigned char *loc;
-int arg1, arg2;
-unsigned char *end;
-{
-       register unsigned char *pfrom = end;
-       register unsigned char *pto = end + 5;
-
-       while (pfrom != loc)
-               *--pto = *--pfrom;
-
-       store_op2(op, loc, arg1, arg2);
-}
-
-
-/* P points to just after a ^ in PATTERN.  Return true if that ^ comes
-   after an alternative or a begin-subexpression.  We assume there is at
-   least one character before the ^.  */
-
-static boolean at_begline_loc_p(pattern, p, syntax)
-const char *pattern, *p;
-reg_syntax_t syntax;
-{
-       const char *prev = p - 2;
-       boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
-
-       return
-           /* After a subexpression?  */
-           (*prev == '('
-            && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
-           /* After an alternative?  */
-           || (*prev == '|'
-               && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
-}
-
-
-/* The dual of at_begline_loc_p.  This one is for $.  We assume there is
-   at least one character after the $, i.e., `P < PEND'.  */
-
-static boolean at_endline_loc_p(p, pend, syntax)
-const char *p, *pend;
-reg_syntax_t syntax;
-{
-       const char *next = p;
-       boolean next_backslash = *next == '\\';
-       const char *next_next = p + 1 < pend ? p + 1 : NULL;
-
-       return
-           /* Before a subexpression?  */
-           (syntax & RE_NO_BK_PARENS ? *next == ')'
-            : next_backslash && next_next && *next_next == ')')
-           /* Before an alternative?  */
-           || (syntax & RE_NO_BK_VBAR ? *next == '|'
-               : next_backslash && next_next && *next_next == '|');
-}
-
-
-/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
-   false if it's not.  */
-
-static boolean group_in_compile_stack(compile_stack, regnum)
-compile_stack_type compile_stack;
-regnum_t regnum;
-{
-       int this_element;
-
-       for (this_element = compile_stack.avail - 1;
-            this_element >= 0; this_element--)
-               if (compile_stack.stack[this_element].regnum == regnum)
-                       return true;
-
-       return false;
-}
-
-
-/* Read the ending character of a range (in a bracket expression) from the
-   uncompiled pattern *P_PTR (which ends at PEND).  We assume the
-   starting character is in `P[-2]'.  (`P[-1]' is the character `-'.)
-   Then we set the translation of all bits between the starting and
-   ending characters (inclusive) in the compiled pattern B.
-
-   Return an error code.
-
-   We use these short variable names so we can use the same macros as
-   `regex_compile' itself.  */
-
-static reg_errcode_t compile_range(p_ptr, pend, translate, syntax, b)
-const char **p_ptr, *pend;
-char *translate;
-reg_syntax_t syntax;
-unsigned char *b;
-{
-       unsigned this_char;
-
-       const char *p = *p_ptr;
-       int range_start, range_end;
-
-       if (p == pend)
-               return REG_ERANGE;
-
-       /* Even though the pattern is a signed `char *', we need to fetch
-          with unsigned char *'s; if the high bit of the pattern character
-          is set, the range endpoints will be negative if we fetch using a
-          signed char *.
-
-          We also want to fetch the endpoints without translating them; the
-          appropriate translation is done in the bit-setting loop below.  */
-       range_start = ((unsigned char *) p)[-2];
-       range_end = ((unsigned char *) p)[0];
-
-       /* Have to increment the pointer into the pattern string, so the
-          caller isn't still at the ending character.  */
-       (*p_ptr)++;
-
-       /* If the start is after the end, the range is empty.  */
-       if (range_start > range_end)
-               return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE :
-                   REG_NOERROR;
-
-       /* Here we see why `this_char' has to be larger than an `unsigned
-          char' -- the range is inclusive, so if `range_end' == 0xff
-          (assuming 8-bit characters), we would otherwise go into an infinite
-          loop, since all characters <= 0xff.  */
-       for (this_char = range_start; this_char <= range_end; this_char++) {
-               SET_LIST_BIT(TRANSLATE(this_char));
-       }
-       return REG_NOERROR;
-}
-
-/* Failure stack declarations and macros; both re_compile_fastmap and
-   re_match_2 use a failure stack.  These have to be macros because of
-   REGEX_ALLOCATE.  */
-
-
-/* Number of failure points for which to initially allocate space
-   when matching.  If this number is exceeded, we allocate more
-   space, so it is not a hard limit.  */
-#define INIT_FAILURE_ALLOC 5
-
-/* Roughly the maximum number of failure points on the stack.  Would be
-   exactly that if always used MAX_FAILURE_SPACE each time we failed.
-   This is a variable only so users of regex can assign to it; we never
-   change it ourselves.  */
-int re_max_failures = 2000;
-
-typedef const unsigned char *fail_stack_elt_t;
-
-typedef struct {
-       fail_stack_elt_t *stack;
-       unsigned size;
-       unsigned avail;         /* Offset of next open position.  */
-} fail_stack_type;
-
-#define FAIL_STACK_EMPTY()     (fail_stack.avail == 0)
-#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
-#define FAIL_STACK_FULL()      (fail_stack.avail == fail_stack.size)
-#define FAIL_STACK_TOP()       (fail_stack.stack[fail_stack.avail])
-
-
-/* Initialize `fail_stack'.  Do `return -2' if the alloc fails.  */
-
-#define INIT_FAIL_STACK()                                              \
-  do {                                                                 \
-    fail_stack.stack = (fail_stack_elt_t *)                            \
-      REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
-                                                                       \
-    if (fail_stack.stack == NULL)                                      \
-      return -2;                                                       \
-                                                                       \
-    fail_stack.size = INIT_FAILURE_ALLOC;                              \
-    fail_stack.avail = 0;                                              \
-  } while (0)
-
-
-/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
-
-   Return 1 if succeeds, and 0 if either ran out of memory
-   allocating space for it or it was already too large.
-
-   REGEX_REALLOCATE requires `destination' be declared.   */
-
-#define DOUBLE_FAIL_STACK(fail_stack)                                  \
-  ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS             \
-   ? 0                                                                 \
-   : ((fail_stack).stack = (fail_stack_elt_t *)                                \
-       REGEX_REALLOCATE ((fail_stack).stack,                           \
-         (fail_stack).size * sizeof (fail_stack_elt_t),                \
-         ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)),        \
-                                                                       \
-      (fail_stack).stack == NULL                                       \
-      ? 0                                                              \
-      : ((fail_stack).size <<= 1,                                      \
-        1)))
-
-
-/* Push PATTERN_OP on FAIL_STACK.
-
-   Return 1 if was able to do so and 0 if ran out of memory allocating
-   space to do so.  */
-#define PUSH_PATTERN_OP(pattern_op, fail_stack)                                \
-  ((FAIL_STACK_FULL ()                                                 \
-    && !DOUBLE_FAIL_STACK (fail_stack))                                        \
-    ? 0                                                                        \
-    : ((fail_stack).stack[(fail_stack).avail++] = pattern_op,          \
-       1))
-
-/* This pushes an item onto the failure stack.  Must be a four-byte
-   value.  Assumes the variable `fail_stack'.  Probably should only
-   be called from within `PUSH_FAILURE_POINT'.  */
-#define PUSH_FAILURE_ITEM(item)                                                \
-  fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
-
-/* The complement operation.  Assumes `fail_stack' is nonempty.  */
-#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
-
-/* Used to omit pushing failure point id's when we're not debugging.  */
-#define DEBUG_PUSH(item)
-#define DEBUG_POP(item_addr)
-
-
-/* Push the information about the state we will need
-   if we ever fail back to it.
-
-   Requires variables fail_stack, regstart, regend, reg_info, and
-   num_regs be declared.  DOUBLE_FAIL_STACK requires `destination' be
-   declared.
-
-   Does `return FAILURE_CODE' if runs out of memory.  */
-
-#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code)  \
-  do {                                                                 \
-    char *destination;                                                 \
-    /* Must be int, so when we don't save any registers, the arithmetic        \
-       of 0 + -1 isn't done as unsigned.  */                           \
-    /* Can't be int, since there is not a shred of a guarantee that int \
-       is wide enough to hold a value of something to which pointer can \
-       be assigned */                                                  \
-    s_reg_t this_reg;                                                  \
-                                                                       \
-    DEBUG_STATEMENT (failure_id++);                                    \
-    DEBUG_STATEMENT (nfailure_points_pushed++);                                \
-    DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id);          \
-    DEBUG_PRINT2 ("  Before push, next avail: %d\n", (fail_stack).avail);\
-    DEBUG_PRINT2 ("                     size: %d\n", (fail_stack).size);\
-                                                                       \
-    DEBUG_PRINT2 ("  slots needed: %d\n", NUM_FAILURE_ITEMS);          \
-    DEBUG_PRINT2 ("     available: %d\n", REMAINING_AVAIL_SLOTS);      \
-                                                                       \
-    /* Ensure we have enough space allocated for what we will push.  */        \
-    while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS)                  \
-      {                                                                        \
-       if (!DOUBLE_FAIL_STACK (fail_stack))                    \
-         return failure_code;                                          \
-                                                                       \
-       DEBUG_PRINT2 ("\n  Doubled stack; size now: %d\n",              \
-                      (fail_stack).size);                              \
-       DEBUG_PRINT2 ("  slots available: %d\n", REMAINING_AVAIL_SLOTS);\
-      }
-
-#define PUSH_FAILURE_POINT2(pattern_place, string_place, failure_code) \
-    /* Push the info, starting with the registers.  */                 \
-    DEBUG_PRINT1 ("\n");                                               \
-                                                                       \
-    PUSH_FAILURE_POINT_LOOP ();                                                \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing  low active reg: %d\n", lowest_active_reg);\
-    PUSH_FAILURE_ITEM (lowest_active_reg);                             \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing high active reg: %d\n", highest_active_reg);\
-    PUSH_FAILURE_ITEM (highest_active_reg);                            \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing pattern 0x%x: ", pattern_place);          \
-    DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend);          \
-    PUSH_FAILURE_ITEM (pattern_place);                                 \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing string 0x%x: `", string_place);           \
-    DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2,  \
-                                size2);                                \
-    DEBUG_PRINT1 ("'\n");                                              \
-    PUSH_FAILURE_ITEM (string_place);                                  \
-                                                                       \
-    DEBUG_PRINT2 ("  Pushing failure id: %u\n", failure_id);           \
-    DEBUG_PUSH (failure_id);                                           \
-  } while (0)
-
-/*  Pulled out of PUSH_FAILURE_POINT() to shorten the definition
-    of that macro.  (for VAX C) */
-#define PUSH_FAILURE_POINT_LOOP()                                      \
-    for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
-        this_reg++)                                                    \
-      {                                                                        \
-       DEBUG_PRINT2 ("  Pushing reg: %d\n", this_reg);                 \
-       DEBUG_STATEMENT (num_regs_pushed++);                            \
-                                                                       \
-       DEBUG_PRINT2 ("    start: 0x%x\n", regstart[this_reg]);         \
-       PUSH_FAILURE_ITEM (regstart[this_reg]);                         \
-                                                                       \
-       DEBUG_PRINT2 ("    end: 0x%x\n", regend[this_reg]);             \
-       PUSH_FAILURE_ITEM (regend[this_reg]);                           \
-                                                                       \
-       DEBUG_PRINT2 ("    info: 0x%x\n      ", reg_info[this_reg]);    \
-       DEBUG_PRINT2 (" match_null=%d",                                 \
-                     REG_MATCH_NULL_STRING_P (reg_info[this_reg]));    \
-       DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg]));    \
-       DEBUG_PRINT2 (" matched_something=%d",                          \
-                     MATCHED_SOMETHING (reg_info[this_reg]));          \
-       DEBUG_PRINT2 (" ever_matched=%d",                               \
-                     EVER_MATCHED_SOMETHING (reg_info[this_reg]));     \
-       DEBUG_PRINT1 ("\n");                                            \
-       PUSH_FAILURE_ITEM (reg_info[this_reg].word);                    \
-      }
-
-/* This is the number of items that are pushed and popped on the stack
-   for each register.  */
-#define NUM_REG_ITEMS  3
-
-/* Individual items aside from the registers.  */
-#define NUM_NONREG_ITEMS 4
-
-/* We push at most this many items on the stack.  */
-#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
-
-/* We actually push this many items.  */
-#define NUM_FAILURE_ITEMS                                              \
-  ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS                \
-    + NUM_NONREG_ITEMS)
-
-/* How many items can still be added to the stack without overflowing it.  */
-#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
-
-
-/* Pops what PUSH_FAIL_STACK pushes.
-
-   We restore into the parameters, all of which should be lvalues:
-     STR -- the saved data position.
-     PAT -- the saved pattern position.
-     LOW_REG, HIGH_REG -- the highest and lowest active registers.
-     REGSTART, REGEND -- arrays of string positions.
-     REG_INFO -- array of information about each subexpression.
-
-   Also assumes the variables `fail_stack' and (if debugging), `bufp',
-   `pend', `string1', `size1', `string2', and `size2'.  */
-
-#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
-{                                                                      \
-  DEBUG_STATEMENT (fail_stack_elt_t failure_id;)                       \
-  s_reg_t this_reg;                                                    \
-  const unsigned char *string_temp;                                    \
-                                                                       \
-  assert (!FAIL_STACK_EMPTY ());                                       \
-                                                                       \
-  /* Remove failure points and point to how many regs pushed.  */      \
-  DEBUG_PRINT1 ("POP_FAILURE_POINT:\n");                               \
-  DEBUG_PRINT2 ("  Before pop, next avail: %d\n", fail_stack.avail);   \
-  DEBUG_PRINT2 ("                    size: %d\n", fail_stack.size);    \
-                                                                       \
-  assert (fail_stack.avail >= NUM_NONREG_ITEMS);                       \
-                                                                       \
-  DEBUG_POP (&failure_id);                                             \
-  DEBUG_PRINT2 ("  Popping failure id: %u\n", failure_id);             \
-                                                                       \
-  /* If the saved string location is NULL, it came from an             \
-     on_failure_keep_string_jump opcode, and we want to throw away the \
-     saved NULL, thus retaining our current position in the string.  */        \
-  string_temp = POP_FAILURE_ITEM ();                                   \
-  if (string_temp != NULL)                                             \
-    str = (const char *) string_temp;                                  \
-                                                                       \
-  DEBUG_PRINT2 ("  Popping string 0x%x: `", str);                      \
-  DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2);     \
-  DEBUG_PRINT1 ("'\n");                                                        \
-                                                                       \
-  pat = (unsigned char *) POP_FAILURE_ITEM ();                         \
-  DEBUG_PRINT2 ("  Popping pattern 0x%x: ", pat);                      \
-  DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend);                      \
-                                                                       \
-  POP_FAILURE_POINT2 (low_reg, high_reg, regstart, regend, reg_info);
-
-/*  Pulled out of POP_FAILURE_POINT() to shorten the definition
-    of that macro.  (for MSC 5.1) */
-#define POP_FAILURE_POINT2(low_reg, high_reg, regstart, regend, reg_info) \
-                                                                       \
-  /* Restore register info.  */                                                \
-  high_reg = (active_reg_t) POP_FAILURE_ITEM ();                       \
-  DEBUG_PRINT2 ("  Popping high active reg: %d\n", high_reg);          \
-                                                                       \
-  low_reg = (active_reg_t) POP_FAILURE_ITEM ();                                \
-  DEBUG_PRINT2 ("  Popping  low active reg: %d\n", low_reg);           \
-                                                                       \
-  for (this_reg = high_reg; this_reg >= low_reg; this_reg--)           \
-    {                                                                  \
-      DEBUG_PRINT2 ("    Popping reg: %d\n", this_reg);                        \
-                                                                       \
-      reg_info[this_reg].word = POP_FAILURE_ITEM ();                   \
-      DEBUG_PRINT2 ("      info: 0x%x\n", reg_info[this_reg]);         \
-                                                                       \
-      regend[this_reg] = (const char *) POP_FAILURE_ITEM ();           \
-      DEBUG_PRINT2 ("      end: 0x%x\n", regend[this_reg]);            \
-                                                                       \
-      regstart[this_reg] = (const char *) POP_FAILURE_ITEM ();         \
-      DEBUG_PRINT2 ("      start: 0x%x\n", regstart[this_reg]);                \
-    }                                                                  \
-                                                                       \
-  DEBUG_STATEMENT (nfailure_points_popped++);                          \
-}                              /* POP_FAILURE_POINT */
-
-/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
-   BUFP.  A fastmap records which of the (1 << BYTEWIDTH) possible
-   characters can start a string that matches the pattern.  This fastmap
-   is used by re_search to skip quickly over impossible starting points.
-
-   The caller must supply the address of a (1 << BYTEWIDTH)-byte data
-   area as BUFP->fastmap.
-
-   We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
-   the pattern buffer.
-
-   Returns 0 if we succeed, -2 if an internal error.   */
-
-int re_compile_fastmap(bufp)
-struct re_pattern_buffer *bufp;
-{
-       int j, k;
-       fail_stack_type fail_stack;
-       char *destination;
-       /* We don't push any register information onto the failure stack.  */
-       unsigned num_regs = 0;
-
-       register char *fastmap = bufp->fastmap;
-       unsigned char *pattern = bufp->buffer;
-       const unsigned char *p = pattern;
-       register unsigned char *pend = pattern + bufp->used;
-
-       /* Assume that each path through the pattern can be null until
-          proven otherwise.  We set this false at the bottom of switch
-          statement, to which we get only if a particular path doesn't
-          match the empty string.  */
-       boolean path_can_be_null = true;
-
-       /* We aren't doing a `succeed_n' to begin with.  */
-       boolean succeed_n_p = false;
-
-       assert(fastmap != NULL && p != NULL);
-
-       INIT_FAIL_STACK();
-       bzero(fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid.  */
-       bufp->fastmap_accurate = 1;     /* It will be when we're done.  */
-       bufp->can_be_null = 0;
-
-       while (p != pend || !FAIL_STACK_EMPTY()) {
-               if (p == pend) {
-                       bufp->can_be_null |= path_can_be_null;
-
-                       /* Reset for next path.  */
-                       path_can_be_null = true;
-
-                       p = fail_stack.stack[--fail_stack.avail];
-               }
-
-               /* We should never be about to go beyond the end of the pattern.  */
-               assert(p < pend);
-
-               switch ((re_opcode_t) * p++) {
-
-                       /* I guess the idea here is to simply not bother with a fastmap
-                          if a backreference is used, since it's too hard to figure out
-                          the fastmap for the corresponding group.  Setting
-                          `can_be_null' stops `re_search_2' from using the fastmap, so
-                          that is all we do.  */
-               case duplicate:
-                       bufp->can_be_null = 1;
-                       return 0;
-
-
-                       /* Following are the cases which match a character.  These end
-                          with `break'.  */
-
-               case exactn:
-                       fastmap[p[1]] = 1;
-                       break;
-
-
-               case charset:
-                       for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
-                               if (p[j / BYTEWIDTH] &
-                                   (1 << (j % BYTEWIDTH)))
-                                       fastmap[j] = 1;
-                       break;
-
-
-               case charset_not:
-                       /* Chars beyond end of map must be allowed.  */
-                       for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
-                               fastmap[j] = 1;
-
-                       for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
-                               if (!
-                                   (p[j / BYTEWIDTH] &
-                                    (1 << (j % BYTEWIDTH))))
-                                       fastmap[j] = 1;
-                       break;
-
-
-               case wordchar:
-                       for (j = 0; j < (1 << BYTEWIDTH); j++)
-                               if (SYNTAX(j) == Sword)
-                                       fastmap[j] = 1;
-                       break;
-
-
-               case notwordchar:
-                       for (j = 0; j < (1 << BYTEWIDTH); j++)
-                               if (SYNTAX(j) != Sword)
-                                       fastmap[j] = 1;
-                       break;
-
-
-               case anychar:
-                       /* `.' matches anything ...  */
-                       for (j = 0; j < (1 << BYTEWIDTH); j++)
-                               fastmap[j] = 1;
-
-                       /* ... except perhaps newline.  */
-                       if (!(bufp->syntax & RE_DOT_NEWLINE))
-                               fastmap['\n'] = 0;
-
-                       /* Return if we have already set `can_be_null'; if we have,
-                          then the fastmap is irrelevant.  Something's wrong here.  */
-                       else if (bufp->can_be_null)
-                               return 0;
-
-                       /* Otherwise, have to check alternative paths.  */
-                       break;
-
-               case no_op:
-               case begline:
-               case endline:
-               case begbuf:
-               case endbuf:
-               case wordbound:
-               case notwordbound:
-               case wordbeg:
-               case wordend:
-               case push_dummy_failure:
-                       continue;
-
-
-               case jump_n:
-               case pop_failure_jump:
-               case maybe_pop_jump:
-               case jump:
-               case jump_past_alt:
-               case dummy_failure_jump:
-                       EXTRACT_NUMBER_AND_INCR(j, p);
-                       p += j;
-                       if (j > 0)
-                               continue;
-
-                       /* Jump backward implies we just went through the body of a
-                          loop and matched nothing.  Opcode jumped to should be
-                          `on_failure_jump' or `succeed_n'.  Just treat it like an
-                          ordinary jump.  For a * loop, it has pushed its failure
-                          point already; if so, discard that as redundant.  */
-                       if ((re_opcode_t) * p != on_failure_jump
-                           && (re_opcode_t) * p != succeed_n)
-                               continue;
-
-                       p++;
-                       EXTRACT_NUMBER_AND_INCR(j, p);
-                       p += j;
-
-                       /* If what's on the stack is where we are now, pop it.  */
-                       if (!FAIL_STACK_EMPTY()
-                           && fail_stack.stack[fail_stack.avail - 1] == p)
-                               fail_stack.avail--;
-
-                       continue;
-
-
-               case on_failure_jump:
-               case on_failure_keep_string_jump:
-                     handle_on_failure_jump:
-                       EXTRACT_NUMBER_AND_INCR(j, p);
-
-                       /* For some patterns, e.g., `(a?)?', `p+j' here points to the
-                          end of the pattern.  We don't want to push such a point,
-                          since when we restore it above, entering the switch will
-                          increment `p' past the end of the pattern.  We don't need
-                          to push such a point since we obviously won't find any more
-                          fastmap entries beyond `pend'.  Such a pattern can match
-                          the null string, though.  */
-                       if (p + j < pend) {
-                               if (!PUSH_PATTERN_OP(p + j, fail_stack))
-                                       return -2;
-                       } else
-                               bufp->can_be_null = 1;
-
-                       if (succeed_n_p) {
-                               EXTRACT_NUMBER_AND_INCR(k, p);  /* Skip the n.  */
-                               succeed_n_p = false;
-                       }
-
-                       continue;
-
-
-               case succeed_n:
-                       /* Get to the number of times to succeed.  */
-                       p += 2;
-
-                       /* Increment p past the n for when k != 0.  */
-                       EXTRACT_NUMBER_AND_INCR(k, p);
-                       if (k == 0) {
-                               p -= 4;
-                               succeed_n_p = true;     /* Spaghetti code alert.  */
-                               goto handle_on_failure_jump;
-                       }
-                       continue;
-
-
-               case set_number_at:
-                       p += 4;
-                       continue;
-
-
-               case start_memory:
-               case stop_memory:
-                       p += 2;
-                       continue;
-
-
-               default:
-                       abort();        /* We have listed all the cases.  */
-               }               /* switch *p++ */
-
-               /* Getting here means we have found the possible starting
-                  characters for one path of the pattern -- and that the empty
-                  string does not match.  We need not follow this path further.
-                  Instead, look at the next alternative (remembered on the
-                  stack), or quit if no more.  The test at the top of the loop
-                  does these things.  */
-               path_can_be_null = false;
-               p = pend;
-       }                       /* while p */
-
-       /* Set `can_be_null' for the last path (also the first path, if the
-          pattern is empty).  */
-       bufp->can_be_null |= path_can_be_null;
-       return 0;
-}                              /* re_compile_fastmap */
-
-/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
-   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
-   this memory for recording register information.  STARTS and ENDS
-   must be allocated using the malloc library routine, and must each
-   be at least NUM_REGS * sizeof (regoff_t) bytes long.
-
-   If NUM_REGS == 0, then subsequent matches should allocate their own
-   register data.
-
-   Unless this function is called, the first search or match using
-   PATTERN_BUFFER will allocate its own register data, without
-   freeing the old data.  */
-
-void re_set_registers(bufp, regs, num_regs, starts, ends)
-struct re_pattern_buffer *bufp;
-struct re_registers *regs;
-unsigned num_regs;
-regoff_t *starts, *ends;
-{
-       if (num_regs) {
-               bufp->regs_allocated = REGS_REALLOCATE;
-               regs->num_regs = num_regs;
-               regs->start = starts;
-               regs->end = ends;
-       } else {
-               bufp->regs_allocated = REGS_UNALLOCATED;
-               regs->num_regs = 0;
-               regs->start = regs->end = 0;
-       }
-}
-
-/* Searching routines.  */
-
-/* Like re_search_2, below, but only one string is specified, and
-   doesn't let you say where to stop matching. */
-
-int re_search(bufp, string, size, startpos, range, regs)
-struct re_pattern_buffer *bufp;
-const char *string;
-int size, startpos, range;
-struct re_registers *regs;
-{
-       return re_search_2(bufp, NULL, 0, string, size, startpos, range,
-                          regs, size);
-}
-
-
-/* Using the compiled pattern in BUFP->buffer, first tries to match the
-   virtual concatenation of STRING1 and STRING2, starting first at index
-   STARTPOS, then at STARTPOS + 1, and so on.
-
-   STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
-
-   RANGE is how far to scan while trying to match.  RANGE = 0 means try
-   only at STARTPOS; in general, the last start tried is STARTPOS +
-   RANGE.
-
-   In REGS, return the indices of the virtual concatenation of STRING1
-   and STRING2 that matched the entire BUFP->buffer and its contained
-   subexpressions.
-
-   Do not consider matching one past the index STOP in the virtual
-   concatenation of STRING1 and STRING2.
-
-   We return either the position in the strings at which the match was
-   found, -1 if no match, or -2 if error (such as failure
-   stack overflow).  */
-
-int
-re_search_2(bufp, string1, size1, string2, size2, startpos, range, regs,
-           stop)
-struct re_pattern_buffer *bufp;
-const char *string1, *string2;
-int size1, size2;
-int startpos;
-int range;
-struct re_registers *regs;
-int stop;
-{
-       int val;
-       register char *fastmap = bufp->fastmap;
-       register char *translate = bufp->translate;
-       int total_size = size1 + size2;
-       int endpos = startpos + range;
-
-       /* Check for out-of-range STARTPOS.  */
-       if (startpos < 0 || startpos > total_size)
-               return -1;
-
-       /* Fix up RANGE if it might eventually take us outside
-          the virtual concatenation of STRING1 and STRING2.  */
-       if (endpos < -1)
-               range = -1 - startpos;
-       else if (endpos > total_size)
-               range = total_size - startpos;
-
-       /* If the search isn't to be a backwards one, don't waste time in a
-          search for a pattern that must be anchored.  */
-       if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf
-           && range > 0) {
-               if (startpos > 0)
-                       return -1;
-               else
-                       range = 1;
-       }
-
-       /* Update the fastmap now if not correct already.  */
-       if (fastmap && !bufp->fastmap_accurate)
-               if (re_compile_fastmap(bufp) == -2)
-                       return -2;
-
-       /* Loop through the string, looking for a place to start matching.  */
-       for (;;) {
-               /* If a fastmap is supplied, skip quickly over characters that
-                  cannot be the start of a match.  If the pattern can match the
-                  null string, however, we don't need to skip characters; we want
-                  the first null string.  */
-               if (fastmap && startpos < total_size && !bufp->can_be_null) {
-                       if (range > 0) {        /* Searching forwards.  */
-                               register const char *d;
-                               register int lim = 0;
-                               int irange = range;
-
-                               if (startpos < size1
-                                   && startpos + range >= size1)
-                                       lim = range - (size1 - startpos);
-
-                               d = (startpos >=
-                                    size1 ? string2 - size1 : string1) +
-                                   startpos;
-
-                               /* Written out as an if-else to avoid testing `translate'
-                                  inside the loop.  */
-                               if (translate)
-                                       while (range > lim
-                                              && !fastmap[(unsigned char)
-                                                          translate[(unsigned char) *d++]])
-                                               range--;
-                               else
-                                       while (range > lim
-                                              && !fastmap[(unsigned char)
-                                                          *d++])
-                                               range--;
-
-                               startpos += irange - range;
-                       } else {        /* Searching backwards.  */
-
-                               register char c = (size1 == 0
-                                                  || startpos >=
-                                                  size1 ? string2[startpos
-                                                                  - size1]
-                                                  : string1[startpos]);
-
-                               if (!fastmap[(unsigned char) TRANSLATE(c)])
-                                       goto advance;
-                       }
-               }
-
-               /* If can't match the null string, and that's all we have left, fail.  */
-               if (range >= 0 && startpos == total_size && fastmap
-                   && !bufp->can_be_null)
-                       return -1;
-
-               val = re_match_2(bufp, string1, size1, string2, size2,
-                                startpos, regs, stop);
-               if (val >= 0)
-                       return startpos;
-
-               if (val == -2)
-                       return -2;
-
-             advance:
-               if (!range)
-                       break;
-               else if (range > 0) {
-                       range--;
-                       startpos++;
-               } else {
-                       range++;
-                       startpos--;
-               }
-       }
-       return -1;
-}                              /* re_search_2 */
-
-/* Structure for per-register (a.k.a. per-group) information.
-   This must not be longer than one word, because we push this value
-   onto the failure stack.  Other register information, such as the
-   starting and ending positions (which are addresses), and the list of
-   inner groups (which is a bits list) are maintained in separate
-   variables.
-
-   We are making a (strictly speaking) nonportable assumption here: that
-   the compiler will pack our bit fields into something that fits into
-   the type of `word', i.e., is something that fits into one item on the
-   failure stack.  */
-
-/* Declarations and macros for re_match_2.  */
-
-typedef union {
-       fail_stack_elt_t word;
-       struct {
-               /* This field is one if this group can match the empty string,
-                  zero if not.  If not yet determined,  `MATCH_NULL_UNSET_VALUE'.  */
-#define MATCH_NULL_UNSET_VALUE 3
-               unsigned match_null_string_p:2;
-               unsigned is_active:1;
-               unsigned matched_something:1;
-               unsigned ever_matched_something:1;
-       } bits;
-} register_info_type;
-
-#define REG_MATCH_NULL_STRING_P(R)  ((R).bits.match_null_string_p)
-#define IS_ACTIVE(R)  ((R).bits.is_active)
-#define MATCHED_SOMETHING(R)  ((R).bits.matched_something)
-#define EVER_MATCHED_SOMETHING(R)  ((R).bits.ever_matched_something)
-
-static boolean group_match_null_string_p (unsigned char **p,
-                                         unsigned char *end,
-                                         register_info_type *
-                                         reg_info);
-
-static boolean alt_match_null_string_p (unsigned char *p, unsigned char *end,
-                                       register_info_type * reg_info);
-
-static boolean common_op_match_null_string_p (unsigned char **p,
-                                             unsigned char *end,
-                                             register_info_type * reg_info);
-
-static int bcmp_translate (const char *s1, const char *s2,
-                          int len, char *translate);
-
-/* Call this when have matched a real character; it sets `matched' flags
-   for the subexpressions which we are currently inside.  Also records
-   that those subexprs have matched.  */
-#define SET_REGS_MATCHED()                                             \
-  do                                                                   \
-    {                                                                  \
-      active_reg_t r;                                                  \
-      for (r = lowest_active_reg; r <= highest_active_reg; r++)                \
-        {                                                              \
-          MATCHED_SOMETHING (reg_info[r])                              \
-            = EVER_MATCHED_SOMETHING (reg_info[r])                     \
-            = 1;                                                       \
-        }                                                              \
-    }                                                                  \
-  while (0)
-
-
-/* This converts PTR, a pointer into one of the search strings `string1'
-   and `string2' into an offset from the beginning of that string.  */
-#define POINTER_TO_OFFSET(ptr)                                         \
-  (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
-
-/* Registers are set to a sentinel when they haven't yet matched.  */
-#define REG_UNSET_VALUE ((char *) -1)
-#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
-
-
-/* Macros for dealing with the split strings in re_match_2.  */
-
-#define MATCHING_IN_FIRST_STRING  (dend == end_match_1)
-
-/* Call before fetching a character with *d.  This switches over to
-   string2 if necessary.  */
-#define PREFETCH()                                                     \
-  while (d == dend)                                                    \
-    {                                                                  \
-      /* End of string2 => fail.  */                                   \
-      if (dend == end_match_2)                                         \
-       goto fail;                                                      \
-      /* End of string1 => advance to string2.  */                     \
-      d = string2;                                                     \
-      dend = end_match_2;                                              \
-    }
-
-
-/* Test if at very beginning or at very end of the virtual concatenation
-   of `string1' and `string2'.  If only one string, it's `string2'.  */
-#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
-#define AT_STRINGS_END(d) ((d) == end2)
-
-
-/* Test if D points to a character which is word-constituent.  We have
-   two special cases to check for: if past the end of string1, look at
-   the first character in string2; and if before the beginning of
-   string2, look at the last character in string1.  */
-#define WORDCHAR_P(d)                                                  \
-  (SYNTAX ((d) == end1 ? *string2                                      \
-          : (d) == string2 - 1 ? *(end1 - 1) : *(d))                   \
-   == Sword)
-
-/* Test if the character before D and the one at D differ with respect
-   to being word-constituent.  */
-#define AT_WORD_BOUNDARY(d)                                            \
-  (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)                            \
-   || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
-
-
-/* Free everything we malloc.  */
-#define FREE_VARIABLES() alloca (0)
-
-/* These values must meet several constraints.  They must not be valid
-   register values; since we have a limit of 255 registers (because
-   we use only one byte in the pattern for the register number), we can
-   use numbers larger than 255.  They must differ by 1, because of
-   NUM_FAILURE_ITEMS above.  And the value for the lowest register must
-   be larger than the value for the highest register, so we do not try
-   to actually save any registers when none are active.  */
-#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
-#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
-
-/* Matching routines.  */
-
-/* re_match is like re_match_2 except it takes only a single string.  */
-
-int re_match(bufp, string, size, pos, regs)
-struct re_pattern_buffer *bufp;
-const char *string;
-int size, pos;
-struct re_registers *regs;
-{
-       return re_match_2(bufp, NULL, 0, string, size, pos, regs, size);
-}
-
-/* re_match_2 matches the compiled pattern in BUFP against the
-   the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
-   and SIZE2, respectively).  We start matching at POS, and stop
-   matching at STOP.
-
-   If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
-   store offsets for the substring each group matched in REGS.  See the
-   documentation for exactly how many groups we fill.
-
-   We return -1 if no match, -2 if an internal error (such as the
-   failure stack overflowing).  Otherwise, we return the length of the
-   matched substring.  */
-
-int re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop)
-struct re_pattern_buffer *bufp;
-const char *string1, *string2;
-int size1, size2;
-int pos;
-struct re_registers *regs;
-int stop;
-{
-       /* General temporaries.  */
-       int mcnt;
-       unsigned char *p1;
-
-       /* Just past the end of the corresponding string.  */
-       const char *end1, *end2;
-
-       /* Pointers into string1 and string2, just past the last characters in
-          each to consider matching.  */
-       const char *end_match_1, *end_match_2;
-
-       /* Where we are in the data, and the end of the current string.  */
-       const char *d, *dend;
-
-       /* Where we are in the pattern, and the end of the pattern.  */
-       unsigned char *p = bufp->buffer;
-       register unsigned char *pend = p + bufp->used;
-
-       /* We use this to map every character in the string.  */
-       char *translate = bufp->translate;
-
-       /* Failure point stack.  Each place that can handle a failure further
-          down the line pushes a failure point on this stack.  It consists of
-          restart, regend, and reg_info for all registers corresponding to
-          the subexpressions we're currently inside, plus the number of such
-          registers, and, finally, two char *'s.  The first char * is where
-          to resume scanning the pattern; the second one is where to resume
-          scanning the strings.  If the latter is zero, the failure point is
-          a ``dummy''; if a failure happens and the failure point is a dummy,
-          it gets discarded and the next next one is tried.  */
-       fail_stack_type fail_stack;
-
-       /* We fill all the registers internally, independent of what we
-          return, for use in backreferences.  The number here includes
-          an element for register zero.  */
-       size_t num_regs = bufp->re_nsub + 1;
-
-       /* The currently active registers.  */
-       active_reg_t lowest_active_reg = NO_LOWEST_ACTIVE_REG;
-       active_reg_t highest_active_reg = NO_HIGHEST_ACTIVE_REG;
-
-       /* Information on the contents of registers. These are pointers into
-          the input strings; they record just what was matched (on this
-          attempt) by a subexpression part of the pattern, that is, the
-          regnum-th regstart pointer points to where in the pattern we began
-          matching and the regnum-th regend points to right after where we
-          stopped matching the regnum-th subexpression.  (The zeroth register
-          keeps track of what the whole pattern matches.)  */
-       const char **regstart = 0, **regend = 0;
-
-       /* If a group that's operated upon by a repetition operator fails to
-          match anything, then the register for its start will need to be
-          restored because it will have been set to wherever in the string we
-          are when we last see its open-group operator.  Similarly for a
-          register's end.  */
-       const char **old_regstart = 0, **old_regend = 0;
-
-       /* The is_active field of reg_info helps us keep track of which (possibly
-          nested) subexpressions we are currently in. The matched_something
-          field of reg_info[reg_num] helps us tell whether or not we have
-          matched any of the pattern so far this time through the reg_num-th
-          subexpression.  These two fields get reset each time through any
-          loop their register is in.  */
-       register_info_type *reg_info = 0;
-
-       /* The following record the register info as found in the above
-          variables when we find a match better than any we've seen before.
-          This happens as we backtrack through the failure points, which in
-          turn happens only if we have not yet matched the entire string. */
-       unsigned best_regs_set = false;
-       const char **best_regstart = 0, **best_regend = 0;
-
-       /* Logically, this is `best_regend[0]'.  But we don't want to have to
-          allocate space for that if we're not allocating space for anything
-          else (see below).  Also, we never need info about register 0 for
-          any of the other register vectors, and it seems rather a kludge to
-          treat `best_regend' differently than the rest.  So we keep track of
-          the end of the best match so far in a separate variable.  We
-          initialize this to NULL so that when we backtrack the first time
-          and need to test it, it's not garbage.  */
-       const char *match_end = NULL;
-
-       /* Used when we pop values we don't care about.  */
-       const char **reg_dummy = 0;
-       register_info_type *reg_info_dummy = 0;
-
-       DEBUG_PRINT1("\n\nEntering re_match_2.\n");
-
-       INIT_FAIL_STACK();
-
-       /* Do not bother to initialize all the register variables if there are
-          no groups in the pattern, as it takes a fair amount of time.  If
-          there are groups, we include space for register 0 (the whole
-          pattern), even though we never use it, since it simplifies the
-          array indexing.  We should fix this.  */
-       if (bufp->re_nsub) {
-               regstart = REGEX_TALLOC(num_regs, const char *);
-               regend = REGEX_TALLOC(num_regs, const char *);
-               old_regstart = REGEX_TALLOC(num_regs, const char *);
-               old_regend = REGEX_TALLOC(num_regs, const char *);
-               best_regstart = REGEX_TALLOC(num_regs, const char *);
-               best_regend = REGEX_TALLOC(num_regs, const char *);
-               reg_info = REGEX_TALLOC(num_regs, register_info_type);
-               reg_dummy = REGEX_TALLOC(num_regs, const char *);
-               reg_info_dummy =
-                   REGEX_TALLOC(num_regs, register_info_type);
-
-               if (!
-                   (regstart && regend && old_regstart && old_regend
-                    && reg_info && best_regstart && best_regend
-                    && reg_dummy && reg_info_dummy)) {
-                       FREE_VARIABLES();
-                       return -2;
-               }
-       }
-
-       /* The starting position is bogus.  */
-       if (pos < 0 || pos > size1 + size2) {
-               FREE_VARIABLES();
-               return -1;
-       }
-
-       /* Initialize subexpression text positions to -1 to mark ones that no
-          start_memory/stop_memory has been seen for. Also initialize the
-          register information struct.  */
-       for (mcnt = 1; mcnt < num_regs; mcnt++) {
-               regstart[mcnt] = regend[mcnt]
-                   = old_regstart[mcnt] = old_regend[mcnt] =
-                   REG_UNSET_VALUE;
-
-               REG_MATCH_NULL_STRING_P(reg_info[mcnt]) =
-                   MATCH_NULL_UNSET_VALUE;
-               IS_ACTIVE(reg_info[mcnt]) = 0;
-               MATCHED_SOMETHING(reg_info[mcnt]) = 0;
-               EVER_MATCHED_SOMETHING(reg_info[mcnt]) = 0;
-       }
-
-       /* We move `string1' into `string2' if the latter's empty -- but not if
-          `string1' is null.  */
-       if (size2 == 0 && string1 != NULL) {
-               string2 = string1;
-               size2 = size1;
-               string1 = 0;
-               size1 = 0;
-       }
-       end1 = string1 + size1;
-       end2 = string2 + size2;
-
-       /* Compute where to stop matching, within the two strings.  */
-       if (stop <= size1) {
-               end_match_1 = string1 + stop;
-               end_match_2 = string2;
-       } else {
-               end_match_1 = end1;
-               end_match_2 = string2 + stop - size1;
-       }
-
-       /* `p' scans through the pattern as `d' scans through the data.
-          `dend' is the end of the input string that `d' points within.  `d'
-          is advanced into the following input string whenever necessary, but
-          this happens before fetching; therefore, at the beginning of the
-          loop, `d' can be pointing at the end of a string, but it cannot
-          equal `string2'.  */
-       if (size1 > 0 && pos <= size1) {
-               d = string1 + pos;
-               dend = end_match_1;
-       } else {
-               d = string2 + pos - size1;
-               dend = end_match_2;
-       }
-
-       DEBUG_PRINT1("The compiled pattern is: ");
-       DEBUG_PRINT_COMPILED_PATTERN(bufp, p, pend);
-       DEBUG_PRINT1("The string to match is: `");
-       DEBUG_PRINT_DOUBLE_STRING(d, string1, size1, string2, size2);
-       DEBUG_PRINT1("'\n");
-
-       /* This loops over pattern commands.  It exits by returning from the
-          function if the match is complete, or it drops through if the match
-          fails at this starting point in the input data.  */
-       for (;;) {
-               DEBUG_PRINT2("\n0x%x: ", p);
-
-               if (p == pend) {        /* End of pattern means we might have succeeded.  */
-                       DEBUG_PRINT1("end of pattern ... ");
-
-                       /* If we haven't matched the entire string, and we want the
-                          longest match, try backtracking.  */
-                       if (d != end_match_2) {
-                               DEBUG_PRINT1("backtracking.\n");
-
-                               if (!FAIL_STACK_EMPTY()) {      /* More failure points to try.  */
-                                       boolean same_str_p =
-                                           (FIRST_STRING_P(match_end)
-                                            == MATCHING_IN_FIRST_STRING);
-
-                                       /* If exceeds best match so far, save it.  */
-                                       if (!best_regs_set
-                                           || (same_str_p
-                                               && d > match_end)
-                                           || (!same_str_p
-                                               &&
-                                               !MATCHING_IN_FIRST_STRING))
-                                       {
-                                               best_regs_set = true;
-                                               match_end = d;
-
-                                               DEBUG_PRINT1
-                                                   ("\nSAVING match as best so far.\n");
-
-                                               for (mcnt = 1;
-                                                    mcnt < num_regs;
-                                                    mcnt++) {
-                                                       best_regstart[mcnt]
-                                                           =
-                                                           regstart[mcnt];
-                                                       best_regend[mcnt] =
-                                                           regend[mcnt];
-                                               }
-                                       }
-                                       goto fail;
-                               }
-
-                               /* If no failure points, don't restore garbage.  */
-                               else if (best_regs_set) {
-                                     restore_best_regs:
-                                       /* Restore best match.  It may happen that `dend ==
-                                          end_match_1' while the restored d is in string2.
-                                          For example, the pattern `x.*y.*z' against the
-                                          strings `x-' and `y-z-', if the two strings are
-                                          not consecutive in memory.  */
-                                       DEBUG_PRINT1
-                                           ("Restoring best registers.\n");
-
-                                       d = match_end;
-                                       dend = ((d >= string1 && d <= end1)
-                                               ? end_match_1 :
-                                               end_match_2);
-
-                                       for (mcnt = 1; mcnt < num_regs;
-                                            mcnt++) {
-                                               regstart[mcnt] =
-                                                   best_regstart[mcnt];
-                                               regend[mcnt] =
-                                                   best_regend[mcnt];
-                                       }
-                               }
-                       }
-                       /* d != end_match_2 */
-                       DEBUG_PRINT1("Accepting match.\n");
-
-                       /* If caller wants register contents data back, do it.  */
-                       if (regs && !bufp->no_sub) {
-                               /* Have the register data arrays been allocated?  */
-                               if (bufp->regs_allocated == REGS_UNALLOCATED) { /* No.  So allocate them with malloc.  We need one
-                                                                                  extra element beyond `num_regs' for the `-1' marker
-                                                                                  GNU code uses.  */
-                                       regs->num_regs =
-                                           MAX(RE_NREGS, num_regs + 1);
-                                       regs->start =
-                                           TALLOC(regs->num_regs,
-                                                  regoff_t);
-                                       regs->end =
-                                           TALLOC(regs->num_regs,
-                                                  regoff_t);
-                                       if (regs->start == NULL
-                                           || regs->end == NULL)
-                                               return -2;
-                                       bufp->regs_allocated =
-                                           REGS_REALLOCATE;
-                               } else if (bufp->regs_allocated == REGS_REALLOCATE) {   /* Yes.  If we need more elements than were already
-                                                                                          allocated, reallocate them.  If we need fewer, just
-                                                                                          leave it alone.  */
-                                       if (regs->num_regs < num_regs + 1) {
-                                               regs->num_regs =
-                                                   num_regs + 1;
-                                               RETALLOC(regs->start,
-                                                        regs->num_regs,
-                                                        regoff_t);
-                                               RETALLOC(regs->end,
-                                                        regs->num_regs,
-                                                        regoff_t);
-                                               if (regs->start == NULL
-                                                   || regs->end == NULL)
-                                                       return -2;
-                                       }
-                               } else {
-                                       /* These braces fend off a "empty body in an else-statement"
-                                          warning under GCC when assert expands to nothing.  */
-                                       assert(bufp->regs_allocated ==
-                                              REGS_FIXED);
-                               }
-
-                               /* Convert the pointer data in `regstart' and `regend' to
-                                  indices.  Register zero has to be set differently,
-                                  since we haven't kept track of any info for it.  */
-                               if (regs->num_regs > 0) {
-                                       regs->start[0] = pos;
-                                       regs->end[0] =
-                                           (MATCHING_IN_FIRST_STRING ? d -
-                                            string1 : d - string2 +
-                                            size1);
-                               }
-
-                               /* Go through the first `min (num_regs, regs->num_regs)'
-                                  registers, since that is all we initialized.  */
-                               for (mcnt = 1;
-                                    mcnt < MIN(num_regs, regs->num_regs);
-                                    mcnt++) {
-                                       if (REG_UNSET(regstart[mcnt])
-                                           || REG_UNSET(regend[mcnt]))
-                                               regs->start[mcnt] =
-                                                   regs->end[mcnt] = -1;
-                                       else {
-                                               regs->start[mcnt] =
-                                                   POINTER_TO_OFFSET
-                                                   (regstart[mcnt]);
-                                               regs->end[mcnt] =
-                                                   POINTER_TO_OFFSET
-                                                   (regend[mcnt]);
-                                       }
-                               }
-
-                               /* If the regs structure we return has more elements than
-                                  were in the pattern, set the extra elements to -1.  If
-                                  we (re)allocated the registers, this is the case,
-                                  because we always allocate enough to have at least one
-                                  -1 at the end.  */
-                               for (mcnt = num_regs;
-                                    mcnt < regs->num_regs; mcnt++)
-                                       regs->start[mcnt] =
-                                           regs->end[mcnt] = -1;
-                       }
-                       /* regs && !bufp->no_sub */
-                       FREE_VARIABLES();
-                       DEBUG_PRINT4
-                           ("%u failure points pushed, %u popped (%u remain).\n",
-                            nfailure_points_pushed,
-                            nfailure_points_popped,
-                            nfailure_points_pushed -
-                            nfailure_points_popped);
-                       DEBUG_PRINT2("%u registers pushed.\n",
-                                    num_regs_pushed);
-
-                       mcnt = d - pos - (MATCHING_IN_FIRST_STRING
-                                         ? string1 : string2 - size1);
-
-                       DEBUG_PRINT2("Returning %d from re_match_2.\n",
-                                    mcnt);
-
-                       return mcnt;
-               }
-
-               /* Otherwise match next pattern command.  */
-               switch ((re_opcode_t) * p++) {
-                       /* Ignore these.  Used to ignore the n of succeed_n's which
-                          currently have n == 0.  */
-               case no_op:
-                       DEBUG_PRINT1("EXECUTING no_op.\n");
-                       break;
-
-
-                       /* Match the next n pattern characters exactly.  The following
-                          byte in the pattern defines n, and the n bytes after that
-                          are the characters to match.  */
-               case exactn:
-                       mcnt = *p++;
-                       DEBUG_PRINT2("EXECUTING exactn %d.\n", mcnt);
-
-                       /* This is written out as an if-else so we don't waste time
-                          testing `translate' inside the loop.  */
-                       if (translate) {
-                               do {
-                                       PREFETCH();
-                                       if (translate[(unsigned char) *d++]
-                                           != (char) *p++)
-                                               goto fail;
-                               }
-                               while (--mcnt);
-                       } else {
-                               do {
-                                       PREFETCH();
-                                       if (*d++ != (char) *p++)
-                                               goto fail;
-                               }
-                               while (--mcnt);
-                       }
-                       SET_REGS_MATCHED();
-                       break;
-
-
-                       /* Match any character except possibly a newline or a null.  */
-               case anychar:
-                       DEBUG_PRINT1("EXECUTING anychar.\n");
-
-                       PREFETCH();
-
-                       if ((!(bufp->syntax & RE_DOT_NEWLINE)
-                            && TRANSLATE(*d) == '\n')
-                           || (bufp->syntax & RE_DOT_NOT_NULL
-                               && TRANSLATE(*d) == '\000'))
-                               goto fail;
-
-                       SET_REGS_MATCHED();
-                       DEBUG_PRINT2("  Matched `%d'.\n", *d);
-                       d++;
-                       break;
-
-
-               case charset:
-               case charset_not:
-                       {
-                               register unsigned char c;
-                               boolean not =
-                                   (re_opcode_t) * (p - 1) == charset_not;
-
-                               DEBUG_PRINT2("EXECUTING charset%s.\n",
-                                            not ? "_not" : "");
-
-                               PREFETCH();
-                               c = TRANSLATE(*d);      /* The character to match.  */
-
-                               /* Cast to `unsigned' instead of `unsigned char' in case the
-                                  bit list is a full 32 bytes long.  */
-                               if (c < (unsigned) (*p * BYTEWIDTH)
-                                   && p[1 +
-                                        c / BYTEWIDTH] & (1 << (c %
-                                                                BYTEWIDTH)))
-                                       not = !not;
-
-                               p += 1 + *p;
-
-                               if (!not)
-                                       goto fail;
-
-                               SET_REGS_MATCHED();
-                               d++;
-                               break;
-                       }
-
-
-                       /* The beginning of a group is represented by start_memory.
-                          The arguments are the register number in the next byte, and the
-                          number of groups inner to this one in the next.  The text
-                          matched within the group is recorded (in the internal
-                          registers data structure) under the register number.  */
-               case start_memory:
-                       DEBUG_PRINT3("EXECUTING start_memory %d (%d):\n",
-                                    *p, p[1]);
-
-                       /* Find out if this group can match the empty string.  */
-                       p1 = p; /* To send to group_match_null_string_p.  */
-
-                       if (REG_MATCH_NULL_STRING_P(reg_info[*p]) ==
-                           MATCH_NULL_UNSET_VALUE)
-                               REG_MATCH_NULL_STRING_P(reg_info[*p])
-                                   = group_match_null_string_p(&p1, pend,
-                                                               reg_info);
-
-                       /* Save the position in the string where we were the last time
-                          we were at this open-group operator in case the group is
-                          operated upon by a repetition operator, e.g., with `(a*)*b'
-                          against `ab'; then we want to ignore where we are now in
-                          the string in case this attempt to match fails.  */
-                       old_regstart[*p] =
-                           REG_MATCH_NULL_STRING_P(reg_info[*p])
-                           ? REG_UNSET(regstart[*p]) ? d : regstart[*p]
-                           : regstart[*p];
-                       DEBUG_PRINT2("  old_regstart: %d\n",
-                                    POINTER_TO_OFFSET(old_regstart[*p]));
-
-                       regstart[*p] = d;
-                       DEBUG_PRINT2("  regstart: %d\n",
-                                    POINTER_TO_OFFSET(regstart[*p]));
-
-                       IS_ACTIVE(reg_info[*p]) = 1;
-                       MATCHED_SOMETHING(reg_info[*p]) = 0;
-
-                       /* This is the new highest active register.  */
-                       highest_active_reg = *p;
-
-                       /* If nothing was active before, this is the new lowest active
-                          register.  */
-                       if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
-                               lowest_active_reg = *p;
-
-                       /* Move past the register number and inner group count.  */
-                       p += 2;
-                       break;
-
-
-                       /* The stop_memory opcode represents the end of a group.  Its
-                          arguments are the same as start_memory's: the register
-                          number, and the number of inner groups.  */
-               case stop_memory:
-                       DEBUG_PRINT3("EXECUTING stop_memory %d (%d):\n",
-                                    *p, p[1]);
-
-                       /* We need to save the string position the last time we were at
-                          this close-group operator in case the group is operated
-                          upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
-                          against `aba'; then we want to ignore where we are now in
-                          the string in case this attempt to match fails.  */
-                       old_regend[*p] =
-                           REG_MATCH_NULL_STRING_P(reg_info[*p])
-                           ? REG_UNSET(regend[*p]) ? d : regend[*p]
-                           : regend[*p];
-                       DEBUG_PRINT2("      old_regend: %d\n",
-                                    POINTER_TO_OFFSET(old_regend[*p]));
-
-                       regend[*p] = d;
-                       DEBUG_PRINT2("      regend: %d\n",
-                                    POINTER_TO_OFFSET(regend[*p]));
-
-                       /* This register isn't active anymore.  */
-                       IS_ACTIVE(reg_info[*p]) = 0;
-
-                       /* If this was the only register active, nothing is active
-                          anymore.  */
-                       if (lowest_active_reg == highest_active_reg) {
-                               lowest_active_reg = NO_LOWEST_ACTIVE_REG;
-                               highest_active_reg = NO_HIGHEST_ACTIVE_REG;
-                       } else {        /* We must scan for the new highest active register, since
-                                          it isn't necessarily one less than now: consider
-                                          (a(b)c(d(e)f)g).  When group 3 ends, after the f), the
-                                          new highest active register is 1.  */
-                               unsigned char r = *p - 1;
-                               while (r > 0 && !IS_ACTIVE(reg_info[r]))
-                                       r--;
-
-                               /* If we end up at register zero, that means that we saved
-                                  the registers as the result of an `on_failure_jump', not
-                                  a `start_memory', and we jumped to past the innermost
-                                  `stop_memory'.  For example, in ((.)*) we save
-                                  registers 1 and 2 as a result of the *, but when we pop
-                                  back to the second ), we are at the stop_memory 1.
-                                  Thus, nothing is active.  */
-                               if (r == 0) {
-                                       lowest_active_reg =
-                                           NO_LOWEST_ACTIVE_REG;
-                                       highest_active_reg =
-                                           NO_HIGHEST_ACTIVE_REG;
-                               } else
-                                       highest_active_reg = r;
-                       }
-
-                       /* If just failed to match something this time around with a
-                          group that's operated on by a repetition operator, try to
-                          force exit from the ``loop'', and restore the register
-                          information for this group that we had before trying this
-                          last match.  */
-                       if ((!MATCHED_SOMETHING(reg_info[*p])
-                            || (re_opcode_t) p[-3] == start_memory)
-                           && (p + 2) < pend) {
-                               boolean is_a_jump_n = false;
-
-                               p1 = p + 2;
-                               mcnt = 0;
-                               switch ((re_opcode_t) * p1++) {
-                               case jump_n:
-                                       is_a_jump_n = true;
-                               case pop_failure_jump:
-                               case maybe_pop_jump:
-                               case jump:
-                               case dummy_failure_jump:
-                                       EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-                                       if (is_a_jump_n)
-                                               p1 += 2;
-                                       break;
-
-                               default:
-                                       /* do nothing */ ;
-                               }
-                               p1 += mcnt;
-
-                               /* If the next operation is a jump backwards in the pattern
-                                  to an on_failure_jump right before the start_memory
-                                  corresponding to this stop_memory, exit from the loop
-                                  by forcing a failure after pushing on the stack the
-                                  on_failure_jump's jump in the pattern, and d.  */
-                               if (mcnt < 0
-                                   && (re_opcode_t) * p1 ==
-                                   on_failure_jump
-                                   && (re_opcode_t) p1[3] == start_memory
-                                   && p1[4] == *p) {
-                                       /* If this group ever matched anything, then restore
-                                          what its registers were before trying this last
-                                          failed match, e.g., with `(a*)*b' against `ab' for
-                                          regstart[1], and, e.g., with `((a*)*(b*)*)*'
-                                          against `aba' for regend[3].
-
-                                          Also restore the registers for inner groups for,
-                                          e.g., `((a*)(b*))*' against `aba' (register 3 would
-                                          otherwise get trashed).  */
-
-                                       if (EVER_MATCHED_SOMETHING
-                                           (reg_info[*p])) {
-                                               unsigned r;
-
-                                               EVER_MATCHED_SOMETHING
-                                                   (reg_info[*p]) = 0;
-
-                                               /* Restore this and inner groups' (if any) registers.  */
-                                               for (r = *p;
-                                                    r < *p + *(p + 1);
-                                                    r++) {
-                                                       regstart[r] =
-                                                           old_regstart
-                                                           [r];
-
-                                                       /* xx why this test?  */
-                                                       if ((s_reg_t)
-                                                           old_regend[r]
-                                                           >=
-                                                           (s_reg_t)
-                                                           regstart[r])
-                                                               regend[r] =
-                                                                   old_regend
-                                                                   [r];
-                                               }
-                                       }
-                                       p1++;
-                                       EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-                                       PUSH_FAILURE_POINT(p1 + mcnt, d,
-                                                          -2);
-                                       PUSH_FAILURE_POINT2(p1 + mcnt, d,
-                                                           -2);
-
-                                       goto fail;
-                               }
-                       }
-
-                       /* Move past the register number and the inner group count.  */
-                       p += 2;
-                       break;
-
-
-                       /* \<digit> has been turned into a `duplicate' command which is
-                          followed by the numeric value of <digit> as the register number.  */
-               case duplicate:
-                       {
-                               register const char *d2, *dend2;
-                               int regno = *p++;       /* Get which register to match against.  */
-                               DEBUG_PRINT2("EXECUTING duplicate %d.\n",
-                                            regno);
-
-                               /* Can't back reference a group which we've never matched.  */
-                               if (REG_UNSET(regstart[regno])
-                                   || REG_UNSET(regend[regno]))
-                                       goto fail;
-
-                               /* Where in input to try to start matching.  */
-                               d2 = regstart[regno];
-
-                               /* Where to stop matching; if both the place to start and
-                                  the place to stop matching are in the same string, then
-                                  set to the place to stop, otherwise, for now have to use
-                                  the end of the first string.  */
-
-                               dend2 = ((FIRST_STRING_P(regstart[regno])
-                                         == FIRST_STRING_P(regend[regno]))
-                                        ? regend[regno] : end_match_1);
-                               for (;;) {
-                                       /* If necessary, advance to next segment in register
-                                          contents.  */
-                                       while (d2 == dend2) {
-                                               if (dend2 == end_match_2)
-                                                       break;
-                                               if (dend2 == regend[regno])
-                                                       break;
-
-                                               /* End of string1 => advance to string2. */
-                                               d2 = string2;
-                                               dend2 = regend[regno];
-                                       }
-                                       /* At end of register contents => success */
-                                       if (d2 == dend2)
-                                               break;
-
-                                       /* If necessary, advance to next segment in data.  */
-                                       PREFETCH();
-
-                                       /* How many characters left in this segment to match.  */
-                                       mcnt = dend - d;
-
-                                       /* Want how many consecutive characters we can match in
-                                          one shot, so, if necessary, adjust the count.  */
-                                       if (mcnt > dend2 - d2)
-                                               mcnt = dend2 - d2;
-
-                                       /* Compare that many; failure if mismatch, else move
-                                          past them.  */
-                                       if (translate
-                                           ? bcmp_translate(d, d2, mcnt,
-                                                            translate)
-                                           : bcmp(d, d2, mcnt))
-                                               goto fail;
-                                       d += mcnt, d2 += mcnt;
-                               }
-                       }
-                       break;
-
-
-                       /* begline matches the empty string at the beginning of the string
-                          (unless `not_bol' is set in `bufp'), and, if
-                          `newline_anchor' is set, after newlines.  */
-               case begline:
-                       DEBUG_PRINT1("EXECUTING begline.\n");
-
-                       if (AT_STRINGS_BEG(d)) {
-                               if (!bufp->not_bol)
-                                       break;
-                       } else if (d[-1] == '\n' && bufp->newline_anchor) {
-                               break;
-                       }
-                       /* In all other cases, we fail.  */
-                       goto fail;
-
-
-                       /* endline is the dual of begline.  */
-               case endline:
-                       DEBUG_PRINT1("EXECUTING endline.\n");
-
-                       if (AT_STRINGS_END(d)) {
-                               if (!bufp->not_eol)
-                                       break;
-                       }
-
-                       /* We have to ``prefetch'' the next character.  */
-                       else if ((d == end1 ? *string2 : *d) == '\n'
-                                && bufp->newline_anchor) {
-                               break;
-                       }
-                       goto fail;
-
-
-                       /* Match at the very beginning of the data.  */
-               case begbuf:
-                       DEBUG_PRINT1("EXECUTING begbuf.\n");
-                       if (AT_STRINGS_BEG(d))
-                               break;
-                       goto fail;
-
-
-                       /* Match at the very end of the data.  */
-               case endbuf:
-                       DEBUG_PRINT1("EXECUTING endbuf.\n");
-                       if (AT_STRINGS_END(d))
-                               break;
-                       goto fail;
-
-
-                       /* on_failure_keep_string_jump is used to optimize `.*\n'.  It
-                          pushes NULL as the value for the string on the stack.  Then
-                          `pop_failure_point' will keep the current value for the
-                          string, instead of restoring it.  To see why, consider
-                          matching `foo\nbar' against `.*\n'.  The .* matches the foo;
-                          then the . fails against the \n.  But the next thing we want
-                          to do is match the \n against the \n; if we restored the
-                          string value, we would be back at the foo.
-
-                          Because this is used only in specific cases, we don't need to
-                          check all the things that `on_failure_jump' does, to make
-                          sure the right things get saved on the stack.  Hence we don't
-                          share its code.  The only reason to push anything on the
-                          stack at all is that otherwise we would have to change
-                          `anychar's code to do something besides goto fail in this
-                          case; that seems worse than this.  */
-               case on_failure_keep_string_jump:
-                       DEBUG_PRINT1
-                           ("EXECUTING on_failure_keep_string_jump");
-
-                       EXTRACT_NUMBER_AND_INCR(mcnt, p);
-                       DEBUG_PRINT3(" %d (to 0x%x):\n", mcnt, p + mcnt);
-
-                       PUSH_FAILURE_POINT(p + mcnt, NULL, -2);
-                       PUSH_FAILURE_POINT2(p + mcnt, NULL, -2);
-                       break;
-
-
-                       /* Uses of on_failure_jump:
-
-                          Each alternative starts with an on_failure_jump that points
-                          to the beginning of the next alternative.  Each alternative
-                          except the last ends with a jump that in effect jumps past
-                          the rest of the alternatives.  (They really jump to the
-                          ending jump of the following alternative, because tensioning
-                          these jumps is a hassle.)
-
-                          Repeats start with an on_failure_jump that points past both
-                          the repetition text and either the following jump or
-                          pop_failure_jump back to this on_failure_jump.  */
-               case on_failure_jump:
-                     on_failure:
-                       DEBUG_PRINT1("EXECUTING on_failure_jump");
-
-                       EXTRACT_NUMBER_AND_INCR(mcnt, p);
-                       DEBUG_PRINT3(" %d (to 0x%x)", mcnt, p + mcnt);
-
-                       /* If this on_failure_jump comes right before a group (i.e.,
-                          the original * applied to a group), save the information
-                          for that group and all inner ones, so that if we fail back
-                          to this point, the group's information will be correct.
-                          For example, in \(a*\)*\1, we need the preceding group,
-                          and in \(\(a*\)b*\)\2, we need the inner group.  */
-
-                       /* We can't use `p' to check ahead because we push
-                          a failure point to `p + mcnt' after we do this.  */
-                       p1 = p;
-
-                       /* We need to skip no_op's before we look for the
-                          start_memory in case this on_failure_jump is happening as
-                          the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
-                          against aba.  */
-                       while (p1 < pend && (re_opcode_t) * p1 == no_op)
-                               p1++;
-
-                       if (p1 < pend
-                           && (re_opcode_t) * p1 == start_memory) {
-                               /* We have a new highest active register now.  This will
-                                  get reset at the start_memory we are about to get to,
-                                  but we will have saved all the registers relevant to
-                                  this repetition op, as described above.  */
-                               highest_active_reg = *(p1 + 1) + *(p1 + 2);
-                               if (lowest_active_reg ==
-                                   NO_LOWEST_ACTIVE_REG)
-                                       lowest_active_reg = *(p1 + 1);
-                       }
-
-                       DEBUG_PRINT1(":\n");
-                       PUSH_FAILURE_POINT(p + mcnt, d, -2);
-                       PUSH_FAILURE_POINT2(p + mcnt, d, -2);
-                       break;
-
-
-                       /* A smart repeat ends with `maybe_pop_jump'.
-                          We change it to either `pop_failure_jump' or `jump'.  */
-               case maybe_pop_jump:
-                       EXTRACT_NUMBER_AND_INCR(mcnt, p);
-                       DEBUG_PRINT2("EXECUTING maybe_pop_jump %d.\n",
-                                    mcnt);
-                       {
-                               register unsigned char *p2 = p;
-
-                               /* Compare the beginning of the repeat with what in the
-                                  pattern follows its end. If we can establish that there
-                                  is nothing that they would both match, i.e., that we
-                                  would have to backtrack because of (as in, e.g., `a*a')
-                                  then we can change to pop_failure_jump, because we'll
-                                  never have to backtrack.
-
-                                  This is not true in the case of alternatives: in
-                                  `(a|ab)*' we do need to backtrack to the `ab' alternative
-                                  (e.g., if the string was `ab').  But instead of trying to
-                                  detect that here, the alternative has put on a dummy
-                                  failure point which is what we will end up popping.  */
-
-                               /* Skip over open/close-group commands.  */
-                               while (p2 + 2 < pend
-                                      && ((re_opcode_t) * p2 ==
-                                          stop_memory
-                                          || (re_opcode_t) * p2 ==
-                                          start_memory))
-                                       p2 += 3;        /* Skip over args, too.  */
-
-                               /* If we're at the end of the pattern, we can change.  */
-                               if (p2 == pend) {
-                                       /* Consider what happens when matching ":\(.*\)"
-                                          against ":/".  I don't really understand this code
-                                          yet.  */
-                                       p[-3] =
-                                           (unsigned char)
-                                           pop_failure_jump;
-                                       DEBUG_PRINT1
-                                           ("  End of pattern: change to `pop_failure_jump'.\n");
-                               }
-
-                               else if ((re_opcode_t) * p2 == exactn
-                                        || (bufp->newline_anchor
-                                            && (re_opcode_t) * p2 ==
-                                            endline)) {
-                                       register unsigned char c =
-                                           *p2 ==
-                                           (unsigned char) endline ? '\n'
-                                           : p2[2];
-                                       p1 = p + mcnt;
-
-                                       /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
-                                          to the `maybe_finalize_jump' of this case.  Examine what
-                                          follows.  */
-                                       if ((re_opcode_t) p1[3] == exactn
-                                           && p1[5] != c) {
-                                               p[-3] =
-                                                   (unsigned char)
-                                                   pop_failure_jump;
-                                               DEBUG_PRINT3
-                                                   ("  %c != %c => pop_failure_jump.\n",
-                                                    c, p1[5]);
-                                       }
-
-                                       else if ((re_opcode_t) p1[3] ==
-                                                charset
-                                                || (re_opcode_t) p1[3] ==
-                                                charset_not) {
-                                               int not =
-                                                   (re_opcode_t) p1[3] ==
-                                                   charset_not;
-
-                                               if (c <
-                                                   (unsigned char) (p1[4]
-                                                                    *
-                                                                    BYTEWIDTH)
-                                                   && p1[5 +
-                                                         c /
-                                                         BYTEWIDTH] & (1
-                                                                       <<
-                                                                       (c
-                                                                        %
-                                                                        BYTEWIDTH)))
-                                                       not = !not;
-
-                                               /* `not' is equal to 1 if c would match, which means
-                                                  that we can't change to pop_failure_jump.  */
-                                               if (!not) {
-                                                       p[-3] =
-                                                           (unsigned char)
-                                                           pop_failure_jump;
-                                                       DEBUG_PRINT1
-                                                           ("  No match => pop_failure_jump.\n");
-                                               }
-                                       }
-                               }
-                       }
-                       p -= 2; /* Point at relative address again.  */
-                       if ((re_opcode_t) p[-1] != pop_failure_jump) {
-                               p[-1] = (unsigned char) jump;
-                               DEBUG_PRINT1("  Match => jump.\n");
-                               goto unconditional_jump;
-                       }
-                       /* Note fall through.  */
-
-
-                       /* The end of a simple repeat has a pop_failure_jump back to
-                          its matching on_failure_jump, where the latter will push a
-                          failure point.  The pop_failure_jump takes off failure
-                          points put on by this pop_failure_jump's matching
-                          on_failure_jump; we got through the pattern to here from the
-                          matching on_failure_jump, so didn't fail.  */
-               case pop_failure_jump:
-                       {
-                               /* We need to pass separate storage for the lowest and
-                                  highest registers, even though we don't care about the
-                                  actual values.  Otherwise, we will restore only one
-                                  register from the stack, since lowest will == highest in
-                                  `pop_failure_point'.  */
-                               active_reg_t dummy_low_reg, dummy_high_reg;
-                               unsigned char *pdummy;
-                               const char *sdummy;
-
-                               DEBUG_PRINT1
-                                   ("EXECUTING pop_failure_jump.\n");
-                               POP_FAILURE_POINT(sdummy, pdummy,
-                                                 dummy_low_reg,
-                                                 dummy_high_reg,
-                                                 reg_dummy, reg_dummy,
-                                                 reg_info_dummy);
-                       }
-                       /* Note fall through.  */
-
-
-                       /* Unconditionally jump (without popping any failure points).  */
-               case jump:
-                     unconditional_jump:
-                       EXTRACT_NUMBER_AND_INCR(mcnt, p);       /* Get the amount to jump.  */
-                       DEBUG_PRINT2("EXECUTING jump %d ", mcnt);
-                       p += mcnt;      /* Do the jump.  */
-                       DEBUG_PRINT2("(to 0x%x).\n", p);
-                       break;
-
-
-                       /* We need this opcode so we can detect where alternatives end
-                          in `group_match_null_string_p' et al.  */
-               case jump_past_alt:
-                       DEBUG_PRINT1("EXECUTING jump_past_alt.\n");
-                       goto unconditional_jump;
-
-
-                       /* Normally, the on_failure_jump pushes a failure point, which
-                          then gets popped at pop_failure_jump.  We will end up at
-                          pop_failure_jump, also, and with a pattern of, say, `a+', we
-                          are skipping over the on_failure_jump, so we have to push
-                          something meaningless for pop_failure_jump to pop.  */
-               case dummy_failure_jump:
-                       DEBUG_PRINT1("EXECUTING dummy_failure_jump.\n");
-                       /* It doesn't matter what we push for the string here.  What
-                          the code at `fail' tests is the value for the pattern.  */
-                       PUSH_FAILURE_POINT(0, 0, -2);
-                       PUSH_FAILURE_POINT2(0, 0, -2);
-                       goto unconditional_jump;
-
-
-                       /* At the end of an alternative, we need to push a dummy failure
-                          point in case we are followed by a `pop_failure_jump', because
-                          we don't want the failure point for the alternative to be
-                          popped.  For example, matching `(a|ab)*' against `aab'
-                          requires that we match the `ab' alternative.  */
-               case push_dummy_failure:
-                       DEBUG_PRINT1("EXECUTING push_dummy_failure.\n");
-                       /* See comments just above at `dummy_failure_jump' about the
-                          two zeroes.  */
-                       PUSH_FAILURE_POINT(0, 0, -2);
-                       PUSH_FAILURE_POINT2(0, 0, -2);
-                       break;
-
-                       /* Have to succeed matching what follows at least n times.
-                          After that, handle like `on_failure_jump'.  */
-               case succeed_n:
-                       EXTRACT_NUMBER(mcnt, p + 2);
-                       DEBUG_PRINT2("EXECUTING succeed_n %d.\n", mcnt);
-
-                       assert(mcnt >= 0);
-                       /* Originally, this is how many times we HAVE to succeed.  */
-                       if (mcnt > 0) {
-                               mcnt--;
-                               p += 2;
-                               STORE_NUMBER_AND_INCR(p, mcnt);
-                               DEBUG_PRINT3("  Setting 0x%x to %d.\n", p,
-                                            mcnt);
-                       } else if (mcnt == 0) {
-                               DEBUG_PRINT2
-                                   ("  Setting two bytes from 0x%x to no_op.\n",
-                                    p + 2);
-                               p[2] = (unsigned char) no_op;
-                               p[3] = (unsigned char) no_op;
-                               goto on_failure;
-                       }
-                       break;
-
-               case jump_n:
-                       EXTRACT_NUMBER(mcnt, p + 2);
-                       DEBUG_PRINT2("EXECUTING jump_n %d.\n", mcnt);
-
-                       /* Originally, this is how many times we CAN jump.  */
-                       if (mcnt) {
-                               mcnt--;
-                               STORE_NUMBER(p + 2, mcnt);
-                               goto unconditional_jump;
-                       }
-                       /* If don't have to jump any more, skip over the rest of command.  */
-                       else
-                               p += 4;
-                       break;
-
-               case set_number_at:
-                       {
-                               DEBUG_PRINT1("EXECUTING set_number_at.\n");
-
-                               EXTRACT_NUMBER_AND_INCR(mcnt, p);
-                               p1 = p + mcnt;
-                               EXTRACT_NUMBER_AND_INCR(mcnt, p);
-                               DEBUG_PRINT3("  Setting 0x%x to %d.\n", p1,
-                                            mcnt);
-                               STORE_NUMBER(p1, mcnt);
-                               break;
-                       }
-
-               case wordbound:
-                       DEBUG_PRINT1("EXECUTING wordbound.\n");
-                       if (AT_WORD_BOUNDARY(d))
-                               break;
-                       goto fail;
-
-               case notwordbound:
-                       DEBUG_PRINT1("EXECUTING notwordbound.\n");
-                       if (AT_WORD_BOUNDARY(d))
-                               goto fail;
-                       break;
-
-               case wordbeg:
-                       DEBUG_PRINT1("EXECUTING wordbeg.\n");
-                       if (WORDCHAR_P(d)
-                           && (AT_STRINGS_BEG(d) || !WORDCHAR_P(d - 1)))
-                               break;
-                       goto fail;
-
-               case wordend:
-                       DEBUG_PRINT1("EXECUTING wordend.\n");
-                       if (!AT_STRINGS_BEG(d) && WORDCHAR_P(d - 1)
-                           && (!WORDCHAR_P(d) || AT_STRINGS_END(d)))
-                               break;
-                       goto fail;
-
-               case wordchar:
-                       DEBUG_PRINT1("EXECUTING non-Emacs wordchar.\n");
-                       PREFETCH();
-                       if (!WORDCHAR_P(d))
-                               goto fail;
-                       SET_REGS_MATCHED();
-                       d++;
-                       break;
-
-               case notwordchar:
-                       DEBUG_PRINT1("EXECUTING non-Emacs notwordchar.\n");
-                       PREFETCH();
-                       if (WORDCHAR_P(d))
-                               goto fail;
-                       SET_REGS_MATCHED();
-                       d++;
-                       break;
-
-               default:
-                       abort();
-               }
-               continue;       /* Successfully executed one pattern command; keep going.  */
-
-
-               /* We goto here if a matching operation fails. */
-             fail:
-               if (!FAIL_STACK_EMPTY()) {      /* A restart point is known.  Restore to that state.  */
-                       DEBUG_PRINT1("\nFAIL:\n");
-                       POP_FAILURE_POINT(d, p,
-                                         lowest_active_reg,
-                                         highest_active_reg, regstart,
-                                         regend, reg_info);
-
-                       /* If this failure point is a dummy, try the next one.  */
-                       if (!p)
-                               goto fail;
-
-                       /* If we failed to the end of the pattern, don't examine *p.  */
-                       assert(p <= pend);
-                       if (p < pend) {
-                               boolean is_a_jump_n = false;
-
-                               /* If failed to a backwards jump that's part of a repetition
-                                  loop, need to pop this failure point and use the next one.  */
-                               switch ((re_opcode_t) * p) {
-                               case jump_n:
-                                       is_a_jump_n = true;
-                               case maybe_pop_jump:
-                               case pop_failure_jump:
-                               case jump:
-                                       p1 = p + 1;
-                                       EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-                                       p1 += mcnt;
-
-                                       if ((is_a_jump_n
-                                            && (re_opcode_t) * p1 ==
-                                            succeed_n)
-                                           || (!is_a_jump_n
-                                               && (re_opcode_t) * p1 ==
-                                               on_failure_jump))
-                                               goto fail;
-                                       break;
-                               default:
-                                       /* do nothing */ ;
-                               }
-                       }
-
-                       if (d >= string1 && d <= end1)
-                               dend = end_match_1;
-               } else
-                       break;  /* Matching at this starting point really fails.  */
-       }                       /* for (;;) */
-
-       if (best_regs_set)
-               goto restore_best_regs;
-
-       FREE_VARIABLES();
-
-       return -1;              /* Failure to match.  */
-}                              /* re_match_2 */
-
-/* Subroutine definitions for re_match_2.  */
-
-
-/* We are passed P pointing to a register number after a start_memory.
-
-   Return true if the pattern up to the corresponding stop_memory can
-   match the empty string, and false otherwise.
-
-   If we find the matching stop_memory, sets P to point to one past its number.
-   Otherwise, sets P to an undefined byte less than or equal to END.
-
-   We don't handle duplicates properly (yet).  */
-
-static boolean group_match_null_string_p(p, end, reg_info)
-unsigned char **p, *end;
-register_info_type *reg_info;
-{
-       int mcnt;
-       /* Point to after the args to the start_memory.  */
-       unsigned char *p1 = *p + 2;
-
-       while (p1 < end) {
-               /* Skip over opcodes that can match nothing, and return true or
-                  false, as appropriate, when we get to one that can't, or to the
-                  matching stop_memory.  */
-
-               switch ((re_opcode_t) * p1) {
-                       /* Could be either a loop or a series of alternatives.  */
-               case on_failure_jump:
-                       p1++;
-                       EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-
-                       /* If the next operation is not a jump backwards in the
-                          pattern.  */
-
-                       if (mcnt >= 0) {
-                               /* Go through the on_failure_jumps of the alternatives,
-                                  seeing if any of the alternatives cannot match nothing.
-                                  The last alternative starts with only a jump,
-                                  whereas the rest start with on_failure_jump and end
-                                  with a jump, e.g., here is the pattern for `a|b|c':
-
-                                  /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
-                                  /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
-                                  /exactn/1/c
-
-                                  So, we have to first go through the first (n-1)
-                                  alternatives and then deal with the last one separately.  */
-
-
-                               /* Deal with the first (n-1) alternatives, which start
-                                  with an on_failure_jump (see above) that jumps to right
-                                  past a jump_past_alt.  */
-
-                               while ((re_opcode_t) p1[mcnt - 3] ==
-                                      jump_past_alt) {
-                                       /* `mcnt' holds how many bytes long the alternative
-                                          is, including the ending `jump_past_alt' and
-                                          its number.  */
-
-                                       if (!alt_match_null_string_p
-                                           (p1, p1 + mcnt - 3, reg_info))
-                                               return false;
-
-                                       /* Move to right after this alternative, including the
-                                          jump_past_alt.  */
-                                       p1 += mcnt;
-
-                                       /* Break if it's the beginning of an n-th alternative
-                                          that doesn't begin with an on_failure_jump.  */
-                                       if ((re_opcode_t) * p1 !=
-                                           on_failure_jump)
-                                               break;
-
-                                       /* Still have to check that it's not an n-th
-                                          alternative that starts with an on_failure_jump.  */
-                                       p1++;
-                                       EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-                                       if ((re_opcode_t) p1[mcnt - 3] !=
-                                           jump_past_alt) {
-                                               /* Get to the beginning of the n-th alternative.  */
-                                               p1 -= 3;
-                                               break;
-                                       }
-                               }
-
-                               /* Deal with the last alternative: go back and get number
-                                  of the `jump_past_alt' just before it.  `mcnt' contains
-                                  the length of the alternative.  */
-                               EXTRACT_NUMBER(mcnt, p1 - 2);
-
-                               if (!alt_match_null_string_p
-                                   (p1, p1 + mcnt, reg_info))
-                                       return false;
-
-                               p1 += mcnt;     /* Get past the n-th alternative.  */
-                       }       /* if mcnt > 0 */
-                       break;
-
-
-               case stop_memory:
-                       assert(p1[1] == **p);
-                       *p = p1 + 2;
-                       return true;
-
-
-               default:
-                       if (!common_op_match_null_string_p
-                           (&p1, end, reg_info))
-                               return false;
-               }
-       }                       /* while p1 < end */
-
-       return false;
-}                              /* group_match_null_string_p */
-
-
-/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
-   It expects P to be the first byte of a single alternative and END one
-   byte past the last. The alternative can contain groups.  */
-
-static boolean alt_match_null_string_p(p, end, reg_info)
-unsigned char *p, *end;
-register_info_type *reg_info;
-{
-       int mcnt;
-       unsigned char *p1 = p;
-
-       while (p1 < end) {
-               /* Skip over opcodes that can match nothing, and break when we get
-                  to one that can't.  */
-
-               switch ((re_opcode_t) * p1) {
-                       /* It's a loop.  */
-               case on_failure_jump:
-                       p1++;
-                       EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-                       p1 += mcnt;
-                       break;
-
-               default:
-                       if (!common_op_match_null_string_p
-                           (&p1, end, reg_info))
-                               return false;
-               }
-       }                       /* while p1 < end */
-
-       return true;
-}                              /* alt_match_null_string_p */
-
-
-/* Deals with the ops common to group_match_null_string_p and
-   alt_match_null_string_p.
-
-   Sets P to one after the op and its arguments, if any.  */
-
-static boolean common_op_match_null_string_p(p, end, reg_info)
-unsigned char **p, *end;
-register_info_type *reg_info;
-{
-       int mcnt;
-       boolean ret;
-       int reg_no;
-       unsigned char *p1 = *p;
-
-       switch ((re_opcode_t) * p1++) {
-       case no_op:
-       case begline:
-       case endline:
-       case begbuf:
-       case endbuf:
-       case wordbeg:
-       case wordend:
-       case wordbound:
-       case notwordbound:
-               break;
-
-       case start_memory:
-               reg_no = *p1;
-               assert(reg_no > 0 && reg_no <= MAX_REGNUM);
-               ret = group_match_null_string_p(&p1, end, reg_info);
-
-               /* Have to set this here in case we're checking a group which
-                  contains a group and a back reference to it.  */
-
-               if (REG_MATCH_NULL_STRING_P(reg_info[reg_no]) ==
-                   MATCH_NULL_UNSET_VALUE)
-                       REG_MATCH_NULL_STRING_P(reg_info[reg_no]) = ret;
-
-               if (!ret)
-                       return false;
-               break;
-
-               /* If this is an optimized succeed_n for zero times, make the jump.  */
-       case jump:
-               EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-               if (mcnt >= 0)
-                       p1 += mcnt;
-               else
-                       return false;
-               break;
-
-       case succeed_n:
-               /* Get to the number of times to succeed.  */
-               p1 += 2;
-               EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-
-               if (mcnt == 0) {
-                       p1 -= 4;
-                       EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-                       p1 += mcnt;
-               } else
-                       return false;
-               break;
-
-       case duplicate:
-               if (!REG_MATCH_NULL_STRING_P(reg_info[*p1]))
-                       return false;
-               break;
-
-       case set_number_at:
-               p1 += 4;
-
-       default:
-               /* All other opcodes mean we cannot match the empty string.  */
-               return false;
-       }
-
-       *p = p1;
-       return true;
-}                              /* common_op_match_null_string_p */
-
-
-/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
-   bytes; nonzero otherwise.  */
-
-static int bcmp_translate(s1, s2, len, translate)
-const char *s1, *s2;
-register int len;
-char *translate;
-{
-       register const unsigned char *p1 = (const unsigned char *) s1,
-           *p2 = (const unsigned char *) s2;
-       while (len) {
-               if (translate[*p1++] != translate[*p2++])
-                       return 1;
-               len--;
-       }
-       return 0;
-}
-
-/* Entry points for GNU code.  */
-
-/* re_compile_pattern is the GNU regular expression compiler: it
-   compiles PATTERN (of length SIZE) and puts the result in BUFP.
-   Returns 0 if the pattern was valid, otherwise an error string.
-
-   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
-   are set in BUFP on entry.
-
-   We call regex_compile to do the actual compilation.  */
-
-const char *re_compile_pattern(pattern, length, bufp)
-const char *pattern;
-size_t length;
-struct re_pattern_buffer *bufp;
-{
-       reg_errcode_t ret;
-
-       /* GNU code is written to assume at least RE_NREGS registers will be set
-          (and at least one extra will be -1).  */
-       bufp->regs_allocated = REGS_UNALLOCATED;
-
-       /* And GNU code determines whether or not to get register information
-          by passing null for the REGS argument to re_match, etc., not by
-          setting no_sub.  */
-       bufp->no_sub = 0;
-
-       /* Match anchors at newline.  */
-       bufp->newline_anchor = 1;
-
-       ret = regex_compile(pattern, length, re_syntax_options, bufp);
-
-       return re_error_msg[(int) ret];
-}
-
-/* Entry points compatible with 4.2 BSD regex library.  We don't define
-   them if this is an Emacs or POSIX compilation.  */
-
-/* POSIX.2 functions.  Don't define these for Emacs.  */
-
-#if !NO_POSIX_COMPAT
-
-/* regcomp takes a regular expression as a string and compiles it.
-
-   PREG is a regex_t *.  We do not expect any fields to be initialized,
-   since POSIX says we shouldn't.  Thus, we set
-
-     `buffer' to the compiled pattern;
-     `used' to the length of the compiled pattern;
-     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
-       REG_EXTENDED bit in CFLAGS is set; otherwise, to
-       RE_SYNTAX_POSIX_BASIC;
-     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
-     `fastmap' and `fastmap_accurate' to zero;
-     `re_nsub' to the number of subexpressions in PATTERN.
-
-   PATTERN is the address of the pattern string.
-
-   CFLAGS is a series of bits which affect compilation.
-
-     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
-     use POSIX basic syntax.
-
-     If REG_NEWLINE is set, then . and [^...] don't match newline.
-     Also, regexec will try a match beginning after every newline.
-
-     If REG_ICASE is set, then we considers upper- and lowercase
-     versions of letters to be equivalent when matching.
-
-     If REG_NOSUB is set, then when PREG is passed to regexec, that
-     routine will report only success or failure, and nothing about the
-     registers.
-
-   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
-   the return codes and their meanings.)  */
-
-int regcomp(preg, pattern, cflags)
-regex_t *preg;
-const char *pattern;
-int cflags;
-{
-       reg_errcode_t ret;
-       reg_syntax_t syntax
-           = (cflags & REG_EXTENDED) ?
-           RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
-
-       /* regex_compile will allocate the space for the compiled pattern.  */
-       preg->buffer = 0;
-       preg->allocated = 0;
-       preg->used = 0;
-
-       /* Don't bother to use a fastmap when searching.  This simplifies the
-          REG_NEWLINE case: if we used a fastmap, we'd have to put all the
-          characters after newlines into the fastmap.  This way, we just try
-          every character.  */
-       preg->fastmap = 0;
-
-       if (cflags & REG_ICASE) {
-               unsigned i;
-
-               preg->translate = (char *) malloc(CHAR_SET_SIZE);
-               if (preg->translate == NULL)
-                       return (int) REG_ESPACE;
-
-               /* Map uppercase characters to corresponding lowercase ones.  */
-               for (i = 0; i < CHAR_SET_SIZE; i++)
-                       preg->translate[i] = ISUPPER(i) ? tolower(i) : i;
-       } else
-               preg->translate = NULL;
-
-       /* If REG_NEWLINE is set, newlines are treated differently.  */
-       if (cflags & REG_NEWLINE) {     /* REG_NEWLINE implies neither . nor [^...] match newline.  */
-               syntax &= ~RE_DOT_NEWLINE;
-               syntax |= RE_HAT_LISTS_NOT_NEWLINE;
-               /* It also changes the matching behavior.  */
-               preg->newline_anchor = 1;
-       } else
-               preg->newline_anchor = 0;
-
-       preg->no_sub = !!(cflags & REG_NOSUB);
-
-       /* POSIX says a null character in the pattern terminates it, so we
-          can use strlen here in compiling the pattern.  */
-       ret = regex_compile(pattern, strlen(pattern), syntax, preg);
-
-       /* POSIX doesn't distinguish between an unmatched open-group and an
-          unmatched close-group: both are REG_EPAREN.  */
-       if (ret == REG_ERPAREN)
-               ret = REG_EPAREN;
-
-       return (int) ret;
-}
-
-
-/* regexec searches for a given pattern, specified by PREG, in the
-   string STRING.
-
-   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
-   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
-   least NMATCH elements, and we set them to the offsets of the
-   corresponding matched substrings.
-
-   EFLAGS specifies `execution flags' which affect matching: if
-   REG_NOTBOL is set, then ^ does not match at the beginning of the
-   string; if REG_NOTEOL is set, then $ does not match at the end.
-
-   We return 0 if we find a match and REG_NOMATCH if not.  */
-
-int regexec(preg, string, nmatch, pmatch, eflags)
-const regex_t *preg;
-const char *string;
-size_t nmatch;
-regmatch_t pmatch[];
-int eflags;
-{
-       int ret;
-       struct re_registers regs;
-       regex_t private_preg;
-       int len = strlen(string);
-       boolean want_reg_info = !preg->no_sub && nmatch > 0;
-
-       private_preg = *preg;
-
-       private_preg.not_bol = !!(eflags & REG_NOTBOL);
-       private_preg.not_eol = !!(eflags & REG_NOTEOL);
-
-       /* The user has told us exactly how many registers to return
-          information about, via `nmatch'.  We have to pass that on to the
-          matching routines.  */
-       private_preg.regs_allocated = REGS_FIXED;
-
-       if (want_reg_info) {
-               regs.num_regs = nmatch;
-               regs.start = TALLOC(nmatch, regoff_t);
-               regs.end = TALLOC(nmatch, regoff_t);
-               if (regs.start == NULL || regs.end == NULL)
-                       return (int) REG_NOMATCH;
-       }
-
-       /* Perform the searching operation.  */
-       ret = re_search(&private_preg, string, len,
-                       /* start: */ 0, /* range: */ len,
-                       want_reg_info ? &regs : (struct re_registers *) 0);
-
-       /* Copy the register information to the POSIX structure.  */
-       if (want_reg_info) {
-               if (ret >= 0) {
-                       unsigned r;
-
-                       for (r = 0; r < nmatch; r++) {
-                               pmatch[r].rm_so = regs.start[r];
-                               pmatch[r].rm_eo = regs.end[r];
-                       }
-               }
-
-               /* If we needed the temporary register info, free the space now.  */
-               free(regs.start);
-               free(regs.end);
-       }
-
-       /* We want zero return to mean success, unlike `re_search'.  */
-       return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
-}
-
-
-/* Returns a message corresponding to an error code, ERRCODE, returned
-   from either regcomp or regexec.   We don't use PREG here.  */
-
-size_t regerror(errcode, preg, errbuf, errbuf_size)
-int errcode;
-const regex_t *preg;
-char *errbuf;
-size_t errbuf_size;
-{
-       const char *msg;
-       size_t msg_size;
-
-       if (errcode < 0
-           || errcode >= (sizeof(re_error_msg) / sizeof(re_error_msg[0])))
-               /* Only error codes returned by the rest of the code should be passed
-                  to this routine.  If we are given anything else, or if other regex
-                  code generates an invalid error code, then the program has a bug.
-                  Dump core so we can fix it.  */
-               abort();
-
-       msg = re_error_msg[errcode];
-
-       /* POSIX doesn't require that we do anything in this case, but why
-          not be nice.  */
-       if (!msg)
-               msg = "Success";
-
-       msg_size = strlen(msg) + 1;     /* Includes the null.  */
-
-       if (errbuf_size != 0) {
-               if (msg_size > errbuf_size) {
-                       strncpy(errbuf, msg, errbuf_size - 1);
-                       errbuf[errbuf_size - 1] = 0;
-               } else
-                       strcpy(errbuf, msg);
-       }
-
-       return msg_size;
-}
-
-
-/* Free dynamically allocated space used by PREG.  */
-
-void regfree(preg)
-regex_t *preg;
-{
-       if (preg->buffer != NULL)
-               free(preg->buffer);
-       preg->buffer = NULL;
-
-       preg->allocated = 0;
-       preg->used = 0;
-
-       if (preg->fastmap != NULL)
-               free(preg->fastmap);
-       preg->fastmap = NULL;
-       preg->fastmap_accurate = 0;
-
-       if (preg->translate != NULL)
-               free(preg->translate);
-       preg->translate = NULL;
-}
-
-#endif                         /* !NO_POSIX_COMPAT */
diff --git a/libmultipath/regex.h b/libmultipath/regex.h
deleted file mode 100644 (file)
index 4715250..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/* Definitions for data structures and routines for the regular
-   expression library, version 0.12.
-
-   Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993
-   Free Software Foundation, Inc.
-
-   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; either version 2, or (at your option)
-   any later version.
-
-   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
-
-#ifndef __REGEXP_LIBRARY_H__
-#define __REGEXP_LIBRARY_H__
-
-typedef long s_reg_t;
-typedef unsigned long active_reg_t;
-
-typedef unsigned long reg_syntax_t;
-
-#define RE_BACKSLASH_ESCAPE_IN_LISTS   (1L)
-#define RE_BK_PLUS_QM                  (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
-#define RE_CHAR_CLASSES                        (RE_BK_PLUS_QM << 1)
-#define RE_CONTEXT_INDEP_ANCHORS       (RE_CHAR_CLASSES << 1)
-#define RE_CONTEXT_INDEP_OPS           (RE_CONTEXT_INDEP_ANCHORS << 1)
-#define RE_CONTEXT_INVALID_OPS         (RE_CONTEXT_INDEP_OPS << 1)
-#define RE_DOT_NEWLINE                 (RE_CONTEXT_INVALID_OPS << 1)
-#define RE_DOT_NOT_NULL                        (RE_DOT_NEWLINE << 1)
-#define RE_HAT_LISTS_NOT_NEWLINE       (RE_DOT_NOT_NULL << 1)
-#define RE_INTERVALS                   (RE_HAT_LISTS_NOT_NEWLINE << 1)
-#define RE_LIMITED_OPS                 (RE_INTERVALS << 1)
-#define RE_NEWLINE_ALT                 (RE_LIMITED_OPS << 1)
-#define RE_NO_BK_BRACES                        (RE_NEWLINE_ALT << 1)
-#define RE_NO_BK_PARENS                        (RE_NO_BK_BRACES << 1)
-#define RE_NO_BK_REFS                  (RE_NO_BK_PARENS << 1)
-#define RE_NO_BK_VBAR                  (RE_NO_BK_REFS << 1)
-#define RE_NO_EMPTY_RANGES             (RE_NO_BK_VBAR << 1)
-#define RE_UNMATCHED_RIGHT_PAREN_ORD   (RE_NO_EMPTY_RANGES << 1)
-#define RE_NO_GNU_OPS                  (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
-
-extern reg_syntax_t re_syntax_options;
-
-#define RE_SYNTAX_EMACS 0
-
-#define RE_SYNTAX_AWK                                                    \
-       (RE_BACKSLASH_ESCAPE_IN_LISTS   | RE_DOT_NOT_NULL               | \
-        RE_NO_BK_PARENS                | RE_NO_BK_REFS                 | \
-        RE_NO_BK_VBAR                  | RE_NO_EMPTY_RANGES            | \
-        RE_DOT_NEWLINE                 | RE_CONTEXT_INDEP_ANCHORS      | \
-        RE_UNMATCHED_RIGHT_PAREN_ORD   | RE_NO_GNU_OPS)
-
-#define RE_SYNTAX_GNU_AWK                                                \
-       ((RE_SYNTAX_POSIX_EXTENDED      | RE_BACKSLASH_ESCAPE_IN_LISTS) | \
-       & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS))
-
-#define RE_SYNTAX_POSIX_AWK                                              \
-       (RE_SYNTAX_POSIX_EXTENDED       | RE_BACKSLASH_ESCAPE_IN_LISTS  | \
-        RE_INTERVALS                   | RE_NO_GNU_OPS)
-
-#define RE_SYNTAX_GREP                                                   \
-       (RE_BK_PLUS_QM                  | RE_CHAR_CLASSES               | \
-        RE_HAT_LISTS_NOT_NEWLINE       | RE_INTERVALS                  | \
-        RE_NEWLINE_ALT)
-
-#define RE_SYNTAX_EGREP                                                          \
-       (RE_CHAR_CLASSES                | RE_CONTEXT_INDEP_ANCHORS      | \
-        RE_CONTEXT_INDEP_OPS           | RE_HAT_LISTS_NOT_NEWLINE      | \
-        RE_NEWLINE_ALT                 | RE_NO_BK_PARENS               | \
-        RE_NO_BK_VBAR)
-
-#define RE_SYNTAX_POSIX_EGREP                                            \
-       (RE_SYNTAX_EGREP                | RE_INTERVALS                  | \
-        RE_NO_BK_BRACES)
-
-#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
-
-#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
-
-#define _RE_SYNTAX_POSIX_COMMON                                                  \
-       (RE_CHAR_CLASSES                | RE_DOT_NEWLINE                | \
-        RE_DOT_NOT_NULL                | RE_INTERVALS                  | \
-        RE_NO_EMPTY_RANGES)
-
-#define RE_SYNTAX_POSIX_BASIC                                            \
-       (_RE_SYNTAX_POSIX_COMMON        | RE_BK_PLUS_QM)
-
-#define RE_SYNTAX_POSIX_MINIMAL_BASIC                                    \
-       (_RE_SYNTAX_POSIX_COMMON        | RE_LIMITED_OPS)
-
-#define RE_SYNTAX_POSIX_EXTENDED                                         \
-       (_RE_SYNTAX_POSIX_COMMON        | RE_CONTEXT_INDEP_ANCHORS      | \
-        RE_CONTEXT_INDEP_OPS           | RE_NO_BK_BRACES               | \
-        RE_NO_BK_PARENS                | RE_NO_BK_VBAR                 | \
-        RE_UNMATCHED_RIGHT_PAREN_ORD)
-
-#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED                                 \
-       (_RE_SYNTAX_POSIX_COMMON        | RE_CONTEXT_INDEP_ANCHORS      | \
-        RE_CONTEXT_INVALID_OPS         | RE_NO_BK_BRACES               | \
-        RE_NO_BK_PARENS                | RE_NO_BK_REFS                 | \
-        RE_NO_BK_VBAR                  | RE_UNMATCHED_RIGHT_PAREN_ORD)
-
-/* Maximum number of duplicates an interval can allow */
-#define RE_DUP_MAX  (0x7fff)
-
-/* POSIX 'cflags' bits */
-#define REG_EXTENDED   1
-#define REG_ICASE      (REG_EXTENDED << 1)
-#define REG_NEWLINE    (REG_ICASE << 1)
-#define REG_NOSUB      (REG_NEWLINE << 1)
-
-
-/* POSIX `eflags' bits */
-#define REG_NOTBOL     1
-#define REG_NOTEOL     (1 << 1)
-
-/* If any error codes are removed, changed, or added, update the
-   `re_error_msg' table in regex.c.  */
-typedef enum
-{
-  REG_NOERROR = 0,     /* Success.  */
-  REG_NOMATCH,         /* Didn't find a match (for regexec).  */
-
-  /* POSIX regcomp return error codes */
-  REG_BADPAT,          /* Invalid pattern.  */
-  REG_ECOLLATE,                /* Not implemented.  */
-  REG_ECTYPE,          /* Invalid character class name.  */
-  REG_EESCAPE,         /* Trailing backslash.  */
-  REG_ESUBREG,         /* Invalid back reference.  */
-  REG_EBRACK,          /* Unmatched left bracket.  */
-  REG_EPAREN,          /* Parenthesis imbalance.  */
-  REG_EBRACE,          /* Unmatched \{.  */
-  REG_BADBR,           /* Invalid contents of \{\}.  */
-  REG_ERANGE,          /* Invalid range end.  */
-  REG_ESPACE,          /* Ran out of memory.  */
-  REG_BADRPT,          /* No preceding re for repetition op.  */
-
-  /* Error codes we've added */
-  REG_EEND,            /* Premature end.  */
-  REG_ESIZE,           /* Compiled pattern bigger than 2^16 bytes.  */
-  REG_ERPAREN          /* Unmatched ) or \); not returned from regcomp.  */
-} reg_errcode_t;
-
-#define REGS_UNALLOCATED       0
-#define REGS_REALLOCATE                1
-#define REGS_FIXED             2
-
-/* This data structure represents a compiled pattern */
-struct re_pattern_buffer
-{
-  unsigned char *buffer;
-  unsigned long allocated;
-  unsigned long used;
-  reg_syntax_t syntax;
-  char *fastmap;
-  char *translate;
-  size_t re_nsub;
-  unsigned can_be_null : 1;
-  unsigned regs_allocated : 2;
-  unsigned fastmap_accurate : 1;
-  unsigned no_sub : 1;
-  unsigned not_bol : 1;
-  unsigned not_eol : 1;
-  unsigned newline_anchor : 1;
-};
-
-typedef struct re_pattern_buffer regex_t;
-
-/* search.c (search_buffer) in Emacs needs this one opcode value.  It is
-   defined both in `regex.c' and here.  */
-#define RE_EXACTN_VALUE 1
-
-/* Type for byte offsets within the string.  POSIX mandates this.  */
-typedef int regoff_t;
-
-
-/* This is the structure we store register match data in.  See
-   regex.texinfo for a full description of what registers match.  */
-struct re_registers
-{
-  unsigned num_regs;
-  regoff_t *start;
-  regoff_t *end;
-};
-
-
-#ifndef RE_NREGS
-#define RE_NREGS 30
-#endif
-
-
-/* POSIX specification for registers.  Aside from the different names than
-   `re_registers', POSIX uses an array of structures, instead of a
-   structure of arrays.  */
-typedef struct
-{
-  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
-  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
-} regmatch_t;
-
-/* Declarations for routines.  */
-
-extern reg_syntax_t re_set_syntax (reg_syntax_t syntax);
-
-extern const char *re_compile_pattern (const char *pattern, size_t length,
-                                      struct re_pattern_buffer *buffer);
-
-extern int re_compile_fastmap (struct re_pattern_buffer *buffer);
-
-extern int re_search (struct re_pattern_buffer *buffer, const char *string,
-                     int length, int start, int range,
-                     struct re_registers *regs);
-
-extern int re_search_2 (struct re_pattern_buffer *buffer, const char *string1,
-                       int length1, const char *string2, int length2,
-                       int start, int range, struct re_registers *regs,
-                       int stop);
-
-extern int re_match (struct re_pattern_buffer *buffer, const char *string,
-                    int length, int start, struct re_registers *regs);
-
-extern int re_match_2 (struct re_pattern_buffer *buffer, const char *string1,
-                      int length1, const char *string2, int length2,
-                      int start, struct re_registers *regs, int stop);
-
-extern void re_set_registers (struct re_pattern_buffer *buffer,
-                             struct re_registers *regs, unsigned num_regs,
-                             regoff_t *starts, regoff_t *ends);
-
-/* 4.2 bsd compatibility.  */
-extern char *re_comp (const char *);
-extern int re_exec (const char *);
-
-/* POSIX compatibility.  */
-extern int regcomp (regex_t *preg, const char *pattern, int cflags);
-
-extern int regexec (const regex_t *preg, const char *string, size_t nmatch,
-                   regmatch_t pmatch[], int eflags);
-
-extern size_t regerror (int errcode, const regex_t *preg, char *errbuf,
-                       size_t errbuf_size);
-
-extern void regfree (regex_t *preg);
-
-#endif /* not __REGEXP_LIBRARY_H__ */
index 3c0fe90..049f17d 100644 (file)
@@ -45,6 +45,9 @@ free_path (struct path * pp)
        if (checker_selected(&pp->checker))
                checker_put(&pp->checker);
 
+       if (prio_selected(&pp->prio))
+               prio_put(&pp->prio);
+
        if (pp->fd >= 0)
                close(pp->fd);
 
@@ -128,6 +131,7 @@ alloc_multipath (void)
                mpp->bestpg = 1;
                mpp->mpcontext = NULL;
                mpp->no_path_retry = NO_PATH_RETRY_UNDEF;
+               mpp->fast_io_fail = MP_FAST_IO_FAIL_UNSET;
        }
        return mpp;
 }
@@ -555,6 +559,9 @@ remove_feature(char **f, char *o)
         * about to be removed
         */
        p = strchr(*f, ' ');
+       if (!p)
+               /* Internal error, feature string inconsistent */
+               return 1;
        while (*p == ' ')
                p++;
        p--;
index 991ea6e..64de06e 100644 (file)
@@ -3,11 +3,13 @@
 
 #include <sys/types.h>
 
+#include "prio.h"
+
 #define WWID_SIZE              128
 #define SERIAL_SIZE            65
 #define NODE_NAME_SIZE         224
 #define PATH_STR_SIZE          16
-#define PARAMS_SIZE            1024
+#define PARAMS_SIZE            4096
 #define FILE_NAME_SIZE         256
 #define CALLOUT_MAX_SIZE       256
 #define BLK_DEV_SIZE           33
@@ -18,7 +20,7 @@
 #define SCSI_VENDOR_SIZE       9
 #define SCSI_PRODUCT_SIZE      17
 #define SCSI_REV_SIZE          5
-#define SCSI_STATE_SIZE                9
+#define SCSI_STATE_SIZE                19
 
 #define NO_PATH_RETRY_UNDEF    0
 #define NO_PATH_RETRY_FAIL     -1
@@ -65,9 +67,9 @@ enum pgstates {
 };
 
 enum queue_without_daemon_states {
-       QUE_NO_DAEMON_UNDEF,
        QUE_NO_DAEMON_OFF,
        QUE_NO_DAEMON_ON,
+       QUE_NO_DAEMON_FORCE,
 };
 
 enum pgtimeouts {
@@ -99,10 +101,29 @@ enum user_friendly_names_states {
        USER_FRIENDLY_NAMES_ON,
 };
 
-struct scsi_idlun {
-       int dev_id;
-       int host_unique_id;
-       int host_no;
+enum retain_hwhandler_states {
+       RETAIN_HWHANDLER_UNDEF,
+       RETAIN_HWHANDLER_OFF,
+       RETAIN_HWHANDLER_ON,
+};
+
+enum detect_prio_states {
+       DETECT_PRIO_UNDEF,
+       DETECT_PRIO_OFF,
+       DETECT_PRIO_ON,
+};
+
+enum scsi_protocol {
+       SCSI_PROTOCOL_FCP = 0,  /* Fibre Channel */
+       SCSI_PROTOCOL_SPI = 1,  /* parallel SCSI */
+       SCSI_PROTOCOL_SSA = 2,  /* Serial Storage Architecture - Obsolete */
+       SCSI_PROTOCOL_SBP = 3,  /* firewire */
+       SCSI_PROTOCOL_SRP = 4,  /* Infiniband RDMA */
+       SCSI_PROTOCOL_ISCSI = 5,
+       SCSI_PROTOCOL_SAS = 6,
+       SCSI_PROTOCOL_ADT = 7,  /* Media Changers */
+       SCSI_PROTOCOL_ATA = 8,
+       SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */
 };
 
 struct sg_id {
@@ -112,14 +133,8 @@ struct sg_id {
        int lun;
        short h_cmd_per_lun;
        short d_queue_depth;
-       int unused1;
-       int unused2;
-};
-
-struct scsi_dev {
-       char dev[FILE_NAME_SIZE];
-       struct scsi_idlun scsi_id;
-       int host_no;
+       enum scsi_protocol proto_id;
+       int transport_id;
 };
 
 # ifndef HDIO_GETGEO
@@ -132,11 +147,11 @@ struct hd_geometry {
       unsigned long start;
 };
 #endif
+
 struct path {
        char dev[FILE_NAME_SIZE];
        char dev_t[BLK_DEV_SIZE];
        struct udev_device *udev;
-       struct scsi_idlun scsi_id;
        struct sg_id sg_id;
        struct hd_geometry geom;
        char wwid[WWID_SIZE];
@@ -156,8 +171,11 @@ struct path {
        int failcount;
        int priority;
        int pgindex;
+       int detect_prio;
        char * uid_attribute;
-       struct prio * prio;
+       char * getuid;
+       struct prio prio;
+       char * prio_args;
        struct checker checker;
        struct multipath * mpp;
        int fd;
@@ -184,10 +202,10 @@ struct multipath {
        int no_path_retry; /* number of retries after all paths are down */
        int retry_tick;    /* remaining times for retries */
        int minio;
-       int pg_timeout;
        int flush_on_last_del;
        int attribute_flags;
        int fast_io_fail;
+       int retain_hwhandler;
        unsigned int dev_loss;
        uid_t uid;
        gid_t gid;
index 6e70d63..76c7e02 100644 (file)
@@ -68,21 +68,23 @@ adopt_paths (vector pathvec, struct multipath * mpp, int get_info)
                        if (!find_path_by_dev(mpp->paths, pp->dev) &&
                            store_path(mpp->paths, pp))
                                        return 1;
-                       if (get_info)
-                               pathinfo(pp, conf->hwtable,
-                                        DI_PRIO | DI_CHECKER);
+                       if (get_info && pathinfo(pp, conf->hwtable,
+                                                DI_PRIO | DI_CHECKER))
+                               return 1;
                }
        }
        return 0;
 }
 
 extern void
-orphan_path (struct path * pp)
+orphan_path (struct path * pp, const char *reason)
 {
+       condlog(3, "%s: orphan path, %s", pp->dev, reason);
        pp->mpp = NULL;
        pp->dmstate = PSTATE_UNDEF;
        pp->uid_attribute = NULL;
-       pp->prio = NULL;
+       pp->getuid = NULL;
+       prio_put(&pp->prio);
        checker_put(&pp->checker);
        if (pp->fd >= 0)
                close(pp->fd);
@@ -97,8 +99,7 @@ orphan_paths (vector pathvec, struct multipath * mpp)
 
        vector_foreach_slot (pathvec, pp, i) {
                if (pp->mpp == mpp) {
-                       condlog(4, "%s: orphaned", pp->dev);
-                       orphan_path(pp);
+                       orphan_path(pp, "map flushed");
                }
        }
 }
@@ -106,7 +107,7 @@ orphan_paths (vector pathvec, struct multipath * mpp)
 static void
 set_multipath_wwid (struct multipath * mpp)
 {
-       if (mpp->wwid)
+       if (strlen(mpp->wwid))
                return;
 
        dm_get_uuid(mpp->alias, mpp->wwid);
@@ -246,11 +247,15 @@ update_multipath_table (struct multipath *mpp, vector pathvec)
        if (!mpp)
                return 1;
 
-       if (dm_get_map(mpp->alias, &mpp->size, params))
+       if (dm_get_map(mpp->alias, &mpp->size, params)) {
+               condlog(3, "%s: cannot get map", mpp->alias);
                return 1;
+       }
 
-       if (disassemble_map(pathvec, params, mpp))
+       if (disassemble_map(pathvec, params, mpp)) {
+               condlog(3, "%s: cannot disassemble map", mpp->alias);
                return 1;
+       }
 
        return 0;
 }
@@ -263,11 +268,15 @@ update_multipath_status (struct multipath *mpp)
        if (!mpp)
                return 1;
 
-       if(dm_get_status(mpp->alias, status))
+       if (dm_get_status(mpp->alias, status)) {
+               condlog(3, "%s: cannot get status", mpp->alias);
                return 1;
+       }
 
-       if (disassemble_status(status, mpp))
+       if (disassemble_status(status, mpp)) {
+               condlog(3, "%s: cannot disassemble status", mpp->alias);
                return 1;
+       }
 
        return 0;
 }
@@ -298,8 +307,7 @@ set_no_path_retry(struct multipath *mpp)
 {
        mpp->retry_tick = 0;
        mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
-       if (mpp->nr_active > 0)
-               select_no_path_retry(mpp);
+       select_no_path_retry(mpp);
 
        switch (mpp->no_path_retry) {
        case NO_PATH_RETRY_UNDEF:
@@ -337,15 +345,15 @@ __setup_multipath (struct vectors * vecs, struct multipath * mpp, int reset)
                goto out;
        }
 
-       set_multipath_wwid(mpp);
-       mpp->mpe = find_mpe(mpp->wwid);
-       condlog(3, "%s: discover", mpp->alias);
-
        if (update_multipath_strings(mpp, vecs->pathvec)) {
                condlog(0, "%s: failed to setup multipath", mpp->alias);
                goto out;
        }
 
+       set_multipath_wwid(mpp);
+       mpp->mpe = find_mpe(mpp->wwid);
+       condlog(3, "%s: discover", mpp->alias);
+
        if (!mpp->hwe)
                mpp->hwe = extract_hwe_from_path(mpp);
        if (!mpp->hwe) {
@@ -356,7 +364,6 @@ __setup_multipath (struct vectors * vecs, struct multipath * mpp, int reset)
                select_rr_weight(mpp);
                select_pgfailback(mpp);
                set_no_path_retry(mpp);
-               select_pg_timeout(mpp);
                select_flush_on_last_del(mpp);
        }
 
@@ -444,8 +451,7 @@ verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec)
                /*
                 * see if path is in sysfs
                 */
-               if (!pp->udev || sysfs_get_dev(pp->udev, pp->dev_t,
-                                              BLK_DEV_SIZE)) {
+               if (sysfs_get_dev(pp->udev, pp->dev_t, BLK_DEV_SIZE) <= 0) {
                        if (pp->state != PATH_DOWN) {
                                condlog(1, "%s: removing valid path %s in state %d",
                                        mpp->alias, pp->dev, pp->state);
@@ -483,7 +489,7 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset)
        mpp = find_mp_by_alias(vecs->mpvec, mapname);
 
        if (!mpp) {
-               condlog(3, "%s: multipath map not found\n", mapname);
+               condlog(3, "%s: multipath map not found", mapname);
                return 2;
        }
 
index a907e85..c6278ac 100644 (file)
@@ -17,7 +17,7 @@ void set_no_path_retry(struct multipath *mpp);
 
 int adopt_paths (vector pathvec, struct multipath * mpp, int get_info);
 void orphan_paths (vector pathvec, struct multipath * mpp);
-void orphan_path (struct path * pp);
+void orphan_path (struct path * pp, const char *reason);
 
 int verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec);
 int update_mpp_paths(struct multipath * mpp, vector pathvec);
index 8e986e8..e5834f9 100644 (file)
 #include "debug.h"
 #include "devmapper.h"
 
-ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
+/*
+ * When we modify an attribute value we cannot rely on libudev for now,
+ * as libudev lacks the capability to update an attribute value.
+ * So for modified attributes we need to implement our own function.
+ */
+ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
                             char * value, size_t value_len)
 {
        char devpath[PATH_SIZE];
@@ -54,30 +59,85 @@ ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
        condlog(4, "open '%s'", devpath);
        if (stat(devpath, &statbuf) != 0) {
                condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
-               return 0;
+               return -ENXIO;
        }
 
        /* skip directories */
-       if (S_ISDIR(statbuf.st_mode))
-               return 0;
+       if (S_ISDIR(statbuf.st_mode)) {
+               condlog(4, "%s is a directory", devpath);
+               return -EISDIR;
+       }
 
        /* skip non-writeable files */
-       if ((statbuf.st_mode & S_IWUSR) == 0)
+       if ((statbuf.st_mode & S_IRUSR) == 0) {
+               condlog(4, "%s is not readable", devpath);
+               return -EPERM;
+       }
+
+       /* read attribute value */
+       fd = open(devpath, O_RDONLY);
+       if (fd < 0) {
+               condlog(4, "attribute '%s' can not be opened: %s",
+                       devpath, strerror(errno));
+               return -errno;
+       }
+       size = read(fd, value, value_len);
+       if (size < 0) {
+               condlog(4, "read from %s failed: %s", devpath, strerror(errno));
+               size = -errno;
+       } else if (size == value_len) {
+               condlog(4, "overflow while reading from %s", devpath);
+               size = 0;
+       }
+
+       close(fd);
+       return size;
+}
+
+ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
+                            char * value, size_t value_len)
+{
+       char devpath[PATH_SIZE];
+       struct stat statbuf;
+       int fd;
+       ssize_t size = -1;
+
+       if (!dev || !attr_name || !value || !value_len)
                return 0;
 
+       snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
+                attr_name);
+       condlog(4, "open '%s'", devpath);
+       if (stat(devpath, &statbuf) != 0) {
+               condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
+               return -errno;
+       }
+
+       /* skip directories */
+       if (S_ISDIR(statbuf.st_mode)) {
+               condlog(4, "%s is a directory", devpath);
+               return -EISDIR;
+       }
+
+       /* skip non-writeable files */
+       if ((statbuf.st_mode & S_IWUSR) == 0) {
+               condlog(4, "%s is not writeable", devpath);
+               return -EPERM;
+       }
+
        /* write attribute value */
        fd = open(devpath, O_WRONLY);
        if (fd < 0) {
                condlog(4, "attribute '%s' can not be opened: %s",
                        devpath, strerror(errno));
-               return 0;
+               return -errno;
        }
        size = write(fd, value, value_len);
        if (size < 0) {
                condlog(4, "write to %s failed: %s", devpath, strerror(errno));
-               size = 0;
+               size = -errno;
        } else if (size < value_len) {
-               condlog(4, "tried to write %ld to %s. Wrote %ld\n",
+               condlog(4, "tried to write %ld to %s. Wrote %ld",
                        (long)value_len, devpath, (long)size);
                size = 0;
        }
@@ -89,14 +149,14 @@ ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
 int
 sysfs_get_size (struct path *pp, unsigned long long * size)
 {
-       const char * attr;
+       char attr[255];
        int r;
 
-       if (!pp->udev)
+       if (!pp->udev || !size)
                return 1;
 
-       attr = udev_device_get_sysattr_value(pp->udev, "size");
-       if (!attr) {
+       attr[0] = '\0';
+       if (sysfs_attr_get_value(pp->udev, "size", attr, 255) == 0) {
                condlog(3, "%s: No size attribute in sysfs", pp->dev);
                return 1;
        }
@@ -104,8 +164,8 @@ sysfs_get_size (struct path *pp, unsigned long long * size)
        r = sscanf(attr, "%llu\n", size);
 
        if (r != 1) {
-               condlog(3, "%s: Cannot parse size attribute '%s'",
-                       pp->dev, attr);
+               condlog(3, "%s: Cannot parse size attribute", pp->dev);
+               *size = 0;
                return 1;
        }
 
@@ -125,8 +185,10 @@ int sysfs_check_holders(char * check_devt, char * new_devt)
                return 0;
        }
 
-       if (devt2devname(check_dev, PATH_SIZE, check_devt))
+       if (devt2devname(check_dev, PATH_SIZE, check_devt)) {
+               condlog(1, "can't get devname for %s", check_devt);
                return 0;
+       }
 
        condlog(3, "%s: checking holder", check_dev);
 
@@ -153,7 +215,7 @@ int sysfs_check_holders(char * check_devt, char * new_devt)
                }
                table_name = dm_mapname(major, table_minor);
 
-               condlog(3, "%s: reassign table %s old %s new %s", check_dev,
+               condlog(0, "%s: reassign table %s old %s new %s", check_dev,
                        table_name, check_devt, new_devt);
 
                dm_reassign_table(table_name, check_devt, new_devt);
index 13d7545..34f3e00 100644 (file)
@@ -7,6 +7,8 @@
 
 ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
                             char * value, size_t value_len);
+ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
+                            char * value, size_t value_len);
 int sysfs_get_size (struct path *pp, unsigned long long * size);
 int sysfs_check_holders(char * check_devt, char * new_devt);
 #endif
index 0b7eb7a..9ee3ade 100644 (file)
 #include "list.h"
 #include "uevent.h"
 #include "vector.h"
-#include "config.h"
 
 typedef int (uev_trigger)(struct uevent *, void * trigger_data);
 
 pthread_t uevq_thr;
 LIST_HEAD(uevq);
-pthread_mutex_t uevq_lock, *uevq_lockp = &uevq_lock;
-pthread_cond_t  uev_cond,  *uev_condp  = &uev_cond;
+pthread_mutex_t uevq_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t *uevq_lockp = &uevq_lock;
+pthread_cond_t uev_cond = PTHREAD_COND_INITIALIZER;
+pthread_cond_t *uev_condp = &uev_cond;
 uev_trigger *my_uev_trigger;
 void * my_trigger_data;
 int servicing_uev;
@@ -125,11 +126,25 @@ service_uevq(struct list_head *tmpq)
 
 static void uevq_stop(void *arg)
 {
+       struct udev *udev = arg;
+
        condlog(3, "Stopping uev queue");
        pthread_mutex_lock(uevq_lockp);
        my_uev_trigger = NULL;
        pthread_cond_signal(uev_condp);
        pthread_mutex_unlock(uevq_lockp);
+       udev_unref(udev);
+}
+
+void
+uevq_cleanup(struct list_head *tmpq)
+{
+       struct uevent *uev, *tmp;
+
+       list_for_each_entry_safe(uev, tmp, tmpq, node) {
+               list_del_init(&uev->node);
+               FREE(uev);
+       }
 }
 
 /*
@@ -163,6 +178,7 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
                service_uevq(&uevq_tmp);
        }
        condlog(3, "Terminating uev service queue");
+       uevq_cleanup(&uevq);
        return 0;
 }
 
@@ -349,11 +365,20 @@ int failback_listen(void)
                        key = &buffer[bufpos];
                        keylen = strlen(key);
                        uev->envp[i] = key;
+                       /* Filter out sequence number */
+                       if (strncmp(key, "SEQNUM=", 7) == 0) {
+                               char *eptr;
+
+                               uev->seqnum = strtoul(key + 7, &eptr, 10);
+                               if (eptr == key + 7)
+                                       uev->seqnum = -1;
+                       }
                        bufpos += keylen + 1;
                }
                uev->envp[i] = NULL;
 
-               condlog(3, "uevent '%s' from '%s'", uev->action, uev->devpath);
+               condlog(3, "uevent %ld '%s' from '%s'", uev->seqnum,
+                       uev->action, uev->devpath);
                uev->kernel = strrchr(uev->devpath, '/');
                if (uev->kernel)
                        uev->kernel++;
@@ -376,7 +401,7 @@ exit:
        return 1;
 }
 
-int uevent_listen(void)
+int uevent_listen(struct udev *udev)
 {
        int err;
        struct udev_monitor *monitor = NULL;
@@ -388,15 +413,17 @@ int uevent_listen(void)
         * thereby not getting to empty the socket's receive buffer queue
         * often enough.
         */
-       INIT_LIST_HEAD(&uevq);
-
-       pthread_mutex_init(uevq_lockp, NULL);
-       pthread_cond_init(uev_condp, NULL);
-       pthread_cleanup_push(uevq_stop, NULL);
+       if (!udev) {
+               condlog(1, "no udev context");
+               return 1;
+       }
+       udev_ref(udev);
+       pthread_cleanup_push(uevq_stop, udev);
 
-       monitor = udev_monitor_new_from_netlink(conf->udev, "udev");
+       monitor = udev_monitor_new_from_netlink(udev, "udev");
        if (!monitor) {
                condlog(2, "failed to create udev monitor");
+               err = 2;
                goto out;
        }
 #ifdef LIBUDEV_API_RECVBUF
@@ -422,10 +449,10 @@ int uevent_listen(void)
        err = udev_monitor_filter_add_match_subsystem_devtype(monitor, "block",
                                                              NULL);
        if (err)
-               condlog(2, "failed to create filter : %s\n", strerror(-err));
+               condlog(2, "failed to create filter : %s", strerror(-err));
        err = udev_monitor_enable_receiving(monitor);
        if (err) {
-               condlog(2, "failed to enable receiving : %s\n", strerror(-err));
+               condlog(2, "failed to enable receiving : %s", strerror(-err));
                goto out;
        }
        while (1) {
@@ -504,8 +531,6 @@ out:
        if (need_failback)
                err = failback_listen();
        pthread_cleanup_pop(1);
-       pthread_mutex_destroy(uevq_lockp);
-       pthread_cond_destroy(uev_condp);
        return err;
 }
 
index 64f00b7..e5fdfcc 100644 (file)
@@ -13,6 +13,8 @@
 #define NETLINK_KOBJECT_UEVENT         15
 #endif
 
+struct udev;
+
 struct uevent {
        struct list_head node;
        struct udev_device *udev;
@@ -20,13 +22,14 @@ struct uevent {
        char *devpath;
        char *action;
        char *kernel;
+       unsigned long seqnum;
        char *envp[HOTPLUG_NUM_ENVP];
 };
 
 int is_uevent_busy(void);
 void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
 
-int uevent_listen(void);
+int uevent_listen(struct udev *udev);
 int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data),
                    void * trigger_data);
 int uevent_get_major(struct uevent *uev);
index 70735e6..06a6311 100644 (file)
 #include "vector.h"
 #include "structs.h"
 
-void
+size_t
 strchop(char *str)
 {
        int i;
 
        for (i=strlen(str)-1; i >=0 && isspace(str[i]); --i) ;
        str[++i] = '\0';
+       return strlen(str);
 }
 
 int
@@ -27,7 +28,7 @@ basenamecpy (const char * str1, char * str2, int str2len)
        if (!str1 || !strlen(str1))
                return 0;
 
-       if (strlen(str1) > str2len)
+       if (strlen(str1) >= str2len)
                return 0;
 
        if (!str2)
@@ -43,8 +44,7 @@ basenamecpy (const char * str1, char * str2, int str2len)
 
        strncpy(str2, p, str2len);
        str2[str2len - 1] = '\0';
-       strchop(str2);
-       return strlen(str2);
+       return strchop(str2);
 }
 
 int
@@ -86,7 +86,7 @@ get_word (char * sentence, char ** word)
        *word = MALLOC(len + 1);
 
        if (!*word) {
-               condlog(0, "get_word : oom\n");
+               condlog(0, "get_word : oom");
                return 0;
        }
        strncpy(*word, sentence, len);
@@ -112,8 +112,7 @@ size_t strlcpy(char *dst, const char *src, size_t size)
                bytes++;
        }
 
-       /* If size == 0 there is no space for a final null... */
-       if (size)
+       if (bytes == size)
                *q = '\0';
        return bytes;
 }
@@ -142,15 +141,6 @@ size_t strlcat(char *dst, const char *src, size_t size)
        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';
-}
-
 extern int
 devt2devname (char *devname, int devname_len, char *devt)
 {
@@ -161,6 +151,7 @@ devt2devname (char *devname, int devname_len, char *devt)
        struct stat statbuf;
 
        memset(block_path, 0, sizeof(block_path));
+       memset(dev, 0, sizeof(dev));
        if (sscanf(devt, "%u:%u", &major, &minor) != 2) {
                condlog(0, "Invalid device number %s", devt);
                return 1;
@@ -172,13 +163,13 @@ devt2devname (char *devname, int devname_len, char *devt)
        if (stat("/sys/dev/block", &statbuf) == 0) {
                /* Newer kernels have /sys/dev/block */
                sprintf(block_path,"/sys/dev/block/%u:%u", major, minor);
-               if (stat(block_path, &statbuf) == 0) {
+               if (lstat(block_path, &statbuf) == 0) {
                        if (S_ISLNK(statbuf.st_mode) &&
                            readlink(block_path, dev, FILE_NAME_SIZE) > 0) {
                                char *p = strrchr(dev, '/');
 
                                if (!p) {
-                                       condlog(0, "No sysfs entry for %s\n",
+                                       condlog(0, "No sysfs entry for %s",
                                                block_path);
                                        return 1;
                                }
@@ -208,7 +199,7 @@ devt2devname (char *devname, int devname_len, char *devt)
                if ((major == tmpmaj) && (minor == tmpmin)) {
                        if (snprintf(block_path, sizeof(block_path),
                                     "/sys/block/%s", dev) >= sizeof(block_path)) {
-                               condlog(0, "device name %s is too long\n", dev);
+                               condlog(0, "device name %s is too long", dev);
                                fclose(fd);
                                return 1;
                        }
@@ -218,23 +209,45 @@ devt2devname (char *devname, int devname_len, char *devt)
        fclose(fd);
 skip_proc:
        if (strncmp(block_path,"/sys/block", 10)) {
-               condlog(3, "No device found for %u:%u\n", major, minor);
+               condlog(3, "No device found for %u:%u", major, minor);
                return 1;
        }
 
        if (stat(block_path, &statbuf) < 0) {
-               condlog(0, "No sysfs entry for %s\n", block_path);
+               condlog(0, "No sysfs entry for %s", block_path);
                return 1;
        }
 
        if (S_ISDIR(statbuf.st_mode) == 0) {
-               condlog(0, "sysfs entry %s is not a directory\n", block_path);
+               condlog(0, "sysfs entry %s is not a directory", block_path);
                return 1;
        }
        basenamecpy((const char *)block_path, devname, devname_len);
        return 0;
 }
 
+/* This function returns a pointer inside of the supplied pathname string.
+ * If is_path_device is true, it may also modify the supplied string */
+char *convert_dev(char *name, int is_path_device)
+{
+       char *ptr;
+
+       if (!name)
+               return NULL;
+       if (is_path_device) {
+               ptr = strstr(name, "cciss/");
+               if (ptr) {
+                       ptr += 5;
+                       *ptr = '!';
+               }
+       }
+       if (!strncmp(name, "/dev/", 5) && strlen(name) > 5)
+               ptr = name + 5;
+       else
+               ptr = name;
+       return ptr;
+}
+
 dev_t parse_devt(const char *dev_t)
 {
        int maj, min;
index 44184a1..257912c 100644 (file)
@@ -1,15 +1,15 @@
 #ifndef _UTIL_H
 #define _UTIL_H
 
-void strchop(char *);
+size_t strchop(char *);
 int basenamecpy (const char * src, char * dst, int);
 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);
 int devt2devname (char *, int, char *);
 dev_t parse_devt(const char *dev_t);
+char *convert_dev(char *dev, int is_path_device);
 
 #define safe_sprintf(var, format, args...)     \
        snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
index e786899..aff7a62 100644 (file)
 #include <sys/poll.h>
 #include <signal.h>
 #include <errno.h>
+#ifdef USE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
 
 #include "memory.h"
 #include "uxsock.h"
+#include "debug.h"
 
 /*
  * connect to a unix domain socket
  */
 int ux_socket_connect(const char *name)
 {
-       int fd;
+       int fd, len;
        struct sockaddr_un addr;
 
        memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       strncpy(addr.sun_path, name, sizeof(addr.sun_path));
+       addr.sun_family = AF_LOCAL;
+       addr.sun_path[0] = '\0';
+       len = strlen(name) + 1 + sizeof(sa_family_t);
+       strncpy(&addr.sun_path[1], name, len);
 
-       fd = socket(AF_UNIX, SOCK_STREAM, 0);
+       fd = socket(AF_LOCAL, SOCK_STREAM, 0);
        if (fd == -1) {
+               condlog(3, "Couldn't create ux_socket, error %d", errno);
                return -1;
        }
 
-       if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+       if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
+               condlog(3, "Couldn't connect to ux_socket, error %d", errno);
                close(fd);
                return -1;
        }
@@ -51,29 +59,46 @@ int ux_socket_connect(const char *name)
  */
 int ux_socket_listen(const char *name)
 {
-       int fd;
+       int fd, len;
+#ifdef USE_SYSTEMD
+       int num;
+#endif
        struct sockaddr_un addr;
 
-       /* get rid of any old socket */
-       unlink(name);
-
-       fd = socket(AF_UNIX, SOCK_STREAM, 0);
-       if (fd == -1) return -1;
+#ifdef USE_SYSTEMD
+       num = sd_listen_fds(0);
+       if (num > 1) {
+               condlog(3, "sd_listen_fds returned %d fds", num);
+               return -1;
+       } else if (num == 1) {
+               fd = SD_LISTEN_FDS_START + 0;
+               condlog(3, "using fd %d from sd_listen_fds", fd);
+               return fd;
+       }
+#endif
+       fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+       if (fd == -1) {
+               condlog(3, "Couldn't create ux_socket, error %d", errno);
+               return -1;
+       }
 
        memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       strncpy(addr.sun_path, name, sizeof(addr.sun_path));
+       addr.sun_family = AF_LOCAL;
+       addr.sun_path[0] = '\0';
+       len = strlen(name) + 1 + sizeof(sa_family_t);
+       strncpy(&addr.sun_path[1], name, len);
 
-       if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+       if (bind(fd, (struct sockaddr *)&addr, len) == -1) {
+               condlog(3, "Couldn't bind to ux_socket, error %d", errno);
                close(fd);
                return -1;
        }
 
        if (listen(fd, 10) == -1) {
+               condlog(3, "Couldn't listen to ux_socket, error %d", errno);
                close(fd);
                return -1;
        }
-
        return fd;
 }
 
@@ -106,9 +131,24 @@ size_t write_all(int fd, const void *buf, size_t len)
 size_t read_all(int fd, void *buf, size_t len)
 {
        size_t total = 0;
+       ssize_t n;
+       int ret;
+       struct pollfd pfd;
 
        while (len) {
-               ssize_t n = read(fd, buf, len);
+               pfd.fd = fd;
+               pfd.events = POLLIN;
+               ret = poll(&pfd, 1, 1000);
+               if (!ret) {
+                       errno = ETIMEDOUT;
+                       return total;
+               } else if (ret < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       return total;
+               } else if (!pfd.revents & POLLIN)
+                       continue;
+               n = read(fd, buf, len);
                if (n < 0) {
                        if ((errno == EINTR) || (errno == EAGAIN))
                                continue;
index 652f118..6266e0a 100644 (file)
@@ -81,7 +81,7 @@ vector_insert_slot(vector v, int slot, void *value)
        if (!vector_alloc_slot(v))
                return NULL;
 
-       for (i = (v->allocated /VECTOR_DEFAULT_SIZE) - 2; i >= slot; i--)
+       for (i = VECTOR_SIZE(v) - 2; i >= slot; i--)
                v->slot[i + 1] = v->slot[i];
 
        v->slot[slot] = value;
@@ -94,7 +94,10 @@ find_slot(vector v, void * addr)
 {
        int i;
 
-       for (i = 0; i < (v->allocated / VECTOR_DEFAULT_SIZE); i++)
+       if (!v)
+               return -1;
+
+       for (i = 0; i < VECTOR_SIZE(v); i++)
                if (v->slot[i] == addr)
                        return i;
 
@@ -109,12 +112,12 @@ vector_del_slot(vector v, int slot)
        if (!v || !v->allocated || slot < 0 || slot > VECTOR_SIZE(v))
                return;
 
-       for (i = slot + 1; i < (v->allocated / VECTOR_DEFAULT_SIZE); i++)
+       for (i = slot + 1; i < VECTOR_SIZE(v); i++)
                v->slot[i-1] = v->slot[i];
 
        v->allocated -= VECTOR_DEFAULT_SIZE;
 
-       if (!v->allocated) {
+       if (v->allocated <= 0) {
                FREE(v->slot);
                v->slot = NULL;
                v->allocated = 0;
@@ -137,7 +140,7 @@ vector_repack(vector v)
        if (!v || !v->allocated)
                return;
 
-       for (i = 0; i < (v->allocated / VECTOR_DEFAULT_SIZE); i++)
+       for (i = 0; i < VECTOR_SIZE(v); i++)
                if (i > 0 && v->slot[i] == NULL)
                        vector_del_slot(v, i--);
 }
index ca42be1..7612b4c 100644 (file)
 
 /* vector definition */
 struct _vector {
-       unsigned int allocated;
+       int allocated;
        void **slot;
 };
 typedef struct _vector *vector;
 
 #define VECTOR_DEFAULT_SIZE 1
-#define VECTOR_SLOT(V,E) (((V) && (E) < (V)->allocated) ? (V)->slot[(E)] : NULL)
-#define VECTOR_SIZE(V)   ((V) ? (V)->allocated : 0)
-#define VECTOR_LAST_SLOT(V)   (((V) && (V)->allocated) ? (V)->slot[((V)->allocated - 1)] : NULL)
+#define VECTOR_SIZE(V)   ((V) ? ((V)->allocated) / VECTOR_DEFAULT_SIZE : 0)
+#define VECTOR_SLOT(V,E) (((V) && (E) < VECTOR_SIZE(V)) ? (V)->slot[(E)] : NULL)
+#define VECTOR_LAST_SLOT(V)   (((V) && VECTOR_SIZE(V) > 0) ? (V)->slot[(VECTOR_SIZE(V) - 1)] : NULL)
 
 #define vector_foreach_slot(v,p,i) \
-       for (i = 0; (v) && i < (v)->allocated && ((p) = (v)->slot[i]); i++)
+       for (i = 0; (v) && i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
 #define vector_foreach_slot_after(v,p,i) \
-       for (; (v) && i < (v)->allocated && ((p) = (v)->slot[i]); i++)
+       for (; (v) && i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
 #define vector_foreach_slot_backwards(v,p,i) \
        for (i = VECTOR_SIZE(v); i > 0 && ((p) = (v)->slot[i-1]); i--)
 
index cefd6b5..8519300 100644 (file)
@@ -20,8 +20,8 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000409
-#define DATE_CODE    0x052110
+#define VERSION_CODE 0x000500
+#define DATE_CODE    0x0c110d
 
 #define PROG    "multipath-tools"
 
index 076539c..7cedd4b 100644 (file)
@@ -45,6 +45,8 @@ void free_waiter (void *data)
 
 void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
 {
+       pthread_t thread;
+
        if (mpp->waiter == (pthread_t)0) {
                condlog(3, "%s: event checker thread already stopped",
                        mpp->alias);
@@ -52,8 +54,10 @@ void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
        }
        condlog(2, "%s: stop event checker thread (%lu)", mpp->alias,
                mpp->waiter);
-       pthread_cancel(mpp->waiter);
+       thread = mpp->waiter;
        mpp->waiter = (pthread_t)0;
+       pthread_cancel(thread);
+       pthread_kill(thread, SIGUSR2);
 }
 
 /*
@@ -62,6 +66,7 @@ void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs)
  */
 int waiteventloop (struct event_thread *waiter)
 {
+       sigset_t set, oldset;
        int event_nr;
        int r;
 
@@ -94,8 +99,15 @@ int waiteventloop (struct event_thread *waiter)
        dm_task_no_open_count(waiter->dmt);
 
        /* wait */
+       sigemptyset(&set);
+       sigaddset(&set, SIGUSR2);
+       pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
+
+       pthread_testcancel();
        r = dm_task_run(waiter->dmt);
+       pthread_testcancel();
 
+       pthread_sigmask(SIG_SETMASK, &oldset, NULL);
        dm_task_destroy(waiter->dmt);
        waiter->dmt = NULL;
 
@@ -154,8 +166,6 @@ void *waitevent (void *et)
        waiter = (struct event_thread *)et;
        pthread_cleanup_push(free_waiter, et);
 
-       block_signal(SIGUSR1, NULL);
-       block_signal(SIGHUP, NULL);
        while (1) {
                r = waiteventloop(waiter);
 
index abd23c5..eca1799 100644 (file)
@@ -4,6 +4,7 @@
 #include <string.h>
 #include <limits.h>
 #include <stdio.h>
+#include <sys/types.h>
 
 #include "checkers.h"
 #include "vector.h"
@@ -82,6 +83,141 @@ write_out_wwid(int fd, char *wwid) {
 }
 
 int
+replace_wwids(vector mp)
+{
+       int i, fd, can_write;
+       struct multipath * mpp;
+       size_t len;
+       int ret = -1;
+
+       fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
+       if (fd < 0)
+               goto out;
+       if (!can_write) {
+               condlog(0, "cannot replace wwids. wwids file is read-only");
+               goto out_file;
+       }
+       if (ftruncate(fd, 0) < 0) {
+               condlog(0, "cannot truncate wwids file : %s", strerror(errno));
+               goto out_file;
+       }
+       if (lseek(fd, 0, SEEK_SET) < 0) {
+               condlog(0, "cannot seek to the start of the file : %s",
+                       strerror(errno));
+               goto out_file;
+       }
+       len = strlen(WWIDS_FILE_HEADER);
+       if (write_all(fd, WWIDS_FILE_HEADER, len) != len) {
+               condlog(0, "Can't write wwid file header : %s",
+                       strerror(errno));
+               /* cleanup partially written header */
+               if (ftruncate(fd, 0) < 0)
+                       condlog(0, "Cannot truncate header : %s",
+                               strerror(errno));
+               goto out_file;
+       }
+       if (!mp || !mp->allocated) {
+               ret = 0;
+               goto out_file;
+       }
+       vector_foreach_slot(mp, mpp, i) {
+               if (write_out_wwid(fd, mpp->wwid) < 0)
+                       goto out_file;
+       }
+       ret = 0;
+out_file:
+       close(fd);
+out:
+       return ret;
+}
+
+int
+do_remove_wwid(int fd, char *str) {
+       char buf[4097];
+       char *ptr;
+       off_t start = 0;
+       int bytes;
+
+       while (1) {
+               if (lseek(fd, start, SEEK_SET) < 0) {
+                       condlog(0, "wwid file read lseek failed : %s",
+                               strerror(errno));
+                       return -1;
+               }
+               bytes = read(fd, buf, 4096);
+               if (bytes < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       condlog(0, "failed to read from wwids file : %s",
+                               strerror(errno));
+                       return -1;
+               }
+               if (!bytes) /* didn't find wwid to remove */
+                       return 1;
+               buf[bytes] = '\0';
+               ptr = strstr(buf, str);
+               if (ptr != NULL) {
+                       condlog(3, "found '%s'", str);
+                       if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) {
+                               condlog(0, "write lseek failed : %s",
+                                               strerror(errno));
+                               return -1;
+                       }
+                       while (1) {
+                               if (write(fd, "#", 1) < 0) {
+                                       if (errno == EINTR || errno == EAGAIN)
+                                               continue;
+                                       condlog(0, "failed to write to wwids file : %s", strerror(errno));
+                                       return -1;
+                               }
+                               return 0;
+                       }
+               }
+               ptr = strrchr(buf, '\n');
+               if (ptr == NULL) { /* shouldn't happen, assume it is EOF */
+                       condlog(4, "couldn't find newline, assuming end of file");
+                       return 1;
+               }
+               start = start + (ptr - buf) + 1;
+       }
+}
+
+
+int
+remove_wwid(char *wwid) {
+       int fd, len, can_write;
+       char *str;
+       int ret = -1;
+
+       len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */
+       str = malloc(len);
+       if (str == NULL) {
+               condlog(0, "can't allocate memory to remove wwid : %s",
+                       strerror(errno));
+               return -1;
+       }
+       if (snprintf(str, len, "/%s/\n", wwid) >= len) {
+               condlog(0, "string overflow trying to remove wwid");
+               goto out;
+       }
+       condlog(3, "removing line '%s' from wwids file", str);
+       fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
+       if (fd < 0)
+               goto out;
+       if (!can_write) {
+               condlog(0, "cannot remove wwid. wwids file is read-only");
+               goto out_file;
+       }
+       ret = do_remove_wwid(fd, str);
+
+out_file:
+       close(fd);
+out:
+       free(str);
+       return ret;
+}
+
+int
 check_wwids_file(char *wwid, int write_wwid)
 {
        int fd, can_write, found, ret;
index 1678f9d..f3b21fa 100644 (file)
@@ -14,5 +14,7 @@
 
 int remember_wwid(char *wwid);
 int check_wwids_file(char *wwid, int write_wwid);
+int remove_wwid(char *wwid);
+int replace_wwids(vector mp);
 
 #endif /* _WWIDS_H */
index 2a8efe6..ad8e607 100644 (file)
@@ -5,7 +5,7 @@ include ../Makefile.inc
 OBJS = main.o 
 
 CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) 
-LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath 
+LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath -ludev
 
 EXEC = mpathpersist
 
index 465fcb1..e3484b5 100644 (file)
@@ -7,6 +7,7 @@
 #include <vector.h>
 #include <structs.h>
 #include <getopt.h>
+#include <libudev.h>
 #include <mpath_persist.h>
 #include "main.h"
 #include <pthread.h>
@@ -68,7 +69,8 @@ int main (int argc, char * argv[])
        int noisy = 0;
        int num_transport =0;
        void *resp = NULL;
-       struct transportid * tmp; 
+       struct transportid * tmp;
+       struct udev *udev = NULL;
 
        if (optind == argc)
        {
@@ -84,8 +86,8 @@ int main (int argc, char * argv[])
                exit (1);
        }
 
-
-       mpath_lib_init();
+       udev = udev_new();
+       mpath_lib_init(udev);
        memset(transportids,0,MPATH_MX_TIDS);
 
        while (1)
@@ -461,12 +463,13 @@ int main (int argc, char * argv[])
        if (res < 0)
        {
                mpath_lib_exit();
+               udev_unref(udev);
                return MPATH_PR_FILE_ERROR;
        }
 
 out :
        mpath_lib_exit();
-
+       udev_unref(udev);
        return (ret >= 0) ? ret : MPATH_PR_OTHER;
 }
 
index 406f385..235e130 100644 (file)
@@ -9,13 +9,6 @@
 ##
 #defaults {
 #      #
-#      # name    : udev_dir
-#      # desc    : directory where udev creates its device nodes
-#      # default : /dev
-#      #
-#      udev_dir        /dev
-#
-#      #
 #      # name    : polling_interval
 #      # scope   : multipathd
 #      # desc    : interval between two path checks in seconds. For
 #      # scope   : multipath & multipathd
 #      # desc    : the default path selector algorithm to use
 #      #           these algorithms are offered by the kernel multipath target
-#      # values  : "round-robin 0"
-#      # default : "round-robin 0"
+#      # values  : "round-robin 0"  = Loop through every path in the path group,
+#      #                              sending the same amount of IO to each.
+#      #           "queue-length 0" = Send the next bunch of IO down the path
+#      #                              with the least amount of outstanding IO.
+#      #           "service-time 0" = Choose the path for the next bunch of IO
+#      #                              based on the amount of outstanding IO to
+#      #                              the path and its relative throughput.
+#      # default : "service-time 0"
 #      #
-#      path_selector   "round-robin 0"
+#      path_selector   "service-time 0"
 #
 #      #
 #      # name    : path_grouping_policy
 #      path_grouping_policy    multibus
 #
 #      #
+#      # name    : uid_attribute
+#      # scope   : multipath & multipathd
+#      # desc    : the default udev attribute from which the path
+#      #           identifier should be generated.
+#      # default : ID_SERIAL
+#      #
+#      uid_attribute   "ID_SERIAL"
+#
+#      #
 #      # name    : getuid_callout
 #      # scope   : multipath & multipathd
 #      # desc    : the default program and args to callout to obtain a unique 
-#      #           path identifier. Absolute path required
+#      #           path identifier. This parameter is deprecated.
+#      #           This parameter is deprecated, superseded by uid_attribute
 #      # default : /lib/udev/scsi_id --whitelisted --device=/dev/%n
 #      #
 #      getuid_callout  "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
 #      #
 #      # name    : features
 #      # scope   : multipath & multipathd
-#      # desc    : The default extra features of multipath devices. The
-#      #           only existing feature currently is queue_if_no_path, which
-#      #           is the same as setting no_path_retry to queue.
-#      # values  : "1 queue_if_no_path"
-#      # default : (null)
-#      #
+#      # desc    : The default extra features of multipath devices.
+#      #           Syntax is "num[ feature_0 feature_1 ...]", where `num' is the
+#      #           number of features in the following (possibly empty) list of
+#      #           features.
+#      # values  : queue_if_no_path = Queue IO if no path is active; consider
+#      #                              using the `no_path_retry' keyword instead.
+#      #           no_partitions    = Disable automatic partitions generation via
+#      #                              kpartx.
+#      # default : "0"
+#      #
+#      features        "0"
 #      features        "1 queue_if_no_path"
+#      features        "1 no_partitions"
+#      features        "2 queue_if_no_path no_partitions"
 #
 #      #
 #      # name    : path_checker, checker
 #      # name    : rr_min_io
 #      # scope   : multipath & multipathd
 #      # desc    : the number of IO to route to a path before switching
-#      #           to the next in the same path group
+#      #           to the next in the same path group for the bio-based
+#      #           multipath implementation. This parameter is used for
+#      #           kernels version up to 2.6.31; newer kernel version
+#      #           use the parameter rr_min_io_rq
 #      # default : 1000
 #      #
-#      rr_min_io       100
+#      rr_min_io       1000
+#
+#      #
+#      # name    : rr_min_io_rq
+#      # scope   : multipath & multipathd
+#      # desc    : the number of IO to route to a path before switching
+#      #           to the next in the same path group for the request-based
+#      #           multipath implementation. This parameter is used for
+#      #           kernels versions later than 2.6.31.
+#      # default : 1
+#      #
+#      rr_min_io_rq    1
 #
 #      #
 #      # name    : flush_on_last_del
 #      # desc    : The number of seconds the scsi layer will wait after a
 #      #           problem has been detected on a FC remote port before
 #      #           removing it from the system.
-#      # values  : n > 0
+#      # values  : infinity | n > 0
 #      # default : determined by the OS
 #      dev_loss_tmo 600
+#
 #      # name    : bindings_file
 #      # scope   : multipath
 #      # desc    : The location of the bindings file that is used with
 #      #           the user_friendly_names option.
 #      # values  : <full_pathname>
 #      # default : "/var/lib/multipath/bindings"
-#      bindings_file "/etc/multipath_bindings"
+#      bindings_file "/etc/multipath/bindings"
+#
+#      # name    : wwids_file
+#      # scope   : multipath
+#      # desc    : The location of the wwids file multipath uses to
+#       #           keep track of the created multipath devices.
+#      # values  : <full_pathname>
+#      # default : "/var/lib/multipath/wwids"
+#      wwids_file "/etc/multipath/wwids"
+#
+#      # name    : reservation_key
+#      # scope   : multipath
+#      # desc    : Service action reservation key used by mpathpersist.
+#      # values  : <key>
+#      # default : (null)
+#      reservation_key "mpathkey"
 #
 #}
 #      
 #              #
 #              path_checker            directio
 #
-#              #
-#              # name    : path_selector
-#              # scope   : multipathd & multipathd
-#              # desc    : the path selector algorithm to use for this mpath
-#              #           these algo are offered by the kernel mpath target
-#              # values  : "round-robin 0"
-#              #
-#              path_selector           "round-robin 0"
+#              # as already described
+#              path_selector           "service-time 0"
 #
-#              #
-#              # name    : features
-#              # scope   : multipath & multipathd
-#              # desc    : The extra features of multipath devices. The only
-#              #           existing feature currently is queue_if_no_path,
-#              #           which is the same as setting no_path_retry to queue.
-#              # values  : "1 queue_if_no_path"
-#              #
-#              features        "1 queue_if_no_path"
+#              # as already described
+#              features        "0"
 #
 #              #
 #              # name    : hardware_handler
index cbe7254..e761902 100644 (file)
 # These are the compiled in default settings.  They will be used unless you
 # overwrite these values in your config file.
-
 #defaults {
-#      udev_dir                /dev
-#      polling_interval        5
-#      path_selector           "round-robin 0"
-#      path_grouping_policy    failover
-#      getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#      prio                    const
-#      prio_args               ""
-#      path_checker            directio
-#      rr_min_io               1000
-#      rr_weight               uniform
-#      failback                manual
-#      no_path_retry           fail
-#      user_friendly_names     no
+#      verbosity 2
+#      polling_interval 5
+#      path_selector "service-time 0"
+#      path_grouping_policy "failover"
+#      uid_attribute "ID_SERIAL"
+#      prio "const"
+#      prio_args ""
+#      features "0"
+#      path_checker "directio"
+#      alias_prefix "mpath"
+#      failback "manual"
+#      rr_min_io 1000
+#      rr_min_io_rq 1
+#      max_fds "max"
+#      rr_weight "uniform"
+#      queue_without_daemon "yes"
+#      flush_on_last_del "no"
+#      user_friendly_names "no"
+#      fast_io_fail_tmo 5
+#      bindings_file "/etc/multipath/bindings"
+#      wwids_file /etc/multipath/wwids
+#      log_checker_err always
+#      retain_attached_hw_handler no
+#      detect_prio no
 #}
-#
 #blacklist {
-#      devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
-#      devnode "^hd[a-z]"
-#      devnode "^dcssblk[0-9]*"
+#      devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
+#      devnode "^hd[a-z]"
+#      devnode "^dcssblk[0-9]*"
+#      device {
+#              vendor "DGC"
+#              product "LUNZ"
+#      }
+#      device {
+#              vendor "EMC"
+#              product "LUNZ"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "Universal Xport"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "S/390.*"
+#      }
+#      device {
+#              vendor "DELL"
+#              product "Universal Xport"
+#      }
+#      device {
+#              vendor "SGI"
+#              product "Universal Xport"
+#      }
+#      device {
+#              vendor "STK"
+#              product "Universal Xport"
+#      }
+#      device {
+#              vendor "SUN"
+#              product "Universal Xport"
+#      }
+#      device {
+#              vendor "(LSI|ENGENIO)"
+#              product "Universal Xport"
+#      }
+#}
+#blacklist_exceptions {
+#      property "(ID_SCSI_VPD|ID_WWN)"
 #}
-#
 #devices {
 #      device {
-#              vendor                  "APPLE*"
-#              product                 "Xserve RAID"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "3PARdata"
-#              product                 "VV"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "DEC"
-#              product                 "HSG80"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "1 hp_sw"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            hp_sw
-#              prio                    hp_sw
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "HP"
-#              product                 "A6189A"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              no_path_retry           12
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "(COMPAQ|HP)"
-#              product                 "(MSA|HSV)1.0.*"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "1 hp_sw"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              rr_weight               uniform
-#              no_path_retry           12
-#              rr_min_io               1000
-#              path_checker            hp_sw
-#              prio                    hp_sw
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "(COMPAQ|HP)"
-#              product                 "MSA VOLUME"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           12
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    alua
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "HP"
-#              product                 "MSA2000s*"
-#              getuid_callout          "/sbin/cciss_id %n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           12
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "(COMPAQ|HP)"
-#              product                 "HSV1[01]1|HSV2[01]0|HSV300|HSV4[05]0"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           12
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    alua
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "HP"
-#              product                 "MSA2[02]12*"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           12
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "HP"
-#              product                 "LOGICAL VOLUME.*"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           12
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "DDN"
-#              product                 "SAN DataDirector"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "EMC"
-#              product                 "SYMMETRIX"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --page=pre-spc3-83 --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "DGC"
-#              product                 ".*"
-#              product_blacklist       "LUNZ"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "1 emc"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           60
-#              rr_min_io               1000
-#              path_checker            emc_clariion
-#              prio                    emc
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "EMC"
-#              product                 "Invista"
-#              product_blacklist       "LUNZ"
-#              getuid_callout          "/sbin/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    failover
-#              rr_weight               uniform
-#              no_path_retry           5
-#              rr_min_io               1000
-#              path_checker            tur
-#      }
-#      device {
-#              vendor                  "EMC"
-#              product                 "Invista"
-#              product_blacklist       "LUNZ"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --page=pre-spc3-83 --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              no_path_retry           5
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "FSC"
-#              product                 "CentricStor"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_serial
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "FUJITSU"
-#              product                 "ETERNUS_DX(L|400|8000)"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           10
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    alua
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "(HITACHI|HP)"
-#              product                 "OPEN-.*"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               100
-#              path_checker            tur
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "HITACHI"
-#              product                 "DF.*"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    hds
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "ProFibre 4000R"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            readsector0
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "1722-600"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           300
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "1742"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           queue
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "1745|1746"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "2 pg_init_retries 50"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           queue
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "1814"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           queue
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "1815"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           queue
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "3526"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           queue
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "3542"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_serial
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "2105(800|F20)"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_serial
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "1750500"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    alua
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "2107900"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "2145"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    alua
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "S/390 DASD ECKD"
-#              product_blacklist       "S/390.*"
-#              getuid_callout          "/sbin/dasd_id /dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "NETAPP"
-#              product                 "LUN.*"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              rr_min_io               128
-#              path_checker            directio
-#              prio                    ontap
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "NEXENTA"
-#              product                 "COMSTAR"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_serial
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           30
-#              rr_min_io               128
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "Nseries.*"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              rr_min_io               128
-#              path_checker            directio
-#              prio                    ontap
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "Pillar"
-#              product                 "Axiom.*"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    alua
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "IBM"
-#              product                 "3303      NVDISK"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#               path_grouping_policy    failover
-#              failback                immediate
-#              no_path_retry           60
-#              rr_weight               uniform
-#              rr_min_io               1000
-#               path_checker            tur
-#      }
-#      device {
-#              vendor                  "AIX"
-#              product                 "VDASD"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#               path_grouping_policy    multibus
-#              failback                immediate
-#              no_path_retry           60
-#              rr_weight               uniform
-#              rr_min_io               1000
-#               path_checker            directio
-#      }
-#      device {
-#              vendor                  "AIX"
-#              product                 "NVDISK"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 alua"
-#               path_grouping_policy    group_by_prio
-#              failback                immediate
-#              no_path_retry           60
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    alua
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "SGI"
-#              product                 "TP9[13]00"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "SGI"
-#              product                 "TP9[45]00"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           queue
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "SGI"
-#              product                 "IS.*"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "2 pg_init_retries 50"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           15
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "STK"
-#              product                 "OPENstorage D280"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "SUN"
-#              product                 "(StorEdge 3510|T4)"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "PIVOT3"
-#              product                 "RAIGE VOLUME"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --page=0x80 --device=/dev/%n"
-#              features                "1 queue_if_no_path"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    multibus
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    const
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "SUN"
-#              product                 "CSM200_R"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           queue
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "SUN"
-#              product                 "LCSM100_[IF]"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           queue
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "STK"
-#              product                 "FLEXLINE 380"
-#              product_blacklist       "Universal Xport"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 rdac"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              no_path_retry           queue
-#              rr_min_io               1000
-#              path_checker            rdac
-#              prio                    rdac
-#              prio_args               ""
-#      }
-#      device {
-#              vendor                  "EUROLOGC"
-#              product                 "FC2502"
-#              getuid_callout          "/lib/udev/scsi_id --page=0x80 --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "0"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            directio
-#              prio                    const
-#      }
-#      device {
-#              vendor                  "NEC"
-#              product                 "DISK ARRAY"
-#              getuid_callout          "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
-#              features                "0"
-#              hardware_handler        "1 alua"
-#              path_selector           "round-robin 0"
-#              path_grouping_policy    group_by_prio
-#              failback                immediate
-#              rr_weight               uniform
-#              rr_min_io               1000
-#              path_checker            tur
-#              prio                    alua
+#              vendor "COMPELNT"
+#              product "Compellent Vol"
+#              path_grouping_policy "multibus"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "APPLE*"
+#              product "Xserve RAID "
+#              path_grouping_policy "multibus"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "3PARdata"
+#              product "VV"
+#              path_grouping_policy "multibus"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "DEC"
+#              product "HSG80"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "hp_sw"
+#              features "1 queue_if_no_path"
+#              hardware_handler "1 hp_sw"
+#              prio "hp_sw"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "HP"
+#              product "A6189A"
+#              path_grouping_policy "multibus"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#              no_path_retry 12
+#      }
+#      device {
+#              vendor "(COMPAQ|HP)"
+#              product "(MSA|HSV)1.0.*"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "hp_sw"
+#              features "1 queue_if_no_path"
+#              hardware_handler "1 hp_sw"
+#              prio "hp_sw"
+#              rr_weight "uniform"
+#              no_path_retry 12
+#              rr_min_io 100
+#      }
+#      device {
+#              vendor "(COMPAQ|HP)"
+#              product "MSA VOLUME"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 12
+#              rr_min_io 100
+#      }
+#      device {
+#              vendor "(COMPAQ|HP)"
+#              product "HSV1[01]1|HSV2[01]0|HSV3[046]0|HSV4[05]0"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 12
+#              rr_min_io 100
+#      }
+#      device {
+#              vendor "HP"
+#              product "MSA2[02]12fc|MSA2012i"
+#              path_grouping_policy "multibus"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 18
+#              rr_min_io 100
+#      }
+#      device {
+#              vendor "HP"
+#              product "MSA2012sa|MSA23(12|24)(fc|i|sa)|MSA2000s VOLUME"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 18
+#              rr_min_io 100
+#      }
+#      device {
+#              vendor "HP"
+#              product "HSVX700"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "1 alua"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 12
+#              rr_min_io 100
+#      }
+#      device {
+#              vendor "HP"
+#              product "LOGICAL VOLUME.*"
+#              path_grouping_policy "multibus"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#              no_path_retry 12
+#      }
+#      device {
+#              vendor "HP"
+#              product "P2000 G3 FC|P2000G3 FC/iSCSI|P2000 G3 SAS|P2000 G3 iSCSI"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 18
+#              rr_min_io 100
+#      }
+#      device {
+#              vendor "DDN"
+#              product "SAN DataDirector"
+#              path_grouping_policy "multibus"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "EMC"
+#              product "SYMMETRIX"
+#              path_grouping_policy "multibus"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#              no_path_retry 6
+#      }
+#      device {
+#              vendor "DGC"
+#              product ".*"
+#              product_blacklist "LUNZ"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "emc_clariion"
+#              features "1 queue_if_no_path"
+#              hardware_handler "1 emc"
+#              prio "emc"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 60
+#      }
+#      device {
+#              vendor "EMC"
+#              product "Invista"
+#              product_blacklist "LUNZ"
+#              path_grouping_policy "multibus"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#              no_path_retry 5
+#      }
+#      device {
+#              vendor "FSC"
+#              product "CentricStor"
+#              path_grouping_policy "group_by_serial"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "FUJITSU"
+#              product "ETERNUS_DX(L|400|8000)"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 10
+#      }
+#      device {
+#              vendor "(HITACHI|HP)"
+#              product "OPEN-.*"
+#              path_grouping_policy "multibus"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "HITACHI"
+#              product "DF.*"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "hds"
+#              failback "immediate"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "ProFibre 4000R"
+#              path_grouping_policy "multibus"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^1722-600"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "1 queue_if_no_path"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 300
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^1724"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "1 queue_if_no_path"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 300
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^1726"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "1 queue_if_no_path"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 300
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^1742"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^1745|^1746"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "2 pg_init_retries 50"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 15
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^1814"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^1815"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^1818"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^3526"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^3542"
+#              path_grouping_policy "group_by_serial"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^2105800"
+#              path_grouping_policy "group_by_serial"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^2105F20"
+#              path_grouping_policy "group_by_serial"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^1750500"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^2107900"
+#              path_grouping_policy "multibus"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^2145"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "S/390 DASD ECKD"
+#              product_blacklist "S/390.*"
+#              path_grouping_policy "multibus"
+#              uid_attribute "ID_UID"
+#              path_checker "directio"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "S/390 DASD FBA"
+#              product_blacklist "S/390.*"
+#              path_grouping_policy "multibus"
+#              uid_attribute "ID_UID"
+#              path_checker "directio"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "^IPR.*"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "1 alua"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "IBM"
+#              product "1820N00"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#              rr_min_io 100
+#      }
+#      device {
+#              vendor "IBM"
+#              product "2810XIV"
+#              path_grouping_policy "multibus"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "const"
+#              failback 15
+#              rr_weight "uniform"
+#              rr_min_io 15
+#      }
+#      device {
+#              vendor "AIX"
+#              product "VDASD"
+#              path_grouping_policy "multibus"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 60
+#      }
+#      device {
+#              vendor "IBM"
+#              product "3303      NVDISK"
+#              path_grouping_policy "failover"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 60
+#      }
+#      device {
+#              vendor "AIX"
+#              product "NVDISK"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "1 alua"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 60
+#      }
+#      device {
+#              vendor "DELL"
+#              product "MD3000"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "2 pg_init_retries 50"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 15
+#      }
+#      device {
+#              vendor "DELL"
+#              product "MD3000i"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "2 pg_init_retries 50"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 15
+#      }
+#      device {
+#              vendor "DELL"
+#              product "MD32xx"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "2 pg_init_retries 50"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 15
+#      }
+#      device {
+#              vendor "DELL"
+#              product "MD32xxi"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "2 pg_init_retries 50"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 15
+#      }
+#      device {
+#              vendor "NETAPP"
+#              product "LUN.*"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "3 queue_if_no_path pg_init_retries 50"
+#              hardware_handler "0"
+#              prio "ontap"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              rr_min_io 128
+#              flush_on_last_del "yes"
+#              dev_loss_tmo "infinity"
+#              retain_attached_hw_handler "yes"
+#              detect_prio "yes"
+#      }
+#      device {
+#              vendor "NEXENTA"
+#              product "COMSTAR"
+#              path_grouping_policy "group_by_serial"
+#              path_checker "directio"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "const"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 30
+#              rr_min_io 128
+#      }
+#      device {
+#              vendor "IBM"
+#              product "Nseries.*"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "directio"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "ontap"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              rr_min_io 128
+#      }
+#      device {
+#              vendor "Pillar"
+#              product "Axiom.*"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "alua"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "SGI"
+#              product "TP9[13]00"
+#              path_grouping_policy "multibus"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "SGI"
+#              product "TP9[45]00"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "SGI"
+#              product "IS.*"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "2 pg_init_retries 50"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 15
+#      }
+#      device {
+#              vendor "NEC"
+#              product "DISK ARRAY"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "1 alua"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "STK"
+#              product "OPENstorage D280"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "SUN"
+#              product "(StorEdge 3510|T4)"
+#              path_grouping_policy "multibus"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "SUN"
+#              product "STK6580_6780"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "EUROLOGC"
+#              product "FC2502"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "directio"
+#              features "0"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#      }
+#      device {
+#              vendor "PIVOT3"
+#              product "RAIGE VOLUME"
+#              path_grouping_policy "multibus"
+#              path_checker "tur"
+#              features "1 queue_if_no_path"
+#              hardware_handler "0"
+#              prio "const"
+#              rr_weight "uniform"
+#              rr_min_io 100
+#      }
+#      device {
+#              vendor "SUN"
+#              product "CSM200_R"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "SUN"
+#              product "LCSM100_[IEFS]"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "SUN"
+#              product "SUN_6180"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#              rr_min_io 1000
+#              rr_min_io_rq 1
+#      }
+#      device {
+#              vendor "(NETAPP|LSI|ENGENIO)"
+#              product "INF-01-00"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "2 pg_init_retries 50"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry 30
+#              detect_prio "yes"
+#              retain_attached_hw_handler "yes"
+#      }
+#      device {
+#              vendor "STK"
+#              product "FLEXLINE 380"
+#              product_blacklist "Universal Xport"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "rdac"
+#              features "0"
+#              hardware_handler "1 rdac"
+#              prio "rdac"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "Intel"
+#              product "Multi-Flex"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "1 alua"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "DataCore"
+#              product "SANmelody"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
+#      }
+#      device {
+#              vendor "DataCore"
+#              product "Virtual Disk"
+#              path_grouping_policy "group_by_prio"
+#              path_checker "tur"
+#              features "0"
+#              hardware_handler "0"
+#              prio "alua"
+#              failback "immediate"
+#              rr_weight "uniform"
+#              no_path_retry "queue"
 #      }
 #}
+#multipaths {
+#}
index f748417..5e5958d 100644 (file)
@@ -7,7 +7,7 @@ include ../Makefile.inc
 OBJS = main.o
 
 CFLAGS += -I$(multipathdir)
-LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath
+LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev
 
 EXEC = multipath
 
@@ -21,15 +21,12 @@ $(EXEC): $(OBJS)
 install:
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
        $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
-       $(INSTALL_PROGRAM) -d $(DESTDIR)/etc/udev/rules.d
-       $(INSTALL_PROGRAM) -m 644 multipath.rules $(DESTDIR)/etc/udev/rules.d/
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir)
        $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir)
        $(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5.gz $(DESTDIR)$(man5dir)
 
 uninstall:
-       rm $(DESTDIR)/etc/udev/rules.d/multipath.rules
        rm $(DESTDIR)$(bindir)/$(EXEC)
        rm $(DESTDIR)$(mandir)/$(EXEC).8.gz
        rm $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz
index 92e852b..64c8fc5 100644 (file)
@@ -27,6 +27,7 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <ctype.h>
+#include <libudev.h>
 
 #include <checkers.h>
 #include <prio.h>
@@ -83,7 +84,7 @@ usage (char * progname)
 {
        fprintf (stderr, VERSION_STRING);
        fprintf (stderr, "Usage:\n");
-       fprintf (stderr, "  %s [-c] [-d] [-r] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname);
+       fprintf (stderr, "  %s [-c|-w|-W] [-d] [-r] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname);
        fprintf (stderr, "  %s -l|-ll|-f [-v lvl] [-b fil] [dev]\n", progname);
        fprintf (stderr, "  %s -F [-v lvl]\n", progname);
        fprintf (stderr, "  %s -t\n", progname);
@@ -101,8 +102,11 @@ usage (char * progname)
                "  -d      dry run, do not create or update devmaps\n" \
                "  -t      dump internal hardware table\n" \
                "  -r      force devmap reload\n" \
+               "  -B      treat the bindings file as read only\n" \
                "  -p      policy failover|multibus|group_by_serial|group_by_prio\n" \
                "  -b fil  bindings file location\n" \
+               "  -w      remove a device from the wwids file\n" \
+               "  -W      reset the wwids file include only the current devices\n" \
                "  -p pol  force all maps to specified path grouping policy :\n" \
                "          . failover            one path per priority group\n" \
                "          . multibus            all paths in one priority group\n" \
@@ -148,16 +152,21 @@ update_paths (struct multipath * mpp)
                                        continue;
                                }
                                pp->mpp = mpp;
-                               pathinfo(pp, conf->hwtable, DI_ALL);
+                               if (pathinfo(pp, conf->hwtable, DI_ALL))
+                                       pp->state = PATH_UNCHECKED;
                                continue;
                        }
                        pp->mpp = mpp;
                        if (pp->state == PATH_UNCHECKED ||
-                           pp->state == PATH_WILD)
-                               pathinfo(pp, conf->hwtable, DI_CHECKER);
+                           pp->state == PATH_WILD) {
+                               if (pathinfo(pp, conf->hwtable, DI_CHECKER))
+                                       pp->state = PATH_UNCHECKED;
+                       }
 
-                       if (pp->priority == PRIO_UNDEF)
-                               pathinfo(pp, conf->hwtable, DI_PRIO);
+                       if (pp->priority == PRIO_UNDEF) {
+                               if (pathinfo(pp, conf->hwtable, DI_PRIO))
+                                       pp->priority = PRIO_UNDEF;
+                       }
                }
        }
        return 0;
@@ -211,7 +220,6 @@ get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
 
                if (!conf->dry_run)
                        reinstate_paths(mpp);
-               remember_wwid(mpp->wwid);
        }
        return 0;
 }
@@ -247,21 +255,12 @@ configure (void)
        vecs.pathvec = pathvec;
        vecs.mpvec = curmp;
 
-       /*
-        * dev is "/dev/" . "sysfs block dev"
-        */
-       if (conf->dev) {
-               if (!strncmp(conf->dev, "/dev/", 5) &&
-                   strlen(conf->dev) > 5)
-                       dev = conf->dev + 5;
-               else
-                       dev = conf->dev;
-       }
+       dev = convert_dev(conf->dev, (conf->dev_type == DEV_DEVNODE));
 
        /*
         * if we have a blacklisted device parameter, exit early
         */
-       if (dev &&
+       if (dev && conf->dev_type == DEV_DEVNODE && conf->dry_run != 3 &&
            (filter_devnode(conf->blist_devnode,
                            conf->elist_devnode, dev) > 0)) {
                if (conf->dry_run == 2)
@@ -274,16 +273,27 @@ configure (void)
         * failing the translation is fatal (by policy)
         */
        if (conf->dev) {
-               refwwid = get_refwwid(conf->dev, conf->dev_type, pathvec);
-
+               int failed = get_refwwid(conf->dev, conf->dev_type, pathvec,
+                                        &refwwid);
                if (!refwwid) {
-                       condlog(3, "scope is nul");
+                       if (failed == 2 && conf->dry_run == 2)
+                               printf("%s is not a valid multipath device path\n", conf->dev);
+                       else
+                               condlog(3, "scope is nul");
                        goto out;
                }
-               condlog(3, "scope limited to %s", refwwid);
-               if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
-                               refwwid) > 0)
+               if (conf->dry_run == 3) {
+                       r = remove_wwid(refwwid);
+                       if (r == 0)
+                               printf("wwid '%s' removed\n", refwwid);
+                       else if (r == 1) {
+                               printf("wwid '%s' not in wwids file\n",
+                                       refwwid);
+                               r = 0;
+                       }
                        goto out;
+               }
+               condlog(3, "scope limited to %s", refwwid);
                if (conf->dry_run == 2) {
                        if (check_wwids_file(refwwid, 0) == 0){
                                printf("%s is a valid multipath device path\n", conf->dev);
@@ -409,36 +419,21 @@ get_dev_type(char *dev) {
                return DEV_DEVMAP;
 }
 
-static void
-convert_dev(char *dev)
-{
-       char *ptr = strstr(dev, "cciss/");
-       if (ptr) {
-               ptr += 5;
-               *ptr = '!';
-       }
-}
-
 int
 main (int argc, char *argv[])
 {
+       struct udev *udev;
        int arg;
        extern char *optarg;
        extern int optind;
        int r = 1;
 
-       if (getuid() != 0) {
-               fprintf(stderr, "need to be root\n");
-               exit(1);
-       }
-
-       if (dm_prereq())
-               exit(1);
+       udev = udev_new();
 
-       if (load_config(DEFAULT_CONFIGFILE))
+       if (load_config(DEFAULT_CONFIGFILE, udev))
                exit(1);
 
-       while ((arg = getopt(argc, argv, ":dchl::FfM:v:p:b:Brtq")) != EOF ) {
+       while ((arg = getopt(argc, argv, ":dchl::FfM:v:p:b:BrtqwW")) != EOF ) {
                switch(arg) {
                case 1: printf("optarg : %s\n",optarg);
                        break;
@@ -499,12 +494,18 @@ main (int argc, char *argv[])
                        break;
                case 't':
                        r = dump_config();
-                       goto out;
+                       goto out_free_config;
                case 'h':
                        usage(argv[0]);
                        exit(0);
+               case 'w':
+                       conf->dry_run = 3;
+                       break;
+               case 'W':
+                       conf->dry_run = 4;
+                       break;
                case ':':
-                       fprintf(stderr, "Missing option arguement\n");
+                       fprintf(stderr, "Missing option argument\n");
                        usage(argv[0]);
                        exit(1);
                case '?':
@@ -516,6 +517,16 @@ main (int argc, char *argv[])
                        exit(1);
                }
        }
+
+       if (getuid() != 0) {
+               fprintf(stderr, "need to be root\n");
+               exit(1);
+       }
+
+       if (dm_prereq())
+               exit(1);
+       dm_drv_version(conf->version, TGT_MPATH);
+
        if (optind < argc) {
                conf->dev = MALLOC(FILE_NAME_SIZE);
 
@@ -524,8 +535,6 @@ main (int argc, char *argv[])
 
                strncpy(conf->dev, argv[optind], FILE_NAME_SIZE);
                conf->dev_type = get_dev_type(conf->dev);
-               if (conf->dev_type == DEV_DEVNODE)
-                       convert_dev(conf->dev);
        }
        conf->daemon = 0;
 
@@ -535,17 +544,17 @@ main (int argc, char *argv[])
                fd_limit.rlim_cur = conf->max_fds;
                fd_limit.rlim_max = conf->max_fds;
                if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0)
-                       condlog(0, "can't set open fds limit to %d : %s\n",
+                       condlog(0, "can't set open fds limit to %d : %s",
                                conf->max_fds, strerror(errno));
        }
 
        if (init_checkers()) {
                condlog(0, "failed to initialize checkers");
-               exit(1);
+               goto out;
        }
        if (init_prio()) {
                condlog(0, "failed to initialize prioritizers");
-               exit(1);
+               goto out;
        }
        dm_init();
 
@@ -554,10 +563,35 @@ main (int argc, char *argv[])
                condlog(0, "the -c option requires a path to check");
                goto out;
        }
+       if (conf->dry_run == 3 && !conf->dev) {
+               condlog(0, "the -w option requires a device");
+               goto out;
+       }
+       if (conf->dry_run == 4) {
+               struct multipath * mpp;
+               int i;
+               vector curmp;
+
+               curmp = vector_alloc();
+               if (!curmp) {
+                       condlog(0, "can't allocate memory for mp list");
+                       goto out;
+               }
+               if (dm_get_maps(curmp) == 0)
+                       r = replace_wwids(curmp);
+               if (r == 0)
+                       printf("successfully reset wwids\n");
+               vector_foreach_slot_backwards(curmp, mpp, i) {
+                       vector_del_slot(curmp, i);
+                       free_multipath(mpp, KEEP_PATHS);
+               }
+               vector_free(curmp);
+               goto out;
+       }
        if (conf->remove == FLUSH_ONE) {
-               if (conf->dev_type == DEV_DEVMAP)
-                       r = dm_flush_map(conf->dev);
-               else
+               if (conf->dev_type == DEV_DEVMAP) {
+                       r = dm_suspend_and_flush_map(conf->dev);
+               else
                        condlog(0, "must provide a map name to remove");
 
                goto out;
@@ -577,6 +611,8 @@ out:
 
        cleanup_prio();
        cleanup_checkers();
+
+out_free_config:
        /*
         * Freeing config must be done after dm_lib_exit(), because
         * the logging function (dm_write_log()), which is called there,
@@ -584,7 +620,7 @@ out:
         */
        free_config(conf);
        conf = NULL;
-
+       udev_unref(udev);
 #ifdef _DEBUG_
        dbg_free_final(NULL);
 #endif
index a112df7..a2262ac 100644 (file)
@@ -5,14 +5,16 @@ multipath \- Device mapper target autoconfig
 .B multipath
 .RB [\| \-v\ \c
 .IR verbosity \|]
+.RB [\| \-b\ \c
+.IR bindings_file \|]
 .RB [\| \-d \|]
-.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F \| \-B \|]
+.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-q | \|-r | \-w | \-W \|]
 .RB [\| \-p\ \c
 .BR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|]
 .RB [\| device \|]
 .SH DESCRIPTION
 .B multipath
-is used to detect multiple paths to devices for fail-over or performance reasons and coalesces them
+is used to detect and coalesce multiple paths to devices, for fail-over or performance reasons.
 .SH OPTIONS
 .TP
 .B \-v " level"
@@ -56,6 +58,22 @@ force devmap reload
 .B \-B
 treat the bindings file as read only
 .TP
+.B \-b " bindings_file"
+set user_friendly_names bindings file location.  The default is
+/etc/multipath/bindings
+.TP
+.B \-c
+check if a block device should be a path in a multipath device
+.TP
+.B \-q
+allow device tables with queue_if_no_path when multipathd is not running
+.TP
+.B \-w
+remove the wwid for the specified device from the wwids file
+.TP
+.B \-W
+reset the wwids file to only include the current multipath devices
+.TP
 .BI \-p " policy"
 force new maps to use the specified policy:
 .RS 1.2i
@@ -80,13 +98,19 @@ in /sys/class/fc_transport/target*/node_name.
 Existing maps are not modified.
 .TP
 .BI device
-update only the devmap the path pointed by
-.I device
-is in. 
-.I device
-is in the /dev/sdb (as shown by udev in the $DEVNAME variable) or major:minor format.
-.I device
-may alternatively be a multipath mapname
+update only the devmap specified by
+.IR device ,
+which is either:
+.RS 1.2i
+.IP \[bu]
+a devmap name
+.IP \[bu]
+a path associated with the desired devmap; the path may be in one of the following formats:
+.RS 1.2i
+.IP \[bu]
+.B /dev/sdb
+.IP \[bu]
+.B major:minor
 .SH "SEE ALSO"
 .BR multipathd (8),
 .BR multipath.conf (5),
index 9d6266c..cf5bec0 100644 (file)
@@ -34,7 +34,7 @@ The configuration file contains entries of the form:
 .LP
 Each \fIsection\fP contains one or more attributes or subsections. The
 recognized keywords for attributes or subsections depend on the
-section in which they occor.
+section in which they occur.
 .LP
 The following \fIsection\fP keywords are recognized:
 .TP 17
@@ -71,8 +71,11 @@ section recognizes the following keywords:
 .B polling_interval
 interval between two path checks in seconds. For properly functioning paths,
 the interval between checks will gradually increase to
-.B max_polling_interval;
-default is
+.B max_polling_interval.
+This value will be overridden by the
+.B WatchdogSec
+setting in the multipathd.service definition if systemd is used.
+Default is
 .I 5
 .TP
 .B max_polling_interval
@@ -136,7 +139,7 @@ per-multipath option in the configuration file.
 1 priority group per target node name. Target node names are fetched
 in /sys/class/fc_transport/target*/node_name.
 .TP
-Default value is \fImultibus\fR.
+Default value is \fIfailover\fR.
 .RE
 .TP
 .B uid_attribute
@@ -144,6 +147,11 @@ The udev attribute providing a unique path
 identifier. Default value is
 .I ID_SERIAL
 .TP
+.B getuid_callout
+The default program and args to callout to obtain a unique path
+identifier. Should be specified with an absolute path.
+This parameter is deprecated; \fIuid_attribute\fR should be used instead.
+.TP
 .B prio
 The name of the path priority routine. The specified routine
 should return a numeric value specifying the relative priority
@@ -270,7 +278,7 @@ The number of IO to route to a path before switching to the next in
 the same path group. This is only for BIO based multipath. Default is
 .I 1000
 .TP
-.B rr_min_io_q
+.B rr_min_io_rq
 The number of IO requests to route to a path before switching to the
 next in the same path group. This is only for request based multipath.
 Default is
@@ -290,7 +298,8 @@ Specify the number of retries until disable queueing, or
 .I fail
 for immediate failure (no queueing),
 .I queue
-for never stop queueing. Default is 0.
+for never stop queueing. If unset no queueing is attempted.
+Default is unset.
 .TP
 .B user_friendly_names
 If set to 
@@ -301,7 +310,7 @@ to assign a persistent and unique alias to the multipath, in the form of mpath<n
 If set to 
 .I no
 use the WWID as the alias. In either case this be will
-be overriden by any specific aliases in the \fImultipaths\fR section.
+be overridden by any specific aliases in the \fImultipaths\fR section.
 Default is
 .I no
 .TP
@@ -314,7 +323,7 @@ deleted. Default is
 .TP
 .B max_fds
 Specify the maximum number of file descriptors that can be opened by multipath
-and multipathd.  This is equivalent to ulimit -n. A value of \fImax\fR will set
+and multipathd.  This is equivalent to ulimit \-n. A value of \fImax\fR will set
 this to the system limit from /proc/sys/fs/nr_open. If this is not set, the
 maximum number of open fds is taken from the calling process. It is usually
 1024. To be safe, this should be set to the maximum number of paths plus 32,
@@ -322,7 +331,7 @@ if that number is greated than 1024.
 .TP
 .B checker_timeout
 Specify the timeout to user for path checkers that issue scsi commands with an
-explict timeout, in seconds; default taken from
+explicit timeout, in seconds; default taken from
 .I /sys/block/sd<x>/device/timeout
 .TP
 .B fast_io_fail_tmo
@@ -336,7 +345,13 @@ will disable the timeout.
 Specify the number of seconds the scsi layer will wait after a problem has
 been detected on a FC remote port before removing it from the system. This
 can be set to "infinity" which sets it to the max value of 2147483647
-seconds, or 68 years.
+seconds, or 68 years. It will be automatically adjusted to the overall
+retry interval
+\fIno_path_retry\fR * \fIpolling_interval\fR
+if a number of retries is given with \fIno_path_retry\fR and the
+overall retry interval is longer than the specified \fIdev_loss_tmo\fR value.
+The linux kernel will cap this value to \fI300\fR if \fBfast_io_fail_tmo\fR
+is not set.
 .TP
 .B queue_without_daemon
 If set to
@@ -369,6 +384,31 @@ errors are logged at level 3 until the device is restored. If set to
 .I always
 , multipathd always logs the path checker error at logging level 2. Default is
 .I always
+.TP
+.B reservation_key
+This is the service action reservation key used by mpathpersist.  It must be
+set for all multipath devices using persistent reservations, and it must be
+the same as the RESERVATION KEY field of the PERSISTENT RESERVE OUT parameter
+list which contains an 8-byte value provided by the application client to the
+device server to identify the I_T nexus. It is unset by default.
+.TP
+.B retain_attached_hw_handler
+If set to
+.I yes
+and the scsi layer has already attached a hardware_handler to the device,
+multipath will not force the device to use the hardware_handler specified by
+mutipath.conf. If the scsi layer has not attached a hardware handler,
+multipath will continue to use its configured hardware handler. Default is
+.I no
+.TP
+.B detect_prio
+If set to
+.I yes
+, multipath will try to detect if the device supports ALUA. If so, the device
+will automatically use the
+.I alua
+prioritizer. If not, the prioritizer will be selected as usual. Default is
+.I no
 .
 .SH "blacklist section"
 The
@@ -385,6 +425,9 @@ The \fIWorld Wide Identification\fR of a device.
 .B devnode
 Regular expression of the device nodes to be excluded.
 .TP
+.B property
+Regular expresion of the udev property to be excluded.
+.TP
 .B device
 Subsection for the device description. This subsection recognizes the
 .I vendor
@@ -399,7 +442,7 @@ The
 section is used to revert the actions of the
 .I blacklist
 section, ie to include specific device in the
-multipath topology. This allows to selectively include devices which
+multipath topology. This allows one to selectively include devices which
 would normally be excluded via the
 .I blacklist
 section.
@@ -409,8 +452,12 @@ The following keywords are recognized:
 .B wwid
 The \fIWorld Wide Identification\fR of a device.
 .TP
+.B property
+Regular expresion of the udev property to be whitelisted. Defaults to
+.I (ID_WWN|ID_SCSI_VPD)
+.TP
 .B devnode
-Regular expression of the device nodes to be excluded.
+Regular expression of the device nodes to be whitelisted.
 .TP
 .B device
 Subsection for the device description. This subsection recognizes the
@@ -420,6 +467,16 @@ and
 keywords. For a full description of these keywords please see the
 .I devices
 section description.
+.LP
+The
+.I property
+blacklist and whitelist handling is different from the usual handling
+in the sense that the whitelist
+.B has
+to be set, otherwise the device will be blacklisted.
+In these cases the message
+.I blacklisted, udev property missing
+will be displayed.
 .SH "multipaths section"
 The only recognized attribute for the
 .B multipaths
@@ -465,9 +522,11 @@ section:
 .TP
 .B rr_min_io
 .TP
-.B rr_min_io_q
+.B rr_min_io_rq
 .TP
 .B features
+.TP
+.B reservation_key
 .RE
 .PD
 .LP
@@ -554,6 +613,10 @@ section:
 .B dev_loss_tmo
 .TP
 .B flush_on_last_del
+.TP
+.B retain_attached_hw_handler
+.TP
+.B detect_prio
 .RE
 .PD
 .LP
index cc7c04b..de1cc1f 100644 (file)
@@ -110,7 +110,7 @@ case "$1" in
                partlist=$(/sbin/kpartx -l -p _part /dev/disk/by-id/dm-name-$map | sed 's/\([^ ]*\) :.*/\1/p')
                for part in $partlist; do
                    [ -e /dev/disk/by-id/dm-name-$part ] && continue
-                   num$((num + 1))
+                   num=$((num + 1))
                done
            done
            [ $num -eq 0 ] && break
@@ -146,12 +146,12 @@ case "$1" in
        fi
        rc_status -v
        ;;
-    reload)
+    restart)
        $0 stop
        $0 start
        ;;
     *)
-       echo "Usage: $0 {start|stop|status}"
+       echo "Usage: $0 {start|stop|status|restart}"
        exit 1
        ;;
 esac
diff --git a/multipath/multipath.rules b/multipath/multipath.rules
deleted file mode 100644 (file)
index ac97749..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# udev rules for multipathing.
-# The persistent symlinks are created with the kpartx rules
-#
-
-# socket for uevents
-SUBSYSTEM=="block", RUN+="socket:/org/kernel/dm/multipath_event"
index 9553251..781122a 100644 (file)
@@ -6,8 +6,15 @@ include ../Makefile.inc
 # basic flags setting
 #
 CFLAGS += -I$(multipathdir) -I$(mpathpersistdir)
-LDFLAGS += -lpthread -ldevmapper -lreadline -lncurses -ludev -ldl \
-          -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist
+ifdef SYSTEMD
+       CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
+endif
+LDFLAGS += -lpthread -ldevmapper -lreadline
+ifdef SYSTEMD
+       LDFLAGS += -lsystemd-daemon
+endif
+LDFLAGS += -ludev -ldl \
+       -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist
 
 #
 # debuging stuff
@@ -35,8 +42,11 @@ install:
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
        $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(rcdir)
+ifdef SYSTEMD
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(unitdir)
        $(INSTALL_PROGRAM) -m 644 $(EXEC).service $(DESTDIR)$(unitdir)
+       $(INSTALL_PROGRAM) -m 644 $(EXEC).socket $(DESTDIR)$(unitdir)
+endif
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir)
        $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
 
@@ -45,6 +55,7 @@ uninstall:
        rm -f $(DESTDIR)$(rcdir)/$(EXEC)
        rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz
        rm -f $(DESTDIR)$(unitdir)/$(EXEC).service
+       rm -f $(DESTDIR)$(unitdir)/$(EXEC).socket
 
 clean:
        rm -f core *.o $(EXEC) *.gz
index d95cba0..2a5edfa 100644 (file)
@@ -162,6 +162,7 @@ load_keys (void)
        r += add_key(keys, "resize", RESIZE, 0);
        r += add_key(keys, "reset", RESET, 0);
        r += add_key(keys, "reload", RELOAD, 0);
+       r += add_key(keys, "forcequeueing", FORCEQ, 0);
        r += add_key(keys, "disablequeueing", DISABLEQ, 0);
        r += add_key(keys, "restorequeueing", RESTOREQ, 0);
        r += add_key(keys, "paths", PATHS, 0);
@@ -459,6 +460,8 @@ cli_init (void) {
        add_handler(GETPRSTATUS+MAP, NULL);
        add_handler(SETPRSTATUS+MAP, NULL);
        add_handler(UNSETPRSTATUS+MAP, NULL);
+       add_handler(FORCEQ+DAEMON, NULL);
+       add_handler(RESTOREQ+DAEMON, NULL);
 
        return 0;
 }
index 6b288d4..09fdc68 100644 (file)
@@ -10,6 +10,7 @@ enum {
        __RESIZE,
        __RESET,
        __RELOAD,
+       __FORCEQ,
        __DISABLEQ,
        __RESTOREQ,
        __PATHS,
@@ -45,6 +46,7 @@ enum {
 #define RESIZE         (1 << __RESIZE)
 #define RESET          (1 << __RESET)
 #define RELOAD         (1 << __RELOAD)
+#define FORCEQ         (1 << __FORCEQ)
 #define DISABLEQ       (1 << __DISABLEQ)
 #define RESTOREQ       (1 << __RESTOREQ)
 #define PATHS          (1 << __PATHS)
index 544cbfb..f7fc522 100644 (file)
@@ -235,6 +235,7 @@ cli_list_map_topology (void * v, char ** reply, int * len, void * data)
        struct vectors * vecs = (struct vectors *)data;
        char * param = get_keyparam(v, MAP);
        
+       param = convert_dev(param, 0);
        get_path_layout(vecs->pathvec, 0);
        mpp = find_mp_by_str(vecs->mpvec, param);
 
@@ -416,6 +417,7 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
        struct path *pp;
        int r;
 
+       param = convert_dev(param, 1);
        condlog(2, "%s: add path (operator)", param);
 
        if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
@@ -433,19 +435,18 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
                udevice = udev_device_new_from_subsystem_sysname(conf->udev,
                                                                 "block",
                                                                 param);
-               pp = store_pathinfo(vecs->pathvec, conf->hwtable,
-                                   udevice, DI_ALL);
+               r = store_pathinfo(vecs->pathvec, conf->hwtable,
+                                  udevice, DI_ALL, &pp);
                udev_device_unref(udevice);
                if (!pp) {
+                       if (r == 2)
+                               goto blacklisted;
                        condlog(0, "%s: failed to store path info", param);
                        return 1;
                }
                pp->checkint = conf->checkint;
        }
-       r = ev_add_path(pp, vecs);
-       if (r == 2)
-               goto blacklisted;
-       return r;
+       return ev_add_path(pp, vecs);
 blacklisted:
        *reply = strdup("blacklisted\n");
        *len = strlen(*reply) + 1;
@@ -460,6 +461,7 @@ cli_del_path (void * v, char ** reply, int * len, void * data)
        char * param = get_keyparam(v, PATH);
        struct path *pp;
 
+       param = convert_dev(param, 1);
        condlog(2, "%s: remove path (operator)", param);
        pp = find_path_by_dev(vecs->pathvec, param);
        if (!pp) {
@@ -479,6 +481,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
        char *alias;
        int rc;
 
+       param = convert_dev(param, 0);
        condlog(2, "%s: add map (operator)", param);
 
        if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param) > 0) {
@@ -519,6 +522,7 @@ cli_del_map (void * v, char ** reply, int * len, void * data)
        char *alias;
        int rc;
 
+       param = convert_dev(param, 0);
        condlog(2, "%s: remove map (operator)", param);
        minor = dm_get_minor(param);
        if (minor < 0) {
@@ -550,6 +554,7 @@ cli_reload(void *v, char **reply, int *len, void *data)
        struct multipath *mpp;
        int minor;
 
+       mapname = convert_dev(mapname, 0);
        condlog(2, "%s: reload map (operator)", mapname);
        if (sscanf(mapname, "dm-%d", &minor) == 1)
                mpp = find_mp_by_minor(vecs->mpvec, minor);
@@ -561,13 +566,14 @@ cli_reload(void *v, char **reply, int *len, void *data)
                return 1;
        }
 
-       return reload_map(vecs, mpp);
+       return reload_map(vecs, mpp, 0);
 }
 
 int resize_map(struct multipath *mpp, unsigned long long size,
               struct vectors * vecs)
 {
        char params[PARAMS_SIZE] = {0};
+       unsigned long long orig_size = mpp->size;
 
        mpp->size = size;
        update_mpp_paths(mpp, vecs->pathvec);
@@ -576,6 +582,7 @@ int resize_map(struct multipath *mpp, unsigned long long size,
        if (domap(mpp, params) <= 0) {
                condlog(0, "%s: failed to resize map : %s", mpp->alias,
                        strerror(errno));
+               mpp->size = orig_size;
                return 1;
        }
        return 0;
@@ -592,6 +599,7 @@ cli_resize(void *v, char **reply, int *len, void *data)
        struct pathgroup *pgp;
        struct path *pp;
 
+       mapname = convert_dev(mapname, 0);
        condlog(2, "%s: resize map (operator)", mapname);
        if (sscanf(mapname, "dm-%d", &minor) == 1)
                mpp = find_mp_by_minor(vecs->mpvec, minor);
@@ -604,7 +612,18 @@ cli_resize(void *v, char **reply, int *len, void *data)
        }
 
        pgp = VECTOR_SLOT(mpp->pg, 0);
+
+       if (!pgp){
+               condlog(0, "%s: couldn't get path group. cannot resize",
+                       mapname);
+               return 1;
+       }
        pp = VECTOR_SLOT(pgp->paths, 0);
+
+       if (!pp){
+               condlog(0, "%s: couldn't get path. cannot resize", mapname);
+               return 1;
+       }
        if (!pp->udev || sysfs_get_size(pp, &size)) {
                condlog(0, "%s: couldn't get size for sysfs. cannot resize",
                        mapname);
@@ -622,13 +641,32 @@ cli_resize(void *v, char **reply, int *len, void *data)
                return 1;
 
        dm_lib_release();
-       setup_multipath(vecs, mpp);
+       if (setup_multipath(vecs, mpp) != 0)
+               return 1;
        sync_map_state(mpp);
 
        return 0;
 }
 
 int
+cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
+{
+       condlog(2, "force queue_without_daemon (operator)");
+       if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF)
+               conf->queue_without_daemon = QUE_NO_DAEMON_FORCE;
+       return 0;
+}
+
+int
+cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
+{
+       condlog(2, "restore queue_without_daemon (operator)");
+       if (conf->queue_without_daemon == QUE_NO_DAEMON_FORCE)
+               conf->queue_without_daemon = QUE_NO_DAEMON_OFF;
+       return 0;
+}
+
+int
 cli_restore_queueing(void *v, char **reply, int *len, void *data)
 {
        struct vectors * vecs = (struct vectors *)data;
@@ -636,6 +674,7 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data)
        struct multipath *mpp;
        int minor;
 
+       mapname = convert_dev(mapname, 0);
        condlog(2, "%s: restore map queueing (operator)", mapname);
        if (sscanf(mapname, "dm-%d", &minor) == 1)
                mpp = find_mp_by_minor(vecs->mpvec, minor);
@@ -687,6 +726,7 @@ cli_disable_queueing(void *v, char **reply, int *len, void *data)
        struct multipath *mpp;
        int minor;
 
+       mapname = convert_dev(mapname, 0);
        condlog(2, "%s: disable map queueing (operator)", mapname);
        if (sscanf(mapname, "dm-%d", &minor) == 1)
                mpp = find_mp_by_minor(vecs->mpvec, minor);
@@ -724,6 +764,7 @@ cli_switch_group(void * v, char ** reply, int * len, void * data)
        char * mapname = get_keyparam(v, MAP);
        int groupnum = atoi(get_keyparam(v, GROUP));
 
+       mapname = convert_dev(mapname, 0);
        condlog(2, "%s: switch to path group #%i (operator)", mapname, groupnum);
 
        return dm_switchgroup(mapname, groupnum);
@@ -746,6 +787,7 @@ cli_suspend(void * v, char ** reply, int * len, void * data)
        char * param = get_keyparam(v, MAP);
        int r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param);
 
+       param = convert_dev(param, 0);
        condlog(2, "%s: suspend (operator)", param);
 
        if (!r) /* error */
@@ -767,6 +809,7 @@ cli_resume(void * v, char ** reply, int * len, void * data)
        char * param = get_keyparam(v, MAP);
        int r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param);
 
+       param = convert_dev(param, 0);
        condlog(2, "%s: resume (operator)", param);
 
        if (!r) /* error */
@@ -788,6 +831,7 @@ cli_reinstate(void * v, char ** reply, int * len, void * data)
        char * param = get_keyparam(v, PATH);
        struct path * pp;
 
+       param = convert_dev(param, 1);
        pp = find_path_by_dev(vecs->pathvec, param);
 
        if (!pp)
@@ -808,6 +852,7 @@ cli_reassign (void * v, char ** reply, int * len, void * data)
 {
        char * param = get_keyparam(v, MAP);
 
+       param = convert_dev(param, 0);
        condlog(3, "%s: reset devices (operator)", param);
 
        dm_reassign(param);
@@ -822,6 +867,7 @@ cli_fail(void * v, char ** reply, int * len, void * data)
        struct path * pp;
        int r;
 
+       param = convert_dev(param, 1);
        pp = find_path_by_dev(vecs->pathvec, param);
 
        if (!pp)
@@ -922,8 +968,8 @@ int
 cli_shutdown (void * v, char ** reply, int * len, void * data)
 {
        condlog(3, "shutdown (operator)");
-
-       return exit_daemon(0);
+       exit_daemon();
+       return 0;
 }
 
 int
@@ -933,6 +979,7 @@ cli_getprstatus (void * v, char ** reply, int * len, void * data)
        struct vectors * vecs = (struct vectors *)data;
        char * param = get_keyparam(v, MAP);
 
+       param = convert_dev(param, 0);
        get_path_layout(vecs->pathvec, 0);
        mpp = find_mp_by_str(vecs->mpvec, param);
 
@@ -947,7 +994,7 @@ cli_getprstatus (void * v, char ** reply, int * len, void * data)
 
 
        sprintf(*reply,"%d",mpp->prflag);
-       *reply[1]='\0';
+       (*reply)[1]='\0';
 
 
        condlog(3, "%s: reply = %s", param, *reply);
@@ -962,6 +1009,7 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
        struct vectors * vecs = (struct vectors *)data;
        char * param = get_keyparam(v, MAP);
 
+       param = convert_dev(param, 0);
        get_path_layout(vecs->pathvec, 0);
        mpp = find_mp_by_str(vecs->mpvec, param);
 
@@ -984,6 +1032,7 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
        struct vectors * vecs = (struct vectors *)data;
        char * param = get_keyparam(v, MAP);
 
+       param = convert_dev(param, 0);
        get_path_layout(vecs->pathvec, 0);
        mpp = find_mp_by_str(vecs->mpvec, param);
 
index c62a273..de51961 100644 (file)
@@ -28,6 +28,8 @@ int cli_suspend(void * v, char ** reply, int * len, void * data);
 int cli_resume(void * v, char ** reply, int * len, void * data);
 int cli_reinstate(void * v, char ** reply, int * len, void * data);
 int cli_fail(void * v, char ** reply, int * len, void * data);
+int cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data);
+int cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data);
 int cli_quit(void * v, char ** reply, int * len, void * data);
 int cli_shutdown(void * v, char ** reply, int * len, void * data);
 int cli_reassign (void * v, char ** reply, int * len, void * data);
index 64c1a0c..af93f32 100644 (file)
 #include <limits.h>
 #include <linux/oom.h>
 #include <libudev.h>
+#ifdef USE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+#include <semaphore.h>
 #include <mpath_persist.h>
+#include <time.h>
 
 /*
  * libcheckers
@@ -51,6 +56,7 @@
 #include <prio.h>
 #include <pgpolicies.h>
 #include <uevent.h>
+#include <log.h>
 
 #include "main.h"
 #include "pidfile.h"
@@ -60,6 +66,7 @@
 #include "cli_handlers.h"
 #include "lock.h"
 #include "waiter.h"
+#include "wwids.h"
 
 #define FILE_NAME_SIZE 256
 #define CMDSIZE 160
@@ -80,18 +87,18 @@ struct mpath_event_param
 
 unsigned int mpath_mx_alloc_len;
 
-pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER;
-pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
-
 int logsink;
 enum daemon_status running_state;
 pid_t daemon_pid;
 
+static sem_t exit_sem;
 /*
  * global copy of vecs for use in sig handlers
  */
 struct vectors * gvecs;
 
+struct udev * udev;
+
 static int
 need_switch_pathgroup (struct multipath * mpp, int refresh)
 {
@@ -133,9 +140,9 @@ coalesce_maps(struct vectors *vecs, vector nmpv)
        struct multipath * ompp;
        vector ompv = vecs->mpvec;
        unsigned int i;
-       int j;
 
        vector_foreach_slot (ompv, ompp, i) {
+               condlog(3, "%s: coalesce map", ompp->alias);
                if (!find_mp_by_wwid(nmpv, ompp->wwid)) {
                        /*
                         * remove all current maps not allowed by the
@@ -147,16 +154,17 @@ coalesce_maps(struct vectors *vecs, vector nmpv)
                                /*
                                 * may be just because the device is open
                                 */
+                               if (setup_multipath(vecs, ompp) != 0) {
+                                       i--;
+                                       continue;
+                               }
                                if (!vector_alloc_slot(nmpv))
                                        return 1;
 
                                vector_set_slot(nmpv, ompp);
-                               setup_multipath(vecs, ompp);
-
-                               if ((j = find_slot(ompv, (void *)ompp)) != -1)
-                                       vector_del_slot(ompv, j);
 
-                               continue;
+                               vector_del_slot(ompv, i);
+                               i--;
                        }
                        else {
                                dm_lib_release();
@@ -226,7 +234,7 @@ flush_map(struct multipath * mpp, struct vectors * vecs)
        }
        else {
                dm_lib_release();
-               condlog(2, "%s: devmap removed", mpp->alias);
+               condlog(2, "%s: map flushed", mpp->alias);
        }
 
        orphan_paths(vecs->pathvec, mpp);
@@ -299,7 +307,7 @@ ev_add_map (char * dev, char * alias, struct vectors * vecs)
                condlog(2, "%s: devmap %s registered", alias, dev);
                return 0;
        }
-       refwwid = get_refwwid(dev, DEV_DEVMAP, vecs->pathvec);
+       r = get_refwwid(dev, DEV_DEVMAP, vecs->pathvec, &refwwid);
 
        if (refwwid) {
                r = coalesce_paths(vecs, NULL, refwwid, 0);
@@ -308,6 +316,8 @@ ev_add_map (char * dev, char * alias, struct vectors * vecs)
 
        if (!r)
                condlog(2, "%s: devmap %s added", alias, dev);
+       else if (r == 2)
+               condlog(2, "%s: uev_add_map %s blacklisted", alias, dev);
        else
                condlog(0, "%s: uev_add_map %s failed", alias, dev);
 
@@ -373,6 +383,7 @@ static int
 uev_add_path (struct uevent *uev, struct vectors * vecs)
 {
        struct path *pp;
+       int ret, i;
 
        condlog(2, "%s: add path (uevent)", uev->kernel);
        if (strstr(uev->kernel, "..") != NULL) {
@@ -389,12 +400,32 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)
                        uev->kernel);
                if (pp->mpp)
                        return 0;
+               if (!strlen(pp->wwid)) {
+                       udev_device_unref(pp->udev);
+                       pp->udev = udev_device_ref(uev->udev);
+                       ret = pathinfo(pp, conf->hwtable,
+                                      DI_ALL | DI_BLACKLIST);
+                       if (ret == 2) {
+                               i = find_slot(vecs->pathvec, (void *)pp);
+                               if (i != -1)
+                                       vector_del_slot(vecs->pathvec, i);
+                               free_path(pp);
+                               return 0;
+                       } else if (ret == 1) {
+                               condlog(0, "%s: failed to reinitialize path",
+                                       uev->kernel);
+                               return 1;
+                       }
+               }
        } else {
                /*
                 * get path vital state
                 */
-               if (!(pp = store_pathinfo(vecs->pathvec, conf->hwtable,
-                                         uev->udev, DI_ALL))) {
+               ret = store_pathinfo(vecs->pathvec, conf->hwtable,
+                                    uev->udev, DI_ALL, &pp);
+               if (!pp) {
+                       if (ret == 2)
+                               return 0;
                        condlog(0, "%s: failed to store path info",
                                uev->kernel);
                        return 1;
@@ -402,14 +433,13 @@ uev_add_path (struct uevent *uev, struct vectors * vecs)
                pp->checkint = conf->checkint;
        }
 
-       return (ev_add_path(pp, vecs) != 1)? 0 : 1;
+       return ev_add_path(pp, vecs);
 }
 
 /*
  * returns:
  * 0: added
  * 1: error
- * 2: blacklisted
  */
 int
 ev_add_path (struct path * pp, struct vectors * vecs)
@@ -427,13 +457,6 @@ ev_add_path (struct path * pp, struct vectors * vecs)
                condlog(0, "%s: failed to get path uid", pp->dev);
                goto fail; /* leave path added to pathvec */
        }
-       if (filter_path(conf, pp) > 0){
-               int i = find_slot(vecs->pathvec, (void *)pp);
-               if (i != -1)
-                       vector_del_slot(vecs->pathvec, i);
-               free_path(pp);
-               return 2;
-       }
        mpp = pp->mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid);
 rescan:
        if (mpp) {
@@ -533,7 +556,8 @@ rescan:
                        goto fail_map;
 
        if (retries >= 0) {
-               condlog(2, "%s path added to devmap %s", pp->dev, mpp->alias);
+               condlog(2, "%s [%s]: path added to devmap %s",
+                       pp->dev, pp->dev_t, mpp->alias);
                return 0;
        }
        else
@@ -542,7 +566,7 @@ rescan:
 fail_map:
        remove_map(mpp, vecs, 1);
 fail:
-       orphan_path(pp);
+       orphan_path(pp, "failed to add path");
        return 1;
 }
 
@@ -638,8 +662,8 @@ ev_remove_path (struct path *pp, struct vectors * vecs)
                        }
                        sync_map_state(mpp);
 
-                       condlog(2, "%s: path removed from map %s",
-                               pp->dev, mpp->alias);
+                       condlog(2, "%s [%s]: path removed from map %s",
+                               pp->dev, pp->dev_t, mpp->alias);
                }
        }
 
@@ -674,11 +698,12 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
                                uev->kernel);
                        return 1;
                }
-               if (pp->mpp)
-                       retval = reload_map(vecs, pp->mpp);
+               if (pp->mpp) {
+                       retval = reload_map(vecs, pp->mpp, 0);
 
-               condlog(2, "%s: map %s reloaded (retval %d)",
-                       uev->kernel, pp->mpp->alias, retval);
+                       condlog(2, "%s: map %s reloaded (retval %d)",
+                               uev->kernel, pp->mpp->alias, retval);
+               }
 
        }
 
@@ -815,10 +840,9 @@ out:
 static void *
 ueventloop (void * ap)
 {
-       block_signal(SIGUSR1, NULL);
-       block_signal(SIGHUP, NULL);
+       struct udev *udev = ap;
 
-       if (uevent_listen())
+       if (uevent_listen(udev))
                condlog(0, "error starting uevent listener");
 
        return NULL;
@@ -827,9 +851,6 @@ ueventloop (void * ap)
 static void *
 uevqloop (void * ap)
 {
-       block_signal(SIGUSR1, NULL);
-       block_signal(SIGHUP, NULL);
-
        if (uevent_dispatch(&uev_trigger, ap))
                condlog(0, "error starting uevent dispatcher");
 
@@ -838,9 +859,6 @@ uevqloop (void * ap)
 static void *
 uxlsnrloop (void * ap)
 {
-       block_signal(SIGUSR1, NULL);
-       block_signal(SIGHUP, NULL);
-
        if (cli_init())
                return NULL;
 
@@ -881,6 +899,8 @@ uxlsnrloop (void * ap)
        set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus);
        set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus);
        set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
+       set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q);
+       set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q);
 
        umask(077);
        uxsock_listen(&uxsock_trigger, ap);
@@ -888,20 +908,10 @@ uxlsnrloop (void * ap)
        return NULL;
 }
 
-int
-exit_daemon (int status)
+void
+exit_daemon (void)
 {
-       if (status != 0)
-               fprintf(stderr, "bad exit status. see daemon.log\n");
-
-       condlog(3, "unlink pidfile");
-       unlink(DEFAULT_PIDFILE);
-
-       pthread_mutex_lock(&exit_mutex);
-       pthread_cond_signal(&exit_cond);
-       pthread_mutex_unlock(&exit_mutex);
-
-       return status;
+       sem_post(&exit_sem);
 }
 
 const char *
@@ -1086,25 +1096,9 @@ int update_prio(struct path *pp, int refresh_all)
 
 int update_path_groups(struct multipath *mpp, struct vectors *vecs, int refresh)
 {
-       int i;
-       struct path * pp;
-       char params[PARAMS_SIZE];
-
-       update_mpp_paths(mpp, vecs->pathvec);
-       if (refresh) {
-               vector_foreach_slot (mpp->paths, pp, i)
-                       pathinfo(pp, conf->hwtable, DI_PRIO);
-       }
-       params[0] = '\0';
-       if (setup_map(mpp, params, PARAMS_SIZE))
+       if (reload_map(vecs, mpp, refresh))
                return 1;
 
-       mpp->action = ACT_RELOAD;
-       if (domap(mpp, params) <= 0) {
-               condlog(0, "%s: failed to update map : %s", mpp->alias,
-                       strerror(errno));
-               return 1;
-       }
        dm_lib_release();
        if (setup_multipath(vecs, mpp) != 0)
                return 1;
@@ -1113,7 +1107,10 @@ int update_path_groups(struct multipath *mpp, struct vectors *vecs, int refresh)
        return 0;
 }
 
-void
+/*
+ * Returns '1' if the path has been checked, '0' otherwise
+ */
+int
 check_path (struct vectors * vecs, struct path * pp)
 {
        int newstate;
@@ -1122,10 +1119,10 @@ check_path (struct vectors * vecs, struct path * pp)
        int oldchkrstate = pp->chkrstate;
 
        if (!pp->mpp)
-               return;
+               return 0;
 
        if (pp->tick && --pp->tick)
-               return; /* don't check this path yet */
+               return 0; /* don't check this path yet */
 
        /*
         * provision a next check soonest,
@@ -1134,13 +1131,31 @@ check_path (struct vectors * vecs, struct path * pp)
        pp->tick = conf->checkint;
 
        newstate = path_offline(pp);
+       if (newstate == PATH_REMOVED) {
+               condlog(2, "%s: remove path (checker)", pp->dev);
+               ev_remove_path(pp, vecs);
+               return 0;
+       }
        if (newstate == PATH_UP)
                newstate = get_state(pp, 1);
+       else
+               checker_clear_message(&pp->checker);
 
        if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) {
                condlog(2, "%s: unusable path", pp->dev);
                pathinfo(pp, conf->hwtable, 0);
-               return;
+               return 1;
+       }
+       if (!pp->mpp) {
+               if (!strlen(pp->wwid) &&
+                   (newstate == PATH_UP || newstate == PATH_GHOST)) {
+                       condlog(2, "%s: add missing path", pp->dev);
+                       if (pathinfo(pp, conf->hwtable, DI_ALL) == 0) {
+                               ev_add_path(pp, vecs);
+                               pp->tick = 1;
+                       }
+               }
+               return 0;
        }
        /*
         * Async IO in flight. Keep the previous path state
@@ -1148,13 +1163,13 @@ check_path (struct vectors * vecs, struct path * pp)
         */
        if (newstate == PATH_PENDING) {
                pp->tick = 1;
-               return;
+               return 0;
        }
        /*
         * Synchronize with kernel state
         */
        if (update_multipath_strings(pp->mpp, vecs->pathvec)) {
-               condlog(1, "%s: Could not synchronize with kernel state\n",
+               condlog(1, "%s: Could not synchronize with kernel state",
                        pp->dev);
                pp->dmstate = PSTATE_UNDEF;
        }
@@ -1162,7 +1177,9 @@ check_path (struct vectors * vecs, struct path * pp)
        if (newstate != pp->state) {
                int oldstate = pp->state;
                pp->state = newstate;
-               LOG_MSG(1, checker_message(&pp->checker));
+
+               if (strlen(checker_message(&pp->checker)))
+                       LOG_MSG(1, checker_message(&pp->checker));
 
                /*
                 * upon state change, reset the checkint
@@ -1186,7 +1203,7 @@ check_path (struct vectors * vecs, struct path * pp)
                        pp->mpp->failback_tick = 0;
 
                        pp->mpp->stat_path_failures++;
-                       return;
+                       return 1;
                }
 
                if(newstate == PATH_UP || newstate == PATH_GHOST){
@@ -1228,21 +1245,24 @@ check_path (struct vectors * vecs, struct path * pp)
                        reinstate_path(pp, 0);
                } else {
                        LOG_MSG(4, checker_message(&pp->checker));
-                       /*
-                        * double the next check delay.
-                        * max at conf->max_checkint
-                        */
-                       if (pp->checkint < (conf->max_checkint / 2))
-                               pp->checkint = 2 * pp->checkint;
-                       else
-                               pp->checkint = conf->max_checkint;
+                       if (pp->checkint != conf->max_checkint) {
+                               /*
+                                * double the next check delay.
+                                * max at conf->max_checkint
+                                */
+                               if (pp->checkint < (conf->max_checkint / 2))
+                                       pp->checkint = 2 * pp->checkint;
+                               else
+                                       pp->checkint = conf->max_checkint;
 
+                               condlog(4, "%s: delay next check %is",
+                                       pp->dev_t, pp->checkint);
+                       }
                        pp->tick = pp->checkint;
-                       condlog(4, "%s: delay next check %is",
-                               pp->dev_t, pp->tick);
                }
        }
-       else if (newstate == PATH_DOWN) {
+       else if (newstate == PATH_DOWN &&
+                strlen(checker_message(&pp->checker))) {
                if (conf->log_checker_err == LOG_CHKR_ERR_ONCE)
                        LOG_MSG(3, checker_message(&pp->checker));
                else
@@ -1269,6 +1289,7 @@ check_path (struct vectors * vecs, struct path * pp)
                         (chkr_new_path_up && followover_should_failback(pp)))
                        switch_pathgroup(pp->mpp);
        }
+       return 1;
 }
 
 static void *
@@ -1278,7 +1299,6 @@ checkerloop (void *ap)
        struct path *pp;
        int count = 0;
        unsigned int i;
-       sigset_t old;
 
        mlockall(MCL_CURRENT | MCL_FUTURE);
        vecs = (struct vectors *)ap;
@@ -1292,15 +1312,22 @@ checkerloop (void *ap)
        }
 
        while (1) {
-               block_signal(SIGHUP, &old);
+               struct timeval diff_time, start_time, end_time;
+               int num_paths = 0;
+
+               if (gettimeofday(&start_time, NULL) != 0)
+                       start_time.tv_sec = 0;
                pthread_cleanup_push(cleanup_lock, &vecs->lock);
                lock(vecs->lock);
                pthread_testcancel();
                condlog(4, "tick");
-
+#ifdef USE_SYSTEMD
+               if (conf->watchdog)
+                       sd_notify(0, "WATCHDOG=1");
+#endif
                if (vecs->pathvec) {
                        vector_foreach_slot (vecs->pathvec, pp, i) {
-                               check_path(vecs, pp);
+                               num_paths += check_path(vecs, pp);
                        }
                }
                if (vecs->mpvec) {
@@ -1316,7 +1343,14 @@ checkerloop (void *ap)
                }
 
                lock_cleanup_pop(vecs->lock);
-               pthread_sigmask(SIG_SETMASK, &old, NULL);
+               if (start_time.tv_sec &&
+                   gettimeofday(&end_time, NULL) == 0 &&
+                   num_paths) {
+                       timersub(&end_time, &start_time, &diff_time);
+                       condlog(3, "checked %d path%s in %lu.%06lu secs",
+                               num_paths, num_paths > 1 ? "s" : "",
+                               diff_time.tv_sec, diff_time.tv_usec);
+               }
                sleep(1);
        }
        return NULL;
@@ -1373,6 +1407,7 @@ configure (struct vectors * vecs, int start_waiters)
 
        sync_maps_state(mpvec);
        vector_foreach_slot(mpvec, mpp, i){
+               remember_wwid(mpp->wwid);
                update_map_pr(mpp);
        }
 
@@ -1418,7 +1453,11 @@ reconfigure (struct vectors * vecs)
        vecs->pathvec = NULL;
        conf = NULL;
 
-       if (!load_config(DEFAULT_CONFIGFILE)) {
+       /* Re-read any timezone changes */
+       tzset();
+
+       if (!load_config(DEFAULT_CONFIGFILE, udev)) {
+               dm_drv_version(conf->version, TGT_MPATH);
                conf->verbosity = old->verbosity;
                conf->daemon = 1;
                configure(vecs, 1);
@@ -1475,40 +1514,66 @@ signal_set(int signo, void (*func) (int))
                return (osig.sa_handler);
 }
 
+void
+handle_signals(void)
+{
+       if (reconfig_sig && running_state == DAEMON_RUNNING) {
+               condlog(2, "reconfigure (signal)");
+               pthread_cleanup_push(cleanup_lock,
+                               &gvecs->lock);
+               lock(gvecs->lock);
+               pthread_testcancel();
+               reconfigure(gvecs);
+               lock_cleanup_pop(gvecs->lock);
+       }
+       if (log_reset_sig) {
+               condlog(2, "reset log (signal)");
+               pthread_mutex_lock(&logq_lock);
+               log_reset("multipathd");
+               pthread_mutex_unlock(&logq_lock);
+       }
+       reconfig_sig = 0;
+       log_reset_sig = 0;
+}
+
 static void
 sighup (int sig)
 {
-       condlog(2, "reconfigure (SIGHUP)");
-
-       if (running_state != DAEMON_RUNNING)
-               return;
-
-       lock(gvecs->lock);
-       reconfigure(gvecs);
-       unlock(gvecs->lock);
-
-#ifdef _DEBUG_
-       dbg_free_final(NULL);
-#endif
+       reconfig_sig = 1;
 }
 
 static void
 sigend (int sig)
 {
-       exit_daemon(0);
+       exit_daemon();
 }
 
 static void
 sigusr1 (int sig)
 {
-       condlog(3, "SIGUSR1 received");
+       log_reset_sig = 1;
+}
+
+static void
+sigusr2 (int sig)
+{
+       condlog(3, "SIGUSR2 received");
 }
 
 static void
 signal_init(void)
 {
+       sigset_t set;
+
+       sigemptyset(&set);
+       sigaddset(&set, SIGHUP);
+       sigaddset(&set, SIGUSR1);
+       sigaddset(&set, SIGUSR2);
+       pthread_sigmask(SIG_BLOCK, &set, NULL);
+
        signal_set(SIGHUP, sighup);
        signal_set(SIGUSR1, sigusr1);
+       signal_set(SIGUSR2, sigusr2);
        signal_set(SIGINT, sigend);
        signal_set(SIGTERM, sigend);
        signal(SIGPIPE, SIG_IGN);
@@ -1543,7 +1608,13 @@ set_oom_adj (void)
 #endif
        FILE *fp;
        struct stat st;
+       char *envp;
 
+       envp = getenv("OOMScoreAdjust");
+       if (envp) {
+               condlog(3, "Using systemd provided OOMScoreAdjust");
+               return;
+       }
        do {
                if (stat(file, &st) == 0){
                        fp = fopen(file, "w");
@@ -1561,8 +1632,12 @@ set_oom_adj (void)
                                strerror(errno));
                        return;
                }
+#ifdef OOM_ADJUST_MIN
                file = "/proc/self/oom_adj";
                score = OOM_ADJUST_MIN;
+#else
+               retry = 0;
+#endif
        } while (retry--);
        condlog(0, "couldn't adjust oom score");
 }
@@ -1571,18 +1646,27 @@ static int
 child (void * param)
 {
        pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr;
-       pthread_attr_t log_attr, misc_attr;
+       pthread_attr_t log_attr, misc_attr, uevent_attr;
        struct vectors * vecs;
        struct multipath * mpp;
        int i;
-       int rc;
+#ifdef USE_SYSTEMD
+       unsigned long checkint;
+#endif
+       int rc, pid_rc;
+       char *envp;
 
        mlockall(MCL_CURRENT | MCL_FUTURE);
+       sem_init(&exit_sem, 0, 0);
+       signal_init();
+
+       udev = udev_new();
 
        setup_thread_attr(&misc_attr, 64 * 1024, 1);
+       setup_thread_attr(&uevent_attr, 128 * 1024, 1);
        setup_thread_attr(&waiter_attr, 32 * 1024, 1);
 
-       if (logsink) {
+       if (logsink == 1) {
                setup_thread_attr(&log_attr, 64 * 1024, 0);
                log_thread_start(&log_attr);
                pthread_attr_destroy(&log_attr);
@@ -1590,28 +1674,36 @@ child (void * param)
 
        running_state = DAEMON_START;
 
+#ifdef USE_SYSTEMD
+       sd_notify(0, "STATUS=startup");
+#endif
        condlog(2, "--------start up--------");
        condlog(2, "read " DEFAULT_CONFIGFILE);
 
-       if (load_config(DEFAULT_CONFIGFILE))
-               exit(1);
+       if (load_config(DEFAULT_CONFIGFILE, udev))
+               goto failed;
 
+       dm_drv_version(conf->version, TGT_MPATH);
        if (init_checkers()) {
                condlog(0, "failed to initialize checkers");
-               exit(1);
+               goto failed;
        }
        if (init_prio()) {
                condlog(0, "failed to initialize prioritizers");
-               exit(1);
+               goto failed;
        }
 
        setlogmask(LOG_UPTO(conf->verbosity + 3));
 
-       if (conf->max_fds) {
+       envp = getenv("LimitNOFILE");
+
+       if (envp) {
+               condlog(2,"Using systemd provided open fds limit of %s", envp);
+       } else if (conf->max_fds) {
                struct rlimit fd_limit;
 
                if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
-                       condlog(0, "can't get open fds limit: %s\n",
+                       condlog(0, "can't get open fds limit: %s",
                                strerror(errno));
                        fd_limit.rlim_cur = 0;
                        fd_limit.rlim_max = 0;
@@ -1622,48 +1714,66 @@ child (void * param)
                                fd_limit.rlim_max = conf->max_fds;
                        if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
                                condlog(0, "can't set open fds limit to "
-                                       "%lu/%lu : %s\n",
+                                       "%lu/%lu : %s",
                                        fd_limit.rlim_cur, fd_limit.rlim_max,
                                        strerror(errno));
                        } else {
-                               condlog(3, "set open fds limit to %lu/%lu\n",
+                               condlog(3, "set open fds limit to %lu/%lu",
                                        fd_limit.rlim_cur, fd_limit.rlim_max);
                        }
                }
 
        }
 
-       signal_init();
-       setscheduler();
-       set_oom_adj();
        vecs = gvecs = init_vecs();
-
        if (!vecs)
-               exit(1);
+               goto failed;
+
+       setscheduler();
+       set_oom_adj();
 
        conf->daemon = 1;
        udev_set_sync_support(0);
+#ifdef USE_SYSTEMD
+       envp = getenv("WATCHDOG_USEC");
+       if (envp && sscanf(envp, "%lu", &checkint) == 1) {
+               /* Value is in microseconds */
+               conf->max_checkint = checkint / 1000000;
+               /* Rescale checkint */
+               if (conf->checkint > conf->max_checkint)
+                       conf->checkint = conf->max_checkint;
+               else
+                       conf->checkint = conf->max_checkint / 4;
+               condlog(3, "enabling watchdog, interval %d max %d",
+                       conf->checkint, conf->max_checkint);
+               conf->watchdog = conf->checkint;
+       }
+#endif
        /*
         * Start uevent listener early to catch events
         */
-       if ((rc = pthread_create(&uevent_thr, &misc_attr, ueventloop, vecs))) {
+       if ((rc = pthread_create(&uevent_thr, &uevent_attr, ueventloop, udev))) {
                condlog(0, "failed to create uevent thread: %d", rc);
-               exit(1);
+               goto failed;
        }
+       pthread_attr_destroy(&uevent_attr);
        if ((rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs))) {
                condlog(0, "failed to create cli listener: %d", rc);
-               exit(1);
+               goto failed;
        }
        /*
         * fetch and configure both paths and multipaths
         */
-       lock(vecs->lock);
+#ifdef USE_SYSTEMD
+       sd_notify(0, "STATUS=configure");
+#endif
        running_state = DAEMON_CONFIGURE;
 
+       lock(vecs->lock);
        if (configure(vecs, 1)) {
                unlock(vecs->lock);
                condlog(0, "failure during configuration");
-               exit(1);
+               goto failed;
        }
        unlock(vecs->lock);
 
@@ -1672,28 +1782,32 @@ child (void * param)
         */
        if ((rc = pthread_create(&check_thr, &misc_attr, checkerloop, vecs))) {
                condlog(0,"failed to create checker loop thread: %d", rc);
-               exit(1);
+               goto failed;
        }
        if ((rc = pthread_create(&uevq_thr, &misc_attr, uevqloop, vecs))) {
                condlog(0, "failed to create uevent dispatcher: %d", rc);
-               exit(1);
+               goto failed;
        }
        pthread_attr_destroy(&misc_attr);
 
-       pthread_mutex_lock(&exit_mutex);
        /* Startup complete, create logfile */
-       if (pidfile_create(DEFAULT_PIDFILE, daemon_pid))
-               /* Ignore errors, we can live without */
-               condlog(1, "failed to create pidfile");
+       pid_rc = pidfile_create(DEFAULT_PIDFILE, daemon_pid);
+       /* Ignore errors, we can live without */
 
        running_state = DAEMON_RUNNING;
-       pthread_cond_wait(&exit_cond, &exit_mutex);
+#ifdef USE_SYSTEMD
+       sd_notify(0, "READY=1\nSTATUS=running");
+#endif
 
        /*
         * exit path
         */
+       while(sem_wait(&exit_sem) != 0); /* Do nothing */
+
+#ifdef USE_SYSTEMD
+       sd_notify(0, "STATUS=shutdown");
+#endif
        running_state = DAEMON_SHUTDOWN;
-       block_signal(SIGHUP, NULL);
        lock(vecs->lock);
        if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF)
                vector_foreach_slot(vecs->mpvec, mpp, i)
@@ -1713,7 +1827,8 @@ child (void * param)
        /* Now all the waitevent threads will start rushing in. */
        while (vecs->lock.depth > 0) {
                sleep (1); /* This is weak. */
-               condlog(3,"Have %d wait event checkers threads to de-alloc, waiting..\n", vecs->lock.depth);
+               condlog(3, "Have %d wait event checkers threads to de-alloc,"
+                       " waiting...", vecs->lock.depth);
        }
        pthread_mutex_destroy(vecs->lock.mutex);
        FREE(vecs->lock.mutex);
@@ -1729,12 +1844,14 @@ child (void * param)
        dm_lib_exit();
 
        /* We're done here */
-       condlog(3, "unlink pidfile");
-       unlink(DEFAULT_PIDFILE);
+       if (!pid_rc) {
+               condlog(3, "unlink pidfile");
+               unlink(DEFAULT_PIDFILE);
+       }
 
        condlog(2, "--------shut down-------");
 
-       if (logsink)
+       if (logsink == 1)
                log_thread_stop();
 
        /*
@@ -1744,12 +1861,22 @@ child (void * param)
         */
        free_config(conf);
        conf = NULL;
-
+       udev_unref(udev);
+       udev = NULL;
 #ifdef _DEBUG_
        dbg_free_final(NULL);
 #endif
 
+#ifdef USE_SYSTEMD
+       sd_notify(0, "ERRNO=0");
+#endif
        exit(0);
+
+failed:
+#ifdef USE_SYSTEMD
+       sd_notify(0, "ERRNO=1");
+#endif
+       exit(1);
 }
 
 static int
@@ -1833,7 +1960,7 @@ main (int argc, char *argv[])
        if (!conf)
                exit(1);
 
-       while ((arg = getopt(argc, argv, ":dv:k::")) != EOF ) {
+       while ((arg = getopt(argc, argv, ":dsv:k::")) != EOF ) {
        switch(arg) {
                case 'd':
                        logsink = 0;
@@ -1846,6 +1973,9 @@ main (int argc, char *argv[])
 
                        conf->verbosity = atoi(optarg);
                        break;
+               case 's':
+                       logsink = -1;
+                       break;
                case 'k':
                        uxclnt(optarg);
                        exit(0);
@@ -1870,7 +2000,7 @@ main (int argc, char *argv[])
                exit(0);
        }
 
-       if (!logsink)
+       if (logsink < 1)
                err = 0;
        else
                err = daemonize();
@@ -1900,18 +2030,18 @@ void *  mpath_pr_event_handler_fn (void * pathp )
 
        resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
        if (!resp){
-               condlog(0,"%s Alloc failed for prin response \n", pp->dev);
+               condlog(0,"%s Alloc failed for prin response", pp->dev);
                return NULL;
        }
 
        ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, 0);
        if (ret != MPATH_PR_SUCCESS )
        {
-               condlog(0,"%s : pr in read keys service action failed. Error=%d\n", pp->dev, ret);
+               condlog(0,"%s : pr in read keys service action failed. Error=%d", pp->dev, ret);
                goto out;
        }
 
-       condlog(3, " event pr=%d addlen=%d\n",resp->prin_descriptor.prin_readkeys.prgeneration,
+       condlog(3, " event pr=%d addlen=%d",resp->prin_descriptor.prin_readkeys.prgeneration,
                        resp->prin_descriptor.prin_readkeys.additional_length );
 
        if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
@@ -1933,7 +2063,7 @@ void *  mpath_pr_event_handler_fn (void * pathp )
        isFound =0;
        for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
        {
-               condlog(2, "PR IN READKEYS[%d]  reservation key:\n",i);
+               condlog(2, "PR IN READKEYS[%d]  reservation key:",i);
                dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , -1);
                if (!memcmp(mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
                {
@@ -1945,7 +2075,7 @@ void *  mpath_pr_event_handler_fn (void * pathp )
        if (!isFound)
        {
                condlog(0, "%s: Either device not registered or ", pp->dev);
-               condlog(0, "host is not authorised for registration. Skip path\n");
+               condlog(0, "host is not authorised for registration. Skip path");
                ret = MPATH_PR_OTHER;
                goto out;
        }
@@ -1959,12 +2089,12 @@ void *  mpath_pr_event_handler_fn (void * pathp )
        }
        param->num_transportid = 0;
 
-       condlog(3, "device %s:%s \n", pp->dev, pp->mpp->wwid);
+       condlog(3, "device %s:%s", pp->dev, pp->mpp->wwid);
 
        ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, param, 0);
        if (ret != MPATH_PR_SUCCESS )
        {
-               condlog(0,"%s: Reservation registration failed. Error: %d\n", pp->dev, ret);
+               condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
        }
        mpp->prflag = 1;
 
@@ -1991,7 +2121,7 @@ int mpath_pr_event_handle(struct path *pp)
 
        rc = pthread_create(&thread, NULL , mpath_pr_event_handler_fn, pp);
        if (rc) {
-               condlog(0, "%s: ERROR; return code from pthread_create() is %d\n", pp->dev, rc);
+               condlog(0, "%s: ERROR; return code from pthread_create() is %d", pp->dev, rc);
                return -1;
        }
        pthread_attr_destroy(&attr);
index 242f5e1..10378ef 100644 (file)
@@ -16,7 +16,7 @@ struct prin_resp;
 
 extern pid_t daemon_pid;
 
-int exit_daemon(int);
+void exit_daemon(void);
 const char * daemon_status(void);
 int reconfigure (struct vectors *);
 int ev_add_path (struct path *, struct vectors *);
@@ -35,5 +35,6 @@ int mpath_pr_event_handle(struct path *pp);
 void * mpath_pr_event_handler_fn (void * );
 int update_map_pr(struct multipath *mpp);
 void * mpath_pr_event_handler_fn (void * pathp );
+void handle_signals(void);
 
 #endif /* MAIN_H */
index cbc40a4..6da9d2b 100644 (file)
@@ -20,8 +20,11 @@ devmap reconfiguration, so that it can refresh its failed path list.
 .SH OPTIONS
 .TP
 .B \-d
-Forground Mode. Don't daemonize, and print all messages to stdout and stderr.
-.TP 
+Foreground Mode. Don't daemonize, and print all messages to stdout and stderr.
+.TP
+.B \-s
+Suppress timestamps. Do not prefix logging messages with a timestamp.
+.TP
 .B -v "level"
 Verbosity level. Print additional information while running multipathd. A  level of 0 means only print errors. A level of 3 or greater prints debugging information as well. 
 .TP
@@ -53,10 +56,10 @@ Show the status of all multipath devices that the multipathd is monitoring.
 Show some statistics of all multipath devices that the multipathd is monitoring.
 .TP
 .B list|show maps|multipaths topology
-Show the current multipath topology. Same as "multipath -ll".
+Show the current multipath topology. Same as "multipath \-ll".
 .TP
 .B list|show topology
-Show the current multipath topology. Same as "multipath -ll".
+Show the current multipath topology. Same as "multipath \-ll".
 .TP
 .B list|show map|multipath $map topology
 Show topology of a single multipath device specified by $map, e.g. 36005076303ffc56200000000000010aa.
@@ -83,16 +86,16 @@ Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g
 .B remove|del path $path
 Stop monitoring a path. $path is as listed in /sys/block (e.g. sda).
 .TP
-.B add map $map
+.B add map|multipath $map
 Add a multipath device to the list of monitored devices. $map can either be a device-mapper device as listed in /sys/block (e.g. dm-0) or it can be the alias for the multipath device (e.g. mpath1) or the uid of the multipath device (e.g. 36005076303ffc56200000000000010aa). 
 .TP
-.B remove|del map $map
+.B remove|del map|multipath $map
 Stop monitoring a multipath device.
 .TP
 .B resize map|multipath $map
 Resizes map $map to the given size
 .TP 
-.B switch|switchgroup map $map group $group
+.B switch|switchgroup map|multipath $map group $group
 Force a multipath device to switch to a specific path group. $group is the path group index, starting with 1.
 .TP
 .B reconfigure
@@ -125,10 +128,53 @@ Restore queuing on multipahted map $map
 .B quit|exit
 End interactive session.
 
+.SH "SYSTEMD INTEGRATION"
+When compiled with systemd support two systemd service files are
+installed,
+.I multipathd.service
+and
+.I multipathd.socket
+The
+.I multipathd.socket
+service instructs systemd to intercept the CLI command socket, so
+that any call to the CLI interface will start-up the daemon if
+required.
+The
+.I multipathd.service
+file carries the definitions for controlling the multipath daemon.
+The daemon itself uses the
+.B sd_notify(3)
+interface to communicate with systemd. The following unit keywords are
+recognized:
+.TP
+.I WatchdogSec=
+Enables the internal watchdog from systemd. multipath will send a
+notification via
+.B sd_notify(3)
+to systemd to reset the watchdog. If specified the
+.I polling_interval
+and
+.I max_polling_interval
+settings will be overridden by the watchdog settings.
+
+Please note that systemd prior to version 207 has issues which prevent
+the systemd-provided watchdog from working correctly. So the watchdog
+is not enabled per default, but has to be enabled manually by updating
+the multipathd.service file.
+.TP
+.I OOMScoreAdjust=
+Overrides the internal OOM adjust mechanism
+.TP
+.I LimitNOFILE=
+Overrides the
+.I max_fds
+configuration setting.
+
 .SH "SEE ALSO"
 .BR multipath (8)
 .BR kpartx (8)
-.BR hotplug (8)
+.BR sd_notify (3)
+.BR system.service (5)
 .SH "AUTHORS"
 .B multipathd
 was developed by Christophe Varoqui, <christophe.varoqui@opensvc.com> and others.
index 15b5753..d46ef80 100644 (file)
@@ -81,23 +81,28 @@ force_stop() {
        echo
 }
 
-stop() {
+check_root() {
         root_dev=$(awk '{ if ($1 !~ /^[ \t]*#/ && $2 == "/") { print $1; }}' /etc/mtab)
        dm_num=`dmsetup info -c --noheadings -o minor $root_dev 2> /dev/null`
        if [ $? -eq 0 ]; then
                root_dm_device="dm-$dm_num"
                [ -d $syspath/$root_dm_device ] && teardown_slaves $syspath/$root_dm_device
        fi
+}
 
-       force_stop
+force_queue_without_daemon() {
+       $DAEMON forcequeueing daemon
 }
 
 restart() {
-       stop
+       force_queue_without_daemon
+       check_root
+       force_stop
        start
 }
 
 force_restart() {
+       force_queue_without_daemon
        force_stop
        start
 }
@@ -115,7 +120,8 @@ start)
        start
        ;;
 stop)
-       stop
+       check_root
+       force_stop
        ;;
 force-stop)
        force_stop
index 03a1151..d1319b1 100644 (file)
@@ -38,7 +38,7 @@ case "$1" in
     start)
        echo -n "Starting multipathd"
        ulimit -c unlimited
-       if $DAEMON -k"show daemon" > /dev/null 2>&1 ; then
+       if $DAEMON -k"reconfigure" > /dev/null 2>&1 ; then
            echo -n " (multipathd running)"
            rc_status -v
            rc_exit
index 38189c5..be3ba3f 100644 (file)
@@ -1,14 +1,18 @@
 [Unit]
 Description=Device-Mapper Multipath Device Controller
-Before=iscsi.service iscsid.service
+Before=iscsi.service iscsid.service lvm2-activation-early.service
 After=syslog.target
+DefaultDependencies=no
+Conflicts=shutdown.target
 
 [Service]
-Type=forking
-PIDFile=/var/run/multipathd.pid
-ExecStart=/sbin/multipathd
+Type=notify
+NotifyAccess=main
+LimitCORE=infinity
+ExecStartPre=/sbin/modprobe dm-multipath
+ExecStart=/sbin/multipathd -d -s
 ExecReload=/sbin/multipathd reconfigure
-#ExecStop=/path/to/scrip delete-me if not necessary
 
 [Install]
-WantedBy=multi-user.target
+WantedBy=sysinit.target
+Also=multipathd.socket
diff --git a/multipathd/multipathd.socket b/multipathd/multipathd.socket
new file mode 100644 (file)
index 0000000..3d4b6da
--- /dev/null
@@ -0,0 +1,5 @@
+[Socket]
+ListenStream=@/org/kernel/linux/storage/multipathd
+
+[Install]
+WantedBy=sockets.target
index 4e3ed26..e86be21 100644 (file)
@@ -37,6 +37,29 @@ static void print_reply(char *s)
                putchar(*s++);
        }
 }
+
+static int need_quit(char *str, size_t len)
+{
+       char *ptr, *start;
+       size_t trimed_len = len;
+
+       for (ptr = str; trimed_len && isspace(*ptr);
+            trimed_len--, ptr++)
+               ;
+
+       start = ptr;
+
+       for (ptr = str + len - 1; trimed_len && isspace(*ptr);
+            trimed_len--, ptr--)
+               ;
+
+       if ((trimed_len == 4 && !strncmp(start, "exit", 4)) ||
+           (trimed_len == 4 && !strncmp(start, "quit", 4)))
+               return 1;
+
+       return 0;
+}
+
 /*
  * process the client
  */
@@ -56,9 +79,8 @@ static void process(int fd)
                        free(line);
                        continue;
                }
-               if (!strncmp(line, "exit", 4) && llen == 4)
-                       break;
-               if (!strncmp(line, "quit", 4) && llen == 4)
+
+               if (need_quit(line, llen))
                        break;
 
                if (send_packet(fd, line, llen + 1) != 0) break;
@@ -99,10 +121,8 @@ int uxclnt(char * inbuf)
        int fd;
 
        fd = ux_socket_connect(DEFAULT_SOCKET);
-       if (fd == -1) {
-               perror("ux_socket_connect");
+       if (fd == -1)
                exit(1);
-       }
 
        if (inbuf)
                process_req(fd, inbuf);
index 85dbd70..ed8e012 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Original author : tridge@samba.org, January 2002
- * 
+ *
  * Copyright (c) 2005 Christophe Varoqui
  * Copyright (c) 2005 Benjamin Marzinski, Redhat
  */
@@ -8,6 +8,7 @@
 /*
  * A simple domain socket listener
  */
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/poll.h>
-
+#include <signal.h>
 #include <checkers.h>
-
 #include <memory.h>
 #include <debug.h>
 #include <vector.h>
 #include <structs.h>
+#include <structs_vec.h>
 #include <uxsock.h>
 #include <defaults.h>
 
+#include "main.h"
 #include "cli.h"
 #include "uxlsnr.h"
 
-#define SLEEP_TIME 5000
+struct timespec sleep_time = {5, 0};
 
 struct client {
        int fd;
@@ -42,6 +44,8 @@ struct client {
 static struct client *clients;
 static unsigned num_clients;
 struct pollfd *polls;
+volatile sig_atomic_t reconfig_sig = 0;
+volatile sig_atomic_t log_reset_sig = 0;
 
 /*
  * handle a new client joining
@@ -54,7 +58,7 @@ static void new_client(int ux_sock)
        int fd;
 
        fd = accept(ux_sock, &addr, &len);
-       
+
        if (fd == -1)
                return;
 
@@ -104,18 +108,19 @@ void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *),
        int rlen;
        char *inbuf;
        char *reply;
+       sigset_t mask;
 
        ux_sock = ux_socket_listen(DEFAULT_SOCKET);
 
-       if (ux_sock == -1) {
-               condlog(0, "ux_socket_listen error");
+       if (ux_sock == -1)
                exit(1);
-       }
 
        pthread_cleanup_push(uxsock_cleanup, NULL);
 
        polls = (struct pollfd *)MALLOC(0);
-
+       pthread_sigmask(SIG_SETMASK, NULL, &mask);
+       sigdelset(&mask, SIGHUP);
+       sigdelset(&mask, SIGUSR1);
        while (1) {
                struct client *c;
                int i, poll_count;
@@ -132,11 +137,13 @@ void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *),
                }
 
                /* most of our life is spent in this call */
-               poll_count = poll(polls, i, SLEEP_TIME);
-               
+               poll_count = ppoll(polls, i, &sleep_time, &mask);
+
                if (poll_count == -1) {
-                       if (errno == EINTR)
+                       if (errno == EINTR) {
+                               handle_signals();
                                continue;
+                       }
 
                        /* something went badly wrong! */
                        condlog(0, "poll");
index c646d5d..8b5a525 100644 (file)
@@ -4,5 +4,8 @@
 void * uxsock_listen(int (*uxsock_trigger)
                        (char *, char **, int *, void *),
                        void * trigger_data);
+
+extern volatile sig_atomic_t reconfig_sig;
+extern volatile sig_atomic_t log_reset_sig;
 #endif