*.so.0
*.a
*.gz
-kpartx
-multipath
-multipathd
+kpartx/kpartx
+multipath/multipath
+multipathd/multipathd
+mpathpersist/mpathpersist
+.nfs*
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
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
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
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)
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
# 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
return sectors * (blocksize >> 9);
}
+typedef unsigned int __attribute__((__may_alias__)) label_ints_t;
+
/*
*/
int
/*
* 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) {
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;
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);
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);
#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,
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;
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 {
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)
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);
}
}
#include <byteswap.h>
#include <linux/fs.h>
#include "crc32.h"
+#include "kpartx.h"
#if BYTE_ORDER == LITTLE_ENDIAN
# define __le16_to_cpu(x) (x)
/************************************************************
- * 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:
* - filedes is an open file descriptor, suitable for reading
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)
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++;
}
Add partition mappings
.TP
.B \-r
-Readonly partition mappings
+Read-only partition mappings
.TP
.B \-d
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
.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
.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)
#include <string.h>
#include <unistd.h>
#include <stdint.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
} pts[MAXTYPES];
int ptct = 0;
+int udev_sync = 0;
static void
addpts(char *t, ptreader f)
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";
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;
what = DELETE;
break;
case 's':
- sync = 1;
+ udev_sync = 1;
break;
case 'u':
what = UPDATE;
}
#ifdef LIBDM_API_COOKIE
- if (!sync)
+ if (!udev_sync)
dm_udev_set_sync_support(0);
#endif
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);
}
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);
d = c;
while (c) {
for (j = 0; j < n; j++) {
- uint64_t start;
int k = slices[j].container - 1;
if (slices[j].size == 0)
}
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);
}
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;
+}
#define _KPARTX_H
#include <stdint.h>
+#include <sys/ioctl.h>
/*
* For each partition type there is a routine that takes
#define safe_sprintf(var, format, args...) \
snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
+#ifndef BLKSSZGET
+#define BLKSSZGET _IO(0x12,104) /* get block device sector size */
+#endif
+
+int
+get_sector_size(int filedes);
+
/*
* units: 512 byte sectors
*/
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);
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"
# 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
;;
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
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
#include <sys/stat.h>
#include <sys/mman.h>
#include <sysmacros.h>
-
-#if defined(__hppa__) || defined(__powerpc64__) || defined (__alpha__) \
- || defined (__x86_64__)
-typedef unsigned long __kernel_old_dev_t;
-#elif defined(__powerpc__) || defined(__ia64__) || (defined(__sparc__) && defined (__arch64__))
-typedef unsigned int __kernel_old_dev_t;
-#else
-typedef unsigned short __kernel_old_dev_t;
-#endif
-
-#define dev_t __kernel_old_dev_t
-
+#include <asm/posix_types.h>
#include <linux/loop.h>
#include "lopart.h"
#include "xstrncpy.h"
+#ifndef LOOP_CTL_GET_FREE
+#define LOOP_CTL_GET_FREE 0x4C82
+#endif
+
#if !defined (__alpha__) && !defined (__ia64__) && !defined (__x86_64__) \
&& !defined (__s390x__)
#define int2ptr(x) ((void *) ((int) x))
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)) {
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
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) {
extern int
del_loop (const char *device)
{
+ int retries = 3;
int fd;
if ((fd = open (device, O_RDONLY)) < 0) {
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);
--- /dev/null
+#include "kpartx.h"
+#include "byteorder.h"
+#include <sys/types.h>
+#include <string.h>
+
+#define SECTOR_SIZE 512
+#define MAX_ACL_ENTRIES 8
+#define MAX_PARTITIONS 8
+
+#define MAGIC1 0x0FACE0FFULL
+#define MAGIC2 0xDEADFACEULL
+
+struct p_acl_entry {
+ u_int64_t laid;
+ u_int64_t rights;
+};
+
+struct d_partition {
+ u_int64_t p_start;
+ u_int64_t p_size;
+ struct p_acl_entry p_acl[MAX_ACL_ENTRIES];
+};
+
+struct disklabel {
+ u_int8_t d_res1[16];
+ u_int64_t d_magic1;
+ u_int64_t d_magic2;
+ u_int64_t d_res2;
+ u_int64_t d_res3;
+ struct d_partition d_partitions[MAX_PARTITIONS];
+ u_int8_t d_pad[0x600 - MAX_PARTITIONS * sizeof(struct d_partition)- 0x30];
+};
+
+static int
+read_disklabel(int fd, struct disklabel *label) {
+ unsigned char *data;
+ int i;
+
+ for (i = 0; i < sizeof(struct disklabel) / SECTOR_SIZE; i++) {
+ data = (unsigned char *) getblock(fd, i);
+ if (!data)
+ return 0;
+
+ memcpy((unsigned char *) label + i * SECTOR_SIZE, data, SECTOR_SIZE);
+ }
+
+ return 1;
+}
+
+int
+read_ps3_pt(int fd, struct slice all, struct slice *sp, int ns) {
+ struct disklabel label;
+ int n = 0;
+ int i;
+
+ if (!read_disklabel(fd, &label))
+ return -1;
+
+ if ((be64_to_cpu(label.d_magic1) != MAGIC1) ||
+ (be64_to_cpu(label.d_magic2) != MAGIC2))
+ return -1;
+
+ for (i = 0; i < MAX_PARTITIONS; i++) {
+ if (label.d_partitions[i].p_start && label.d_partitions[i].p_size) {
+ sp[n].start = be64_to_cpu(label.d_partitions[i].p_start);
+ sp[n].size = be64_to_cpu(label.d_partitions[i].p_size);
+ n++;
+ }
+ }
+
+ return n;
+}
+
+
+
+
+
-#include "mpath_persist.h"
#include <libdevmapper.h>
#include <defaults.h>
#include <sys/stat.h>
#include <checkers.h>
#include <structs.h>
#include <structs_vec.h>
+#include <libudev.h>
#include <prio.h>
#include <unistd.h>
#include <ctype.h>
#include <propsel.h>
+#include "mpath_persist.h"
#include "mpathpr.h"
#include "mpath_pr_ioctl.h"
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;
}
*
* RETURNS: 0->Success, 1->Failed.
*/
-extern int mpath_lib_init (void );
+extern int mpath_lib_init (struct udev *udev);
/*
*
* @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,
* @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
.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
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
-#include "mpath_pr_ioctl.h"
-#include <mpath_persist.h>
+#include <libudev.h>
+#include "mpath_pr_ioctl.h"
+#include <mpath_persist.h>
#include <debug.h>
#include <debug.h>
#include "memory.h"
#include "../libmultipath/uxsock.h"
+#include "../libmultipath/defaults.h"
unsigned long mem_allocated; /* Total memory used in Bytes */
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 ;
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)
CFLAGS += -DLIBUDEV_API_RECVBUF
endif
+ifdef SYSTEMD
+ CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
+endif
all: $(LIBS)
#include "uxsock.h"
#include "alias.h"
#include "file.h"
+#include "vector.h"
+#include "checkers.h"
+#include "structs.h"
/*
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';
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++;
}
{
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;
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",
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? */
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;
}
}
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;
}
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);
* Copyright (c) 2004, 2005 Christophe Varoqui
*/
#include <stdio.h>
+#include <libudev.h>
#include "checkers.h"
#include "memory.h"
}
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;
}
int
-_blacklist (vector blist, char * str)
+_blacklist (vector blist, const char * str)
{
int i;
struct blentry * ble;
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.
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;
}
}
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;
}
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;
}
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;
}
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)
{
#ifndef _BLACKLIST_H
#define _BLACKLIST_H
+#include <libudev.h>
#include "regex.h"
-#define MATCH_NOTHING 0
-#define MATCH_WWID_BLIST 1
-#define MATCH_DEVICE_BLIST 2
-#define MATCH_DEVNODE_BLIST 3
-#define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST
-#define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST
-#define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST
+#define MATCH_NOTHING 0
+#define MATCH_WWID_BLIST 1
+#define MATCH_DEVICE_BLIST 2
+#define MATCH_DEVNODE_BLIST 3
+#define MATCH_PROPERTY_BLIST 4
+#define MATCH_PROPERTY_BLIST_MISSING 5
+#define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST
+#define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST
+#define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST
+#define MATCH_PROPERTY_BLIST_EXCEPT -MATCH_PROPERTY_BLIST
struct blentry {
char * str;
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);
--- /dev/null
+/*
+ * Source: copy of the udev package source file
+ *
+ * Copyrights of the source file apply
+ * Copyright (c) 2004 Christophe Varoqui
+ */
+#include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include "checkers.h"
+#include "vector.h"
+#include "structs.h"
+#include "util.h"
+#include "debug.h"
+
+int execute_program(char *path, char *value, int len)
+{
+ int retval;
+ int count;
+ int status;
+ int fds[2], null_fd;
+ pid_t pid;
+ char *pos;
+ char arg[CALLOUT_MAX_SIZE];
+ int argc = sizeof(arg) / 2;
+ char *argv[argc + 1];
+ int i;
+
+ i = 0;
+
+ if (strchr(path, ' ')) {
+ strlcpy(arg, path, sizeof(arg));
+ pos = arg;
+ while (pos != NULL && i < argc) {
+ if (pos[0] == '\'') {
+ /* don't separate if in apostrophes */
+ pos++;
+ argv[i] = strsep(&pos, "\'");
+ while (pos[0] == ' ')
+ pos++;
+ } else {
+ argv[i] = strsep(&pos, " ");
+ }
+ i++;
+ }
+ } else {
+ argv[i++] = path;
+ }
+ argv[i] = NULL;
+
+ retval = pipe(fds);
+
+ if (retval != 0) {
+ condlog(0, "error creating pipe for callout: %s", strerror(errno));
+ return -1;
+ }
+
+ pid = fork();
+
+ switch(pid) {
+ case 0:
+ /* child */
+ close(STDOUT_FILENO);
+
+ /* dup write side of pipe to STDOUT */
+ if (dup(fds[1]) < 0)
+ return -1;
+
+ /* Ignore writes to stderr */
+ null_fd = open("/dev/null", O_WRONLY);
+ if (null_fd > 0) {
+ close(STDERR_FILENO);
+ retval = dup(null_fd);
+ close(null_fd);
+ }
+
+ retval = execv(argv[0], argv);
+ condlog(0, "error execing %s : %s", argv[0], strerror(errno));
+ exit(-1);
+ case -1:
+ condlog(0, "fork failed: %s", strerror(errno));
+ close(fds[0]);
+ close(fds[1]);
+ return -1;
+ default:
+ /* parent reads from fds[0] */
+ close(fds[1]);
+ retval = 0;
+ i = 0;
+ while (1) {
+ count = read(fds[0], value + i, len - i-1);
+ if (count <= 0)
+ break;
+
+ i += count;
+ if (i >= len-1) {
+ condlog(0, "not enough space for response from %s", argv[0]);
+ retval = -1;
+ break;
+ }
+ }
+
+ if (count < 0) {
+ condlog(0, "no response from %s", argv[0]);
+ retval = -1;
+ }
+
+ if (i > 0 && value[i-1] == '\n')
+ i--;
+ value[i] = '\0';
+
+ wait(&status);
+ close(fds[0]);
+
+ retval = -1;
+ if (WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ if (status == 0)
+ retval = 0;
+ else
+ condlog(0, "%s exitted with %d", argv[0], status);
+ }
+ else if (WIFSIGNALED(status))
+ condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status));
+ else
+ condlog(0, "%s terminated abnormally", argv[0]);
+ }
+ return retval;
+}
+
+extern int
+apply_format (char * string, char * cmd, struct path * pp)
+{
+ char * pos;
+ char * dst;
+ char * p;
+ char * q;
+ int len;
+ int myfree;
+
+ if (!string)
+ return 1;
+
+ if (!cmd)
+ return 1;
+
+ dst = cmd;
+ p = dst;
+ pos = strchr(string, '%');
+ myfree = CALLOUT_MAX_SIZE;
+
+ if (!pos) {
+ strcpy(dst, string);
+ return 0;
+ }
+
+ len = (int) (pos - string) + 1;
+ myfree -= len;
+
+ if (myfree < 2)
+ return 1;
+
+ snprintf(p, len, "%s", string);
+ p += len - 1;
+ pos++;
+
+ switch (*pos) {
+ case 'n':
+ len = strlen(pp->dev) + 1;
+ myfree -= len;
+
+ if (myfree < 2)
+ return 1;
+
+ snprintf(p, len, "%s", pp->dev);
+ for (q = p; q < p + len; q++) {
+ if (q && *q == '!')
+ *q = '/';
+ }
+ p += len - 1;
+ break;
+ case 'd':
+ len = strlen(pp->dev_t) + 1;
+ myfree -= len;
+
+ if (myfree < 2)
+ return 1;
+
+ snprintf(p, len, "%s", pp->dev_t);
+ p += len - 1;
+ break;
+ default:
+ break;
+ }
+ pos++;
+
+ if (!*pos)
+ return 0;
+
+ len = strlen(pos) + 1;
+ myfree -= len;
+
+ if (myfree < 2)
+ return 1;
+
+ snprintf(p, len, "%s", pos);
+ condlog(3, "reformated callout = %s", dst);
+ return 0;
+}
--- /dev/null
+#ifndef _CALLOUT_H
+#define _CALLOUT_H
+
+int execute_program(char *, char *, int);
+int apply_format (char *, char *, struct path *);
+
+#endif /* _CALLOUT_H */
"up",
"shaky",
"ghost",
- "pending"
+ "pending",
+ "timeout",
+ "removed",
};
static LIST_HEAD(checkers);
* 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,
PATH_SHAKY,
PATH_GHOST,
PATH_PENDING,
+ PATH_TIMEOUT,
+ PATH_REMOVED,
PATH_MAX_STATE
};
#include "../libmultipath/sg_include.h"
#include "libsg.h"
#include "checkers.h"
+#include "debug.h"
#define INQUIRY_CMD 0x12
#define INQUIRY_CMDLEN 6
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");
* 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
} 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;
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;
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)
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;
#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];
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;
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;
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*/
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 */
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);
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);
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));
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;
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;
if (hwe->revision)
FREE(hwe->revision);
+ if (hwe->getuid)
+ FREE(hwe->getuid);
+
if (hwe->uid_attribute)
FREE(hwe->uid_attribute);
if (mpe->selector)
FREE(mpe->selector);
+ if (mpe->getuid)
+ FREE(mpe->getuid);
+
if (mpe->uid_attribute)
FREE(mpe->uid_attribute);
merge_str(vendor);
merge_str(product);
merge_str(revision);
+ merge_str(getuid);
merge_str(uid_attribute);
merge_str(features);
merge_str(hwhandler);
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;
}
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;
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;
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;
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 *
if (conf->dev)
FREE(conf->dev);
- if (conf->udev)
- udev_unref(conf->udev);
-
if (conf->multipath_dir)
FREE(conf->multipath_dir);
if (conf->uid_attribute)
FREE(conf->uid_attribute);
+ if (conf->getuid)
+ FREE(conf->getuid);
+
if (conf->features)
FREE(conf->features);
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);
}
int
-load_config (char * file)
+load_config (char * file, struct udev *udev)
{
if (!conf)
conf = alloc_config();
- if (!conf)
+ if (!conf || !udev)
return 1;
/*
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;
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
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();
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)
* 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)
char * product;
char * revision;
char * uid_attribute;
+ char * getuid;
char * features;
char * hwhandler;
char * selector;
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;
};
char * wwid;
char * alias;
char * uid_attribute;
+ char * getuid;
char * selector;
char * features;
int no_path_retry;
int minio;
int minio_rq;
- int pg_timeout;
int flush_on_last_del;
int attribute_flags;
int user_friendly_names;
};
struct config {
- int dmrq;
int verbosity;
int dry_run;
int list;
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;
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;
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;
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);
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);
/*
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;
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);
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;
/* 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;
}
/* 3. if path has disappeared */
if (!pp1->size) {
- orphan_path(pp1);
+ orphan_path(pp1, "invalid size");
continue;
}
"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);
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);
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;
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;
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
*/
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;
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;
}
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);
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
#define DEFAULT_UID_ATTRIBUTE "ID_SERIAL"
#define DEFAULT_UDEVDIR "/dev"
#define DEFAULT_MULTIPATHDIR "/" LIB_STRING "/multipath"
-#define DEFAULT_SELECTOR "round-robin 0"
+#define DEFAULT_SELECTOR "service-time 0"
#define DEFAULT_ALIAS_PREFIX "mpath"
#define DEFAULT_FEATURES "0"
#define DEFAULT_HWHANDLER "0"
#define DEFAULT_USER_FRIENDLY_NAMES 0
#define DEFAULT_VERBOSITY 2
#define DEFAULT_REASSIGN_MAPS 1
+#define DEFAULT_FAST_IO_FAIL 5
+#define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_OFF
+#define DEFAULT_DETECT_PRIO DETECT_PRIO_OFF
#define DEFAULT_CHECKINT 5
#define MAX_CHECKINT(a) (a << 2)
#define MAX_DEV_LOSS_TMO 0x7FFFFFFF
#define DEFAULT_PIDFILE "/var/run/multipathd.pid"
-#define DEFAULT_SOCKET "/var/run/multipathd.sock"
+#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd"
#define DEFAULT_CONFIGFILE "/etc/multipath.conf"
#define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings"
#define DEFAULT_WWIDS_FILE "/etc/multipath/wwids"
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");
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)
{
return 1;
}
-static int
+int
dm_drv_version (unsigned int * version, char * str)
{
int r = 2;
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;
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)
{
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);
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;
}
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);
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);
}
}
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;
}
extern int
+dm_suspend_and_flush_map (const char * mapname)
+{
+ int s = 0, queue_if_no_path = 0;
+ unsigned long long mapsize;
+ char params[PARAMS_SIZE] = {0};
+
+ if (!dm_map_present(mapname))
+ return 0;
+
+ if (dm_type(mapname, TGT_MPATH) <= 0)
+ return 0; /* nothing to do */
+
+ if (!dm_get_map(mapname, &mapsize, params)) {
+ if (strstr(params, "queue_if_no_path"))
+ queue_if_no_path = 1;
+ }
+
+ if (queue_if_no_path)
+ s = dm_queue_if_no_path((char *)mapname, 0);
+ /* Leave queue_if_no_path alone if unset failed */
+ if (s)
+ queue_if_no_path = 0;
+ else
+ s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 0);
+
+ if (!dm_flush_map(mapname)) {
+ condlog(4, "multipath map %s removed", mapname);
+ return 0;
+ }
+ condlog(2, "failed to remove multipath map %s", mapname);
+ dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname);
+ if (queue_if_no_path)
+ s = dm_queue_if_no_path((char *)mapname, 1);
+ return 1;
+}
+
+extern int
dm_flush_maps (void)
{
int r = 0;
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);
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)
{
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)
{
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;
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;
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);
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 */
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;
}
static int
+def_getuid_callout_handler(vector strvec)
+{
+ conf->getuid = set_value(strvec);
+
+ if (!conf->getuid)
+ return 1;
+
+ return 0;
+}
+
+static int
def_prio_handler(vector strvec)
{
conf->prio_name = set_value(strvec);
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;
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;
static int
def_pg_timeout_handler(vector strvec)
{
- int pg_timeout;
char * buff;
buff = set_value(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;
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
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
*/
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;
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;
}
static int
+ble_property_handler(vector strvec)
+{
+ char * buff;
+
+ buff = set_value(strvec);
+
+ if (!buff)
+ return 1;
+
+ return store_ble(conf->blist_property, buff, ORIGIN_CONFIG);
+}
+
+static int
+ble_except_property_handler(vector strvec)
+{
+ char * buff;
+
+ buff = set_value(strvec);
+
+ if (!buff)
+ return 1;
+
+ return store_ble(conf->elist_property, buff, ORIGIN_CONFIG);
+}
+
+static int
ble_device_handler(vector strvec)
{
return alloc_ble_device(conf->blist_device);
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;
}
static int
+hw_getuid_callout_handler(vector strvec)
+{
+ struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+
+ hwe->getuid = set_value(strvec);
+
+ if (!hwe->getuid)
+ return 1;
+
+ return 0;
+}
+
+static int
hw_selector_handler(vector strvec)
{
struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
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;
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
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
*/
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;
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
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;
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
{
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);
}
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;
}
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);
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;
}
!strcmp(mpe->features, conf->features))
return 0;
- return snprintf(buff, len, "%s", mpe->features);
+ return snprintf(buff, len, "\"%s\"", mpe->features);
}
static int
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;
}
if (!mpe->prio_name)
return 0;
- return snprintf(buff, len, "%s", mpe->prio_name);
+ return snprintf(buff, len, "\"%s\"", mpe->prio_name);
}
static int
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
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);
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);
}
}
static int
+snprint_hw_getuid_callout (char * buff, int len, void * data)
+{
+ struct hwentry * hwe = (struct hwentry *)data;
+
+ if (!hwe->getuid)
+ return 0;
+
+ return snprintf(buff, len, "\"%s\"", hwe->getuid);
+}
+
+static int
snprint_hw_prio (char * buff, int len, void * data)
{
struct hwentry * hwe = (struct hwentry *)data;
if (!hwe->prio_name)
return 0;
- return snprintf(buff, len, "%s", hwe->prio_name);
+ return snprintf(buff, len, "\"%s\"", hwe->prio_name);
}
static int
get_pgpolicy_name(str, POLICY_NAME_SIZE, hwe->pgpolicy);
- return snprintf(buff, len, "%s", str);
+ return snprintf(buff, len, "\"%s\"", str);
}
static int
{
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);
}
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;
}
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);
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;
}
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;
}
if (!hwe->checker_name)
return 0;
- return snprintf(buff, len, "%s", hwe->checker_name);
+ return snprintf(buff, len, "\"%s\"", hwe->checker_name);
}
static int
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
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);
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);
}
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");
}
get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy);
- return snprintf(buff, len, "%s", str);
+ return snprintf(buff, len, "\"%s\"", str);
}
static int
}
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
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);
}
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
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;
}
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);
{
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;
}
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;
}
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;
}
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
{
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
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
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);
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);
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);
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);
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);
#include "structs.h"
#include "config.h"
#include "blacklist.h"
+#include "callout.h"
#include "debug.h"
#include "propsel.h"
#include "sg_include.h"
#include "prio.h"
#include "defaults.h"
-struct path *
+int
store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice,
- int flag)
+ int flag, struct path **pp_ptr)
{
+ int err = 1;
struct path * pp;
const char * devname;
+ if (pp_ptr)
+ *pp_ptr = NULL;
+
devname = udev_device_get_sysname(udevice);
if (!devname)
- return NULL;
+ return 1;
pp = alloc_path();
if (!pp)
- return NULL;
+ return 1;
if(safe_sprintf(pp->dev, "%s", devname)) {
condlog(0, "pp->dev too small");
goto out;
}
pp->udev = udev_device_ref(udevice);
- if (pathinfo(pp, hwtable, flag))
+ err = pathinfo(pp, hwtable,
+ (conf->dry_run == 3)? flag : (flag | DI_BLACKLIST));
+ if (err)
goto out;
- if (store_path(pathvec, pp))
+ err = store_path(pathvec, pp);
+ if (err)
goto out;
- return pp;
out:
- free_path(pp);
- return NULL;
+ if (err)
+ free_path(pp);
+ else if (pp_ptr)
+ *pp_ptr = pp;
+ return err;
}
static int
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);
}
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);
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);
}
#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
unsigned int t;
if (!pp->udev || pp->bus != SYSFS_BUS_SCSI)
- return 1;
+ return -ENOSYS;
parent = pp->udev;
while (parent) {
}
if (!attr) {
condlog(3, "%s: No timeout value in sysfs", pp->dev);
- return 1;
+ return -ENXIO;
}
r = sscanf(attr, "%u\n", &t);
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;
}
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)
/* 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;
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)
{
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;
}
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;
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);
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);
}
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)) {
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;
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);
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;
}
{
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;
return PATH_DOWN;
}
-extern int
+int
sysfs_pathinfo(struct path * pp)
{
if (common_sysfs_pathinfo(pp))
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;
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;
}
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) {
}
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--;
*c = '\0';
c--;
}
- condlog(3, "%s: uid = %s (udev)", pp->dev,
- *pp->wwid == '\0' ? "<empty>" : pp->wwid);
+ condlog(3, "%s: uid = %s (%s)", pp->dev,
+ *pp->wwid == '\0' ? "<empty>" : pp->wwid, origin);
return 0;
}
{
int path_state;
+ if (!pp)
+ return 1;
+
condlog(3, "%s: mask = 0x%x", pp->dev, 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",
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;
}
}
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:
#endif
#ifndef DEF_TIMEOUT
-#define DEF_TIMEOUT 300000
+#define DEF_TIMEOUT 30
#endif
/*
struct config;
-int sysfs_get_dev (struct udev_device *udev, char * buff, size_t len);
+ssize_t sysfs_get_dev (struct udev_device *udev, char * buff, size_t len);
int path_discovery (vector pathvec, struct config * conf, int flag);
int do_tur (char *);
int path_offline (struct path *);
int get_state (struct path * pp, int daemon);
int pathinfo (struct path *, vector hwtable, int mask);
-struct path * store_pathinfo (vector pathvec, vector hwtable,
- struct udev_device *udevice, int flag);
+int store_pathinfo (vector pathvec, vector hwtable,
+ struct udev_device *udevice, int flag,
+ struct path **pp_ptr);
int sysfs_set_scsi_tmo (struct multipath *mpp);
int sysfs_get_timeout(struct path *pp, unsigned int *timeout);
__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)
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;
} 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,
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;
&& 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",
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
num_pg_args = atoi(word);
if (merge_words(&mpp->selector, word, 1)) {
- FREE(word);
goto out1;
}
FREE(word);
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);
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;
}
.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,
.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,
.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,
.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,
.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,
.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,
.product = "MSA VOLUME",
.features = DEFAULT_FEATURES,
.hwhandler = DEFAULT_HWHANDLER,
- .selector = DEFAULT_SELECTOR,
.pgpolicy = GROUP_BY_PRIO,
.pgfailback = -FAILBACK_IMMEDIATE,
.rr_weight = RR_WEIGHT_NONE,
{
/* 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,
.product = "MSA2[02]12fc|MSA2012i",
.features = DEFAULT_FEATURES,
.hwhandler = DEFAULT_HWHANDLER,
- .selector = DEFAULT_SELECTOR,
.pgpolicy = MULTIBUS,
.pgfailback = -FAILBACK_IMMEDIATE,
.rr_weight = RR_WEIGHT_NONE,
.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,
.product = "HSVX700",
.features = DEFAULT_FEATURES,
.hwhandler = "1 alua",
- .selector = DEFAULT_SELECTOR,
.pgpolicy = GROUP_BY_PRIO,
.pgfailback = -FAILBACK_IMMEDIATE,
.rr_weight = RR_WEIGHT_NONE,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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,
.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",
.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
*/
#include <pthread.h>
-#include <signal.h>
#include "lock.h"
#include <stdio.h>
-void block_signal (int signum, sigset_t *old)
-{
- sigset_t set;
- sigemptyset(&set);
- sigaddset(&set, signum);
- pthread_sigmask(SIG_BLOCK, &set, old);
-}
-
void cleanup_lock (void * data)
{
unlock ((*(struct mutex_lock *)data));
#endif
void cleanup_lock (void * data);
-void block_signal(int signum, sigset_t *old);
#endif /* _LOCK_H */
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
+#include <time.h>
#include "memory.h"
#include "log.h"
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;
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 *);
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <syslog.h>
#include <pthread.h>
#include <sys/mman.h>
#include "log.h"
#include "lock.h"
+pthread_t log_thr;
+
+pthread_mutex_t logq_lock;
+pthread_mutex_t logev_lock;
+pthread_cond_t logev_cond;
+
+int logq_running;
+
void log_safe (int prio, const char * fmt, va_list ap)
{
- sigset_t old;
-
- block_signal(SIGUSR1, &old);
- block_signal(SIGHUP, NULL);
+ if (log_thr == (pthread_t)0) {
+ syslog(prio, fmt, ap);
+ return;
+ }
- pthread_mutex_lock(logq_lock);
+ pthread_mutex_lock(&logq_lock);
log_enqueue(prio, fmt, ap);
- pthread_mutex_unlock(logq_lock);
+ pthread_mutex_unlock(&logq_lock);
+
+ pthread_mutex_lock(&logev_lock);
+ pthread_cond_signal(&logev_cond);
+ pthread_mutex_unlock(&logev_lock);
+}
- pthread_mutex_lock(logev_lock);
- pthread_cond_signal(logev_cond);
- pthread_mutex_unlock(logev_lock);
+void log_thread_flush (void)
+{
+ int empty;
- pthread_sigmask(SIG_SETMASK, &old, NULL);
+ do {
+ pthread_mutex_lock(&logq_lock);
+ empty = log_dequeue(la->buff);
+ pthread_mutex_unlock(&logq_lock);
+ if (!empty)
+ log_syslog(la->buff);
+ } while (empty == 0);
}
static void flush_logqueue (void)
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);
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;
}
{
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;
}
{
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();
}
+
#include <pthread.h>
-pthread_t log_thr;
+extern pthread_t log_thr;
-pthread_mutex_t *logq_lock;
-pthread_mutex_t *logev_lock;
-pthread_cond_t *logev_cond;
+extern pthread_mutex_t logq_lock;
+extern pthread_mutex_t logev_lock;
+extern pthread_cond_t logev_cond;
+
+extern int logq_running;
void log_safe(int prio, const char * fmt, va_list ap);
void log_thread_start(pthread_attr_t *attr);
void log_thread_stop(void);
+void log_thread_flush(void);
#endif /* _LOG_PTHREAD_H */
buf = (char *) MALLOC(MAXBUF);
- if (!buf)
+ if (!buf) {
+ vector_free(elements);
return NULL;
+ }
while (read_line(buf, MAXBUF)) {
vec = alloc_strvec(buf);
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);
FREE(dup);
out:
FREE(buf);
+ vector_free(elements);
return NULL;
}
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
+#include <string.h>
+#include <errno.h>
#include "checkers.h"
#include "vector.h"
#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)
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");
}
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.
* 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
}
static int
+snprint_path_mpp (char * buff, size_t len, struct path * pp)
+{
+ if (!pp->mpp)
+ return snprintf(buff, len, "[orphan]");
+ if (!pp->mpp->alias)
+ return snprintf(buff, len, "[unknown]");
+ return snprint_str(buff, len, pp->mpp->alias);
+}
+
+static int
snprint_path_checker (char * buff, size_t len, struct path * pp)
{
struct checker * c = &pp->checker;
{'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}
};
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);
}
if ((len - fwd - threshold) <= 0)
return len;
+ fwd += snprintf(buff + fwd, len - fwd, "udev property rules:\n"
+ "- blacklist:\n");
+ if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_property))
+ return len;
+
+ if ((len - fwd - threshold) <= 0)
+ return len;
+ fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
+ if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_property) == 0)
+ return len;
+
+ if ((len - fwd - threshold) <= 0)
+ return len;
fwd += snprintf(buff + fwd, len - fwd, "wwid rules:\n"
"- blacklist:\n");
if (snprint_blacklist_group(buff, len, &fwd, &conf->blist_wwid) == 0)
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;
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;
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) {
{
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;
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) {
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:
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;
{
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);
+}
*/
#include "checkers.h"
#include "vector.h"
-#include "structs.h"
+
+/* forward declaration to avoid circular dependency */
+struct path;
+
#include "list.h"
#include "memory.h"
struct prio {
void *handle;
+ int refcount;
struct list_head node;
char name[PRIO_NAME_LEN];
char args[PRIO_ARGS_LEN];
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 *);
#include <debug.h>
#include <prio.h>
+#include <structs.h>
#include "alua.h"
default:
rc = 0;
}
- if (priopath)
+ if (priopath && aas != AAS_OPTIMIZED)
rc += 80;
} else {
switch(-rc) {
#include <sg_include.h>
#include <debug.h>
#include <prio.h>
+#include <structs.h>
#define INQ_REPLY_LEN 255
#define INQ_CMD_CODE 0x12
#include <sg_include.h>
#include <debug.h>
#include <prio.h>
+#include <structs.h>
#define INQUIRY_CMD 0x12
#define INQUIRY_CMDLEN 6
#include <sg_include.h>
#include <debug.h>
#include <prio.h>
+#include <structs.h>
#define INQ_REPLY_LEN 255
#define INQ_CMD_CODE 0x12
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];
}
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);
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);
#include <sg_include.h>
#include <debug.h>
#include <prio.h>
+#include <structs.h>
#define TUR_CMD_LEN 6
#define SCSI_CHECK_CONDITION 0x2
#include <prio.h>
#include <debug.h>
#include <unistd.h>
+#include <structs.h>
//
// This prioritizer suits iSCSI needs, makes it possible to prefer one path.
#include <sg_include.h>
#include <debug.h>
#include <prio.h>
+#include <structs.h>
#define INQUIRY_CMD 0x12
#define INQUIRY_CMDLEN 6
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);
#include <sg_include.h>
#include <debug.h>
#include <prio.h>
+#include <structs.h>
#define INQUIRY_CMD 0x12
#define INQUIRY_CMDLEN 6
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);
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);
strcpy(path, pp->dev);
} else {
condlog(0, "%s: %s - Invalid arguments", pp->dev,
- pp->prio->name);
+ pp->prio.name);
return priority;
}
#include "devmapper.h"
#include "prio.h"
#include "discovery.h"
+#include "prioritizers/alua_rtpg.h"
#include <inttypes.h>
pgpolicyfn *pgpolicies[] = {
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);
}
} 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)",
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 {
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)",
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;
}
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;
}
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;
}
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;
+}
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);
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);
+++ /dev/null
-/* Extended regular expression matching and search library,
- version 0.12.
- (Implements POSIX draft P10003.2/D11.2, except for
- internationalization features.)
-
- Copyright (C) 1993 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <sys/types.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifndef bcmp
-#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
-#endif
-#ifndef bcopy
-#define bcopy(s, d, n) memcpy ((d), (s), (n))
-#endif
-#ifndef bzero
-#define bzero(s, n) memset ((s), 0, (n))
-#endif
-
-/* Define the syntax stuff for \<, \>, etc. */
-
-#ifndef Sword
-#define Sword 1
-#endif
-
-#define CHAR_SET_SIZE 256
-
-static char re_syntax_table[CHAR_SET_SIZE];
-
-static void init_syntax_once(void)
-{
- register int c;
- static int done = 0;
-
- if (done)
- return;
-
- bzero(re_syntax_table, sizeof re_syntax_table);
-
- for (c = 'a'; c <= 'z'; c++)
- re_syntax_table[c] = Sword;
-
- for (c = 'A'; c <= 'Z'; c++)
- re_syntax_table[c] = Sword;
-
- for (c = '0'; c <= '9'; c++)
- re_syntax_table[c] = Sword;
-
- re_syntax_table['_'] = Sword;
-
- done = 1;
-}
-
-#define SYNTAX(c) re_syntax_table[c]
-
-#include "regex.h"
-#include <ctype.h>
-
-#ifdef isblank
-#define ISBLANK(c) (isascii (c) && isblank (c))
-#else
-#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
-#endif
-#ifdef isgraph
-#define ISGRAPH(c) (isascii (c) && isgraph (c))
-#else
-#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
-#endif
-
-#define ISPRINT(c) (isascii (c) && isprint (c))
-#define ISDIGIT(c) (isascii (c) && isdigit (c))
-#define ISALNUM(c) (isascii (c) && isalnum (c))
-#define ISALPHA(c) (isascii (c) && isalpha (c))
-#define ISCNTRL(c) (isascii (c) && iscntrl (c))
-#define ISLOWER(c) (isascii (c) && islower (c))
-#define ISPUNCT(c) (isascii (c) && ispunct (c))
-#define ISSPACE(c) (isascii (c) && isspace (c))
-#define ISUPPER(c) (isascii (c) && isupper (c))
-#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
-
-#undef SIGN_EXTEND_CHAR
-#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
-
-#ifndef alloca
-#ifdef __GNUC__
-#define alloca __builtin_alloca
-#endif /* not __GNUC__ */
-#endif /* not alloca */
-
-#define REGEX_ALLOCATE alloca
-
-/* Assumes a `char *destination' variable. */
-#define REGEX_REALLOCATE(source, osize, nsize) \
- (destination = (char *) alloca (nsize), \
- bcopy (source, destination, osize), \
- destination)
-
-/* True if `size1' is non-NULL and PTR is pointing anywhere inside
- `string1' or just past its end. This works if PTR is NULL, which is
- a good thing. */
-#define FIRST_STRING_P(ptr) \
- (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
-
-/* (Re)Allocate N items of type T using malloc, or fail. */
-#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
-#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
-#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
-
-#define BYTEWIDTH 8 /* In bits. */
-
-#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
-
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-typedef char boolean;
-#define false 0
-#define true 1
-
-typedef enum {
- no_op = 0,
- exactn = 1,
- anychar,
- charset,
- charset_not,
- start_memory,
- stop_memory,
- duplicate,
- begline,
- endline,
- begbuf,
- endbuf,
- jump,
- jump_past_alt,
- on_failure_jump,
- on_failure_keep_string_jump,
- pop_failure_jump,
- maybe_pop_jump,
- dummy_failure_jump,
- push_dummy_failure,
- succeed_n,
- jump_n,
- set_number_at,
- wordchar,
- notwordchar,
- wordbeg,
- wordend,
- wordbound,
- notwordbound
-} re_opcode_t;
-
-#define STORE_NUMBER(destination, number) \
- do { \
- (destination)[0] = (number) & 0377; \
- (destination)[1] = (number) >> 8; \
- } while (0)
-
-#define STORE_NUMBER_AND_INCR(destination, number) \
- do { \
- STORE_NUMBER (destination, number); \
- (destination) += 2; \
- } while (0)
-
-#define EXTRACT_NUMBER(destination, source) \
- do { \
- (destination) = *(source) & 0377; \
- (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \
- } while (0)
-
-#define EXTRACT_NUMBER_AND_INCR(destination, source) \
- do { \
- EXTRACT_NUMBER (destination, source); \
- (source) += 2; \
- } while (0)
-
-#undef assert
-#define assert(e)
-
-#define DEBUG_STATEMENT(e)
-#define DEBUG_PRINT1(x)
-#define DEBUG_PRINT2(x1, x2)
-#define DEBUG_PRINT3(x1, x2, x3)
-#define DEBUG_PRINT4(x1, x2, x3, x4)
-#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
-#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
-
-reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
-reg_syntax_t re_set_syntax(syntax)
-reg_syntax_t syntax;
-{
- reg_syntax_t ret = re_syntax_options;
-
- re_syntax_options = syntax;
- return ret;
-}
-
-/* This table gives an error message for each of the error codes listed
- in regex.h. Obviously the order here has to be same as there. */
-
-static const char *re_error_msg[] = { NULL, /* REG_NOERROR */
- "No match", /* REG_NOMATCH */
- "Invalid regular expression", /* REG_BADPAT */
- "Invalid collation character", /* REG_ECOLLATE */
- "Invalid character class name", /* REG_ECTYPE */
- "Trailing backslash", /* REG_EESCAPE */
- "Invalid back reference", /* REG_ESUBREG */
- "Unmatched [ or [^", /* REG_EBRACK */
- "Unmatched ( or \\(", /* REG_EPAREN */
- "Unmatched \\{", /* REG_EBRACE */
- "Invalid content of \\{\\}", /* REG_BADBR */
- "Invalid range end", /* REG_ERANGE */
- "Memory exhausted", /* REG_ESPACE */
- "Invalid preceding regular expression", /* REG_BADRPT */
- "Premature end of regular expression", /* REG_EEND */
- "Regular expression too big", /* REG_ESIZE */
- "Unmatched ) or \\)", /* REG_ERPAREN */
-};
-
-/* Subroutine declarations and macros for regex_compile. */
-
-static reg_errcode_t regex_compile (const char *pattern, size_t size,
- reg_syntax_t syntax,
- struct re_pattern_buffer * bufp);
-
-static void store_op1 (re_opcode_t op, unsigned char *loc, int arg);
-
-static void store_op2 (re_opcode_t op, unsigned char *loc, int arg1, int arg2);
-
-static void insert_op1 (re_opcode_t op, unsigned char *loc, int arg,
- unsigned char *end);
-
-static void insert_op2 (re_opcode_t op, unsigned char *loc, int arg1, int arg2,
- unsigned char *end);
-
-static boolean at_begline_loc_p (const char *pattern, const char *p,
- reg_syntax_t syntax);
-
-static boolean at_endline_loc_p (const char *p, const char *pend,
- reg_syntax_t syntax);
-
-static reg_errcode_t compile_range (const char **p_ptr, const char *pend,
- char *translate, reg_syntax_t syntax,
- unsigned char *b);
-
-/* Fetch the next character in the uncompiled pattern---translating it
- if necessary. Also cast from a signed character in the constant
- string passed to us by the user to an unsigned char that we can use
- as an array index (in, e.g., `translate'). */
-#define PATFETCH(c) \
- do {if (p == pend) return REG_EEND; \
- c = (unsigned char) *p++; \
- if (translate) c = translate[c]; \
- } while (0)
-
-/* Fetch the next character in the uncompiled pattern, with no
- translation. */
-#define PATFETCH_RAW(c) \
- do {if (p == pend) return REG_EEND; \
- c = (unsigned char) *p++; \
- } while (0)
-
-/* Go backwards one character in the pattern. */
-#define PATUNFETCH p--
-
-
-/* If `translate' is non-null, return translate[D], else just D. We
- cast the subscript to translate because some data is declared as
- `char *', to avoid warnings when a string constant is passed. But
- when we use a character as a subscript we must make it unsigned. */
-#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
-
-
-/* Macros for outputting the compiled pattern into `buffer'. */
-
-/* If the buffer isn't allocated when it comes in, use this. */
-#define INIT_BUF_SIZE 32
-
-/* Make sure we have at least N more bytes of space in buffer. */
-#define GET_BUFFER_SPACE(n) \
- while (b - bufp->buffer + (n) > bufp->allocated) \
- EXTEND_BUFFER ()
-
-/* Make sure we have one more byte of buffer space and then add C to it. */
-#define BUF_PUSH(c) \
- do { \
- GET_BUFFER_SPACE (1); \
- *b++ = (unsigned char) (c); \
- } while (0)
-
-
-/* Ensure we have two more bytes of buffer space and then append C1 and C2. */
-#define BUF_PUSH_2(c1, c2) \
- do { \
- GET_BUFFER_SPACE (2); \
- *b++ = (unsigned char) (c1); \
- *b++ = (unsigned char) (c2); \
- } while (0)
-
-
-/* As with BUF_PUSH_2, except for three bytes. */
-#define BUF_PUSH_3(c1, c2, c3) \
- do { \
- GET_BUFFER_SPACE (3); \
- *b++ = (unsigned char) (c1); \
- *b++ = (unsigned char) (c2); \
- *b++ = (unsigned char) (c3); \
- } while (0)
-
-
-/* Store a jump with opcode OP at LOC to location TO. We store a
- relative address offset by the three bytes the jump itself occupies. */
-#define STORE_JUMP(op, loc, to) \
- store_op1 (op, loc, (int)((to) - (loc) - 3))
-
-/* Likewise, for a two-argument jump. */
-#define STORE_JUMP2(op, loc, to, arg) \
- store_op2 (op, loc, (int)((to) - (loc) - 3), arg)
-
-/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */
-#define INSERT_JUMP(op, loc, to) \
- insert_op1 (op, loc, (int)((to) - (loc) - 3), b)
-
-/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */
-#define INSERT_JUMP2(op, loc, to, arg) \
- insert_op2 (op, loc, (int)((to) - (loc) - 3), arg, b)
-
-
-/* This is not an arbitrary limit: the arguments which represent offsets
- into the pattern are two bytes long. So if 2^16 bytes turns out to
- be too small, many things would have to change. */
-#define MAX_BUF_SIZE (1L << 16)
-#define REALLOC realloc
-
-/* Extend the buffer by twice its current size via realloc and
- reset the pointers that pointed into the old block to point to the
- correct places in the new one. If extending the buffer results in it
- being larger than MAX_BUF_SIZE, then flag memory exhausted. */
-#define EXTEND_BUFFER() \
- do { \
- unsigned char *old_buffer = bufp->buffer; \
- if (bufp->allocated == MAX_BUF_SIZE) \
- return REG_ESIZE; \
- bufp->allocated <<= 1; \
- if (bufp->allocated > MAX_BUF_SIZE) \
- bufp->allocated = MAX_BUF_SIZE; \
- bufp->buffer = (unsigned char *) REALLOC(bufp->buffer, bufp->allocated);\
- if (bufp->buffer == NULL) \
- return REG_ESPACE; \
- /* If the buffer moved, move all the pointers into it. */ \
- if (old_buffer != bufp->buffer) \
- { \
- b = (b - old_buffer) + bufp->buffer; \
- begalt = (begalt - old_buffer) + bufp->buffer; \
- if (fixup_alt_jump) \
- fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
- if (laststart) \
- laststart = (laststart - old_buffer) + bufp->buffer; \
- if (pending_exact) \
- pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
- } \
- } while (0)
-
-
-/* Since we have one byte reserved for the register number argument to
- {start,stop}_memory, the maximum number of groups we can report
- things about is what fits in that byte. */
-#define MAX_REGNUM 255
-
-/* But patterns can have more than `MAX_REGNUM' registers. We just
- ignore the excess. */
-typedef unsigned regnum_t;
-
-
-/* Macros for the compile stack. */
-
-/* Since offsets can go either forwards or backwards, this type needs to
- be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */
-/* int may be not enough when sizeof(int) == 2 */
-typedef long pattern_offset_t;
-
-typedef struct {
- pattern_offset_t begalt_offset;
- pattern_offset_t fixup_alt_jump;
- pattern_offset_t inner_group_offset;
- pattern_offset_t laststart_offset;
- regnum_t regnum;
-} compile_stack_elt_t;
-
-
-typedef struct {
- compile_stack_elt_t *stack;
- unsigned size;
- unsigned avail; /* Offset of next open position. */
-} compile_stack_type;
-
-
-#define INIT_COMPILE_STACK_SIZE 32
-
-#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
-#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
-
-/* The next available element. */
-#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
-
-
-/* Set the bit for character C in a list. */
-#define SET_LIST_BIT(c) \
- (b[((unsigned char) (c)) / BYTEWIDTH] \
- |= 1 << (((unsigned char) c) % BYTEWIDTH))
-
-
-/* Get the next unsigned number in the uncompiled pattern. */
-#define GET_UNSIGNED_NUMBER(num) \
- { if (p != pend) \
- { \
- PATFETCH (c); \
- while (ISDIGIT (c)) \
- { \
- if (num < 0) \
- num = 0; \
- num = num * 10 + c - '0'; \
- if (p == pend) \
- break; \
- PATFETCH (c); \
- } \
- } \
- }
-
-#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
-
-#define IS_CHAR_CLASS(string) \
- (STREQ (string, "alpha") || STREQ (string, "upper") \
- || STREQ (string, "lower") || STREQ (string, "digit") \
- || STREQ (string, "alnum") || STREQ (string, "xdigit") \
- || STREQ (string, "space") || STREQ (string, "print") \
- || STREQ (string, "punct") || STREQ (string, "graph") \
- || STREQ (string, "cntrl") || STREQ (string, "blank"))
-
-static boolean group_in_compile_stack (compile_stack_type
- compile_stack, regnum_t regnum);
-
-/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
- Returns one of error codes defined in `regex.h', or zero for success */
-
-static reg_errcode_t regex_compile(pattern, size, syntax, bufp)
-const char *pattern;
-size_t size;
-reg_syntax_t syntax;
-struct re_pattern_buffer *bufp;
-{
- /* We fetch characters from PATTERN here. Even though PATTERN is
- `char *' (i.e., signed), we declare these variables as unsigned, so
- they can be reliably used as array indices. */
- register unsigned char c, c1;
-
- /* A random tempory spot in PATTERN. */
- const char *p1;
-
- /* Points to the end of the buffer, where we should append. */
- register unsigned char *b;
-
- /* Keeps track of unclosed groups. */
- compile_stack_type compile_stack;
-
- /* Points to the current (ending) position in the pattern. */
- const char *p = pattern;
- const char *pend = pattern + size;
-
- /* How to translate the characters in the pattern. */
- char *translate = bufp->translate;
-
- /* Address of the count-byte of the most recently inserted `exactn'
- command. This makes it possible to tell if a new exact-match
- character can be added to that command or if the character requires
- a new `exactn' command. */
- unsigned char *pending_exact = 0;
-
- /* Address of start of the most recently finished expression.
- This tells, e.g., postfix * where to find the start of its
- operand. Reset at the beginning of groups and alternatives. */
- unsigned char *laststart = 0;
-
- /* Address of beginning of regexp, or inside of last group. */
- unsigned char *begalt;
-
- /* Place in the uncompiled pattern (i.e., the {) to
- which to go back if the interval is invalid. */
- const char *beg_interval;
-
- /* Address of the place where a forward jump should go to the end of
- the containing expression. Each alternative of an `or' -- except the
- last -- ends with a forward jump of this sort. */
- unsigned char *fixup_alt_jump = 0;
-
- /* Counts open-groups as they are encountered. Remembered for the
- matching close-group on the compile stack, so the same register
- number is put in the stop_memory as the start_memory. */
- regnum_t regnum = 0;
-
- /* Initialize the compile stack. */
- compile_stack.stack =
- TALLOC(INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
- if (compile_stack.stack == NULL)
- return REG_ESPACE;
-
- compile_stack.size = INIT_COMPILE_STACK_SIZE;
- compile_stack.avail = 0;
-
- /* Initialize the pattern buffer. */
- bufp->syntax = syntax;
- bufp->fastmap_accurate = 0;
- bufp->not_bol = bufp->not_eol = 0;
-
- /* Set `used' to zero, so that if we return an error, the pattern
- printer (for debugging) will think there's no pattern. We reset it
- at the end. */
- bufp->used = 0;
-
- /* Always count groups, whether or not bufp->no_sub is set. */
- bufp->re_nsub = 0;
-
- /* Initialize the syntax table. */
- init_syntax_once();
-
- if (bufp->allocated == 0) {
- if (bufp->buffer) {
- RETALLOC(bufp->buffer, INIT_BUF_SIZE,
- unsigned char);
- } else { /* Caller did not allocate a buffer. Do it for them. */
- bufp->buffer =
- TALLOC(INIT_BUF_SIZE, unsigned char);
- }
- if (!bufp->buffer)
- return REG_ESPACE;
-
- bufp->allocated = INIT_BUF_SIZE;
- }
-
- begalt = b = bufp->buffer;
-
- /* Loop through the uncompiled pattern until we're at the end. */
- while (p != pend) {
- PATFETCH(c);
-
- switch (c) {
- case '^':
- {
- if (p == pattern + 1 ||
- syntax & RE_CONTEXT_INDEP_ANCHORS ||
- at_begline_loc_p(pattern, p, syntax))
- BUF_PUSH(begline);
- else
- goto normal_char;
- }
- break;
-
- case '$':
- {
- if (p == pend ||
- syntax & RE_CONTEXT_INDEP_ANCHORS ||
- at_endline_loc_p(p, pend, syntax))
- BUF_PUSH(endline);
- else
- goto normal_char;
- }
- break;
-
- case '+':
-
- case '?':
- if ((syntax & RE_BK_PLUS_QM) ||
- (syntax & RE_LIMITED_OPS))
- goto normal_char;
- handle_plus:
-
- case '*':
- /* If there is no previous pattern... */
- if (!laststart) {
- if (syntax & RE_CONTEXT_INVALID_OPS)
- return REG_BADRPT;
- else if (!(syntax & RE_CONTEXT_INDEP_OPS))
- goto normal_char;
- }
-
- {
- /* Are we optimizing this jump? */
- boolean keep_string_p = false;
-
- /* 1 means zero (many) matches is allowed. */
- char zero_times_ok = 0, many_times_ok = 0;
-
- for (;;) {
- zero_times_ok |= c != '+';
- many_times_ok |= c != '?';
-
- if (p == pend)
- break;
-
- PATFETCH(c);
-
- if (c == '*' || (!(syntax & RE_BK_PLUS_QM) &&
- (c == '+' || c == '?')));
-
- else if (syntax & RE_BK_PLUS_QM && c == '\\') {
- if (p == pend)
- return REG_EESCAPE;
-
- PATFETCH(c1);
- if (!(c1 == '+' || c1 == '?')) {
- PATUNFETCH;
- PATUNFETCH;
- break;
- }
-
- c = c1;
- } else {
- PATUNFETCH;
- break;
- }
- }
-
- if (!laststart)
- break;
-
- if (many_times_ok) {
- assert(p - 1 > pattern);
-
- /* Allocate the space for the jump. */
- GET_BUFFER_SPACE(3);
-
- if (TRANSLATE(*(p - 2)) == TRANSLATE('.') &&
- zero_times_ok && p < pend &&
- TRANSLATE(*p) == TRANSLATE('\n') &&
- !(syntax & RE_DOT_NEWLINE)) {
- /* We have .*\n. */
- STORE_JUMP(jump, b, laststart);
- keep_string_p = true;
- } else
- STORE_JUMP(maybe_pop_jump, b,
- laststart - 3);
-
- b += 3;
- }
-
- GET_BUFFER_SPACE(3);
- INSERT_JUMP(keep_string_p ?
- on_failure_keep_string_jump :
- on_failure_jump, laststart,
- b + 3);
- pending_exact = 0;
- b += 3;
-
- if (!zero_times_ok) {
- GET_BUFFER_SPACE(3);
- INSERT_JUMP(dummy_failure_jump,
- laststart,
- laststart + 6);
- b += 3;
- }
- }
- break;
-
-
- case '.':
- laststart = b;
- BUF_PUSH(anychar);
- break;
-
- case '[':
- {
- boolean had_char_class = false;
-
- if (p == pend)
- return REG_EBRACK;
-
- GET_BUFFER_SPACE(34);
-
- laststart = b;
-
- /* We test `*p == '^' twice, instead of using an if
- statement, so we only need one BUF_PUSH. */
- BUF_PUSH(*p == '^' ? charset_not : charset);
- if (*p == '^')
- p++;
-
- p1 = p;
-
- /* Push the number of bytes in the bitmap. */
- BUF_PUSH((1 << BYTEWIDTH) / BYTEWIDTH);
-
- /* Clear the whole map. */
- bzero(b, (1 << BYTEWIDTH) / BYTEWIDTH);
-
- if ((re_opcode_t) b[-2] == charset_not
- && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
- SET_LIST_BIT('\n');
-
- /* Read in characters and ranges, setting map bits. */
- for (;;) {
- if (p == pend)
- return REG_EBRACK;
-
- PATFETCH(c);
-
- if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) &&
- c == '\\') {
- if (p == pend)
- return REG_EESCAPE;
-
- PATFETCH(c1);
- SET_LIST_BIT(c1);
- continue;
- }
-
- if (c == ']' && p != p1 + 1)
- break;
-
- if (had_char_class && c == '-' && *p != ']')
- return REG_ERANGE;
-
- if (c == '-' && !(p - 2 >= pattern &&
- p[-2] == '[') && !(p - 3 >= pattern &&
- p[-3] == '[' && p[-2] == '^') &&
- *p != ']') {
- reg_errcode_t ret =
- compile_range(&p, pend, translate,
- syntax, b);
- if (ret != REG_NOERROR)
- return ret;
- }
-
- else if (p[0] == '-' && p[1] != ']') {
- reg_errcode_t ret;
-
- /* Move past the `-'. */
- PATFETCH(c1);
-
- ret = compile_range(&p, pend, translate,
- syntax, b);
- if (ret != REG_NOERROR)
- return ret;
- }
-
- else if (syntax & RE_CHAR_CLASSES &&
- c == '[' && *p == ':') {
- char str[CHAR_CLASS_MAX_LENGTH + 1];
-
- PATFETCH(c);
- c1 = 0;
-
- /* If pattern is `[[:'. */
- if (p == pend)
- return REG_EBRACK;
-
- for (;;) {
- PATFETCH(c);
- if (c == ':' || c == ']' ||
- p == pend || c1 ==
- CHAR_CLASS_MAX_LENGTH)
- break;
- str[c1++] = c;
- }
- str[c1] = '\0';
-
- if (c == ':' && *p == ']') {
- int ch;
- boolean is_alnum =
- STREQ(str, "alnum");
- boolean is_alpha =
- STREQ(str, "alpha");
- boolean is_blank =
- STREQ(str, "blank");
- boolean is_cntrl =
- STREQ(str, "cntrl");
- boolean is_digit =
- STREQ(str, "digit");
- boolean is_graph =
- STREQ(str, "graph");
- boolean is_lower =
- STREQ(str, "lower");
- boolean is_print =
- STREQ(str, "print");
- boolean is_punct =
- STREQ(str, "punct");
- boolean is_space =
- STREQ(str, "space");
- boolean is_upper =
- STREQ(str, "upper");
- boolean is_xdigit =
- STREQ(str, "xdigit");
-
- if (!IS_CHAR_CLASS(str))
- return REG_ECTYPE;
-
- PATFETCH(c);
-
- if (p == pend)
- return REG_EBRACK;
-
- for (ch = 0; ch < 1 <<
- BYTEWIDTH; ch++) {
- if ((is_alnum &&
- ISALNUM(ch)) ||
- (is_alpha &&
- ISALPHA(ch)) ||
- (is_blank &&
- ISBLANK(ch)) ||
- (is_cntrl &&
- ISCNTRL(ch)) ||
- (is_digit &&
- ISDIGIT(ch)) ||
- (is_graph &&
- ISGRAPH(ch)) ||
- (is_lower &&
- ISLOWER(ch)) ||
- (is_print &&
- ISPRINT(ch)) ||
- (is_punct &&
- ISPUNCT(ch)) ||
- (is_space &&
- ISSPACE(ch)) ||
- (is_upper &&
- ISUPPER(ch)) ||
- (is_xdigit &&
- ISXDIGIT(ch)))
- SET_LIST_BIT(ch);
- }
- had_char_class =
- true;
- } else {
- c1++;
- while (c1--)
- PATUNFETCH;
- SET_LIST_BIT('[');
- SET_LIST_BIT(':');
- had_char_class = false;
- }
- } else {
- had_char_class = false;
- SET_LIST_BIT(c);
- }
- }
-
- while ((int) b[-1] > 0
- && b[b[-1] - 1] == 0)
- b[-1]--;
- b += b[-1];
- }
- break;
-
- case '(':
- if (syntax & RE_NO_BK_PARENS)
- goto handle_open;
- else
- goto normal_char;
-
-
- case ')':
- if (syntax & RE_NO_BK_PARENS)
- goto handle_close;
- else
- goto normal_char;
-
-
- case '\n':
- if (syntax & RE_NEWLINE_ALT)
- goto handle_alt;
- else
- goto normal_char;
-
-
- case '|':
- if (syntax & RE_NO_BK_VBAR)
- goto handle_alt;
- else
- goto normal_char;
-
-
- case '{':
- if (syntax & RE_INTERVALS
- && syntax & RE_NO_BK_BRACES)
- goto handle_interval;
- else
- goto normal_char;
-
-
- case '\\':
- if (p == pend)
- return REG_EESCAPE;
-
- PATFETCH_RAW(c);
-
- switch (c) {
- case '(':
- if (syntax & RE_NO_BK_PARENS)
- goto normal_backslash;
-
- handle_open:
- bufp->re_nsub++;
- regnum++;
-
- if (COMPILE_STACK_FULL) {
- RETALLOC(compile_stack.stack,
- compile_stack.size << 1,
- compile_stack_elt_t);
- if (compile_stack.stack == NULL)
- return REG_ESPACE;
-
- compile_stack.size <<= 1;
- }
-
- COMPILE_STACK_TOP.begalt_offset =
- begalt - bufp->buffer;
- COMPILE_STACK_TOP.fixup_alt_jump =
- fixup_alt_jump ? fixup_alt_jump -
- bufp->buffer + 1 : 0;
- COMPILE_STACK_TOP.laststart_offset =
- b - bufp->buffer;
- COMPILE_STACK_TOP.regnum = regnum;
-
- if (regnum <= MAX_REGNUM) {
- COMPILE_STACK_TOP.inner_group_offset =
- b - bufp->buffer + 2;
- BUF_PUSH_3(start_memory, regnum, 0);
- }
-
- compile_stack.avail++;
-
- fixup_alt_jump = 0;
- laststart = 0;
- begalt = b;
- pending_exact = 0;
- break;
-
- case ')':
- if (syntax & RE_NO_BK_PARENS)
- goto normal_backslash;
-
- if (COMPILE_STACK_EMPTY) {
- if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
- goto normal_backslash;
- else
- return REG_ERPAREN;
- }
-
- handle_close:
- if (fixup_alt_jump) {
- BUF_PUSH(push_dummy_failure);
- STORE_JUMP(jump_past_alt,
- fixup_alt_jump, b - 1);
- }
-
- if (COMPILE_STACK_EMPTY) {
- if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
- goto normal_char;
- else
- return REG_ERPAREN;
- }
-
- assert(compile_stack.avail != 0);
- {
- regnum_t this_group_regnum;
-
- compile_stack.avail--;
- begalt = bufp->buffer +
- COMPILE_STACK_TOP.begalt_offset;
- fixup_alt_jump =
- COMPILE_STACK_TOP.fixup_alt_jump ?
- bufp->buffer + COMPILE_STACK_TOP.
- fixup_alt_jump - 1 : 0;
- laststart = bufp->buffer +
- COMPILE_STACK_TOP.laststart_offset;
- this_group_regnum = COMPILE_STACK_TOP.regnum;
- pending_exact = 0;
-
- if (this_group_regnum <= MAX_REGNUM) {
- unsigned char
- *inner_group_loc = bufp->buffer +
- COMPILE_STACK_TOP.
- inner_group_offset;
-
- *inner_group_loc = regnum -
- this_group_regnum;
- BUF_PUSH_3(stop_memory,
- this_group_regnum,
- regnum - this_group_regnum);
- }
- }
- break;
-
-
- case '|': /* `\|'. */
- if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
- goto normal_backslash;
- handle_alt:
- if (syntax & RE_LIMITED_OPS)
- goto normal_char;
-
- GET_BUFFER_SPACE(3);
- INSERT_JUMP(on_failure_jump, begalt, b + 6);
- pending_exact = 0;
- b += 3;
-
- if (fixup_alt_jump)
- STORE_JUMP(jump_past_alt, fixup_alt_jump, b);
-
- fixup_alt_jump = b;
- GET_BUFFER_SPACE(3);
- b += 3;
-
- laststart = 0;
- begalt = b;
- break;
-
-
- case '{':
- /* If \{ is a literal. */
- if (!(syntax & RE_INTERVALS) || ((syntax & RE_INTERVALS)
- && (syntax & RE_NO_BK_BRACES))
- || (p - 2 == pattern && p == pend))
- goto normal_backslash;
-
- handle_interval:
- {
- int lower_bound = -1, upper_bound = -1;
- beg_interval = p - 1;
-
- if (p == pend) {
- if (syntax & RE_NO_BK_BRACES)
- goto unfetch_interval;
- else
- return REG_EBRACE;
- }
-
- GET_UNSIGNED_NUMBER(lower_bound);
-
- if (c == ',') {
- GET_UNSIGNED_NUMBER(upper_bound);
- if (upper_bound < 0)
- upper_bound = RE_DUP_MAX;
- } else
- upper_bound = lower_bound;
-
- if (lower_bound < 0 || upper_bound > RE_DUP_MAX
- || lower_bound > upper_bound) {
- if (syntax & RE_NO_BK_BRACES)
- goto unfetch_interval;
- else
- return REG_BADBR;
- }
-
- if (!(syntax & RE_NO_BK_BRACES)) {
- if (c != '\\')
- return REG_EBRACE;
-
- PATFETCH(c);
- }
-
- if (c != '}') {
- if (syntax & RE_NO_BK_BRACES)
- goto unfetch_interval;
- else
- return REG_BADBR;
- }
-
- if (!laststart) {
- if (syntax & RE_CONTEXT_INVALID_OPS)
- return REG_BADRPT;
- else if (syntax & RE_CONTEXT_INDEP_OPS)
- laststart = b;
- else
- goto unfetch_interval;
- }
-
- if (upper_bound == 0) {
- GET_BUFFER_SPACE(3);
- INSERT_JUMP(jump, laststart, b + 3);
- b += 3;
- }
-
- else {
- unsigned nbytes =
- 10 + (upper_bound > 1) * 10;
-
- GET_BUFFER_SPACE(nbytes);
-
- INSERT_JUMP2(succeed_n, laststart,
- b + 5 + (upper_bound >
- 1) * 5, lower_bound);
- b += 5;
-
- insert_op2(set_number_at, laststart, 5,
- lower_bound, b);
- b += 5;
-
- if (upper_bound > 1) {
- STORE_JUMP2(jump_n, b,
- laststart + 5,
- upper_bound - 1);
- b += 5;
-
- insert_op2(set_number_at,
- laststart,
- b - laststart,
- upper_bound - 1, b);
- b += 5;
- }
- }
- pending_exact = 0;
- beg_interval = NULL;
- }
- break;
-
- unfetch_interval:
- assert(beg_interval);
- p = beg_interval;
- beg_interval = NULL;
-
- /* normal_char and normal_backslash need `c'. */
- PATFETCH(c);
-
- if (!(syntax & RE_NO_BK_BRACES)) {
- if (p > pattern && p[-1] == '\\')
- goto normal_backslash;
- }
- goto normal_char;
-
- case 'w':
- if (re_syntax_options & RE_NO_GNU_OPS)
- goto normal_char;
- laststart = b;
- BUF_PUSH(wordchar);
- break;
-
-
- case 'W':
- if (re_syntax_options & RE_NO_GNU_OPS)
- goto normal_char;
- laststart = b;
- BUF_PUSH(notwordchar);
- break;
-
-
- case '<':
- if (re_syntax_options & RE_NO_GNU_OPS)
- goto normal_char;
- BUF_PUSH(wordbeg);
- break;
-
- case '>':
- if (re_syntax_options & RE_NO_GNU_OPS)
- goto normal_char;
- BUF_PUSH(wordend);
- break;
-
- case 'b':
- if (re_syntax_options & RE_NO_GNU_OPS)
- goto normal_char;
- BUF_PUSH(wordbound);
- break;
-
- case 'B':
- if (re_syntax_options & RE_NO_GNU_OPS)
- goto normal_char;
- BUF_PUSH(notwordbound);
- break;
-
- case '`':
- if (re_syntax_options & RE_NO_GNU_OPS)
- goto normal_char;
- BUF_PUSH(begbuf);
- break;
-
- case '\'':
- if (re_syntax_options & RE_NO_GNU_OPS)
- goto normal_char;
- BUF_PUSH(endbuf);
- break;
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (syntax & RE_NO_BK_REFS)
- goto normal_char;
-
- c1 = c - '0';
-
- if (c1 > regnum)
- return REG_ESUBREG;
-
- /* Can't back reference to a subexpression if inside of it. */
- if (group_in_compile_stack
- (compile_stack, (regnum_t) c1))
- goto normal_char;
-
- laststart = b;
- BUF_PUSH_2(duplicate, c1);
- break;
-
-
- case '+':
- case '?':
- if (syntax & RE_BK_PLUS_QM)
- goto handle_plus;
- else
- goto normal_backslash;
-
- default:
- normal_backslash:
- /* You might think it would be useful for \ to mean
- not to translate; but if we don't translate it
- it will never match anything. */
- c = TRANSLATE(c);
- goto normal_char;
- }
- break;
-
-
- default:
- /* Expects the character in `c'. */
- normal_char:
- /* If no exactn currently being built. */
- if (!pending_exact
- /* If last exactn not at current position. */
- || pending_exact + *pending_exact + 1 != b
- /* We have only one byte following the exactn for the count. */
- || *pending_exact == (1 << BYTEWIDTH) - 1
- /* If followed by a repetition operator. */
- || *p == '*' || *p == '^'
- || ((syntax & RE_BK_PLUS_QM)
- ? *p == '\\' && (p[1] == '+'
- || p[1] == '?')
- : (*p == '+' || *p == '?'))
- || ((syntax & RE_INTERVALS)
- && ((syntax & RE_NO_BK_BRACES)
- ? *p == '{'
- : (p[0] == '\\' && p[1] == '{')))) {
- /* Start building a new exactn. */
-
- laststart = b;
-
- BUF_PUSH_2(exactn, 0);
- pending_exact = b - 1;
- }
-
- BUF_PUSH(c);
- (*pending_exact)++;
- break;
- } /* switch (c) */
- } /* while p != pend */
-
-
- /* Through the pattern now. */
-
- if (fixup_alt_jump)
- STORE_JUMP(jump_past_alt, fixup_alt_jump, b);
-
- if (!COMPILE_STACK_EMPTY)
- return REG_EPAREN;
-
- free(compile_stack.stack);
-
- /* We have succeeded; set the length of the buffer. */
- bufp->used = b - bufp->buffer;
-
- return REG_NOERROR;
-} /* regex_compile */
-
-/* Subroutines for `regex_compile'. */
-
-/* Store OP at LOC followed by two-byte integer parameter ARG. */
-
-static void store_op1(op, loc, arg)
-re_opcode_t op;
-unsigned char *loc;
-int arg;
-{
- *loc = (unsigned char) op;
- STORE_NUMBER(loc + 1, arg);
-}
-
-
-/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */
-
-static void store_op2(op, loc, arg1, arg2)
-re_opcode_t op;
-unsigned char *loc;
-int arg1, arg2;
-{
- *loc = (unsigned char) op;
- STORE_NUMBER(loc + 1, arg1);
- STORE_NUMBER(loc + 3, arg2);
-}
-
-
-/* Copy the bytes from LOC to END to open up three bytes of space at LOC
- for OP followed by two-byte integer parameter ARG. */
-
-static void insert_op1(op, loc, arg, end)
-re_opcode_t op;
-unsigned char *loc;
-int arg;
-unsigned char *end;
-{
- register unsigned char *pfrom = end;
- register unsigned char *pto = end + 3;
-
- while (pfrom != loc)
- *--pto = *--pfrom;
-
- store_op1(op, loc, arg);
-}
-
-
-/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */
-
-static void insert_op2(op, loc, arg1, arg2, end)
-re_opcode_t op;
-unsigned char *loc;
-int arg1, arg2;
-unsigned char *end;
-{
- register unsigned char *pfrom = end;
- register unsigned char *pto = end + 5;
-
- while (pfrom != loc)
- *--pto = *--pfrom;
-
- store_op2(op, loc, arg1, arg2);
-}
-
-
-/* P points to just after a ^ in PATTERN. Return true if that ^ comes
- after an alternative or a begin-subexpression. We assume there is at
- least one character before the ^. */
-
-static boolean at_begline_loc_p(pattern, p, syntax)
-const char *pattern, *p;
-reg_syntax_t syntax;
-{
- const char *prev = p - 2;
- boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
-
- return
- /* After a subexpression? */
- (*prev == '('
- && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
- /* After an alternative? */
- || (*prev == '|'
- && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
-}
-
-
-/* The dual of at_begline_loc_p. This one is for $. We assume there is
- at least one character after the $, i.e., `P < PEND'. */
-
-static boolean at_endline_loc_p(p, pend, syntax)
-const char *p, *pend;
-reg_syntax_t syntax;
-{
- const char *next = p;
- boolean next_backslash = *next == '\\';
- const char *next_next = p + 1 < pend ? p + 1 : NULL;
-
- return
- /* Before a subexpression? */
- (syntax & RE_NO_BK_PARENS ? *next == ')'
- : next_backslash && next_next && *next_next == ')')
- /* Before an alternative? */
- || (syntax & RE_NO_BK_VBAR ? *next == '|'
- : next_backslash && next_next && *next_next == '|');
-}
-
-
-/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
- false if it's not. */
-
-static boolean group_in_compile_stack(compile_stack, regnum)
-compile_stack_type compile_stack;
-regnum_t regnum;
-{
- int this_element;
-
- for (this_element = compile_stack.avail - 1;
- this_element >= 0; this_element--)
- if (compile_stack.stack[this_element].regnum == regnum)
- return true;
-
- return false;
-}
-
-
-/* Read the ending character of a range (in a bracket expression) from the
- uncompiled pattern *P_PTR (which ends at PEND). We assume the
- starting character is in `P[-2]'. (`P[-1]' is the character `-'.)
- Then we set the translation of all bits between the starting and
- ending characters (inclusive) in the compiled pattern B.
-
- Return an error code.
-
- We use these short variable names so we can use the same macros as
- `regex_compile' itself. */
-
-static reg_errcode_t compile_range(p_ptr, pend, translate, syntax, b)
-const char **p_ptr, *pend;
-char *translate;
-reg_syntax_t syntax;
-unsigned char *b;
-{
- unsigned this_char;
-
- const char *p = *p_ptr;
- int range_start, range_end;
-
- if (p == pend)
- return REG_ERANGE;
-
- /* Even though the pattern is a signed `char *', we need to fetch
- with unsigned char *'s; if the high bit of the pattern character
- is set, the range endpoints will be negative if we fetch using a
- signed char *.
-
- We also want to fetch the endpoints without translating them; the
- appropriate translation is done in the bit-setting loop below. */
- range_start = ((unsigned char *) p)[-2];
- range_end = ((unsigned char *) p)[0];
-
- /* Have to increment the pointer into the pattern string, so the
- caller isn't still at the ending character. */
- (*p_ptr)++;
-
- /* If the start is after the end, the range is empty. */
- if (range_start > range_end)
- return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE :
- REG_NOERROR;
-
- /* Here we see why `this_char' has to be larger than an `unsigned
- char' -- the range is inclusive, so if `range_end' == 0xff
- (assuming 8-bit characters), we would otherwise go into an infinite
- loop, since all characters <= 0xff. */
- for (this_char = range_start; this_char <= range_end; this_char++) {
- SET_LIST_BIT(TRANSLATE(this_char));
- }
- return REG_NOERROR;
-}
-
-/* Failure stack declarations and macros; both re_compile_fastmap and
- re_match_2 use a failure stack. These have to be macros because of
- REGEX_ALLOCATE. */
-
-
-/* Number of failure points for which to initially allocate space
- when matching. If this number is exceeded, we allocate more
- space, so it is not a hard limit. */
-#define INIT_FAILURE_ALLOC 5
-
-/* Roughly the maximum number of failure points on the stack. Would be
- exactly that if always used MAX_FAILURE_SPACE each time we failed.
- This is a variable only so users of regex can assign to it; we never
- change it ourselves. */
-int re_max_failures = 2000;
-
-typedef const unsigned char *fail_stack_elt_t;
-
-typedef struct {
- fail_stack_elt_t *stack;
- unsigned size;
- unsigned avail; /* Offset of next open position. */
-} fail_stack_type;
-
-#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
-#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
-#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
-#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail])
-
-
-/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */
-
-#define INIT_FAIL_STACK() \
- do { \
- fail_stack.stack = (fail_stack_elt_t *) \
- REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
- \
- if (fail_stack.stack == NULL) \
- return -2; \
- \
- fail_stack.size = INIT_FAILURE_ALLOC; \
- fail_stack.avail = 0; \
- } while (0)
-
-
-/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
-
- Return 1 if succeeds, and 0 if either ran out of memory
- allocating space for it or it was already too large.
-
- REGEX_REALLOCATE requires `destination' be declared. */
-
-#define DOUBLE_FAIL_STACK(fail_stack) \
- ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
- ? 0 \
- : ((fail_stack).stack = (fail_stack_elt_t *) \
- REGEX_REALLOCATE ((fail_stack).stack, \
- (fail_stack).size * sizeof (fail_stack_elt_t), \
- ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
- \
- (fail_stack).stack == NULL \
- ? 0 \
- : ((fail_stack).size <<= 1, \
- 1)))
-
-
-/* Push PATTERN_OP on FAIL_STACK.
-
- Return 1 if was able to do so and 0 if ran out of memory allocating
- space to do so. */
-#define PUSH_PATTERN_OP(pattern_op, fail_stack) \
- ((FAIL_STACK_FULL () \
- && !DOUBLE_FAIL_STACK (fail_stack)) \
- ? 0 \
- : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \
- 1))
-
-/* This pushes an item onto the failure stack. Must be a four-byte
- value. Assumes the variable `fail_stack'. Probably should only
- be called from within `PUSH_FAILURE_POINT'. */
-#define PUSH_FAILURE_ITEM(item) \
- fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
-
-/* The complement operation. Assumes `fail_stack' is nonempty. */
-#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
-
-/* Used to omit pushing failure point id's when we're not debugging. */
-#define DEBUG_PUSH(item)
-#define DEBUG_POP(item_addr)
-
-
-/* Push the information about the state we will need
- if we ever fail back to it.
-
- Requires variables fail_stack, regstart, regend, reg_info, and
- num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be
- declared.
-
- Does `return FAILURE_CODE' if runs out of memory. */
-
-#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
- do { \
- char *destination; \
- /* Must be int, so when we don't save any registers, the arithmetic \
- of 0 + -1 isn't done as unsigned. */ \
- /* Can't be int, since there is not a shred of a guarantee that int \
- is wide enough to hold a value of something to which pointer can \
- be assigned */ \
- s_reg_t this_reg; \
- \
- DEBUG_STATEMENT (failure_id++); \
- DEBUG_STATEMENT (nfailure_points_pushed++); \
- DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
- DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
- DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
- \
- DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
- DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
- \
- /* Ensure we have enough space allocated for what we will push. */ \
- while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
- { \
- if (!DOUBLE_FAIL_STACK (fail_stack)) \
- return failure_code; \
- \
- DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
- (fail_stack).size); \
- DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
- }
-
-#define PUSH_FAILURE_POINT2(pattern_place, string_place, failure_code) \
- /* Push the info, starting with the registers. */ \
- DEBUG_PRINT1 ("\n"); \
- \
- PUSH_FAILURE_POINT_LOOP (); \
- \
- DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\
- PUSH_FAILURE_ITEM (lowest_active_reg); \
- \
- DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
- PUSH_FAILURE_ITEM (highest_active_reg); \
- \
- DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
- DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
- PUSH_FAILURE_ITEM (pattern_place); \
- \
- DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
- DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
- size2); \
- DEBUG_PRINT1 ("'\n"); \
- PUSH_FAILURE_ITEM (string_place); \
- \
- DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
- DEBUG_PUSH (failure_id); \
- } while (0)
-
-/* Pulled out of PUSH_FAILURE_POINT() to shorten the definition
- of that macro. (for VAX C) */
-#define PUSH_FAILURE_POINT_LOOP() \
- for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
- this_reg++) \
- { \
- DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
- DEBUG_STATEMENT (num_regs_pushed++); \
- \
- DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
- PUSH_FAILURE_ITEM (regstart[this_reg]); \
- \
- DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
- PUSH_FAILURE_ITEM (regend[this_reg]); \
- \
- DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
- DEBUG_PRINT2 (" match_null=%d", \
- REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
- DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
- DEBUG_PRINT2 (" matched_something=%d", \
- MATCHED_SOMETHING (reg_info[this_reg])); \
- DEBUG_PRINT2 (" ever_matched=%d", \
- EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
- DEBUG_PRINT1 ("\n"); \
- PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
- }
-
-/* This is the number of items that are pushed and popped on the stack
- for each register. */
-#define NUM_REG_ITEMS 3
-
-/* Individual items aside from the registers. */
-#define NUM_NONREG_ITEMS 4
-
-/* We push at most this many items on the stack. */
-#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
-
-/* We actually push this many items. */
-#define NUM_FAILURE_ITEMS \
- ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
- + NUM_NONREG_ITEMS)
-
-/* How many items can still be added to the stack without overflowing it. */
-#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
-
-
-/* Pops what PUSH_FAIL_STACK pushes.
-
- We restore into the parameters, all of which should be lvalues:
- STR -- the saved data position.
- PAT -- the saved pattern position.
- LOW_REG, HIGH_REG -- the highest and lowest active registers.
- REGSTART, REGEND -- arrays of string positions.
- REG_INFO -- array of information about each subexpression.
-
- Also assumes the variables `fail_stack' and (if debugging), `bufp',
- `pend', `string1', `size1', `string2', and `size2'. */
-
-#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
-{ \
- DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \
- s_reg_t this_reg; \
- const unsigned char *string_temp; \
- \
- assert (!FAIL_STACK_EMPTY ()); \
- \
- /* Remove failure points and point to how many regs pushed. */ \
- DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
- DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
- DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
- \
- assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
- \
- DEBUG_POP (&failure_id); \
- DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
- \
- /* If the saved string location is NULL, it came from an \
- on_failure_keep_string_jump opcode, and we want to throw away the \
- saved NULL, thus retaining our current position in the string. */ \
- string_temp = POP_FAILURE_ITEM (); \
- if (string_temp != NULL) \
- str = (const char *) string_temp; \
- \
- DEBUG_PRINT2 (" Popping string 0x%x: `", str); \
- DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
- DEBUG_PRINT1 ("'\n"); \
- \
- pat = (unsigned char *) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \
- DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
- \
- POP_FAILURE_POINT2 (low_reg, high_reg, regstart, regend, reg_info);
-
-/* Pulled out of POP_FAILURE_POINT() to shorten the definition
- of that macro. (for MSC 5.1) */
-#define POP_FAILURE_POINT2(low_reg, high_reg, regstart, regend, reg_info) \
- \
- /* Restore register info. */ \
- high_reg = (active_reg_t) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \
- \
- low_reg = (active_reg_t) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \
- \
- for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
- { \
- DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \
- \
- reg_info[this_reg].word = POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \
- \
- regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
- \
- regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
- } \
- \
- DEBUG_STATEMENT (nfailure_points_popped++); \
-} /* POP_FAILURE_POINT */
-
-/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
- BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
- characters can start a string that matches the pattern. This fastmap
- is used by re_search to skip quickly over impossible starting points.
-
- The caller must supply the address of a (1 << BYTEWIDTH)-byte data
- area as BUFP->fastmap.
-
- We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
- the pattern buffer.
-
- Returns 0 if we succeed, -2 if an internal error. */
-
-int re_compile_fastmap(bufp)
-struct re_pattern_buffer *bufp;
-{
- int j, k;
- fail_stack_type fail_stack;
- char *destination;
- /* We don't push any register information onto the failure stack. */
- unsigned num_regs = 0;
-
- register char *fastmap = bufp->fastmap;
- unsigned char *pattern = bufp->buffer;
- const unsigned char *p = pattern;
- register unsigned char *pend = pattern + bufp->used;
-
- /* Assume that each path through the pattern can be null until
- proven otherwise. We set this false at the bottom of switch
- statement, to which we get only if a particular path doesn't
- match the empty string. */
- boolean path_can_be_null = true;
-
- /* We aren't doing a `succeed_n' to begin with. */
- boolean succeed_n_p = false;
-
- assert(fastmap != NULL && p != NULL);
-
- INIT_FAIL_STACK();
- bzero(fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */
- bufp->fastmap_accurate = 1; /* It will be when we're done. */
- bufp->can_be_null = 0;
-
- while (p != pend || !FAIL_STACK_EMPTY()) {
- if (p == pend) {
- bufp->can_be_null |= path_can_be_null;
-
- /* Reset for next path. */
- path_can_be_null = true;
-
- p = fail_stack.stack[--fail_stack.avail];
- }
-
- /* We should never be about to go beyond the end of the pattern. */
- assert(p < pend);
-
- switch ((re_opcode_t) * p++) {
-
- /* I guess the idea here is to simply not bother with a fastmap
- if a backreference is used, since it's too hard to figure out
- the fastmap for the corresponding group. Setting
- `can_be_null' stops `re_search_2' from using the fastmap, so
- that is all we do. */
- case duplicate:
- bufp->can_be_null = 1;
- return 0;
-
-
- /* Following are the cases which match a character. These end
- with `break'. */
-
- case exactn:
- fastmap[p[1]] = 1;
- break;
-
-
- case charset:
- for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
- if (p[j / BYTEWIDTH] &
- (1 << (j % BYTEWIDTH)))
- fastmap[j] = 1;
- break;
-
-
- case charset_not:
- /* Chars beyond end of map must be allowed. */
- for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
- fastmap[j] = 1;
-
- for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
- if (!
- (p[j / BYTEWIDTH] &
- (1 << (j % BYTEWIDTH))))
- fastmap[j] = 1;
- break;
-
-
- case wordchar:
- for (j = 0; j < (1 << BYTEWIDTH); j++)
- if (SYNTAX(j) == Sword)
- fastmap[j] = 1;
- break;
-
-
- case notwordchar:
- for (j = 0; j < (1 << BYTEWIDTH); j++)
- if (SYNTAX(j) != Sword)
- fastmap[j] = 1;
- break;
-
-
- case anychar:
- /* `.' matches anything ... */
- for (j = 0; j < (1 << BYTEWIDTH); j++)
- fastmap[j] = 1;
-
- /* ... except perhaps newline. */
- if (!(bufp->syntax & RE_DOT_NEWLINE))
- fastmap['\n'] = 0;
-
- /* Return if we have already set `can_be_null'; if we have,
- then the fastmap is irrelevant. Something's wrong here. */
- else if (bufp->can_be_null)
- return 0;
-
- /* Otherwise, have to check alternative paths. */
- break;
-
- case no_op:
- case begline:
- case endline:
- case begbuf:
- case endbuf:
- case wordbound:
- case notwordbound:
- case wordbeg:
- case wordend:
- case push_dummy_failure:
- continue;
-
-
- case jump_n:
- case pop_failure_jump:
- case maybe_pop_jump:
- case jump:
- case jump_past_alt:
- case dummy_failure_jump:
- EXTRACT_NUMBER_AND_INCR(j, p);
- p += j;
- if (j > 0)
- continue;
-
- /* Jump backward implies we just went through the body of a
- loop and matched nothing. Opcode jumped to should be
- `on_failure_jump' or `succeed_n'. Just treat it like an
- ordinary jump. For a * loop, it has pushed its failure
- point already; if so, discard that as redundant. */
- if ((re_opcode_t) * p != on_failure_jump
- && (re_opcode_t) * p != succeed_n)
- continue;
-
- p++;
- EXTRACT_NUMBER_AND_INCR(j, p);
- p += j;
-
- /* If what's on the stack is where we are now, pop it. */
- if (!FAIL_STACK_EMPTY()
- && fail_stack.stack[fail_stack.avail - 1] == p)
- fail_stack.avail--;
-
- continue;
-
-
- case on_failure_jump:
- case on_failure_keep_string_jump:
- handle_on_failure_jump:
- EXTRACT_NUMBER_AND_INCR(j, p);
-
- /* For some patterns, e.g., `(a?)?', `p+j' here points to the
- end of the pattern. We don't want to push such a point,
- since when we restore it above, entering the switch will
- increment `p' past the end of the pattern. We don't need
- to push such a point since we obviously won't find any more
- fastmap entries beyond `pend'. Such a pattern can match
- the null string, though. */
- if (p + j < pend) {
- if (!PUSH_PATTERN_OP(p + j, fail_stack))
- return -2;
- } else
- bufp->can_be_null = 1;
-
- if (succeed_n_p) {
- EXTRACT_NUMBER_AND_INCR(k, p); /* Skip the n. */
- succeed_n_p = false;
- }
-
- continue;
-
-
- case succeed_n:
- /* Get to the number of times to succeed. */
- p += 2;
-
- /* Increment p past the n for when k != 0. */
- EXTRACT_NUMBER_AND_INCR(k, p);
- if (k == 0) {
- p -= 4;
- succeed_n_p = true; /* Spaghetti code alert. */
- goto handle_on_failure_jump;
- }
- continue;
-
-
- case set_number_at:
- p += 4;
- continue;
-
-
- case start_memory:
- case stop_memory:
- p += 2;
- continue;
-
-
- default:
- abort(); /* We have listed all the cases. */
- } /* switch *p++ */
-
- /* Getting here means we have found the possible starting
- characters for one path of the pattern -- and that the empty
- string does not match. We need not follow this path further.
- Instead, look at the next alternative (remembered on the
- stack), or quit if no more. The test at the top of the loop
- does these things. */
- path_can_be_null = false;
- p = pend;
- } /* while p */
-
- /* Set `can_be_null' for the last path (also the first path, if the
- pattern is empty). */
- bufp->can_be_null |= path_can_be_null;
- return 0;
-} /* re_compile_fastmap */
-
-/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
- ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
- this memory for recording register information. STARTS and ENDS
- must be allocated using the malloc library routine, and must each
- be at least NUM_REGS * sizeof (regoff_t) bytes long.
-
- If NUM_REGS == 0, then subsequent matches should allocate their own
- register data.
-
- Unless this function is called, the first search or match using
- PATTERN_BUFFER will allocate its own register data, without
- freeing the old data. */
-
-void re_set_registers(bufp, regs, num_regs, starts, ends)
-struct re_pattern_buffer *bufp;
-struct re_registers *regs;
-unsigned num_regs;
-regoff_t *starts, *ends;
-{
- if (num_regs) {
- bufp->regs_allocated = REGS_REALLOCATE;
- regs->num_regs = num_regs;
- regs->start = starts;
- regs->end = ends;
- } else {
- bufp->regs_allocated = REGS_UNALLOCATED;
- regs->num_regs = 0;
- regs->start = regs->end = 0;
- }
-}
-
-/* Searching routines. */
-
-/* Like re_search_2, below, but only one string is specified, and
- doesn't let you say where to stop matching. */
-
-int re_search(bufp, string, size, startpos, range, regs)
-struct re_pattern_buffer *bufp;
-const char *string;
-int size, startpos, range;
-struct re_registers *regs;
-{
- return re_search_2(bufp, NULL, 0, string, size, startpos, range,
- regs, size);
-}
-
-
-/* Using the compiled pattern in BUFP->buffer, first tries to match the
- virtual concatenation of STRING1 and STRING2, starting first at index
- STARTPOS, then at STARTPOS + 1, and so on.
-
- STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
-
- RANGE is how far to scan while trying to match. RANGE = 0 means try
- only at STARTPOS; in general, the last start tried is STARTPOS +
- RANGE.
-
- In REGS, return the indices of the virtual concatenation of STRING1
- and STRING2 that matched the entire BUFP->buffer and its contained
- subexpressions.
-
- Do not consider matching one past the index STOP in the virtual
- concatenation of STRING1 and STRING2.
-
- We return either the position in the strings at which the match was
- found, -1 if no match, or -2 if error (such as failure
- stack overflow). */
-
-int
-re_search_2(bufp, string1, size1, string2, size2, startpos, range, regs,
- stop)
-struct re_pattern_buffer *bufp;
-const char *string1, *string2;
-int size1, size2;
-int startpos;
-int range;
-struct re_registers *regs;
-int stop;
-{
- int val;
- register char *fastmap = bufp->fastmap;
- register char *translate = bufp->translate;
- int total_size = size1 + size2;
- int endpos = startpos + range;
-
- /* Check for out-of-range STARTPOS. */
- if (startpos < 0 || startpos > total_size)
- return -1;
-
- /* Fix up RANGE if it might eventually take us outside
- the virtual concatenation of STRING1 and STRING2. */
- if (endpos < -1)
- range = -1 - startpos;
- else if (endpos > total_size)
- range = total_size - startpos;
-
- /* If the search isn't to be a backwards one, don't waste time in a
- search for a pattern that must be anchored. */
- if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf
- && range > 0) {
- if (startpos > 0)
- return -1;
- else
- range = 1;
- }
-
- /* Update the fastmap now if not correct already. */
- if (fastmap && !bufp->fastmap_accurate)
- if (re_compile_fastmap(bufp) == -2)
- return -2;
-
- /* Loop through the string, looking for a place to start matching. */
- for (;;) {
- /* If a fastmap is supplied, skip quickly over characters that
- cannot be the start of a match. If the pattern can match the
- null string, however, we don't need to skip characters; we want
- the first null string. */
- if (fastmap && startpos < total_size && !bufp->can_be_null) {
- if (range > 0) { /* Searching forwards. */
- register const char *d;
- register int lim = 0;
- int irange = range;
-
- if (startpos < size1
- && startpos + range >= size1)
- lim = range - (size1 - startpos);
-
- d = (startpos >=
- size1 ? string2 - size1 : string1) +
- startpos;
-
- /* Written out as an if-else to avoid testing `translate'
- inside the loop. */
- if (translate)
- while (range > lim
- && !fastmap[(unsigned char)
- translate[(unsigned char) *d++]])
- range--;
- else
- while (range > lim
- && !fastmap[(unsigned char)
- *d++])
- range--;
-
- startpos += irange - range;
- } else { /* Searching backwards. */
-
- register char c = (size1 == 0
- || startpos >=
- size1 ? string2[startpos
- - size1]
- : string1[startpos]);
-
- if (!fastmap[(unsigned char) TRANSLATE(c)])
- goto advance;
- }
- }
-
- /* If can't match the null string, and that's all we have left, fail. */
- if (range >= 0 && startpos == total_size && fastmap
- && !bufp->can_be_null)
- return -1;
-
- val = re_match_2(bufp, string1, size1, string2, size2,
- startpos, regs, stop);
- if (val >= 0)
- return startpos;
-
- if (val == -2)
- return -2;
-
- advance:
- if (!range)
- break;
- else if (range > 0) {
- range--;
- startpos++;
- } else {
- range++;
- startpos--;
- }
- }
- return -1;
-} /* re_search_2 */
-
-/* Structure for per-register (a.k.a. per-group) information.
- This must not be longer than one word, because we push this value
- onto the failure stack. Other register information, such as the
- starting and ending positions (which are addresses), and the list of
- inner groups (which is a bits list) are maintained in separate
- variables.
-
- We are making a (strictly speaking) nonportable assumption here: that
- the compiler will pack our bit fields into something that fits into
- the type of `word', i.e., is something that fits into one item on the
- failure stack. */
-
-/* Declarations and macros for re_match_2. */
-
-typedef union {
- fail_stack_elt_t word;
- struct {
- /* This field is one if this group can match the empty string,
- zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
-#define MATCH_NULL_UNSET_VALUE 3
- unsigned match_null_string_p:2;
- unsigned is_active:1;
- unsigned matched_something:1;
- unsigned ever_matched_something:1;
- } bits;
-} register_info_type;
-
-#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
-#define IS_ACTIVE(R) ((R).bits.is_active)
-#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
-#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
-
-static boolean group_match_null_string_p (unsigned char **p,
- unsigned char *end,
- register_info_type *
- reg_info);
-
-static boolean alt_match_null_string_p (unsigned char *p, unsigned char *end,
- register_info_type * reg_info);
-
-static boolean common_op_match_null_string_p (unsigned char **p,
- unsigned char *end,
- register_info_type * reg_info);
-
-static int bcmp_translate (const char *s1, const char *s2,
- int len, char *translate);
-
-/* Call this when have matched a real character; it sets `matched' flags
- for the subexpressions which we are currently inside. Also records
- that those subexprs have matched. */
-#define SET_REGS_MATCHED() \
- do \
- { \
- active_reg_t r; \
- for (r = lowest_active_reg; r <= highest_active_reg; r++) \
- { \
- MATCHED_SOMETHING (reg_info[r]) \
- = EVER_MATCHED_SOMETHING (reg_info[r]) \
- = 1; \
- } \
- } \
- while (0)
-
-
-/* This converts PTR, a pointer into one of the search strings `string1'
- and `string2' into an offset from the beginning of that string. */
-#define POINTER_TO_OFFSET(ptr) \
- (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
-
-/* Registers are set to a sentinel when they haven't yet matched. */
-#define REG_UNSET_VALUE ((char *) -1)
-#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
-
-
-/* Macros for dealing with the split strings in re_match_2. */
-
-#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
-
-/* Call before fetching a character with *d. This switches over to
- string2 if necessary. */
-#define PREFETCH() \
- while (d == dend) \
- { \
- /* End of string2 => fail. */ \
- if (dend == end_match_2) \
- goto fail; \
- /* End of string1 => advance to string2. */ \
- d = string2; \
- dend = end_match_2; \
- }
-
-
-/* Test if at very beginning or at very end of the virtual concatenation
- of `string1' and `string2'. If only one string, it's `string2'. */
-#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
-#define AT_STRINGS_END(d) ((d) == end2)
-
-
-/* Test if D points to a character which is word-constituent. We have
- two special cases to check for: if past the end of string1, look at
- the first character in string2; and if before the beginning of
- string2, look at the last character in string1. */
-#define WORDCHAR_P(d) \
- (SYNTAX ((d) == end1 ? *string2 \
- : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \
- == Sword)
-
-/* Test if the character before D and the one at D differ with respect
- to being word-constituent. */
-#define AT_WORD_BOUNDARY(d) \
- (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \
- || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
-
-
-/* Free everything we malloc. */
-#define FREE_VARIABLES() alloca (0)
-
-/* These values must meet several constraints. They must not be valid
- register values; since we have a limit of 255 registers (because
- we use only one byte in the pattern for the register number), we can
- use numbers larger than 255. They must differ by 1, because of
- NUM_FAILURE_ITEMS above. And the value for the lowest register must
- be larger than the value for the highest register, so we do not try
- to actually save any registers when none are active. */
-#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
-#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
-
-/* Matching routines. */
-
-/* re_match is like re_match_2 except it takes only a single string. */
-
-int re_match(bufp, string, size, pos, regs)
-struct re_pattern_buffer *bufp;
-const char *string;
-int size, pos;
-struct re_registers *regs;
-{
- return re_match_2(bufp, NULL, 0, string, size, pos, regs, size);
-}
-
-/* re_match_2 matches the compiled pattern in BUFP against the
- the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
- and SIZE2, respectively). We start matching at POS, and stop
- matching at STOP.
-
- If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
- store offsets for the substring each group matched in REGS. See the
- documentation for exactly how many groups we fill.
-
- We return -1 if no match, -2 if an internal error (such as the
- failure stack overflowing). Otherwise, we return the length of the
- matched substring. */
-
-int re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop)
-struct re_pattern_buffer *bufp;
-const char *string1, *string2;
-int size1, size2;
-int pos;
-struct re_registers *regs;
-int stop;
-{
- /* General temporaries. */
- int mcnt;
- unsigned char *p1;
-
- /* Just past the end of the corresponding string. */
- const char *end1, *end2;
-
- /* Pointers into string1 and string2, just past the last characters in
- each to consider matching. */
- const char *end_match_1, *end_match_2;
-
- /* Where we are in the data, and the end of the current string. */
- const char *d, *dend;
-
- /* Where we are in the pattern, and the end of the pattern. */
- unsigned char *p = bufp->buffer;
- register unsigned char *pend = p + bufp->used;
-
- /* We use this to map every character in the string. */
- char *translate = bufp->translate;
-
- /* Failure point stack. Each place that can handle a failure further
- down the line pushes a failure point on this stack. It consists of
- restart, regend, and reg_info for all registers corresponding to
- the subexpressions we're currently inside, plus the number of such
- registers, and, finally, two char *'s. The first char * is where
- to resume scanning the pattern; the second one is where to resume
- scanning the strings. If the latter is zero, the failure point is
- a ``dummy''; if a failure happens and the failure point is a dummy,
- it gets discarded and the next next one is tried. */
- fail_stack_type fail_stack;
-
- /* We fill all the registers internally, independent of what we
- return, for use in backreferences. The number here includes
- an element for register zero. */
- size_t num_regs = bufp->re_nsub + 1;
-
- /* The currently active registers. */
- active_reg_t lowest_active_reg = NO_LOWEST_ACTIVE_REG;
- active_reg_t highest_active_reg = NO_HIGHEST_ACTIVE_REG;
-
- /* Information on the contents of registers. These are pointers into
- the input strings; they record just what was matched (on this
- attempt) by a subexpression part of the pattern, that is, the
- regnum-th regstart pointer points to where in the pattern we began
- matching and the regnum-th regend points to right after where we
- stopped matching the regnum-th subexpression. (The zeroth register
- keeps track of what the whole pattern matches.) */
- const char **regstart = 0, **regend = 0;
-
- /* If a group that's operated upon by a repetition operator fails to
- match anything, then the register for its start will need to be
- restored because it will have been set to wherever in the string we
- are when we last see its open-group operator. Similarly for a
- register's end. */
- const char **old_regstart = 0, **old_regend = 0;
-
- /* The is_active field of reg_info helps us keep track of which (possibly
- nested) subexpressions we are currently in. The matched_something
- field of reg_info[reg_num] helps us tell whether or not we have
- matched any of the pattern so far this time through the reg_num-th
- subexpression. These two fields get reset each time through any
- loop their register is in. */
- register_info_type *reg_info = 0;
-
- /* The following record the register info as found in the above
- variables when we find a match better than any we've seen before.
- This happens as we backtrack through the failure points, which in
- turn happens only if we have not yet matched the entire string. */
- unsigned best_regs_set = false;
- const char **best_regstart = 0, **best_regend = 0;
-
- /* Logically, this is `best_regend[0]'. But we don't want to have to
- allocate space for that if we're not allocating space for anything
- else (see below). Also, we never need info about register 0 for
- any of the other register vectors, and it seems rather a kludge to
- treat `best_regend' differently than the rest. So we keep track of
- the end of the best match so far in a separate variable. We
- initialize this to NULL so that when we backtrack the first time
- and need to test it, it's not garbage. */
- const char *match_end = NULL;
-
- /* Used when we pop values we don't care about. */
- const char **reg_dummy = 0;
- register_info_type *reg_info_dummy = 0;
-
- DEBUG_PRINT1("\n\nEntering re_match_2.\n");
-
- INIT_FAIL_STACK();
-
- /* Do not bother to initialize all the register variables if there are
- no groups in the pattern, as it takes a fair amount of time. If
- there are groups, we include space for register 0 (the whole
- pattern), even though we never use it, since it simplifies the
- array indexing. We should fix this. */
- if (bufp->re_nsub) {
- regstart = REGEX_TALLOC(num_regs, const char *);
- regend = REGEX_TALLOC(num_regs, const char *);
- old_regstart = REGEX_TALLOC(num_regs, const char *);
- old_regend = REGEX_TALLOC(num_regs, const char *);
- best_regstart = REGEX_TALLOC(num_regs, const char *);
- best_regend = REGEX_TALLOC(num_regs, const char *);
- reg_info = REGEX_TALLOC(num_regs, register_info_type);
- reg_dummy = REGEX_TALLOC(num_regs, const char *);
- reg_info_dummy =
- REGEX_TALLOC(num_regs, register_info_type);
-
- if (!
- (regstart && regend && old_regstart && old_regend
- && reg_info && best_regstart && best_regend
- && reg_dummy && reg_info_dummy)) {
- FREE_VARIABLES();
- return -2;
- }
- }
-
- /* The starting position is bogus. */
- if (pos < 0 || pos > size1 + size2) {
- FREE_VARIABLES();
- return -1;
- }
-
- /* Initialize subexpression text positions to -1 to mark ones that no
- start_memory/stop_memory has been seen for. Also initialize the
- register information struct. */
- for (mcnt = 1; mcnt < num_regs; mcnt++) {
- regstart[mcnt] = regend[mcnt]
- = old_regstart[mcnt] = old_regend[mcnt] =
- REG_UNSET_VALUE;
-
- REG_MATCH_NULL_STRING_P(reg_info[mcnt]) =
- MATCH_NULL_UNSET_VALUE;
- IS_ACTIVE(reg_info[mcnt]) = 0;
- MATCHED_SOMETHING(reg_info[mcnt]) = 0;
- EVER_MATCHED_SOMETHING(reg_info[mcnt]) = 0;
- }
-
- /* We move `string1' into `string2' if the latter's empty -- but not if
- `string1' is null. */
- if (size2 == 0 && string1 != NULL) {
- string2 = string1;
- size2 = size1;
- string1 = 0;
- size1 = 0;
- }
- end1 = string1 + size1;
- end2 = string2 + size2;
-
- /* Compute where to stop matching, within the two strings. */
- if (stop <= size1) {
- end_match_1 = string1 + stop;
- end_match_2 = string2;
- } else {
- end_match_1 = end1;
- end_match_2 = string2 + stop - size1;
- }
-
- /* `p' scans through the pattern as `d' scans through the data.
- `dend' is the end of the input string that `d' points within. `d'
- is advanced into the following input string whenever necessary, but
- this happens before fetching; therefore, at the beginning of the
- loop, `d' can be pointing at the end of a string, but it cannot
- equal `string2'. */
- if (size1 > 0 && pos <= size1) {
- d = string1 + pos;
- dend = end_match_1;
- } else {
- d = string2 + pos - size1;
- dend = end_match_2;
- }
-
- DEBUG_PRINT1("The compiled pattern is: ");
- DEBUG_PRINT_COMPILED_PATTERN(bufp, p, pend);
- DEBUG_PRINT1("The string to match is: `");
- DEBUG_PRINT_DOUBLE_STRING(d, string1, size1, string2, size2);
- DEBUG_PRINT1("'\n");
-
- /* This loops over pattern commands. It exits by returning from the
- function if the match is complete, or it drops through if the match
- fails at this starting point in the input data. */
- for (;;) {
- DEBUG_PRINT2("\n0x%x: ", p);
-
- if (p == pend) { /* End of pattern means we might have succeeded. */
- DEBUG_PRINT1("end of pattern ... ");
-
- /* If we haven't matched the entire string, and we want the
- longest match, try backtracking. */
- if (d != end_match_2) {
- DEBUG_PRINT1("backtracking.\n");
-
- if (!FAIL_STACK_EMPTY()) { /* More failure points to try. */
- boolean same_str_p =
- (FIRST_STRING_P(match_end)
- == MATCHING_IN_FIRST_STRING);
-
- /* If exceeds best match so far, save it. */
- if (!best_regs_set
- || (same_str_p
- && d > match_end)
- || (!same_str_p
- &&
- !MATCHING_IN_FIRST_STRING))
- {
- best_regs_set = true;
- match_end = d;
-
- DEBUG_PRINT1
- ("\nSAVING match as best so far.\n");
-
- for (mcnt = 1;
- mcnt < num_regs;
- mcnt++) {
- best_regstart[mcnt]
- =
- regstart[mcnt];
- best_regend[mcnt] =
- regend[mcnt];
- }
- }
- goto fail;
- }
-
- /* If no failure points, don't restore garbage. */
- else if (best_regs_set) {
- restore_best_regs:
- /* Restore best match. It may happen that `dend ==
- end_match_1' while the restored d is in string2.
- For example, the pattern `x.*y.*z' against the
- strings `x-' and `y-z-', if the two strings are
- not consecutive in memory. */
- DEBUG_PRINT1
- ("Restoring best registers.\n");
-
- d = match_end;
- dend = ((d >= string1 && d <= end1)
- ? end_match_1 :
- end_match_2);
-
- for (mcnt = 1; mcnt < num_regs;
- mcnt++) {
- regstart[mcnt] =
- best_regstart[mcnt];
- regend[mcnt] =
- best_regend[mcnt];
- }
- }
- }
- /* d != end_match_2 */
- DEBUG_PRINT1("Accepting match.\n");
-
- /* If caller wants register contents data back, do it. */
- if (regs && !bufp->no_sub) {
- /* Have the register data arrays been allocated? */
- if (bufp->regs_allocated == REGS_UNALLOCATED) { /* No. So allocate them with malloc. We need one
- extra element beyond `num_regs' for the `-1' marker
- GNU code uses. */
- regs->num_regs =
- MAX(RE_NREGS, num_regs + 1);
- regs->start =
- TALLOC(regs->num_regs,
- regoff_t);
- regs->end =
- TALLOC(regs->num_regs,
- regoff_t);
- if (regs->start == NULL
- || regs->end == NULL)
- return -2;
- bufp->regs_allocated =
- REGS_REALLOCATE;
- } else if (bufp->regs_allocated == REGS_REALLOCATE) { /* Yes. If we need more elements than were already
- allocated, reallocate them. If we need fewer, just
- leave it alone. */
- if (regs->num_regs < num_regs + 1) {
- regs->num_regs =
- num_regs + 1;
- RETALLOC(regs->start,
- regs->num_regs,
- regoff_t);
- RETALLOC(regs->end,
- regs->num_regs,
- regoff_t);
- if (regs->start == NULL
- || regs->end == NULL)
- return -2;
- }
- } else {
- /* These braces fend off a "empty body in an else-statement"
- warning under GCC when assert expands to nothing. */
- assert(bufp->regs_allocated ==
- REGS_FIXED);
- }
-
- /* Convert the pointer data in `regstart' and `regend' to
- indices. Register zero has to be set differently,
- since we haven't kept track of any info for it. */
- if (regs->num_regs > 0) {
- regs->start[0] = pos;
- regs->end[0] =
- (MATCHING_IN_FIRST_STRING ? d -
- string1 : d - string2 +
- size1);
- }
-
- /* Go through the first `min (num_regs, regs->num_regs)'
- registers, since that is all we initialized. */
- for (mcnt = 1;
- mcnt < MIN(num_regs, regs->num_regs);
- mcnt++) {
- if (REG_UNSET(regstart[mcnt])
- || REG_UNSET(regend[mcnt]))
- regs->start[mcnt] =
- regs->end[mcnt] = -1;
- else {
- regs->start[mcnt] =
- POINTER_TO_OFFSET
- (regstart[mcnt]);
- regs->end[mcnt] =
- POINTER_TO_OFFSET
- (regend[mcnt]);
- }
- }
-
- /* If the regs structure we return has more elements than
- were in the pattern, set the extra elements to -1. If
- we (re)allocated the registers, this is the case,
- because we always allocate enough to have at least one
- -1 at the end. */
- for (mcnt = num_regs;
- mcnt < regs->num_regs; mcnt++)
- regs->start[mcnt] =
- regs->end[mcnt] = -1;
- }
- /* regs && !bufp->no_sub */
- FREE_VARIABLES();
- DEBUG_PRINT4
- ("%u failure points pushed, %u popped (%u remain).\n",
- nfailure_points_pushed,
- nfailure_points_popped,
- nfailure_points_pushed -
- nfailure_points_popped);
- DEBUG_PRINT2("%u registers pushed.\n",
- num_regs_pushed);
-
- mcnt = d - pos - (MATCHING_IN_FIRST_STRING
- ? string1 : string2 - size1);
-
- DEBUG_PRINT2("Returning %d from re_match_2.\n",
- mcnt);
-
- return mcnt;
- }
-
- /* Otherwise match next pattern command. */
- switch ((re_opcode_t) * p++) {
- /* Ignore these. Used to ignore the n of succeed_n's which
- currently have n == 0. */
- case no_op:
- DEBUG_PRINT1("EXECUTING no_op.\n");
- break;
-
-
- /* Match the next n pattern characters exactly. The following
- byte in the pattern defines n, and the n bytes after that
- are the characters to match. */
- case exactn:
- mcnt = *p++;
- DEBUG_PRINT2("EXECUTING exactn %d.\n", mcnt);
-
- /* This is written out as an if-else so we don't waste time
- testing `translate' inside the loop. */
- if (translate) {
- do {
- PREFETCH();
- if (translate[(unsigned char) *d++]
- != (char) *p++)
- goto fail;
- }
- while (--mcnt);
- } else {
- do {
- PREFETCH();
- if (*d++ != (char) *p++)
- goto fail;
- }
- while (--mcnt);
- }
- SET_REGS_MATCHED();
- break;
-
-
- /* Match any character except possibly a newline or a null. */
- case anychar:
- DEBUG_PRINT1("EXECUTING anychar.\n");
-
- PREFETCH();
-
- if ((!(bufp->syntax & RE_DOT_NEWLINE)
- && TRANSLATE(*d) == '\n')
- || (bufp->syntax & RE_DOT_NOT_NULL
- && TRANSLATE(*d) == '\000'))
- goto fail;
-
- SET_REGS_MATCHED();
- DEBUG_PRINT2(" Matched `%d'.\n", *d);
- d++;
- break;
-
-
- case charset:
- case charset_not:
- {
- register unsigned char c;
- boolean not =
- (re_opcode_t) * (p - 1) == charset_not;
-
- DEBUG_PRINT2("EXECUTING charset%s.\n",
- not ? "_not" : "");
-
- PREFETCH();
- c = TRANSLATE(*d); /* The character to match. */
-
- /* Cast to `unsigned' instead of `unsigned char' in case the
- bit list is a full 32 bytes long. */
- if (c < (unsigned) (*p * BYTEWIDTH)
- && p[1 +
- c / BYTEWIDTH] & (1 << (c %
- BYTEWIDTH)))
- not = !not;
-
- p += 1 + *p;
-
- if (!not)
- goto fail;
-
- SET_REGS_MATCHED();
- d++;
- break;
- }
-
-
- /* The beginning of a group is represented by start_memory.
- The arguments are the register number in the next byte, and the
- number of groups inner to this one in the next. The text
- matched within the group is recorded (in the internal
- registers data structure) under the register number. */
- case start_memory:
- DEBUG_PRINT3("EXECUTING start_memory %d (%d):\n",
- *p, p[1]);
-
- /* Find out if this group can match the empty string. */
- p1 = p; /* To send to group_match_null_string_p. */
-
- if (REG_MATCH_NULL_STRING_P(reg_info[*p]) ==
- MATCH_NULL_UNSET_VALUE)
- REG_MATCH_NULL_STRING_P(reg_info[*p])
- = group_match_null_string_p(&p1, pend,
- reg_info);
-
- /* Save the position in the string where we were the last time
- we were at this open-group operator in case the group is
- operated upon by a repetition operator, e.g., with `(a*)*b'
- against `ab'; then we want to ignore where we are now in
- the string in case this attempt to match fails. */
- old_regstart[*p] =
- REG_MATCH_NULL_STRING_P(reg_info[*p])
- ? REG_UNSET(regstart[*p]) ? d : regstart[*p]
- : regstart[*p];
- DEBUG_PRINT2(" old_regstart: %d\n",
- POINTER_TO_OFFSET(old_regstart[*p]));
-
- regstart[*p] = d;
- DEBUG_PRINT2(" regstart: %d\n",
- POINTER_TO_OFFSET(regstart[*p]));
-
- IS_ACTIVE(reg_info[*p]) = 1;
- MATCHED_SOMETHING(reg_info[*p]) = 0;
-
- /* This is the new highest active register. */
- highest_active_reg = *p;
-
- /* If nothing was active before, this is the new lowest active
- register. */
- if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
- lowest_active_reg = *p;
-
- /* Move past the register number and inner group count. */
- p += 2;
- break;
-
-
- /* The stop_memory opcode represents the end of a group. Its
- arguments are the same as start_memory's: the register
- number, and the number of inner groups. */
- case stop_memory:
- DEBUG_PRINT3("EXECUTING stop_memory %d (%d):\n",
- *p, p[1]);
-
- /* We need to save the string position the last time we were at
- this close-group operator in case the group is operated
- upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
- against `aba'; then we want to ignore where we are now in
- the string in case this attempt to match fails. */
- old_regend[*p] =
- REG_MATCH_NULL_STRING_P(reg_info[*p])
- ? REG_UNSET(regend[*p]) ? d : regend[*p]
- : regend[*p];
- DEBUG_PRINT2(" old_regend: %d\n",
- POINTER_TO_OFFSET(old_regend[*p]));
-
- regend[*p] = d;
- DEBUG_PRINT2(" regend: %d\n",
- POINTER_TO_OFFSET(regend[*p]));
-
- /* This register isn't active anymore. */
- IS_ACTIVE(reg_info[*p]) = 0;
-
- /* If this was the only register active, nothing is active
- anymore. */
- if (lowest_active_reg == highest_active_reg) {
- lowest_active_reg = NO_LOWEST_ACTIVE_REG;
- highest_active_reg = NO_HIGHEST_ACTIVE_REG;
- } else { /* We must scan for the new highest active register, since
- it isn't necessarily one less than now: consider
- (a(b)c(d(e)f)g). When group 3 ends, after the f), the
- new highest active register is 1. */
- unsigned char r = *p - 1;
- while (r > 0 && !IS_ACTIVE(reg_info[r]))
- r--;
-
- /* If we end up at register zero, that means that we saved
- the registers as the result of an `on_failure_jump', not
- a `start_memory', and we jumped to past the innermost
- `stop_memory'. For example, in ((.)*) we save
- registers 1 and 2 as a result of the *, but when we pop
- back to the second ), we are at the stop_memory 1.
- Thus, nothing is active. */
- if (r == 0) {
- lowest_active_reg =
- NO_LOWEST_ACTIVE_REG;
- highest_active_reg =
- NO_HIGHEST_ACTIVE_REG;
- } else
- highest_active_reg = r;
- }
-
- /* If just failed to match something this time around with a
- group that's operated on by a repetition operator, try to
- force exit from the ``loop'', and restore the register
- information for this group that we had before trying this
- last match. */
- if ((!MATCHED_SOMETHING(reg_info[*p])
- || (re_opcode_t) p[-3] == start_memory)
- && (p + 2) < pend) {
- boolean is_a_jump_n = false;
-
- p1 = p + 2;
- mcnt = 0;
- switch ((re_opcode_t) * p1++) {
- case jump_n:
- is_a_jump_n = true;
- case pop_failure_jump:
- case maybe_pop_jump:
- case jump:
- case dummy_failure_jump:
- EXTRACT_NUMBER_AND_INCR(mcnt, p1);
- if (is_a_jump_n)
- p1 += 2;
- break;
-
- default:
- /* do nothing */ ;
- }
- p1 += mcnt;
-
- /* If the next operation is a jump backwards in the pattern
- to an on_failure_jump right before the start_memory
- corresponding to this stop_memory, exit from the loop
- by forcing a failure after pushing on the stack the
- on_failure_jump's jump in the pattern, and d. */
- if (mcnt < 0
- && (re_opcode_t) * p1 ==
- on_failure_jump
- && (re_opcode_t) p1[3] == start_memory
- && p1[4] == *p) {
- /* If this group ever matched anything, then restore
- what its registers were before trying this last
- failed match, e.g., with `(a*)*b' against `ab' for
- regstart[1], and, e.g., with `((a*)*(b*)*)*'
- against `aba' for regend[3].
-
- Also restore the registers for inner groups for,
- e.g., `((a*)(b*))*' against `aba' (register 3 would
- otherwise get trashed). */
-
- if (EVER_MATCHED_SOMETHING
- (reg_info[*p])) {
- unsigned r;
-
- EVER_MATCHED_SOMETHING
- (reg_info[*p]) = 0;
-
- /* Restore this and inner groups' (if any) registers. */
- for (r = *p;
- r < *p + *(p + 1);
- r++) {
- regstart[r] =
- old_regstart
- [r];
-
- /* xx why this test? */
- if ((s_reg_t)
- old_regend[r]
- >=
- (s_reg_t)
- regstart[r])
- regend[r] =
- old_regend
- [r];
- }
- }
- p1++;
- EXTRACT_NUMBER_AND_INCR(mcnt, p1);
- PUSH_FAILURE_POINT(p1 + mcnt, d,
- -2);
- PUSH_FAILURE_POINT2(p1 + mcnt, d,
- -2);
-
- goto fail;
- }
- }
-
- /* Move past the register number and the inner group count. */
- p += 2;
- break;
-
-
- /* \<digit> has been turned into a `duplicate' command which is
- followed by the numeric value of <digit> as the register number. */
- case duplicate:
- {
- register const char *d2, *dend2;
- int regno = *p++; /* Get which register to match against. */
- DEBUG_PRINT2("EXECUTING duplicate %d.\n",
- regno);
-
- /* Can't back reference a group which we've never matched. */
- if (REG_UNSET(regstart[regno])
- || REG_UNSET(regend[regno]))
- goto fail;
-
- /* Where in input to try to start matching. */
- d2 = regstart[regno];
-
- /* Where to stop matching; if both the place to start and
- the place to stop matching are in the same string, then
- set to the place to stop, otherwise, for now have to use
- the end of the first string. */
-
- dend2 = ((FIRST_STRING_P(regstart[regno])
- == FIRST_STRING_P(regend[regno]))
- ? regend[regno] : end_match_1);
- for (;;) {
- /* If necessary, advance to next segment in register
- contents. */
- while (d2 == dend2) {
- if (dend2 == end_match_2)
- break;
- if (dend2 == regend[regno])
- break;
-
- /* End of string1 => advance to string2. */
- d2 = string2;
- dend2 = regend[regno];
- }
- /* At end of register contents => success */
- if (d2 == dend2)
- break;
-
- /* If necessary, advance to next segment in data. */
- PREFETCH();
-
- /* How many characters left in this segment to match. */
- mcnt = dend - d;
-
- /* Want how many consecutive characters we can match in
- one shot, so, if necessary, adjust the count. */
- if (mcnt > dend2 - d2)
- mcnt = dend2 - d2;
-
- /* Compare that many; failure if mismatch, else move
- past them. */
- if (translate
- ? bcmp_translate(d, d2, mcnt,
- translate)
- : bcmp(d, d2, mcnt))
- goto fail;
- d += mcnt, d2 += mcnt;
- }
- }
- break;
-
-
- /* begline matches the empty string at the beginning of the string
- (unless `not_bol' is set in `bufp'), and, if
- `newline_anchor' is set, after newlines. */
- case begline:
- DEBUG_PRINT1("EXECUTING begline.\n");
-
- if (AT_STRINGS_BEG(d)) {
- if (!bufp->not_bol)
- break;
- } else if (d[-1] == '\n' && bufp->newline_anchor) {
- break;
- }
- /* In all other cases, we fail. */
- goto fail;
-
-
- /* endline is the dual of begline. */
- case endline:
- DEBUG_PRINT1("EXECUTING endline.\n");
-
- if (AT_STRINGS_END(d)) {
- if (!bufp->not_eol)
- break;
- }
-
- /* We have to ``prefetch'' the next character. */
- else if ((d == end1 ? *string2 : *d) == '\n'
- && bufp->newline_anchor) {
- break;
- }
- goto fail;
-
-
- /* Match at the very beginning of the data. */
- case begbuf:
- DEBUG_PRINT1("EXECUTING begbuf.\n");
- if (AT_STRINGS_BEG(d))
- break;
- goto fail;
-
-
- /* Match at the very end of the data. */
- case endbuf:
- DEBUG_PRINT1("EXECUTING endbuf.\n");
- if (AT_STRINGS_END(d))
- break;
- goto fail;
-
-
- /* on_failure_keep_string_jump is used to optimize `.*\n'. It
- pushes NULL as the value for the string on the stack. Then
- `pop_failure_point' will keep the current value for the
- string, instead of restoring it. To see why, consider
- matching `foo\nbar' against `.*\n'. The .* matches the foo;
- then the . fails against the \n. But the next thing we want
- to do is match the \n against the \n; if we restored the
- string value, we would be back at the foo.
-
- Because this is used only in specific cases, we don't need to
- check all the things that `on_failure_jump' does, to make
- sure the right things get saved on the stack. Hence we don't
- share its code. The only reason to push anything on the
- stack at all is that otherwise we would have to change
- `anychar's code to do something besides goto fail in this
- case; that seems worse than this. */
- case on_failure_keep_string_jump:
- DEBUG_PRINT1
- ("EXECUTING on_failure_keep_string_jump");
-
- EXTRACT_NUMBER_AND_INCR(mcnt, p);
- DEBUG_PRINT3(" %d (to 0x%x):\n", mcnt, p + mcnt);
-
- PUSH_FAILURE_POINT(p + mcnt, NULL, -2);
- PUSH_FAILURE_POINT2(p + mcnt, NULL, -2);
- break;
-
-
- /* Uses of on_failure_jump:
-
- Each alternative starts with an on_failure_jump that points
- to the beginning of the next alternative. Each alternative
- except the last ends with a jump that in effect jumps past
- the rest of the alternatives. (They really jump to the
- ending jump of the following alternative, because tensioning
- these jumps is a hassle.)
-
- Repeats start with an on_failure_jump that points past both
- the repetition text and either the following jump or
- pop_failure_jump back to this on_failure_jump. */
- case on_failure_jump:
- on_failure:
- DEBUG_PRINT1("EXECUTING on_failure_jump");
-
- EXTRACT_NUMBER_AND_INCR(mcnt, p);
- DEBUG_PRINT3(" %d (to 0x%x)", mcnt, p + mcnt);
-
- /* If this on_failure_jump comes right before a group (i.e.,
- the original * applied to a group), save the information
- for that group and all inner ones, so that if we fail back
- to this point, the group's information will be correct.
- For example, in \(a*\)*\1, we need the preceding group,
- and in \(\(a*\)b*\)\2, we need the inner group. */
-
- /* We can't use `p' to check ahead because we push
- a failure point to `p + mcnt' after we do this. */
- p1 = p;
-
- /* We need to skip no_op's before we look for the
- start_memory in case this on_failure_jump is happening as
- the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
- against aba. */
- while (p1 < pend && (re_opcode_t) * p1 == no_op)
- p1++;
-
- if (p1 < pend
- && (re_opcode_t) * p1 == start_memory) {
- /* We have a new highest active register now. This will
- get reset at the start_memory we are about to get to,
- but we will have saved all the registers relevant to
- this repetition op, as described above. */
- highest_active_reg = *(p1 + 1) + *(p1 + 2);
- if (lowest_active_reg ==
- NO_LOWEST_ACTIVE_REG)
- lowest_active_reg = *(p1 + 1);
- }
-
- DEBUG_PRINT1(":\n");
- PUSH_FAILURE_POINT(p + mcnt, d, -2);
- PUSH_FAILURE_POINT2(p + mcnt, d, -2);
- break;
-
-
- /* A smart repeat ends with `maybe_pop_jump'.
- We change it to either `pop_failure_jump' or `jump'. */
- case maybe_pop_jump:
- EXTRACT_NUMBER_AND_INCR(mcnt, p);
- DEBUG_PRINT2("EXECUTING maybe_pop_jump %d.\n",
- mcnt);
- {
- register unsigned char *p2 = p;
-
- /* Compare the beginning of the repeat with what in the
- pattern follows its end. If we can establish that there
- is nothing that they would both match, i.e., that we
- would have to backtrack because of (as in, e.g., `a*a')
- then we can change to pop_failure_jump, because we'll
- never have to backtrack.
-
- This is not true in the case of alternatives: in
- `(a|ab)*' we do need to backtrack to the `ab' alternative
- (e.g., if the string was `ab'). But instead of trying to
- detect that here, the alternative has put on a dummy
- failure point which is what we will end up popping. */
-
- /* Skip over open/close-group commands. */
- while (p2 + 2 < pend
- && ((re_opcode_t) * p2 ==
- stop_memory
- || (re_opcode_t) * p2 ==
- start_memory))
- p2 += 3; /* Skip over args, too. */
-
- /* If we're at the end of the pattern, we can change. */
- if (p2 == pend) {
- /* Consider what happens when matching ":\(.*\)"
- against ":/". I don't really understand this code
- yet. */
- p[-3] =
- (unsigned char)
- pop_failure_jump;
- DEBUG_PRINT1
- (" End of pattern: change to `pop_failure_jump'.\n");
- }
-
- else if ((re_opcode_t) * p2 == exactn
- || (bufp->newline_anchor
- && (re_opcode_t) * p2 ==
- endline)) {
- register unsigned char c =
- *p2 ==
- (unsigned char) endline ? '\n'
- : p2[2];
- p1 = p + mcnt;
-
- /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
- to the `maybe_finalize_jump' of this case. Examine what
- follows. */
- if ((re_opcode_t) p1[3] == exactn
- && p1[5] != c) {
- p[-3] =
- (unsigned char)
- pop_failure_jump;
- DEBUG_PRINT3
- (" %c != %c => pop_failure_jump.\n",
- c, p1[5]);
- }
-
- else if ((re_opcode_t) p1[3] ==
- charset
- || (re_opcode_t) p1[3] ==
- charset_not) {
- int not =
- (re_opcode_t) p1[3] ==
- charset_not;
-
- if (c <
- (unsigned char) (p1[4]
- *
- BYTEWIDTH)
- && p1[5 +
- c /
- BYTEWIDTH] & (1
- <<
- (c
- %
- BYTEWIDTH)))
- not = !not;
-
- /* `not' is equal to 1 if c would match, which means
- that we can't change to pop_failure_jump. */
- if (!not) {
- p[-3] =
- (unsigned char)
- pop_failure_jump;
- DEBUG_PRINT1
- (" No match => pop_failure_jump.\n");
- }
- }
- }
- }
- p -= 2; /* Point at relative address again. */
- if ((re_opcode_t) p[-1] != pop_failure_jump) {
- p[-1] = (unsigned char) jump;
- DEBUG_PRINT1(" Match => jump.\n");
- goto unconditional_jump;
- }
- /* Note fall through. */
-
-
- /* The end of a simple repeat has a pop_failure_jump back to
- its matching on_failure_jump, where the latter will push a
- failure point. The pop_failure_jump takes off failure
- points put on by this pop_failure_jump's matching
- on_failure_jump; we got through the pattern to here from the
- matching on_failure_jump, so didn't fail. */
- case pop_failure_jump:
- {
- /* We need to pass separate storage for the lowest and
- highest registers, even though we don't care about the
- actual values. Otherwise, we will restore only one
- register from the stack, since lowest will == highest in
- `pop_failure_point'. */
- active_reg_t dummy_low_reg, dummy_high_reg;
- unsigned char *pdummy;
- const char *sdummy;
-
- DEBUG_PRINT1
- ("EXECUTING pop_failure_jump.\n");
- POP_FAILURE_POINT(sdummy, pdummy,
- dummy_low_reg,
- dummy_high_reg,
- reg_dummy, reg_dummy,
- reg_info_dummy);
- }
- /* Note fall through. */
-
-
- /* Unconditionally jump (without popping any failure points). */
- case jump:
- unconditional_jump:
- EXTRACT_NUMBER_AND_INCR(mcnt, p); /* Get the amount to jump. */
- DEBUG_PRINT2("EXECUTING jump %d ", mcnt);
- p += mcnt; /* Do the jump. */
- DEBUG_PRINT2("(to 0x%x).\n", p);
- break;
-
-
- /* We need this opcode so we can detect where alternatives end
- in `group_match_null_string_p' et al. */
- case jump_past_alt:
- DEBUG_PRINT1("EXECUTING jump_past_alt.\n");
- goto unconditional_jump;
-
-
- /* Normally, the on_failure_jump pushes a failure point, which
- then gets popped at pop_failure_jump. We will end up at
- pop_failure_jump, also, and with a pattern of, say, `a+', we
- are skipping over the on_failure_jump, so we have to push
- something meaningless for pop_failure_jump to pop. */
- case dummy_failure_jump:
- DEBUG_PRINT1("EXECUTING dummy_failure_jump.\n");
- /* It doesn't matter what we push for the string here. What
- the code at `fail' tests is the value for the pattern. */
- PUSH_FAILURE_POINT(0, 0, -2);
- PUSH_FAILURE_POINT2(0, 0, -2);
- goto unconditional_jump;
-
-
- /* At the end of an alternative, we need to push a dummy failure
- point in case we are followed by a `pop_failure_jump', because
- we don't want the failure point for the alternative to be
- popped. For example, matching `(a|ab)*' against `aab'
- requires that we match the `ab' alternative. */
- case push_dummy_failure:
- DEBUG_PRINT1("EXECUTING push_dummy_failure.\n");
- /* See comments just above at `dummy_failure_jump' about the
- two zeroes. */
- PUSH_FAILURE_POINT(0, 0, -2);
- PUSH_FAILURE_POINT2(0, 0, -2);
- break;
-
- /* Have to succeed matching what follows at least n times.
- After that, handle like `on_failure_jump'. */
- case succeed_n:
- EXTRACT_NUMBER(mcnt, p + 2);
- DEBUG_PRINT2("EXECUTING succeed_n %d.\n", mcnt);
-
- assert(mcnt >= 0);
- /* Originally, this is how many times we HAVE to succeed. */
- if (mcnt > 0) {
- mcnt--;
- p += 2;
- STORE_NUMBER_AND_INCR(p, mcnt);
- DEBUG_PRINT3(" Setting 0x%x to %d.\n", p,
- mcnt);
- } else if (mcnt == 0) {
- DEBUG_PRINT2
- (" Setting two bytes from 0x%x to no_op.\n",
- p + 2);
- p[2] = (unsigned char) no_op;
- p[3] = (unsigned char) no_op;
- goto on_failure;
- }
- break;
-
- case jump_n:
- EXTRACT_NUMBER(mcnt, p + 2);
- DEBUG_PRINT2("EXECUTING jump_n %d.\n", mcnt);
-
- /* Originally, this is how many times we CAN jump. */
- if (mcnt) {
- mcnt--;
- STORE_NUMBER(p + 2, mcnt);
- goto unconditional_jump;
- }
- /* If don't have to jump any more, skip over the rest of command. */
- else
- p += 4;
- break;
-
- case set_number_at:
- {
- DEBUG_PRINT1("EXECUTING set_number_at.\n");
-
- EXTRACT_NUMBER_AND_INCR(mcnt, p);
- p1 = p + mcnt;
- EXTRACT_NUMBER_AND_INCR(mcnt, p);
- DEBUG_PRINT3(" Setting 0x%x to %d.\n", p1,
- mcnt);
- STORE_NUMBER(p1, mcnt);
- break;
- }
-
- case wordbound:
- DEBUG_PRINT1("EXECUTING wordbound.\n");
- if (AT_WORD_BOUNDARY(d))
- break;
- goto fail;
-
- case notwordbound:
- DEBUG_PRINT1("EXECUTING notwordbound.\n");
- if (AT_WORD_BOUNDARY(d))
- goto fail;
- break;
-
- case wordbeg:
- DEBUG_PRINT1("EXECUTING wordbeg.\n");
- if (WORDCHAR_P(d)
- && (AT_STRINGS_BEG(d) || !WORDCHAR_P(d - 1)))
- break;
- goto fail;
-
- case wordend:
- DEBUG_PRINT1("EXECUTING wordend.\n");
- if (!AT_STRINGS_BEG(d) && WORDCHAR_P(d - 1)
- && (!WORDCHAR_P(d) || AT_STRINGS_END(d)))
- break;
- goto fail;
-
- case wordchar:
- DEBUG_PRINT1("EXECUTING non-Emacs wordchar.\n");
- PREFETCH();
- if (!WORDCHAR_P(d))
- goto fail;
- SET_REGS_MATCHED();
- d++;
- break;
-
- case notwordchar:
- DEBUG_PRINT1("EXECUTING non-Emacs notwordchar.\n");
- PREFETCH();
- if (WORDCHAR_P(d))
- goto fail;
- SET_REGS_MATCHED();
- d++;
- break;
-
- default:
- abort();
- }
- continue; /* Successfully executed one pattern command; keep going. */
-
-
- /* We goto here if a matching operation fails. */
- fail:
- if (!FAIL_STACK_EMPTY()) { /* A restart point is known. Restore to that state. */
- DEBUG_PRINT1("\nFAIL:\n");
- POP_FAILURE_POINT(d, p,
- lowest_active_reg,
- highest_active_reg, regstart,
- regend, reg_info);
-
- /* If this failure point is a dummy, try the next one. */
- if (!p)
- goto fail;
-
- /* If we failed to the end of the pattern, don't examine *p. */
- assert(p <= pend);
- if (p < pend) {
- boolean is_a_jump_n = false;
-
- /* If failed to a backwards jump that's part of a repetition
- loop, need to pop this failure point and use the next one. */
- switch ((re_opcode_t) * p) {
- case jump_n:
- is_a_jump_n = true;
- case maybe_pop_jump:
- case pop_failure_jump:
- case jump:
- p1 = p + 1;
- EXTRACT_NUMBER_AND_INCR(mcnt, p1);
- p1 += mcnt;
-
- if ((is_a_jump_n
- && (re_opcode_t) * p1 ==
- succeed_n)
- || (!is_a_jump_n
- && (re_opcode_t) * p1 ==
- on_failure_jump))
- goto fail;
- break;
- default:
- /* do nothing */ ;
- }
- }
-
- if (d >= string1 && d <= end1)
- dend = end_match_1;
- } else
- break; /* Matching at this starting point really fails. */
- } /* for (;;) */
-
- if (best_regs_set)
- goto restore_best_regs;
-
- FREE_VARIABLES();
-
- return -1; /* Failure to match. */
-} /* re_match_2 */
-
-/* Subroutine definitions for re_match_2. */
-
-
-/* We are passed P pointing to a register number after a start_memory.
-
- Return true if the pattern up to the corresponding stop_memory can
- match the empty string, and false otherwise.
-
- If we find the matching stop_memory, sets P to point to one past its number.
- Otherwise, sets P to an undefined byte less than or equal to END.
-
- We don't handle duplicates properly (yet). */
-
-static boolean group_match_null_string_p(p, end, reg_info)
-unsigned char **p, *end;
-register_info_type *reg_info;
-{
- int mcnt;
- /* Point to after the args to the start_memory. */
- unsigned char *p1 = *p + 2;
-
- while (p1 < end) {
- /* Skip over opcodes that can match nothing, and return true or
- false, as appropriate, when we get to one that can't, or to the
- matching stop_memory. */
-
- switch ((re_opcode_t) * p1) {
- /* Could be either a loop or a series of alternatives. */
- case on_failure_jump:
- p1++;
- EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-
- /* If the next operation is not a jump backwards in the
- pattern. */
-
- if (mcnt >= 0) {
- /* Go through the on_failure_jumps of the alternatives,
- seeing if any of the alternatives cannot match nothing.
- The last alternative starts with only a jump,
- whereas the rest start with on_failure_jump and end
- with a jump, e.g., here is the pattern for `a|b|c':
-
- /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
- /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
- /exactn/1/c
-
- So, we have to first go through the first (n-1)
- alternatives and then deal with the last one separately. */
-
-
- /* Deal with the first (n-1) alternatives, which start
- with an on_failure_jump (see above) that jumps to right
- past a jump_past_alt. */
-
- while ((re_opcode_t) p1[mcnt - 3] ==
- jump_past_alt) {
- /* `mcnt' holds how many bytes long the alternative
- is, including the ending `jump_past_alt' and
- its number. */
-
- if (!alt_match_null_string_p
- (p1, p1 + mcnt - 3, reg_info))
- return false;
-
- /* Move to right after this alternative, including the
- jump_past_alt. */
- p1 += mcnt;
-
- /* Break if it's the beginning of an n-th alternative
- that doesn't begin with an on_failure_jump. */
- if ((re_opcode_t) * p1 !=
- on_failure_jump)
- break;
-
- /* Still have to check that it's not an n-th
- alternative that starts with an on_failure_jump. */
- p1++;
- EXTRACT_NUMBER_AND_INCR(mcnt, p1);
- if ((re_opcode_t) p1[mcnt - 3] !=
- jump_past_alt) {
- /* Get to the beginning of the n-th alternative. */
- p1 -= 3;
- break;
- }
- }
-
- /* Deal with the last alternative: go back and get number
- of the `jump_past_alt' just before it. `mcnt' contains
- the length of the alternative. */
- EXTRACT_NUMBER(mcnt, p1 - 2);
-
- if (!alt_match_null_string_p
- (p1, p1 + mcnt, reg_info))
- return false;
-
- p1 += mcnt; /* Get past the n-th alternative. */
- } /* if mcnt > 0 */
- break;
-
-
- case stop_memory:
- assert(p1[1] == **p);
- *p = p1 + 2;
- return true;
-
-
- default:
- if (!common_op_match_null_string_p
- (&p1, end, reg_info))
- return false;
- }
- } /* while p1 < end */
-
- return false;
-} /* group_match_null_string_p */
-
-
-/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
- It expects P to be the first byte of a single alternative and END one
- byte past the last. The alternative can contain groups. */
-
-static boolean alt_match_null_string_p(p, end, reg_info)
-unsigned char *p, *end;
-register_info_type *reg_info;
-{
- int mcnt;
- unsigned char *p1 = p;
-
- while (p1 < end) {
- /* Skip over opcodes that can match nothing, and break when we get
- to one that can't. */
-
- switch ((re_opcode_t) * p1) {
- /* It's a loop. */
- case on_failure_jump:
- p1++;
- EXTRACT_NUMBER_AND_INCR(mcnt, p1);
- p1 += mcnt;
- break;
-
- default:
- if (!common_op_match_null_string_p
- (&p1, end, reg_info))
- return false;
- }
- } /* while p1 < end */
-
- return true;
-} /* alt_match_null_string_p */
-
-
-/* Deals with the ops common to group_match_null_string_p and
- alt_match_null_string_p.
-
- Sets P to one after the op and its arguments, if any. */
-
-static boolean common_op_match_null_string_p(p, end, reg_info)
-unsigned char **p, *end;
-register_info_type *reg_info;
-{
- int mcnt;
- boolean ret;
- int reg_no;
- unsigned char *p1 = *p;
-
- switch ((re_opcode_t) * p1++) {
- case no_op:
- case begline:
- case endline:
- case begbuf:
- case endbuf:
- case wordbeg:
- case wordend:
- case wordbound:
- case notwordbound:
- break;
-
- case start_memory:
- reg_no = *p1;
- assert(reg_no > 0 && reg_no <= MAX_REGNUM);
- ret = group_match_null_string_p(&p1, end, reg_info);
-
- /* Have to set this here in case we're checking a group which
- contains a group and a back reference to it. */
-
- if (REG_MATCH_NULL_STRING_P(reg_info[reg_no]) ==
- MATCH_NULL_UNSET_VALUE)
- REG_MATCH_NULL_STRING_P(reg_info[reg_no]) = ret;
-
- if (!ret)
- return false;
- break;
-
- /* If this is an optimized succeed_n for zero times, make the jump. */
- case jump:
- EXTRACT_NUMBER_AND_INCR(mcnt, p1);
- if (mcnt >= 0)
- p1 += mcnt;
- else
- return false;
- break;
-
- case succeed_n:
- /* Get to the number of times to succeed. */
- p1 += 2;
- EXTRACT_NUMBER_AND_INCR(mcnt, p1);
-
- if (mcnt == 0) {
- p1 -= 4;
- EXTRACT_NUMBER_AND_INCR(mcnt, p1);
- p1 += mcnt;
- } else
- return false;
- break;
-
- case duplicate:
- if (!REG_MATCH_NULL_STRING_P(reg_info[*p1]))
- return false;
- break;
-
- case set_number_at:
- p1 += 4;
-
- default:
- /* All other opcodes mean we cannot match the empty string. */
- return false;
- }
-
- *p = p1;
- return true;
-} /* common_op_match_null_string_p */
-
-
-/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
- bytes; nonzero otherwise. */
-
-static int bcmp_translate(s1, s2, len, translate)
-const char *s1, *s2;
-register int len;
-char *translate;
-{
- register const unsigned char *p1 = (const unsigned char *) s1,
- *p2 = (const unsigned char *) s2;
- while (len) {
- if (translate[*p1++] != translate[*p2++])
- return 1;
- len--;
- }
- return 0;
-}
-
-/* Entry points for GNU code. */
-
-/* re_compile_pattern is the GNU regular expression compiler: it
- compiles PATTERN (of length SIZE) and puts the result in BUFP.
- Returns 0 if the pattern was valid, otherwise an error string.
-
- Assumes the `allocated' (and perhaps `buffer') and `translate' fields
- are set in BUFP on entry.
-
- We call regex_compile to do the actual compilation. */
-
-const char *re_compile_pattern(pattern, length, bufp)
-const char *pattern;
-size_t length;
-struct re_pattern_buffer *bufp;
-{
- reg_errcode_t ret;
-
- /* GNU code is written to assume at least RE_NREGS registers will be set
- (and at least one extra will be -1). */
- bufp->regs_allocated = REGS_UNALLOCATED;
-
- /* And GNU code determines whether or not to get register information
- by passing null for the REGS argument to re_match, etc., not by
- setting no_sub. */
- bufp->no_sub = 0;
-
- /* Match anchors at newline. */
- bufp->newline_anchor = 1;
-
- ret = regex_compile(pattern, length, re_syntax_options, bufp);
-
- return re_error_msg[(int) ret];
-}
-
-/* Entry points compatible with 4.2 BSD regex library. We don't define
- them if this is an Emacs or POSIX compilation. */
-
-/* POSIX.2 functions. Don't define these for Emacs. */
-
-#if !NO_POSIX_COMPAT
-
-/* regcomp takes a regular expression as a string and compiles it.
-
- PREG is a regex_t *. We do not expect any fields to be initialized,
- since POSIX says we shouldn't. Thus, we set
-
- `buffer' to the compiled pattern;
- `used' to the length of the compiled pattern;
- `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
- REG_EXTENDED bit in CFLAGS is set; otherwise, to
- RE_SYNTAX_POSIX_BASIC;
- `newline_anchor' to REG_NEWLINE being set in CFLAGS;
- `fastmap' and `fastmap_accurate' to zero;
- `re_nsub' to the number of subexpressions in PATTERN.
-
- PATTERN is the address of the pattern string.
-
- CFLAGS is a series of bits which affect compilation.
-
- If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
- use POSIX basic syntax.
-
- If REG_NEWLINE is set, then . and [^...] don't match newline.
- Also, regexec will try a match beginning after every newline.
-
- If REG_ICASE is set, then we considers upper- and lowercase
- versions of letters to be equivalent when matching.
-
- If REG_NOSUB is set, then when PREG is passed to regexec, that
- routine will report only success or failure, and nothing about the
- registers.
-
- It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
- the return codes and their meanings.) */
-
-int regcomp(preg, pattern, cflags)
-regex_t *preg;
-const char *pattern;
-int cflags;
-{
- reg_errcode_t ret;
- reg_syntax_t syntax
- = (cflags & REG_EXTENDED) ?
- RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
-
- /* regex_compile will allocate the space for the compiled pattern. */
- preg->buffer = 0;
- preg->allocated = 0;
- preg->used = 0;
-
- /* Don't bother to use a fastmap when searching. This simplifies the
- REG_NEWLINE case: if we used a fastmap, we'd have to put all the
- characters after newlines into the fastmap. This way, we just try
- every character. */
- preg->fastmap = 0;
-
- if (cflags & REG_ICASE) {
- unsigned i;
-
- preg->translate = (char *) malloc(CHAR_SET_SIZE);
- if (preg->translate == NULL)
- return (int) REG_ESPACE;
-
- /* Map uppercase characters to corresponding lowercase ones. */
- for (i = 0; i < CHAR_SET_SIZE; i++)
- preg->translate[i] = ISUPPER(i) ? tolower(i) : i;
- } else
- preg->translate = NULL;
-
- /* If REG_NEWLINE is set, newlines are treated differently. */
- if (cflags & REG_NEWLINE) { /* REG_NEWLINE implies neither . nor [^...] match newline. */
- syntax &= ~RE_DOT_NEWLINE;
- syntax |= RE_HAT_LISTS_NOT_NEWLINE;
- /* It also changes the matching behavior. */
- preg->newline_anchor = 1;
- } else
- preg->newline_anchor = 0;
-
- preg->no_sub = !!(cflags & REG_NOSUB);
-
- /* POSIX says a null character in the pattern terminates it, so we
- can use strlen here in compiling the pattern. */
- ret = regex_compile(pattern, strlen(pattern), syntax, preg);
-
- /* POSIX doesn't distinguish between an unmatched open-group and an
- unmatched close-group: both are REG_EPAREN. */
- if (ret == REG_ERPAREN)
- ret = REG_EPAREN;
-
- return (int) ret;
-}
-
-
-/* regexec searches for a given pattern, specified by PREG, in the
- string STRING.
-
- If NMATCH is zero or REG_NOSUB was set in the cflags argument to
- `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
- least NMATCH elements, and we set them to the offsets of the
- corresponding matched substrings.
-
- EFLAGS specifies `execution flags' which affect matching: if
- REG_NOTBOL is set, then ^ does not match at the beginning of the
- string; if REG_NOTEOL is set, then $ does not match at the end.
-
- We return 0 if we find a match and REG_NOMATCH if not. */
-
-int regexec(preg, string, nmatch, pmatch, eflags)
-const regex_t *preg;
-const char *string;
-size_t nmatch;
-regmatch_t pmatch[];
-int eflags;
-{
- int ret;
- struct re_registers regs;
- regex_t private_preg;
- int len = strlen(string);
- boolean want_reg_info = !preg->no_sub && nmatch > 0;
-
- private_preg = *preg;
-
- private_preg.not_bol = !!(eflags & REG_NOTBOL);
- private_preg.not_eol = !!(eflags & REG_NOTEOL);
-
- /* The user has told us exactly how many registers to return
- information about, via `nmatch'. We have to pass that on to the
- matching routines. */
- private_preg.regs_allocated = REGS_FIXED;
-
- if (want_reg_info) {
- regs.num_regs = nmatch;
- regs.start = TALLOC(nmatch, regoff_t);
- regs.end = TALLOC(nmatch, regoff_t);
- if (regs.start == NULL || regs.end == NULL)
- return (int) REG_NOMATCH;
- }
-
- /* Perform the searching operation. */
- ret = re_search(&private_preg, string, len,
- /* start: */ 0, /* range: */ len,
- want_reg_info ? ®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 */
+++ /dev/null
-/* 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__ */
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);
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;
}
* about to be removed
*/
p = strchr(*f, ' ');
+ if (!p)
+ /* Internal error, feature string inconsistent */
+ return 1;
while (*p == ' ')
p++;
p--;
#include <sys/types.h>
+#include "prio.h"
+
#define WWID_SIZE 128
#define SERIAL_SIZE 65
#define NODE_NAME_SIZE 224
#define PATH_STR_SIZE 16
-#define PARAMS_SIZE 1024
+#define PARAMS_SIZE 4096
#define FILE_NAME_SIZE 256
#define CALLOUT_MAX_SIZE 256
#define BLK_DEV_SIZE 33
#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
};
enum queue_without_daemon_states {
- QUE_NO_DAEMON_UNDEF,
QUE_NO_DAEMON_OFF,
QUE_NO_DAEMON_ON,
+ QUE_NO_DAEMON_FORCE,
};
enum pgtimeouts {
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 {
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
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];
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;
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;
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);
vector_foreach_slot (pathvec, pp, i) {
if (pp->mpp == mpp) {
- condlog(4, "%s: orphaned", pp->dev);
- orphan_path(pp);
+ orphan_path(pp, "map flushed");
}
}
}
static void
set_multipath_wwid (struct multipath * mpp)
{
- if (mpp->wwid)
+ if (strlen(mpp->wwid))
return;
dm_get_uuid(mpp->alias, mpp->wwid);
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;
}
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;
}
{
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:
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) {
select_rr_weight(mpp);
select_pgfailback(mpp);
set_no_path_retry(mpp);
- select_pg_timeout(mpp);
select_flush_on_last_del(mpp);
}
/*
* 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);
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;
}
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);
#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];
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;
}
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;
}
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;
}
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);
}
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);
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
#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;
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);
+ }
}
/*
service_uevq(&uevq_tmp);
}
condlog(3, "Terminating uev service queue");
+ uevq_cleanup(&uevq);
return 0;
}
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++;
return 1;
}
-int uevent_listen(void)
+int uevent_listen(struct udev *udev)
{
int err;
struct udev_monitor *monitor = NULL;
* 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
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) {
if (need_failback)
err = failback_listen();
pthread_cleanup_pop(1);
- pthread_mutex_destroy(uevq_lockp);
- pthread_cond_destroy(uev_condp);
return err;
}
#define NETLINK_KOBJECT_UEVENT 15
#endif
+struct udev;
+
struct uevent {
struct list_head node;
struct udev_device *udev;
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);
#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
if (!str1 || !strlen(str1))
return 0;
- if (strlen(str1) > str2len)
+ if (strlen(str1) >= str2len)
return 0;
if (!str2)
strncpy(str2, p, str2len);
str2[str2len - 1] = '\0';
- strchop(str2);
- return strlen(str2);
+ return strchop(str2);
}
int
*word = MALLOC(len + 1);
if (!*word) {
- condlog(0, "get_word : oom\n");
+ condlog(0, "get_word : oom");
return 0;
}
strncpy(*word, sentence, len);
bytes++;
}
- /* If size == 0 there is no space for a final null... */
- if (size)
+ if (bytes == size)
*q = '\0';
return bytes;
}
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)
{
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;
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;
}
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;
}
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;
#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)
#include <sys/poll.h>
#include <signal.h>
#include <errno.h>
+#ifdef USE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
#include "memory.h"
#include "uxsock.h"
+#include "debug.h"
/*
* connect to a unix domain socket
*/
int ux_socket_connect(const char *name)
{
- int fd;
+ int fd, len;
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, name, sizeof(addr.sun_path));
+ addr.sun_family = AF_LOCAL;
+ addr.sun_path[0] = '\0';
+ len = strlen(name) + 1 + sizeof(sa_family_t);
+ strncpy(&addr.sun_path[1], name, len);
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (fd == -1) {
+ condlog(3, "Couldn't create ux_socket, error %d", errno);
return -1;
}
- if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
+ condlog(3, "Couldn't connect to ux_socket, error %d", errno);
close(fd);
return -1;
}
*/
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;
}
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;
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;
{
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;
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;
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--);
}
/* 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--)
#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"
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);
}
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);
}
/*
*/
int waiteventloop (struct event_thread *waiter)
{
+ sigset_t set, oldset;
int event_nr;
int r;
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;
waiter = (struct event_thread *)et;
pthread_cleanup_push(free_waiter, et);
- block_signal(SIGUSR1, NULL);
- block_signal(SIGHUP, NULL);
while (1) {
r = waiteventloop(waiter);
#include <string.h>
#include <limits.h>
#include <stdio.h>
+#include <sys/types.h>
#include "checkers.h"
#include "vector.h"
}
int
+replace_wwids(vector mp)
+{
+ int i, fd, can_write;
+ struct multipath * mpp;
+ size_t len;
+ int ret = -1;
+
+ fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
+ if (fd < 0)
+ goto out;
+ if (!can_write) {
+ condlog(0, "cannot replace wwids. wwids file is read-only");
+ goto out_file;
+ }
+ if (ftruncate(fd, 0) < 0) {
+ condlog(0, "cannot truncate wwids file : %s", strerror(errno));
+ goto out_file;
+ }
+ if (lseek(fd, 0, SEEK_SET) < 0) {
+ condlog(0, "cannot seek to the start of the file : %s",
+ strerror(errno));
+ goto out_file;
+ }
+ len = strlen(WWIDS_FILE_HEADER);
+ if (write_all(fd, WWIDS_FILE_HEADER, len) != len) {
+ condlog(0, "Can't write wwid file header : %s",
+ strerror(errno));
+ /* cleanup partially written header */
+ if (ftruncate(fd, 0) < 0)
+ condlog(0, "Cannot truncate header : %s",
+ strerror(errno));
+ goto out_file;
+ }
+ if (!mp || !mp->allocated) {
+ ret = 0;
+ goto out_file;
+ }
+ vector_foreach_slot(mp, mpp, i) {
+ if (write_out_wwid(fd, mpp->wwid) < 0)
+ goto out_file;
+ }
+ ret = 0;
+out_file:
+ close(fd);
+out:
+ return ret;
+}
+
+int
+do_remove_wwid(int fd, char *str) {
+ char buf[4097];
+ char *ptr;
+ off_t start = 0;
+ int bytes;
+
+ while (1) {
+ if (lseek(fd, start, SEEK_SET) < 0) {
+ condlog(0, "wwid file read lseek failed : %s",
+ strerror(errno));
+ return -1;
+ }
+ bytes = read(fd, buf, 4096);
+ if (bytes < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ condlog(0, "failed to read from wwids file : %s",
+ strerror(errno));
+ return -1;
+ }
+ if (!bytes) /* didn't find wwid to remove */
+ return 1;
+ buf[bytes] = '\0';
+ ptr = strstr(buf, str);
+ if (ptr != NULL) {
+ condlog(3, "found '%s'", str);
+ if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) {
+ condlog(0, "write lseek failed : %s",
+ strerror(errno));
+ return -1;
+ }
+ while (1) {
+ if (write(fd, "#", 1) < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ condlog(0, "failed to write to wwids file : %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+ }
+ }
+ ptr = strrchr(buf, '\n');
+ if (ptr == NULL) { /* shouldn't happen, assume it is EOF */
+ condlog(4, "couldn't find newline, assuming end of file");
+ return 1;
+ }
+ start = start + (ptr - buf) + 1;
+ }
+}
+
+
+int
+remove_wwid(char *wwid) {
+ int fd, len, can_write;
+ char *str;
+ int ret = -1;
+
+ len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */
+ str = malloc(len);
+ if (str == NULL) {
+ condlog(0, "can't allocate memory to remove wwid : %s",
+ strerror(errno));
+ return -1;
+ }
+ if (snprintf(str, len, "/%s/\n", wwid) >= len) {
+ condlog(0, "string overflow trying to remove wwid");
+ goto out;
+ }
+ condlog(3, "removing line '%s' from wwids file", str);
+ fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
+ if (fd < 0)
+ goto out;
+ if (!can_write) {
+ condlog(0, "cannot remove wwid. wwids file is read-only");
+ goto out_file;
+ }
+ ret = do_remove_wwid(fd, str);
+
+out_file:
+ close(fd);
+out:
+ free(str);
+ return ret;
+}
+
+int
check_wwids_file(char *wwid, int write_wwid)
{
int fd, can_write, found, ret;
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 */
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
#include <vector.h>
#include <structs.h>
#include <getopt.h>
+#include <libudev.h>
#include <mpath_persist.h>
#include "main.h"
#include <pthread.h>
int noisy = 0;
int num_transport =0;
void *resp = NULL;
- struct transportid * tmp;
+ struct transportid * tmp;
+ struct udev *udev = NULL;
if (optind == argc)
{
exit (1);
}
-
- mpath_lib_init();
+ udev = udev_new();
+ mpath_lib_init(udev);
memset(transportids,0,MPATH_MX_TIDS);
while (1)
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;
}
##
#defaults {
# #
-# # name : udev_dir
-# # desc : directory where udev creates its device nodes
-# # default : /dev
-# #
-# udev_dir /dev
-#
-# #
# # name : polling_interval
# # scope : multipathd
# # desc : interval between two path checks in seconds. For
# # scope : multipath & multipathd
# # desc : the default path selector algorithm to use
# # these algorithms are offered by the kernel multipath target
-# # values : "round-robin 0"
-# # default : "round-robin 0"
+# # values : "round-robin 0" = Loop through every path in the path group,
+# # sending the same amount of IO to each.
+# # "queue-length 0" = Send the next bunch of IO down the path
+# # with the least amount of outstanding IO.
+# # "service-time 0" = Choose the path for the next bunch of IO
+# # based on the amount of outstanding IO to
+# # the path and its relative throughput.
+# # default : "service-time 0"
# #
-# path_selector "round-robin 0"
+# path_selector "service-time 0"
#
# #
# # name : path_grouping_policy
# path_grouping_policy multibus
#
# #
+# # name : uid_attribute
+# # scope : multipath & multipathd
+# # desc : the default udev attribute from which the path
+# # identifier should be generated.
+# # default : ID_SERIAL
+# #
+# uid_attribute "ID_SERIAL"
+#
+# #
# # name : getuid_callout
# # scope : multipath & multipathd
# # desc : the default program and args to callout to obtain a unique
-# # path identifier. Absolute path required
+# # path identifier. This parameter is deprecated.
+# # This parameter is deprecated, superseded by uid_attribute
# # default : /lib/udev/scsi_id --whitelisted --device=/dev/%n
# #
# getuid_callout "/lib/udev/scsi_id --whitelisted --device=/dev/%n"
# #
# # name : features
# # scope : multipath & multipathd
-# # desc : The default extra features of multipath devices. The
-# # only existing feature currently is queue_if_no_path, which
-# # is the same as setting no_path_retry to queue.
-# # values : "1 queue_if_no_path"
-# # default : (null)
-# #
+# # desc : The default extra features of multipath devices.
+# # Syntax is "num[ feature_0 feature_1 ...]", where `num' is the
+# # number of features in the following (possibly empty) list of
+# # features.
+# # values : queue_if_no_path = Queue IO if no path is active; consider
+# # using the `no_path_retry' keyword instead.
+# # no_partitions = Disable automatic partitions generation via
+# # kpartx.
+# # default : "0"
+# #
+# features "0"
# features "1 queue_if_no_path"
+# features "1 no_partitions"
+# features "2 queue_if_no_path no_partitions"
#
# #
# # name : path_checker, checker
# # name : rr_min_io
# # scope : multipath & multipathd
# # desc : the number of IO to route to a path before switching
-# # to the next in the same path group
+# # to the next in the same path group for the bio-based
+# # multipath implementation. This parameter is used for
+# # kernels version up to 2.6.31; newer kernel version
+# # use the parameter rr_min_io_rq
# # default : 1000
# #
-# rr_min_io 100
+# rr_min_io 1000
+#
+# #
+# # name : rr_min_io_rq
+# # scope : multipath & multipathd
+# # desc : the number of IO to route to a path before switching
+# # to the next in the same path group for the request-based
+# # multipath implementation. This parameter is used for
+# # kernels versions later than 2.6.31.
+# # default : 1
+# #
+# rr_min_io_rq 1
#
# #
# # name : flush_on_last_del
# # desc : The number of seconds the scsi layer will wait after a
# # problem has been detected on a FC remote port before
# # removing it from the system.
-# # values : n > 0
+# # values : infinity | n > 0
# # default : determined by the OS
# dev_loss_tmo 600
+#
# # name : bindings_file
# # scope : multipath
# # desc : The location of the bindings file that is used with
# # the user_friendly_names option.
# # values : <full_pathname>
# # default : "/var/lib/multipath/bindings"
-# bindings_file "/etc/multipath_bindings"
+# bindings_file "/etc/multipath/bindings"
+#
+# # name : wwids_file
+# # scope : multipath
+# # desc : The location of the wwids file multipath uses to
+# # keep track of the created multipath devices.
+# # values : <full_pathname>
+# # default : "/var/lib/multipath/wwids"
+# wwids_file "/etc/multipath/wwids"
+#
+# # name : reservation_key
+# # scope : multipath
+# # desc : Service action reservation key used by mpathpersist.
+# # values : <key>
+# # default : (null)
+# reservation_key "mpathkey"
#
#}
#
# #
# path_checker directio
#
-# #
-# # name : path_selector
-# # scope : multipathd & multipathd
-# # desc : the path selector algorithm to use for this mpath
-# # these algo are offered by the kernel mpath target
-# # values : "round-robin 0"
-# #
-# path_selector "round-robin 0"
+# # as already described
+# path_selector "service-time 0"
#
-# #
-# # name : features
-# # scope : multipath & multipathd
-# # desc : The extra features of multipath devices. The only
-# # existing feature currently is queue_if_no_path,
-# # which is the same as setting no_path_retry to queue.
-# # values : "1 queue_if_no_path"
-# #
-# features "1 queue_if_no_path"
+# # as already described
+# features "0"
#
# #
# # name : hardware_handler
# 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 {
+#}
OBJS = main.o
CFLAGS += -I$(multipathdir)
-LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath
+LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev
EXEC = multipath
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
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
+#include <libudev.h>
#include <checkers.h>
#include <prio.h>
{
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);
" -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" \
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;
if (!conf->dry_run)
reinstate_paths(mpp);
- remember_wwid(mpp->wwid);
}
return 0;
}
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)
* 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);
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;
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 '?':
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);
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;
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();
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;
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,
*/
free_config(conf);
conf = NULL;
-
+ udev_unref(udev);
#ifdef _DEBUG_
dbg_free_final(NULL);
#endif
.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"
.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
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),
.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
.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
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
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
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
.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
If set to
.I no
use the WWID as the alias. In either case this be will
-be overriden by any specific aliases in the \fImultipaths\fR section.
+be overridden by any specific aliases in the \fImultipaths\fR section.
Default is
.I no
.TP
.TP
.B max_fds
Specify the maximum number of file descriptors that can be opened by multipath
-and multipathd. This is equivalent to ulimit -n. A value of \fImax\fR will set
+and multipathd. This is equivalent to ulimit \-n. A value of \fImax\fR will set
this to the system limit from /proc/sys/fs/nr_open. If this is not set, the
maximum number of open fds is taken from the calling process. It is usually
1024. To be safe, this should be set to the maximum number of paths plus 32,
.TP
.B checker_timeout
Specify the timeout to user for path checkers that issue scsi commands with an
-explict timeout, in seconds; default taken from
+explicit timeout, in seconds; default taken from
.I /sys/block/sd<x>/device/timeout
.TP
.B fast_io_fail_tmo
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
.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
.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
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.
.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
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
.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
.B dev_loss_tmo
.TP
.B flush_on_last_del
+.TP
+.B retain_attached_hw_handler
+.TP
+.B detect_prio
.RE
.PD
.LP
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
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
+++ /dev/null
-#
-# 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"
# 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
$(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)
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
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);
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;
}
__RESIZE,
__RESET,
__RELOAD,
+ __FORCEQ,
__DISABLEQ,
__RESTOREQ,
__PATHS,
#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)
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);
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,
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;
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) {
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) {
char *alias;
int rc;
+ param = convert_dev(param, 0);
condlog(2, "%s: remove map (operator)", param);
minor = dm_get_minor(param);
if (minor < 0) {
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);
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);
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;
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);
}
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);
return 1;
dm_lib_release();
- setup_multipath(vecs, mpp);
+ if (setup_multipath(vecs, mpp) != 0)
+ return 1;
sync_map_state(mpp);
return 0;
}
int
+cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
+{
+ condlog(2, "force queue_without_daemon (operator)");
+ if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF)
+ conf->queue_without_daemon = QUE_NO_DAEMON_FORCE;
+ return 0;
+}
+
+int
+cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
+{
+ condlog(2, "restore queue_without_daemon (operator)");
+ if (conf->queue_without_daemon == QUE_NO_DAEMON_FORCE)
+ conf->queue_without_daemon = QUE_NO_DAEMON_OFF;
+ return 0;
+}
+
+int
cli_restore_queueing(void *v, char **reply, int *len, void *data)
{
struct vectors * vecs = (struct vectors *)data;
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);
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);
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);
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 */
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 */
char * param = get_keyparam(v, PATH);
struct path * pp;
+ param = convert_dev(param, 1);
pp = find_path_by_dev(vecs->pathvec, param);
if (!pp)
{
char * param = get_keyparam(v, MAP);
+ param = convert_dev(param, 0);
condlog(3, "%s: reset devices (operator)", param);
dm_reassign(param);
struct path * pp;
int r;
+ param = convert_dev(param, 1);
pp = find_path_by_dev(vecs->pathvec, param);
if (!pp)
cli_shutdown (void * v, char ** reply, int * len, void * data)
{
condlog(3, "shutdown (operator)");
-
- return exit_daemon(0);
+ exit_daemon();
+ return 0;
}
int
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);
sprintf(*reply,"%d",mpp->prflag);
- *reply[1]='\0';
+ (*reply)[1]='\0';
condlog(3, "%s: reply = %s", param, *reply);
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);
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);
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);
#include <limits.h>
#include <linux/oom.h>
#include <libudev.h>
+#ifdef USE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+#include <semaphore.h>
#include <mpath_persist.h>
+#include <time.h>
/*
* libcheckers
#include <prio.h>
#include <pgpolicies.h>
#include <uevent.h>
+#include <log.h>
#include "main.h"
#include "pidfile.h"
#include "cli_handlers.h"
#include "lock.h"
#include "waiter.h"
+#include "wwids.h"
#define FILE_NAME_SIZE 256
#define CMDSIZE 160
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)
{
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
/*
* 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();
}
else {
dm_lib_release();
- condlog(2, "%s: devmap removed", mpp->alias);
+ condlog(2, "%s: map flushed", mpp->alias);
}
orphan_paths(vecs->pathvec, mpp);
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);
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);
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) {
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;
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)
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) {
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
fail_map:
remove_map(mpp, vecs, 1);
fail:
- orphan_path(pp);
+ orphan_path(pp, "failed to add path");
return 1;
}
}
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);
}
}
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);
+ }
}
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;
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");
static void *
uxlsnrloop (void * ap)
{
- block_signal(SIGUSR1, NULL);
- block_signal(SIGHUP, NULL);
-
if (cli_init())
return NULL;
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);
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 *
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;
return 0;
}
-void
+/*
+ * Returns '1' if the path has been checked, '0' otherwise
+ */
+int
check_path (struct vectors * vecs, struct path * pp)
{
int newstate;
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,
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
*/
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;
}
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
pp->mpp->failback_tick = 0;
pp->mpp->stat_path_failures++;
- return;
+ return 1;
}
if(newstate == PATH_UP || newstate == PATH_GHOST){
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
(chkr_new_path_up && followover_should_failback(pp)))
switch_pathgroup(pp->mpp);
}
+ return 1;
}
static void *
struct path *pp;
int count = 0;
unsigned int i;
- sigset_t old;
mlockall(MCL_CURRENT | MCL_FUTURE);
vecs = (struct vectors *)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) {
}
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;
sync_maps_state(mpvec);
vector_foreach_slot(mpvec, mpp, i){
+ remember_wwid(mpp->wwid);
update_map_pr(mpp);
}
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);
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);
#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");
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");
}
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);
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;
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);
*/
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)
/* 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);
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();
/*
*/
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
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;
conf->verbosity = atoi(optarg);
break;
+ case 's':
+ logsink = -1;
+ break;
case 'k':
uxclnt(optarg);
exit(0);
exit(0);
}
- if (!logsink)
+ if (logsink < 1)
err = 0;
else
err = daemonize();
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 )
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))
{
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;
}
}
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;
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);
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 *);
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 */
.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
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.
.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
.B quit|exit
End interactive session.
+.SH "SYSTEMD INTEGRATION"
+When compiled with systemd support two systemd service files are
+installed,
+.I multipathd.service
+and
+.I multipathd.socket
+The
+.I multipathd.socket
+service instructs systemd to intercept the CLI command socket, so
+that any call to the CLI interface will start-up the daemon if
+required.
+The
+.I multipathd.service
+file carries the definitions for controlling the multipath daemon.
+The daemon itself uses the
+.B sd_notify(3)
+interface to communicate with systemd. The following unit keywords are
+recognized:
+.TP
+.I WatchdogSec=
+Enables the internal watchdog from systemd. multipath will send a
+notification via
+.B sd_notify(3)
+to systemd to reset the watchdog. If specified the
+.I polling_interval
+and
+.I max_polling_interval
+settings will be overridden by the watchdog settings.
+
+Please note that systemd prior to version 207 has issues which prevent
+the systemd-provided watchdog from working correctly. So the watchdog
+is not enabled per default, but has to be enabled manually by updating
+the multipathd.service file.
+.TP
+.I OOMScoreAdjust=
+Overrides the internal OOM adjust mechanism
+.TP
+.I LimitNOFILE=
+Overrides the
+.I max_fds
+configuration setting.
+
.SH "SEE ALSO"
.BR multipath (8)
.BR kpartx (8)
-.BR hotplug (8)
+.BR sd_notify (3)
+.BR system.service (5)
.SH "AUTHORS"
.B multipathd
was developed by Christophe Varoqui, <christophe.varoqui@opensvc.com> and others.
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
}
start
;;
stop)
- stop
+ check_root
+ force_stop
;;
force-stop)
force_stop
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
[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
--- /dev/null
+[Socket]
+ListenStream=@/org/kernel/linux/storage/multipathd
+
+[Install]
+WantedBy=sockets.target
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
*/
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;
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);
/*
* Original author : tridge@samba.org, January 2002
- *
+ *
* Copyright (c) 2005 Christophe Varoqui
* Copyright (c) 2005 Benjamin Marzinski, Redhat
*/
/*
* A simple domain socket listener
*/
+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/poll.h>
-
+#include <signal.h>
#include <checkers.h>
-
#include <memory.h>
#include <debug.h>
#include <vector.h>
#include <structs.h>
+#include <structs_vec.h>
#include <uxsock.h>
#include <defaults.h>
+#include "main.h"
#include "cli.h"
#include "uxlsnr.h"
-#define SLEEP_TIME 5000
+struct timespec sleep_time = {5, 0};
struct client {
int fd;
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
int fd;
fd = accept(ux_sock, &addr, &len);
-
+
if (fd == -1)
return;
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;
}
/* 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");
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