From 4bd17eacde2a09a952cffbeb52a14f55c690ce04 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Fri, 14 Jan 2022 13:50:13 +0900 Subject: [PATCH] Imported Upstream version 0.5.0 --- .gitignore | 8 +- Makefile | 7 + Makefile.inc | 10 +- kpartx/Makefile | 3 +- kpartx/byteorder.h | 4 + kpartx/dasd.c | 4 +- kpartx/dasd.h | 2 +- kpartx/devmapper.c | 4 +- kpartx/devmapper.h | 2 + kpartx/dos.c | 17 +- kpartx/gpt.c | 29 +- kpartx/kpartx.8 | 8 +- kpartx/kpartx.c | 33 +- kpartx/kpartx.h | 9 + kpartx/kpartx.rules | 28 +- kpartx/kpartx_id | 14 +- kpartx/lopart.c | 52 +- kpartx/ps3.c | 77 + libmpathpersist/mpath_persist.c | 7 +- libmpathpersist/mpath_persist.h | 10 +- .../mpath_persistent_reserve_out.3 | 2 +- libmpathpersist/mpath_pr_ioctl.c | 5 +- libmpathpersist/mpath_updatepr.c | 3 +- libmultipath/Makefile | 12 +- libmultipath/alias.c | 88 +- libmultipath/alias.h | 2 +- libmultipath/blacklist.c | 196 +- libmultipath/blacklist.h | 19 +- libmultipath/callout.c | 216 + libmultipath/callout.h | 7 + libmultipath/checkers.c | 4 +- libmultipath/checkers.h | 10 + libmultipath/checkers/emc_clariion.c | 7 +- libmultipath/checkers/hp_sw.c | 4 +- libmultipath/checkers/libsg.c | 2 +- libmultipath/checkers/rdac.c | 96 +- libmultipath/checkers/tur.c | 11 +- libmultipath/config.c | 90 +- libmultipath/config.h | 20 +- libmultipath/configure.c | 122 +- libmultipath/configure.h | 4 +- libmultipath/debug.c | 17 +- libmultipath/defaults.h | 7 +- libmultipath/devmapper.c | 217 +- libmultipath/devmapper.h | 15 +- libmultipath/dict.c | 510 ++- libmultipath/discovery.c | 519 ++- libmultipath/discovery.h | 15 +- libmultipath/dmparser.c | 23 +- libmultipath/file.c | 4 +- libmultipath/hwtable.c | 254 +- libmultipath/lock.c | 9 - libmultipath/lock.h | 1 - libmultipath/log.c | 8 + libmultipath/log.h | 1 + libmultipath/log_pthread.c | 104 +- libmultipath/log_pthread.h | 11 +- libmultipath/parser.c | 30 +- libmultipath/print.c | 81 +- libmultipath/prio.c | 54 +- libmultipath/prio.h | 9 +- libmultipath/prioritizers/alua.c | 3 +- libmultipath/prioritizers/datacore.c | 1 + libmultipath/prioritizers/emc.c | 1 + libmultipath/prioritizers/hds.c | 6 +- libmultipath/prioritizers/hp_sw.c | 1 + libmultipath/prioritizers/iet.c | 1 + libmultipath/prioritizers/ontap.c | 2 + libmultipath/prioritizers/rdac.c | 2 + libmultipath/prioritizers/weightedpath.c | 6 +- libmultipath/propsel.c | 187 +- libmultipath/propsel.h | 3 +- libmultipath/regex.c | 4032 ----------------- libmultipath/regex.h | 252 -- libmultipath/structs.c | 7 + libmultipath/structs.h | 54 +- libmultipath/structs_vec.c | 50 +- libmultipath/structs_vec.h | 2 +- libmultipath/sysfs.c | 94 +- libmultipath/sysfs.h | 2 + libmultipath/uevent.c | 55 +- libmultipath/uevent.h | 5 +- libmultipath/util.c | 57 +- libmultipath/util.h | 4 +- libmultipath/uxsock.c | 72 +- libmultipath/vector.c | 13 +- libmultipath/vector.h | 12 +- libmultipath/version.h | 4 +- libmultipath/waiter.c | 16 +- libmultipath/wwids.c | 136 + libmultipath/wwids.h | 2 + mpathpersist/Makefile | 2 +- mpathpersist/main.c | 11 +- multipath.conf.annotated | 109 +- multipath.conf.defaults | 1669 +++---- multipath/Makefile | 5 +- multipath/main.c | 144 +- multipath/multipath.8 | 42 +- multipath/multipath.conf.5 | 89 +- multipath/multipath.init.suse | 6 +- multipath/multipath.rules | 7 - multipathd/Makefile | 15 +- multipathd/cli.c | 3 + multipathd/cli.h | 2 + multipathd/cli_handlers.c | 71 +- multipathd/cli_handlers.h | 2 + multipathd/main.c | 434 +- multipathd/main.h | 3 +- multipathd/multipathd.8 | 62 +- multipathd/multipathd.init.redhat | 14 +- multipathd/multipathd.init.suse | 2 +- multipathd/multipathd.service | 16 +- multipathd/multipathd.socket | 5 + multipathd/uxclnt.c | 32 +- multipathd/uxlsnr.c | 31 +- multipathd/uxlsnr.h | 3 + 116 files changed, 4402 insertions(+), 6596 deletions(-) create mode 100644 kpartx/ps3.c create mode 100644 libmultipath/callout.c create mode 100644 libmultipath/callout.h delete mode 100644 libmultipath/regex.c delete mode 100644 libmultipath/regex.h delete mode 100644 multipath/multipath.rules create mode 100644 multipathd/multipathd.socket diff --git a/.gitignore b/.gitignore index 9b3f663..7f25d0e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ *.so.0 *.a *.gz -kpartx -multipath -multipathd +kpartx/kpartx +multipath/multipath +multipathd/multipathd +mpathpersist/mpathpersist +.nfs* diff --git a/Makefile b/Makefile index 5b0c61a..baf7753 100644 --- 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 diff --git a/Makefile.inc b/Makefile.inc index b0c68f4..f445160 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -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 diff --git a/kpartx/Makefile b/kpartx/Makefile index 1287053..4ba38ba 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -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 diff --git a/kpartx/byteorder.h b/kpartx/byteorder.h index 21962d6..199c66b 100644 --- a/kpartx/byteorder.h +++ b/kpartx/byteorder.h @@ -12,12 +12,16 @@ # 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 diff --git a/kpartx/dasd.c b/kpartx/dasd.c index dcdf678..1fcf778 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -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) { diff --git a/kpartx/dasd.h b/kpartx/dasd.h index 0ed7c80..42f94db 100644 --- a/kpartx/dasd.h +++ b/kpartx/dasd.h @@ -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; diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 4baebd9..24a43ee 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -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); diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h index 8e350a0..0edc063 100644 --- a/kpartx/devmapper.h +++ b/kpartx/devmapper.h @@ -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, diff --git a/kpartx/dos.c b/kpartx/dos.c index 1691105..0e57f0e 100644 --- a/kpartx/dos.c +++ b/kpartx/dos.c @@ -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); } } diff --git a/kpartx/gpt.c b/kpartx/gpt.c index 3082cae..5a54970 100644 --- a/kpartx/gpt.c +++ b/kpartx/gpt.c @@ -38,6 +38,7 @@ #include #include #include "crc32.h" +#include "kpartx.h" #if BYTE_ORDER == LITTLE_ENDIAN # define __le16_to_cpu(x) (x) @@ -115,25 +116,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, §or_size); - if (rc) - sector_size = 512; - return sector_size; -} - /************************************************************ * _get_num_sectors * Requires: @@ -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++; } diff --git a/kpartx/kpartx.8 b/kpartx/kpartx.8 index 8a37d4f..021ddc0 100644 --- a/kpartx/kpartx.8 +++ b/kpartx/kpartx.8 @@ -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) diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index b5e0a32..9a9a5eb 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -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, §or_size); + if (rc) + sector_size = 512; + return sector_size; +} diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h index 43ae3f8..a55c211 100644 --- a/kpartx/kpartx.h +++ b/kpartx/kpartx.h @@ -2,6 +2,7 @@ #define _KPARTX_H #include +#include /* * For each partition type there is a routine that takes @@ -18,6 +19,13 @@ #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); diff --git a/kpartx/kpartx.rules b/kpartx/kpartx.rules index 64863a0..5c0d0ff 100644 --- a/kpartx/kpartx.rules +++ b/kpartx/kpartx.rules @@ -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" diff --git a/kpartx/kpartx_id b/kpartx/kpartx_id index 81f32bf..517b856 100644 --- a/kpartx/kpartx_id +++ b/kpartx/kpartx_id @@ -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 diff --git a/kpartx/lopart.c b/kpartx/lopart.c index 79d8328..6f83048 100644 --- a/kpartx/lopart.c +++ b/kpartx/lopart.c @@ -26,23 +26,16 @@ #include #include #include - -#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 #include #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 index 0000000..d1e5d64 --- /dev/null +++ b/kpartx/ps3.c @@ -0,0 +1,77 @@ +#include "kpartx.h" +#include "byteorder.h" +#include +#include + +#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; +} + + + + + diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c index 3041089..bd30125 100644 --- a/libmpathpersist/mpath_persist.c +++ b/libmpathpersist/mpath_persist.c @@ -1,4 +1,3 @@ -#include "mpath_persist.h" #include #include #include @@ -8,6 +7,7 @@ #include #include #include +#include #include #include @@ -20,6 +20,7 @@ #include #include +#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; } diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h index 42294e9..a5e7868 100644 --- a/libmpathpersist/mpath_persist.h +++ b/libmpathpersist/mpath_persist.h @@ -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 diff --git a/libmpathpersist/mpath_persistent_reserve_out.3 b/libmpathpersist/mpath_persistent_reserve_out.3 index 44f950b..f7f84ff 100644 --- a/libmpathpersist/mpath_persistent_reserve_out.3 +++ b/libmpathpersist/mpath_persistent_reserve_out.3 @@ -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 diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c index de3292e..c85fd10 100644 --- a/libmpathpersist/mpath_pr_ioctl.c +++ b/libmpathpersist/mpath_pr_ioctl.c @@ -10,8 +10,9 @@ #include #include #include -#include "mpath_pr_ioctl.h" -#include +#include +#include "mpath_pr_ioctl.h" +#include #include diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c index 2982947..8597d40 100644 --- a/libmpathpersist/mpath_updatepr.c +++ b/libmpathpersist/mpath_updatepr.c @@ -14,6 +14,7 @@ #include #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 ; diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 0395602..6364364 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -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) diff --git a/libmultipath/alias.c b/libmultipath/alias.c index e1f3073..ab15185 100644 --- a/libmultipath/alias.c +++ b/libmultipath/alias.c @@ -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; } diff --git a/libmultipath/alias.h b/libmultipath/alias.h index c625090..8ddd0b5 100644 --- a/libmultipath/alias.h +++ b/libmultipath/alias.h @@ -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); diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c index f369517..79ddcde 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -2,6 +2,7 @@ * Copyright (c) 2004, 2005 Christophe Varoqui */ #include +#include #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) { diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h index cdbebef..0e90e9a 100644 --- a/libmultipath/blacklist.h +++ b/libmultipath/blacklist.h @@ -1,15 +1,19 @@ #ifndef _BLACKLIST_H #define _BLACKLIST_H +#include #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 index 0000000..c35c7c0 --- /dev/null +++ b/libmultipath/callout.c @@ -0,0 +1,216 @@ +/* + * Source: copy of the udev package source file + * + * Copyrights of the source file apply + * Copyright (c) 2004 Christophe Varoqui + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..ab648e8 --- /dev/null +++ b/libmultipath/callout.h @@ -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 */ diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c index 01dafdd..4a4cd7c 100644 --- a/libmultipath/checkers.c +++ b/libmultipath/checkers.c @@ -16,7 +16,9 @@ char *checker_state_names[] = { "up", "shaky", "ghost", - "pending" + "pending", + "timeout", + "removed", }; static LIST_HEAD(checkers); diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h index 5a96165..e62b52f 100644 --- a/libmultipath/checkers.h +++ b/libmultipath/checkers.h @@ -46,6 +46,14 @@ * 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 }; diff --git a/libmultipath/checkers/emc_clariion.c b/libmultipath/checkers/emc_clariion.c index b42d267..a797734 100644 --- a/libmultipath/checkers/emc_clariion.c +++ b/libmultipath/checkers/emc_clariion.c @@ -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; diff --git a/libmultipath/checkers/hp_sw.c b/libmultipath/checkers/hp_sw.c index b50ac0c..fe5e0f9 100644 --- a/libmultipath/checkers/hp_sw.c +++ b/libmultipath/checkers/hp_sw.c @@ -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) diff --git a/libmultipath/checkers/libsg.c b/libmultipath/checkers/libsg.c index 5a989d3..0d3af1f 100644 --- a/libmultipath/checkers/libsg.c +++ b/libmultipath/checkers/libsg.c @@ -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; diff --git a/libmultipath/checkers/rdac.c b/libmultipath/checkers/rdac.c index 5f24f55..e0b2ea4 100644 --- a/libmultipath/checkers/rdac.c +++ b/libmultipath/checkers/rdac.c @@ -34,6 +34,18 @@ #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 = ¤t; 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); diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c index 9c141aa..bd7372d 100644 --- a/libmultipath/checkers/tur.c +++ b/libmultipath/checkers/tur.c @@ -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; diff --git a/libmultipath/config.c b/libmultipath/config.c index 0a56cd1..e13c307 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -25,13 +25,19 @@ 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) diff --git a/libmultipath/config.h b/libmultipath/config.h index 16f530f..445525b 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -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); diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 1bb45a3..8c09791 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -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; } diff --git a/libmultipath/configure.h b/libmultipath/configure.h index 6c1c493..650f080 100644 --- a/libmultipath/configure.h +++ b/libmultipath/configure.h @@ -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); diff --git a/libmultipath/debug.c b/libmultipath/debug.c index d30517d..bad78a8 100644 --- a/libmultipath/debug.c +++ b/libmultipath/debug.c @@ -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 diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index 74b236f..b83d9fb 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -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" @@ -15,13 +15,16 @@ #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" diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index de7d446..6eb2d96 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -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; @@ -706,6 +681,43 @@ _dm_flush_map (const char * mapname, int need_sync) return 1; } +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) { @@ -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; diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index 0c2e03f..58cd718 100644 --- a/libmultipath/devmapper.h +++ b/libmultipath/devmapper.h @@ -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 */ diff --git a/libmultipath/dict.c b/libmultipath/dict.c index e0535a6..9db4725 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -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; @@ -160,6 +160,17 @@ def_uid_attribute_handler(vector strvec) return 0; } +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) { @@ -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; @@ -710,6 +767,32 @@ ble_except_wwid_handler(vector strvec) return store_ble(conf->elist_wwid, buff, ORIGIN_CONFIG); } +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) { @@ -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; @@ -939,6 +1022,19 @@ hw_uid_attribute_handler(vector strvec) return 0; } +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) { @@ -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); } @@ -2053,6 +2179,17 @@ snprint_hw_uid_attribute (char * buff, int len, void * data) return snprintf(buff, len, "\"%s\"", hwe->uid_attribute); } +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) { @@ -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 @@ -2365,13 +2510,22 @@ snprint_def_uid_attribute (char * buff, int len, void * data) return snprintf(buff, len, "\"%s\"", conf->uid_attribute); } +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); diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index 33e44b6..228ffd3 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -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" @@ -28,37 +29,46 @@ #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' ? "" : pp->wwid); + condlog(3, "%s: uid = %s (%s)", pp->dev, + *pp->wwid == '\0' ? "" : 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: diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index 17108c7..3d2d0ce 100644 --- a/libmultipath/discovery.h +++ b/libmultipath/discovery.h @@ -14,7 +14,7 @@ #endif #ifndef DEF_TIMEOUT -#define DEF_TIMEOUT 300000 +#define DEF_TIMEOUT 30 #endif /* @@ -26,15 +26,16 @@ 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) diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c index 5848ec5..2562ba1 100644 --- a/libmultipath/dmparser.c +++ b/libmultipath/dmparser.c @@ -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); diff --git a/libmultipath/file.c b/libmultipath/file.c index 5ee46dc..74cde64 100644 --- a/libmultipath/file.c +++ b/libmultipath/file.c @@ -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; } diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 668ccec..bb73a40 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -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 */ diff --git a/libmultipath/lock.c b/libmultipath/lock.c index 4439a51..6ce9a74 100644 --- a/libmultipath/lock.c +++ b/libmultipath/lock.c @@ -1,16 +1,7 @@ #include -#include #include "lock.h" #include -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)); diff --git a/libmultipath/lock.h b/libmultipath/lock.h index 6897a74..04ef78d 100644 --- a/libmultipath/lock.h +++ b/libmultipath/lock.h @@ -29,6 +29,5 @@ struct mutex_lock { #endif void cleanup_lock (void * data); -void block_signal(int signum, sigset_t *old); #endif /* _LOCK_H */ diff --git a/libmultipath/log.c b/libmultipath/log.c index 57b7696..ab92e2a 100644 --- a/libmultipath/log.c +++ b/libmultipath/log.c @@ -8,6 +8,7 @@ #include #include #include +#include #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; diff --git a/libmultipath/log.h b/libmultipath/log.h index 6634c83..984f047 100644 --- a/libmultipath/log.h +++ b/libmultipath/log.h @@ -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 *); diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c index d701ba1..47d75a1 100644 --- a/libmultipath/log_pthread.c +++ b/libmultipath/log_pthread.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -13,22 +14,41 @@ #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(); } + diff --git a/libmultipath/log_pthread.h b/libmultipath/log_pthread.h index 77780d8..e5c6499 100644 --- a/libmultipath/log_pthread.h +++ b/libmultipath/log_pthread.h @@ -3,14 +3,17 @@ #include -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 */ diff --git a/libmultipath/parser.c b/libmultipath/parser.c index 79c2d22..526c45b 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c @@ -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; } diff --git a/libmultipath/print.c b/libmultipath/print.c index a29b885..3c526c2 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #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 @@ -421,6 +425,16 @@ snprint_path_serial (char * buff, size_t len, struct path * pp) return snprint_str(buff, len, pp->serial); } +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) { @@ -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); } @@ -1053,6 +1087,19 @@ snprint_blacklist_report (char * buff, int len) if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_devnode) == 0) return 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" @@ -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; diff --git a/libmultipath/prio.c b/libmultipath/prio.c index 61c19b7..05a8cf1 100644 --- a/libmultipath/prio.c +++ b/libmultipath/prio.c @@ -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); +} diff --git a/libmultipath/prio.h b/libmultipath/prio.h index 36929fb..4eeb216 100644 --- a/libmultipath/prio.h +++ b/libmultipath/prio.h @@ -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 *); diff --git a/libmultipath/prioritizers/alua.c b/libmultipath/prioritizers/alua.c index 4da3ee7..4165ec6 100644 --- a/libmultipath/prioritizers/alua.c +++ b/libmultipath/prioritizers/alua.c @@ -16,6 +16,7 @@ #include #include +#include #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) { diff --git a/libmultipath/prioritizers/datacore.c b/libmultipath/prioritizers/datacore.c index 2c16c6c..e3e6a51 100644 --- a/libmultipath/prioritizers/datacore.c +++ b/libmultipath/prioritizers/datacore.c @@ -24,6 +24,7 @@ #include #include #include +#include #define INQ_REPLY_LEN 255 #define INQ_CMD_CODE 0x12 diff --git a/libmultipath/prioritizers/emc.c b/libmultipath/prioritizers/emc.c index 20d727e..87e9a8d 100644 --- a/libmultipath/prioritizers/emc.c +++ b/libmultipath/prioritizers/emc.c @@ -5,6 +5,7 @@ #include #include #include +#include #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 diff --git a/libmultipath/prioritizers/hds.c b/libmultipath/prioritizers/hds.c index 4789340..f748707 100644 --- a/libmultipath/prioritizers/hds.c +++ b/libmultipath/prioritizers/hds.c @@ -75,6 +75,7 @@ #include #include #include +#include #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); diff --git a/libmultipath/prioritizers/hp_sw.c b/libmultipath/prioritizers/hp_sw.c index 2de460f..c24baad 100644 --- a/libmultipath/prioritizers/hp_sw.c +++ b/libmultipath/prioritizers/hp_sw.c @@ -15,6 +15,7 @@ #include #include #include +#include #define TUR_CMD_LEN 6 #define SCSI_CHECK_CONDITION 0x2 diff --git a/libmultipath/prioritizers/iet.c b/libmultipath/prioritizers/iet.c index 2570b61..94406df 100644 --- a/libmultipath/prioritizers/iet.c +++ b/libmultipath/prioritizers/iet.c @@ -8,6 +8,7 @@ #include #include #include +#include // // This prioritizer suits iSCSI needs, makes it possible to prefer one path. diff --git a/libmultipath/prioritizers/ontap.c b/libmultipath/prioritizers/ontap.c index 6e6e3d3..026d45d 100644 --- a/libmultipath/prioritizers/ontap.c +++ b/libmultipath/prioritizers/ontap.c @@ -22,6 +22,7 @@ #include #include #include +#include #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); diff --git a/libmultipath/prioritizers/rdac.c b/libmultipath/prioritizers/rdac.c index 41ea887..441b3b0 100644 --- a/libmultipath/prioritizers/rdac.c +++ b/libmultipath/prioritizers/rdac.c @@ -5,6 +5,7 @@ #include #include #include +#include #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); diff --git a/libmultipath/prioritizers/weightedpath.c b/libmultipath/prioritizers/weightedpath.c index d6c81f0..54c9039 100644 --- a/libmultipath/prioritizers/weightedpath.c +++ b/libmultipath/prioritizers/weightedpath.c @@ -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; } diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 6ac4caa..7b7944d 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -17,6 +17,7 @@ #include "devmapper.h" #include "prio.h" #include "discovery.h" +#include "prioritizers/alua_rtpg.h" #include pgpolicyfn *pgpolicies[] = { @@ -261,8 +262,6 @@ select_alias (struct multipath * mp) mp->alias = get_user_friendly_alias(mp->wwid, 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; +} diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index 4e931d4..05c6a4e 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -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 index 0e13c62..0000000 --- a/libmultipath/regex.c +++ /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 -#include -#include - -#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 - -#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; - - - /* \ has been turned into a `duplicate' command which is - followed by the numeric value of 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 ? ®s : (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 index 4715250..0000000 --- a/libmultipath/regex.h +++ /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__ */ diff --git a/libmultipath/structs.c b/libmultipath/structs.c index 3c0fe90..049f17d 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -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--; diff --git a/libmultipath/structs.h b/libmultipath/structs.h index 991ea6e..64de06e 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -3,11 +3,13 @@ #include +#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; diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 6e70d63..76c7e02 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -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; } diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h index a907e85..c6278ac 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h @@ -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); diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c index 8e986e8..e5834f9 100644 --- a/libmultipath/sysfs.c +++ b/libmultipath/sysfs.c @@ -38,7 +38,12 @@ #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); diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h index 13d7545..34f3e00 100644 --- a/libmultipath/sysfs.h +++ b/libmultipath/sysfs.h @@ -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 diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index 0b7eb7a..9ee3ade 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -47,14 +47,15 @@ #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; } diff --git a/libmultipath/uevent.h b/libmultipath/uevent.h index 64f00b7..e5fdfcc 100644 --- a/libmultipath/uevent.h +++ b/libmultipath/uevent.h @@ -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); diff --git a/libmultipath/util.c b/libmultipath/util.c index 70735e6..06a6311 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -10,13 +10,14 @@ #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; diff --git a/libmultipath/util.h b/libmultipath/util.h index 44184a1..257912c 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h @@ -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) diff --git a/libmultipath/uxsock.c b/libmultipath/uxsock.c index e786899..aff7a62 100644 --- a/libmultipath/uxsock.c +++ b/libmultipath/uxsock.c @@ -16,28 +16,36 @@ #include #include #include +#ifdef USE_SYSTEMD +#include +#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; diff --git a/libmultipath/vector.c b/libmultipath/vector.c index 652f118..6266e0a 100644 --- a/libmultipath/vector.c +++ b/libmultipath/vector.c @@ -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--); } diff --git a/libmultipath/vector.h b/libmultipath/vector.h index ca42be1..7612b4c 100644 --- a/libmultipath/vector.h +++ b/libmultipath/vector.h @@ -25,20 +25,20 @@ /* 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--) diff --git a/libmultipath/version.h b/libmultipath/version.h index cefd6b5..8519300 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -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" diff --git a/libmultipath/waiter.c b/libmultipath/waiter.c index 076539c..7cedd4b 100644 --- a/libmultipath/waiter.c +++ b/libmultipath/waiter.c @@ -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); diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c index abd23c5..eca1799 100644 --- a/libmultipath/wwids.c +++ b/libmultipath/wwids.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "checkers.h" #include "vector.h" @@ -81,6 +82,141 @@ write_out_wwid(int fd, char *wwid) { return 1; } +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) { diff --git a/libmultipath/wwids.h b/libmultipath/wwids.h index 1678f9d..f3b21fa 100644 --- a/libmultipath/wwids.h +++ b/libmultipath/wwids.h @@ -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 */ diff --git a/mpathpersist/Makefile b/mpathpersist/Makefile index 2a8efe6..ad8e607 100644 --- a/mpathpersist/Makefile +++ b/mpathpersist/Makefile @@ -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 diff --git a/mpathpersist/main.c b/mpathpersist/main.c index 465fcb1..e3484b5 100644 --- a/mpathpersist/main.c +++ b/mpathpersist/main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "main.h" #include @@ -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; } diff --git a/multipath.conf.annotated b/multipath.conf.annotated index 406f385..235e130 100644 --- a/multipath.conf.annotated +++ b/multipath.conf.annotated @@ -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 @@ -31,10 +24,16 @@ # # 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 @@ -53,10 +52,20 @@ # 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" @@ -84,13 +93,20 @@ # # # # 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 @@ -105,10 +121,24 @@ # # 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 @@ -235,16 +265,32 @@ # # 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 : # # 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 : +# # default : "/var/lib/multipath/wwids" +# wwids_file "/etc/multipath/wwids" +# +# # name : reservation_key +# # scope : multipath +# # desc : Service action reservation key used by mpathpersist. +# # values : +# # default : (null) +# reservation_key "mpathkey" # #} # @@ -497,24 +543,11 @@ # # # 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 diff --git a/multipath.conf.defaults b/multipath.conf.defaults index cbe7254..e761902 100644 --- a/multipath.conf.defaults +++ b/multipath.conf.defaults @@ -1,776 +1,913 @@ # 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 { +#} diff --git a/multipath/Makefile b/multipath/Makefile index f748417..5e5958d 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -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 diff --git a/multipath/main.c b/multipath/main.c index 92e852b..64c8fc5 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -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 diff --git a/multipath/multipath.8 b/multipath/multipath.8 index a112df7..a2262ac 100644 --- a/multipath/multipath.8 +++ b/multipath/multipath.8 @@ -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), diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 index 9d6266c..cf5bec0 100644 --- a/multipath/multipath.conf.5 +++ b/multipath/multipath.conf.5 @@ -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/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 diff --git a/multipath/multipath.init.suse b/multipath/multipath.init.suse index cc7c04b..de1cc1f 100644 --- a/multipath/multipath.init.suse +++ b/multipath/multipath.init.suse @@ -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 index ac97749..0000000 --- a/multipath/multipath.rules +++ /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" diff --git a/multipathd/Makefile b/multipathd/Makefile index 9553251..781122a 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -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 diff --git a/multipathd/cli.c b/multipathd/cli.c index d95cba0..2a5edfa 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -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; } diff --git a/multipathd/cli.h b/multipathd/cli.h index 6b288d4..09fdc68 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -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) diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index 544cbfb..f7fc522 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -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,12 +641,31 @@ 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) { @@ -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); diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h index c62a273..de51961 100644 --- a/multipathd/cli_handlers.h +++ b/multipathd/cli_handlers.h @@ -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); diff --git a/multipathd/main.c b/multipathd/main.c index 64c1a0c..af93f32 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -17,7 +17,12 @@ #include #include #include +#ifdef USE_SYSTEMD +#include +#endif +#include #include +#include /* * libcheckers @@ -51,6 +56,7 @@ #include #include #include +#include #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); diff --git a/multipathd/main.h b/multipathd/main.h index 242f5e1..10378ef 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -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 */ diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 index cbc40a4..6da9d2b 100644 --- a/multipathd/multipathd.8 +++ b/multipathd/multipathd.8 @@ -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, and others. diff --git a/multipathd/multipathd.init.redhat b/multipathd/multipathd.init.redhat index 15b5753..d46ef80 100644 --- a/multipathd/multipathd.init.redhat +++ b/multipathd/multipathd.init.redhat @@ -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 diff --git a/multipathd/multipathd.init.suse b/multipathd/multipathd.init.suse index 03a1151..d1319b1 100644 --- a/multipathd/multipathd.init.suse +++ b/multipathd/multipathd.init.suse @@ -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 diff --git a/multipathd/multipathd.service b/multipathd/multipathd.service index 38189c5..be3ba3f 100644 --- a/multipathd/multipathd.service +++ b/multipathd/multipathd.service @@ -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 index 0000000..3d4b6da --- /dev/null +++ b/multipathd/multipathd.socket @@ -0,0 +1,5 @@ +[Socket] +ListenStream=@/org/kernel/linux/storage/multipathd + +[Install] +WantedBy=sockets.target diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c index 4e3ed26..e86be21 100644 --- a/multipathd/uxclnt.c +++ b/multipathd/uxclnt.c @@ -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); diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c index 85dbd70..ed8e012 100644 --- a/multipathd/uxlsnr.c +++ b/multipathd/uxlsnr.c @@ -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 #include #include @@ -19,20 +20,21 @@ #include #include #include - +#include #include - #include #include #include #include +#include #include #include +#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"); diff --git a/multipathd/uxlsnr.h b/multipathd/uxlsnr.h index c646d5d..8b5a525 100644 --- a/multipathd/uxlsnr.h +++ b/multipathd/uxlsnr.h @@ -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 -- 2.34.1