r157 - in multipath-tools/trunk: . getuid kpartx libcheckers libmultipath multipath multipathd path_priority path_priority/pp_alua path_priority/pp_balance_units path_priority/pp_emc

Bastian Blank waldi at costa.debian.org
Mon Sep 19 12:44:37 UTC 2005


Author: waldi
Date: Mon Sep 19 12:43:52 2005
New Revision: 157

Added:
   multipath-tools/trunk/getuid/
   multipath-tools/trunk/getuid/usb_id   (contents, props changed)
   multipath-tools/trunk/libcheckers/directio.c
   multipath-tools/trunk/libcheckers/hp_sw.c
   multipath-tools/trunk/libmultipath/print.c
   multipath-tools/trunk/libmultipath/print.h
   multipath-tools/trunk/libmultipath/switchgroup.c
   multipath-tools/trunk/libmultipath/switchgroup.h
   multipath-tools/trunk/libmultipath/uevent.c
   multipath-tools/trunk/libmultipath/uevent.h
   multipath-tools/trunk/libmultipath/uxsock.c
   multipath-tools/trunk/libmultipath/uxsock.h
   multipath-tools/trunk/multipathd/cli.c
   multipath-tools/trunk/multipathd/cli.h
   multipath-tools/trunk/multipathd/cli_handlers.c
   multipath-tools/trunk/multipathd/cli_handlers.h
   multipath-tools/trunk/multipathd/uxclnt.c
   multipath-tools/trunk/multipathd/uxclnt.h
   multipath-tools/trunk/multipathd/uxlsnr.c
   multipath-tools/trunk/multipathd/uxlsnr.h
   multipath-tools/trunk/path_priority/pp_alua/mpath_prio_alua.8
   multipath-tools/trunk/path_priority/pp_balance_units/
   multipath-tools/trunk/path_priority/pp_balance_units/Makefile
   multipath-tools/trunk/path_priority/pp_balance_units/pp_balance_units.c
Removed:
   multipath-tools/trunk/.indent.pro
   multipath-tools/trunk/kpartx/kpartx.dev
   multipath-tools/trunk/multipathd/clone_platform.h
   multipath-tools/trunk/multipathd/copy.c
   multipath-tools/trunk/multipathd/copy.h
   multipath-tools/trunk/path_priority/Makefile
   multipath-tools/trunk/path_priority/pp_balance_units.c
Modified:
   multipath-tools/trunk/   (props changed)
   multipath-tools/trunk/ChangeLog
   multipath-tools/trunk/Makefile
   multipath-tools/trunk/kpartx/dos.c
   multipath-tools/trunk/kpartx/kpartx.c
   multipath-tools/trunk/kpartx/lopart.c
   multipath-tools/trunk/libcheckers/Makefile
   multipath-tools/trunk/libcheckers/checkers.h
   multipath-tools/trunk/libcheckers/path_state.h
   multipath-tools/trunk/libcheckers/readsector0.c
   multipath-tools/trunk/libcheckers/selector.c
   multipath-tools/trunk/libmultipath/Makefile
   multipath-tools/trunk/libmultipath/blacklist.c
   multipath-tools/trunk/libmultipath/blacklist.h
   multipath-tools/trunk/libmultipath/cache.c
   multipath-tools/trunk/libmultipath/cache.h
   multipath-tools/trunk/libmultipath/callout.c
   multipath-tools/trunk/libmultipath/config.c
   multipath-tools/trunk/libmultipath/config.h
   multipath-tools/trunk/libmultipath/debug.c
   multipath-tools/trunk/libmultipath/debug.h
   multipath-tools/trunk/libmultipath/defaults.h
   multipath-tools/trunk/libmultipath/devmapper.c
   multipath-tools/trunk/libmultipath/devmapper.h
   multipath-tools/trunk/libmultipath/dict.c
   multipath-tools/trunk/libmultipath/discovery.c
   multipath-tools/trunk/libmultipath/discovery.h
   multipath-tools/trunk/libmultipath/dmparser.c
   multipath-tools/trunk/libmultipath/hwtable.c
   multipath-tools/trunk/libmultipath/memory.c
   multipath-tools/trunk/libmultipath/memory.h
   multipath-tools/trunk/libmultipath/pgpolicies.c
   multipath-tools/trunk/libmultipath/pgpolicies.h
   multipath-tools/trunk/libmultipath/propsel.c
   multipath-tools/trunk/libmultipath/propsel.h
   multipath-tools/trunk/libmultipath/structs.c
   multipath-tools/trunk/libmultipath/structs.h
   multipath-tools/trunk/libmultipath/util.c
   multipath-tools/trunk/libmultipath/util.h
   multipath-tools/trunk/libmultipath/vector.c
   multipath-tools/trunk/libmultipath/vector.h
   multipath-tools/trunk/multipath.conf.annotated
   multipath-tools/trunk/multipath.conf.synthetic
   multipath-tools/trunk/multipath/Makefile
   multipath-tools/trunk/multipath/main.c
   multipath-tools/trunk/multipath/main.h
   multipath-tools/trunk/multipath/multipath.8
   multipath-tools/trunk/multipathd/Makefile
   multipath-tools/trunk/multipathd/log.c
   multipath-tools/trunk/multipathd/log.h
   multipath-tools/trunk/multipathd/log_pthread.c
   multipath-tools/trunk/multipathd/log_pthread.h
   multipath-tools/trunk/multipathd/main.c
   multipath-tools/trunk/multipathd/main.h
   multipath-tools/trunk/multipathd/multipathd.init.redhat
   multipath-tools/trunk/path_priority/pp_alua/Makefile
   multipath-tools/trunk/path_priority/pp_alua/main.c
   multipath-tools/trunk/path_priority/pp_alua/rtpg.c
   multipath-tools/trunk/path_priority/pp_alua/spc3.h
   multipath-tools/trunk/path_priority/pp_emc/Makefile
Log:
Merge /multipath-tools/upstream/current (0.4.5).


Modified: multipath-tools/trunk/ChangeLog
==============================================================================
--- multipath-tools/trunk/ChangeLog	(original)
+++ multipath-tools/trunk/ChangeLog	Mon Sep 19 12:43:52 2005
@@ -1,4 +1,27 @@
-2005-03-19 multipath-tools-0.4.4
+2005-05-23 multipath-tools-0.4.5
+
+	* [libmultipath] default_prio and prio_callout keyword can be
+	  explicitly set to "none". Suggested by Kiyoshi Ueda, NEC
+	* [path_prio] don't exit pp_balance_units with error when
+	  find_controler() is not successful. It just means no other
+	  path is currently active on this controler.
+	* [path_prio] move balance_units in its own dir
+	* [multipathd] proactively fail_path upon checker up->down
+	  transitions. Suggested by Edward Goggin, EMC
+	* [libmultipath] .priority is clearly an int, not an unsigned
+	  int. /bin/false is now personna non grata as a prio callout.
+	  Kiyoshi Ueda, NEC
+	* [libmultipath] callout.c argv parsing fix. Kiyoshi Ueda,
+	  NEC
+	* [multipathd] check return codes in init_paths(), split out
+	  init_event().
+	* [libmultipath] add find_slot(vec, addr) to vector lib.
+	* [multipath] remove signal sending
+	* [multipathd] use uevent to do paths list housekeeping for
+	  checkers. Remove signal handling.
+	* [libmultipath] add uevent.[ch]
+
+2005-04-23 multipath-tools-0.4.4
 
 	* [path_prio] clarify pp_alua licensing. Stefan Bader, IBM.
 	* [devmap_name] add a target_type filter (suggested by Hannes)

Modified: multipath-tools/trunk/Makefile
==============================================================================
--- multipath-tools/trunk/Makefile	(original)
+++ multipath-tools/trunk/Makefile	Mon Sep 19 12:43:52 2005
@@ -20,12 +20,9 @@
 export KRNLSRC
 export KRNLOBJ
 
-BUILDDIRS = libmultipath libcheckers path_priority \
-	    devmap_name multipath multipathd kpartx
-ALLDIRS	= $(shell find . -type d -maxdepth 1 -mindepth 1)
+BUILDDIRS = $(shell find . -mindepth 2 -name Makefile -exec dirname {} \;)
 
 VERSION = $(shell basename ${PWD} | cut -d'-' -f3)
-INSTALLDIRS = devmap_name multipath multipathd kpartx path_priority
 
 all: recurse
 
@@ -36,17 +33,17 @@
 	done
 
 recurse_clean:
-	@for dir in $(ALLDIRS); do\
+	@for dir in $(BUILDDIRS); do\
 	$(MAKE) -C $$dir clean || exit $?; \
 	done
 
 recurse_install:
-	@for dir in $(INSTALLDIRS); do\
+	@for dir in $(BUILDDIRS); do\
 	$(MAKE) -C $$dir install || exit $?; \
 	done
 
 recurse_uninstall:
-	@for dir in $(INSTALLDIRS); do\
+	@for dir in $(BUILDDIRS); do\
 	$(MAKE) -C $$dir uninstall || exit $?; \
 	done
 

Added: multipath-tools/trunk/getuid/usb_id
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/getuid/usb_id	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,6 @@
+#!/bin/sh
+#
+# copy in /bin and add this line to a device block in multipath.conf :
+# getuid_callout          "/bin/usb_id %n"
+#
+cd /sys/block/$1 && cd $(ls -l device|cut -d">" -f2) && cd ../../../.. && cat serial

Modified: multipath-tools/trunk/kpartx/dos.c
==============================================================================
--- multipath-tools/trunk/kpartx/dos.c	(original)
+++ multipath-tools/trunk/kpartx/dos.c	Mon Sep 19 12:43:52 2005
@@ -26,7 +26,7 @@
 		if (++loopct > 100)
 			return n;
 
-		bp = getblock(fd, here);
+		bp = (unsigned char *)getblock(fd, here);
 		if (bp == NULL)
 			return n;
 
@@ -74,7 +74,7 @@
 	int i, n=0;
 	unsigned char *bp;
 
-	bp = getblock(fd, offset);
+	bp = (unsigned char *)getblock(fd, offset);
 	if (bp == NULL)
 		return -1;
 

Modified: multipath-tools/trunk/kpartx/kpartx.c
==============================================================================
--- multipath-tools/trunk/kpartx/kpartx.c	(original)
+++ multipath-tools/trunk/kpartx/kpartx.c	Mon Sep 19 12:43:52 2005
@@ -429,6 +429,9 @@
 		if (n > 0)
 			break;
 	}
+	dm_lib_release();
+	dm_lib_exit();
+
 	return 0;
 }
 

Modified: multipath-tools/trunk/kpartx/lopart.c
==============================================================================
--- multipath-tools/trunk/kpartx/lopart.c	(original)
+++ multipath-tools/trunk/kpartx/lopart.c	Mon Sep 19 12:43:52 2005
@@ -26,6 +26,18 @@
 #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__)
+typedef unsigned int __kernel_old_dev_t;
+#else
+typedef unsigned short __kernel_old_dev_t;
+#endif
+
+#define dev_t __kernel_old_dev_t
+
 #include <linux/loop.h>
 
 #include "lopart.h"

Modified: multipath-tools/trunk/libcheckers/Makefile
==============================================================================
--- multipath-tools/trunk/libcheckers/Makefile	(original)
+++ multipath-tools/trunk/libcheckers/Makefile	Mon Sep 19 12:43:52 2005
@@ -6,12 +6,12 @@
 
 include ../Makefile.inc
 
-OBJS = readsector0.o tur.o selector.o emc_clariion.o
+OBJS = readsector0.o tur.o selector.o directio.o emc_clariion.o hp_sw.o
 
 all: $(BUILD)
 
 prepare:
-	rm -f core *.o *.gz
+	@file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o *.gz
 
 klibc: prepare $(OBJS)
 	ar rs libcheckers-klibc.a *.o

Modified: multipath-tools/trunk/libcheckers/checkers.h
==============================================================================
--- multipath-tools/trunk/libcheckers/checkers.h	(original)
+++ multipath-tools/trunk/libcheckers/checkers.h	Mon Sep 19 12:43:52 2005
@@ -6,21 +6,25 @@
 #define MAX_CHECKER_MSG_SIZE 256
 
 enum checkers {
-	CHECKER_RESERVED,
+	CHECKER_UNDEF,
 	TUR,
 	READSECTOR0,
-	EMC_CLARIION
+	DIRECTIO,
+	EMC_CLARIION,
+	HP_SW
 };
 
 #define MSG(a) if (msg != NULL) \
-			snprintf(msg, MAX_CHECKER_MSG_SIZE, "%s\n", a);
+		snprintf(msg, MAX_CHECKER_MSG_SIZE, "%s", a);
 
 int get_checker_id (char *);
 void *get_checker_addr (int);
 int get_checker_name (char *, int);
 
 int emc_clariion (int fd, char * msg, void ** ctxt);
+int directio (int fd, char * msg, void ** ctxt);
 int readsector0 (int fd, char * msg, void ** ctxt);
 int tur (int fd, char * msg, void ** ctxt);
+int hp_sw (int fd, char * msg, void ** ctxt);
 
 #endif /* _CHECKERS_H */

Added: multipath-tools/trunk/libcheckers/directio.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libcheckers/directio.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,165 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <errno.h>
+
+#include "path_state.h"
+#include "checkers.h"
+
+#define MSG_DIRECTIO_UNKNOWN	"directio checker is not available"
+#define MSG_DIRECTIO_UP		"directio checker reports path is up"
+#define MSG_DIRECTIO_DOWN	"directio checker reports path is down"
+
+struct readsector0_checker_context {
+	void * dummy;
+};
+
+static int
+direct_read (int fd, unsigned char * buff, int size)
+{
+	long flags;
+	int reset_flags = 0;
+	int res, retval;
+
+	flags = fcntl(fd,F_GETFL);
+
+	if (flags < 0) {
+		return PATH_UNCHECKED;
+	}
+
+	if (!(flags & O_DIRECT)) {
+		flags |= O_DIRECT;
+		if (fcntl(fd,F_SETFL,flags) < 0) {
+			return PATH_UNCHECKED;
+		}
+		reset_flags = 1;
+	}
+
+	while ( (res = read(fd,buff,size)) < 0 && errno == EINTR );
+	if (res < 0) {
+		if (errno == EINVAL) {
+			/* O_DIRECT is not available */
+			retval = PATH_UNCHECKED;
+		} else if (errno == ENOMEM) {
+			retval = PATH_UP;
+		} else {
+			retval = PATH_DOWN;
+		}
+	} else {
+		retval = PATH_UP;
+	}
+	
+	if (reset_flags) {
+		flags &= ~O_DIRECT;
+		/* No point in checking for errors */
+		fcntl(fd,F_SETFL,flags);
+	}
+
+	return retval;
+}
+
+extern int
+directio (int fd, char *msg, void **context)
+{
+	unsigned char *buf, *ptr;
+	struct readsector0_checker_context * ctxt = NULL;
+	unsigned long pgsize, numsect;
+	int ret, blksize;
+
+	pgsize = getpagesize();
+	
+	/*
+	 * caller passed in a context : use its address
+	 */
+	if (context)
+		ctxt = (struct readsector0_checker_context *) (*context);
+
+	/*
+	 * passed in context is uninitialized or volatile context :
+	 * initialize it
+	 */
+	if (!ctxt) {
+		ctxt = malloc(sizeof(struct readsector0_checker_context));
+		memset(ctxt, 0, sizeof(struct readsector0_checker_context));
+
+		if (!ctxt) {
+			MSG("cannot allocate context");
+			return -1;
+		}
+		if (context)
+			*context = ctxt;
+	}
+	if (fd <= 0) {
+		MSG("no usable fd");
+		ret = -1;
+		goto out;
+	}
+	
+	if (ioctl(fd, BLKGETSIZE, &numsect) < 0) {
+		MSG("cannot get number of sectors, set default");
+		numsect = 0;
+	}
+
+	if (ioctl(fd, BLKBSZGET, &blksize) < 0) {
+		MSG("cannot get blocksize, set default");
+		blksize = 512;
+	}
+
+	if (blksize > 4096) {
+		/*
+		 * Sanity check for DASD; BSZGET is broken
+		 */
+		blksize = 4096;
+	}
+
+	if (!blksize) {
+		/*
+		 * Blocksize is 0, assume we can't write
+		 * to this device.
+		 */
+		MSG(MSG_DIRECTIO_DOWN);
+		ret = PATH_DOWN;
+		goto out;
+	}
+
+	buf = (unsigned char *)malloc(blksize + pgsize);
+	if (!buf){
+		goto out;
+	}
+	ptr = (unsigned char *)(((unsigned long)buf + pgsize - 1) &
+				(~(pgsize - 1))); 
+	ret = direct_read(fd, ptr, blksize);
+
+	switch (ret)
+	{
+	case PATH_UNCHECKED:
+		MSG(MSG_DIRECTIO_UNKNOWN);
+		break;
+	case PATH_DOWN:
+		MSG(MSG_DIRECTIO_DOWN);
+		break;
+	case PATH_UP:
+		MSG(MSG_DIRECTIO_UP);
+		break;
+	default:
+		break;
+	}
+	free(buf);
+
+out:
+	/*
+	 * caller told us he doesn't want to keep the context :
+	 * free it
+	 */
+	if (!context)
+		free(ctxt);
+
+	return ret;
+}

Added: multipath-tools/trunk/libcheckers/hp_sw.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libcheckers/hp_sw.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,177 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "path_state.h"
+#include "checkers.h"
+
+#include "../libmultipath/sg_include.h"
+
+#define TUR_CMD_LEN		6
+#define INQUIRY_CMDLEN		6
+#define INQUIRY_CMD		0x12
+#define SENSE_BUFF_LEN		32
+#define DEF_TIMEOUT		60000
+#define SCSI_CHECK_CONDITION	0x2
+#define SCSI_COMMAND_TERMINATED	0x22
+#define SG_ERR_DRIVER_SENSE	0x08
+#define RECOVERED_ERROR		0x01
+#define MX_ALLOC_LEN		255
+#define HEAVY_CHECK_COUNT       10
+
+#define MSG_HP_SW_UP	"hp_sw checker reports path is up"
+#define MSG_HP_SW_DOWN	"hp_sw checker reports path is down"
+#define MSG_HP_SW_GHOST	"hp_sw checker reports path is ghost"
+
+struct sw_checker_context {
+	int run_count;
+};
+
+static int
+do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
+       void *resp, int mx_resp_len, int noisy)
+{
+        unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
+            { INQUIRY_CMD, 0, 0, 0, 0, 0 };
+        unsigned char sense_b[SENSE_BUFF_LEN];
+        struct sg_io_hdr io_hdr;
+                                                                                                                 
+        if (cmddt)
+                inqCmdBlk[1] |= 2;
+        if (evpd)
+                inqCmdBlk[1] |= 1;
+        inqCmdBlk[2] = (unsigned char) pg_op;
+	inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff);
+	inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff);
+        memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof (inqCmdBlk);
+        io_hdr.mx_sb_len = sizeof (sense_b);
+        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+        io_hdr.dxfer_len = mx_resp_len;
+        io_hdr.dxferp = resp;
+        io_hdr.cmdp = inqCmdBlk;
+        io_hdr.sbp = sense_b;
+        io_hdr.timeout = DEF_TIMEOUT;
+ 
+        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
+                return 1;
+ 
+        /* treat SG_ERR here to get rid of sg_err.[ch] */
+        io_hdr.status &= 0x7e;
+        if ((0 == io_hdr.status) && (0 == io_hdr.host_status) &&
+            (0 == io_hdr.driver_status))
+                return 0;
+        if ((SCSI_CHECK_CONDITION == io_hdr.status) ||
+            (SCSI_COMMAND_TERMINATED == io_hdr.status) ||
+            (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) {
+                if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) {
+                        int sense_key;
+                        unsigned char * sense_buffer = io_hdr.sbp;
+                        if (sense_buffer[0] & 0x2)
+                                sense_key = sense_buffer[1] & 0xf;
+                        else
+                                sense_key = sense_buffer[2] & 0xf;
+                        if(RECOVERED_ERROR == sense_key)
+                                return 0;
+                }
+        }
+        return 1;
+}
+
+static int
+do_tur (int fd)
+{
+        unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 };
+        struct sg_io_hdr io_hdr;
+        unsigned char sense_buffer[32];
+
+        memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof (turCmdBlk);
+        io_hdr.mx_sb_len = sizeof (sense_buffer);
+        io_hdr.dxfer_direction = SG_DXFER_NONE;
+        io_hdr.cmdp = turCmdBlk;
+        io_hdr.sbp = sense_buffer;
+        io_hdr.timeout = 20000;
+        io_hdr.pack_id = 0;
+
+        if (ioctl(fd, SG_IO, &io_hdr) < 0)
+		return 1;
+
+        if (io_hdr.info & SG_INFO_OK_MASK)
+		return 1;
+
+	return 0;
+}
+
+extern int
+hp_sw (int fd, char *msg, void **context)
+{
+	char buff[MX_ALLOC_LEN];
+	struct sw_checker_context * ctxt = NULL;
+	int ret;
+
+	/*
+	 * caller passed in a context : use its address
+	 */
+	if (context)
+		ctxt = (struct sw_checker_context *) (*context);
+
+	/*
+	 * passed in context is uninitialized or volatile context :
+	 * initialize it
+	 */
+	if (!ctxt) {
+		ctxt = malloc(sizeof(struct sw_checker_context));
+		memset(ctxt, 0, sizeof(struct sw_checker_context));
+
+		if (!ctxt) {
+			MSG("cannot allocate context");
+			return -1;
+		}
+		if (context)
+			*context = ctxt;
+	}
+	ctxt->run_count++;
+
+	if ((ctxt->run_count % HEAVY_CHECK_COUNT) == 0) {
+		ctxt->run_count = 0;
+		/* do stuff */
+	}
+	if (fd <= 0) {
+		MSG("no usable fd");
+		ret = -1;
+		goto out;
+	}
+	
+	if (0 != do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) {
+		MSG(MSG_HP_SW_DOWN);
+		ret = PATH_DOWN;
+		goto out;
+	}
+
+	if (do_tur(fd)) {
+		MSG(MSG_HP_SW_GHOST);
+                ret = PATH_GHOST;
+        } else {
+		MSG(MSG_HP_SW_UP);
+		ret = PATH_UP;
+	}
+
+out:
+	/*
+	 * caller told us he doesn't want to keep the context :
+	 * free it
+	 */
+	if (!context)
+		free(ctxt);
+
+	return(ret);
+}

Modified: multipath-tools/trunk/libcheckers/path_state.h
==============================================================================
--- multipath-tools/trunk/libcheckers/path_state.h	(original)
+++ multipath-tools/trunk/libcheckers/path_state.h	Mon Sep 19 12:43:52 2005
@@ -2,3 +2,4 @@
 #define PATH_DOWN	1
 #define PATH_UP		2
 #define PATH_SHAKY	3
+#define PATH_GHOST	4

Modified: multipath-tools/trunk/libcheckers/readsector0.c
==============================================================================
--- multipath-tools/trunk/libcheckers/readsector0.c	(original)
+++ multipath-tools/trunk/libcheckers/readsector0.c	Mon Sep 19 12:43:52 2005
@@ -85,7 +85,7 @@
 extern int
 readsector0 (int fd, char *msg, void **context)
 {
-	char buf[512];
+	unsigned char buf[512];
 	struct readsector0_checker_context * ctxt = NULL;
 	int ret;
 

Modified: multipath-tools/trunk/libcheckers/selector.c
==============================================================================
--- multipath-tools/trunk/libcheckers/selector.c	(original)
+++ multipath-tools/trunk/libcheckers/selector.c	Mon Sep 19 12:43:52 2005
@@ -10,8 +10,12 @@
 		return TUR;
 	if (0 == strncmp(str, "readsector0", 11))
 		return READSECTOR0;
+ 	if (0 == strncmp(str, "directio", 8))
+		return DIRECTIO;
  	if (0 == strncmp(str, "emc_clariion", 12))
 		return EMC_CLARIION;
+ 	if (0 == strncmp(str, "hp_sw", 5))
+		return HP_SW;
 	return -1;
 }
 
@@ -27,9 +31,15 @@
 	case READSECTOR0:
 		checker = &readsector0;
 		break;
+	case DIRECTIO:
+		checker = &directio;
+		break;
 	case EMC_CLARIION:
 		checker = &emc_clariion;
 		break;
+	case HP_SW:
+		checker = &hp_sw;
+		break;
 	default:
 		checker = NULL;
 		break;
@@ -49,9 +59,15 @@
 	case READSECTOR0:
 		s = "readsector0";
 		break;
+	case DIRECTIO:
+		s = "directio";
+		break;
 	case EMC_CLARIION:
 		s = "emc_clariion";
 		break;
+	case HP_SW:
+		s = "hp_sw";
+		break;
 	default:
 		s = "undefined";
 		break;

Modified: multipath-tools/trunk/libmultipath/Makefile
==============================================================================
--- multipath-tools/trunk/libmultipath/Makefile	(original)
+++ multipath-tools/trunk/libmultipath/Makefile	Mon Sep 19 12:43:52 2005
@@ -9,7 +9,8 @@
 OBJS = memory.o parser.o vector.o devmapper.o callout.o \
        hwtable.o blacklist.o util.o dmparser.o config.o \
        structs.o cache.o discovery.o propsel.o dict.o \
-       pgpolicies.o debug.o regex.o defaults.o
+       pgpolicies.o debug.o regex.o defaults.o uevent.o \
+       switchgroup.o uxsock.o print.o
 
 CFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes
 
@@ -20,12 +21,14 @@
 all: $(BUILD)
 
 prepare:
-	rm -f core *.o *.gz
+	@rm -f debug.o
+	@file *-$(BUILD).a >/dev/null 2>&1 || rm -f core *.o *.gz
+	@rm -f *-$(BUILD).a
 
-klibc: prepare $(OBJS)
+klibc: $(OBJS)
 	ar rs libmultipath-klibc.a *.o
 
-glibc: prepare $(OBJS)
+glibc: $(OBJS)
 	ar rs libmultipath-glibc.a *.o
 
 install:

Modified: multipath-tools/trunk/libmultipath/blacklist.c
==============================================================================
--- multipath-tools/trunk/libmultipath/blacklist.c	(original)
+++ multipath-tools/trunk/libmultipath/blacklist.c	Mon Sep 19 12:43:52 2005
@@ -10,40 +10,24 @@
 static int
 store_ble (vector blist, char * str)
 {
-	struct blentry * ble;
+	regex_t * ble;
 	
 	if (!str)
 		return 0;
 
-	ble = (struct blentry *)MALLOC(sizeof(struct blentry));
+	ble = MALLOC(sizeof(regex_t));
 
 	if (!ble)
 		goto out;
 
-	ble->preg = MALLOC(sizeof(regex_t));
-
-	if (!ble->preg)
+	if (regcomp(ble, str, REG_EXTENDED|REG_NOSUB))
 		goto out1;
 
-	ble->str = (char *)MALLOC(strlen(str) + 1);
-
-	if (!ble->str)
-		goto out2;
-
-	strcpy(ble->str, str);
-
-	if (regcomp((regex_t *)ble->preg, ble->str, REG_EXTENDED|REG_NOSUB))
-		goto out3;
-
 	if (!vector_alloc_slot(blist))
-		goto out3;
+		goto out1;
 
 	vector_set_slot(blist, ble);
 	return 0;
-out3:
-	FREE(ble->str);
-out2:
-	FREE(ble->preg);
 out1:
 	FREE(ble);
 out:
@@ -55,9 +39,9 @@
 {
 	int r = 0;
 
-	r += store_ble(blist, "(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*");
-	r += store_ble(blist, "hd[a-z]");
-	r += store_ble(blist, "cciss!c[0-9]d[0-9]*");
+	r += store_ble(blist, "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*");
+	r += store_ble(blist, "^hd[a-z]");
+	r += store_ble(blist, "^cciss!c[0-9]d[0-9]*");
 
 	return r;
 }
@@ -66,10 +50,10 @@
 blacklist (vector blist, char * dev)
 {
 	int i;
-	struct blentry *ble;
+	regex_t * ble;
 
 	vector_foreach_slot (blist, ble, i) {
-		if (!regexec(ble->preg, dev, 0, NULL, 0)) {
+		if (!regexec(ble, dev, 0, NULL, 0)) {
 			condlog(3, "%s blacklisted", dev);
 			return 1;
 		}
@@ -92,20 +76,15 @@
 void
 free_blacklist (vector blist)
 {
-	struct blentry * ble;
+	regex_t * ble;
 	int i;
 
 	if (!blist)
 		return;
 
-	vector_foreach_slot (blist, ble, i) {
-		if (ble->str)
-			FREE(ble->str);
-
-		if (ble->preg)
-			FREE(ble->preg);
+	vector_foreach_slot (blist, ble, i)
+		if (ble)
+			FREE(ble);
 
-		FREE(ble);
-	}
 	vector_free(blist);
 }

Modified: multipath-tools/trunk/libmultipath/blacklist.h
==============================================================================
--- multipath-tools/trunk/libmultipath/blacklist.h	(original)
+++ multipath-tools/trunk/libmultipath/blacklist.h	Mon Sep 19 12:43:52 2005
@@ -3,11 +3,6 @@
 
 #define BLIST_ENTRY_SIZE 255
 
-struct blentry {
-	char * str;
-	void * preg;
-};
-
 int setup_default_blist (vector blist);
 int blacklist (vector blist, char * dev);
 int store_regex (vector blist, char * regex);

Modified: multipath-tools/trunk/libmultipath/cache.c
==============================================================================
--- multipath-tools/trunk/libmultipath/cache.c	(original)
+++ multipath-tools/trunk/libmultipath/cache.c	Mon Sep 19 12:43:52 2005
@@ -1,62 +1,45 @@
-#include <stdio.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <time.h>
 
+#include "memory.h"
 #include "vector.h"
 #include "structs.h"
 #include "debug.h"
 #include "cache.h"
+#include "uxsock.h"
+#include "defaults.h"
 
 static void
 revoke_cache_info(struct path * pp)
 {
+	pp->checker_context = NULL;
 	pp->fd = 0;
-}
-
-static int
-lock_fd (int fd, int flag)
-{
-	struct flock fl;
-
-	fl.l_type = flag;
-	fl.l_whence = 0;
-	fl.l_start = 0;
-	fl.l_len = 0;
-
-	alarm(MAX_WAIT);
-
-	if (fcntl(fd, F_SETLKW, &fl) == -1) {
-		condlog(0, "can't take a write lease on cache file\n");
-		return 1;
-	}
-	alarm(0);
-	return 0;
+	pp->mpp = NULL;
+	pp->getuid = NULL;
+	pp->getprio = NULL;
+	pp->checkfn = NULL;
 }
 
 int
 cache_load (vector pathvec)
 {
+	char *reply;
+	size_t len;
 	int fd;
-	int r = 1;
-	off_t record_len;
-	struct path record;
 	struct path * pp;
+	int r = 1;
+	char * p;
 
-	fd = open(CACHE_FILE, O_RDONLY);
+        fd = ux_socket_connect(DEFAULT_SOCKET);
 
-	if (fd < 0)
+	if (fd == -1) {
+		condlog(3, "ux_socket_connect error");
 		return 1;
+	}
 
-	if (lock_fd(fd, F_RDLCK))
-		goto out;
-
-	record_len = sizeof(struct path);
+	send_packet(fd, "dump pathvec", 13);
+	recv_packet(fd, &reply, &len);
 
-	while (read(fd, &record, record_len)) {
+	for (p = reply; p < (reply + len); p += sizeof(struct path)) {
 		pp = alloc_path();
 
 		if (!pp)
@@ -67,63 +50,13 @@
 			goto out;
 		}
 		vector_set_slot(pathvec, pp);
-		memcpy(pp, &record, record_len);
+		memcpy(pp, (void *)p, sizeof(struct path));
 		revoke_cache_info(pp);
 	}
-	r = 0;
-	lock_fd(fd, F_UNLCK);
-out:
-	close(fd);
-	return r;
-}
 
-int
-cache_dump (vector pathvec)
-{
-	int i;
-	int fd;
-	int r = 1;
-	off_t record_len;
-	struct path * pp;
-
-	fd = open(CACHE_TMPFILE, O_RDWR|O_CREAT, 0600);
-
-	if (fd < 0)
-		return 1;
-
-	if (lock_fd(fd, F_WRLCK))
-		goto out;
-
-	ftruncate(fd, 0); 
-	record_len = sizeof(struct path);
-
-	vector_foreach_slot (pathvec, pp, i) {
-		if (write(fd, pp, record_len) < record_len)
-			goto out1;
-	}
-	rename(CACHE_TMPFILE, CACHE_FILE);
 	r = 0;
-out1:
-	lock_fd(fd, F_UNLCK);
 out:
+	FREE(reply);
 	close(fd);
 	return r;
 }
-
-int
-cache_cold (int expire)
-{
-	time_t t;
-	struct stat s;
-
-	if (time(&t) < 0)
-		return 1;
-
-	if(stat(CACHE_FILE, &s))
-		return 1;
-
-	if ((t - s.st_mtime) < expire)
-		return 0;
-
-	return 1;
-}

Modified: multipath-tools/trunk/libmultipath/cache.h
==============================================================================
--- multipath-tools/trunk/libmultipath/cache.h	(original)
+++ multipath-tools/trunk/libmultipath/cache.h	Mon Sep 19 12:43:52 2005
@@ -1,8 +1 @@
-#define CACHE_FILE	"/var/cache/multipath/.multipath.cache"
-#define CACHE_TMPFILE	"/var/cache/multipath/.multipath.cache.tmp"
-#define CACHE_EXPIRE	5
-#define MAX_WAIT	5
-
 int cache_load (vector pathvec);
-int cache_dump (vector pathvec);
-int cache_cold (int expire);

Modified: multipath-tools/trunk/libmultipath/callout.c
==============================================================================
--- multipath-tools/trunk/libmultipath/callout.c	(original)
+++ multipath-tools/trunk/libmultipath/callout.c	Mon Sep 19 12:43:52 2005
@@ -29,6 +29,7 @@
 	int i;
 
 	i = 0;
+
 	if (strchr(path, ' ')) {
 		strfieldcpy(arg, path);
 		pos = arg;
@@ -39,11 +40,13 @@
 				argv[i] = strsep(&pos, "\'");
 				while (pos[0] == ' ')
 					pos++;
-		} else {
+			} else {
 				argv[i] = strsep(&pos, " ");
 			}
 			i++;
 		}
+	} else {
+		argv[i++] = path;
 	}
 	argv[i] =  NULL;
 

Modified: multipath-tools/trunk/libmultipath/config.c
==============================================================================
--- multipath-tools/trunk/libmultipath/config.c	(original)
+++ multipath-tools/trunk/libmultipath/config.c	Mon Sep 19 12:43:52 2005
@@ -14,56 +14,6 @@
 
 #include "../libcheckers/checkers.h"
 
-/*
- * helper function to draw a list of callout binaries found in the config file
- */
-extern int
-push_callout(char * callout)
-{
-	int i;
-	char * bin;
-	char * p;
-
-	/*
-	 * purge command line arguments
-	 */
-	p = callout;
-
-	while (*p != ' ' && *p != '\0')
-		p++;
-
-	if (!conf->binvec)
-		conf->binvec = vector_alloc();
-
-
-	if (!conf->binvec)
-		return 1;
-
-	/*
-	 * if this callout is already stored in binvec, don't store it twice
-	 */
-	vector_foreach_slot (conf->binvec, bin, i)
-		if (memcmp(bin, callout, p - callout) == 0)
-			return 0;
-
-	/*
-	 * else, store it
-	 */
-	bin = MALLOC((p - callout) + 1);
-
-	if (!bin)
-		return 1;
-
-	strncpy(bin, callout, p - callout);
-
-	if (!vector_alloc_slot(conf->binvec))
-		return 1;
-
-	vector_set_slot(conf->binvec, bin);
-
-	return 0;
-}
-
 struct hwentry *
 find_hwe (vector hwtable, char * vendor, char * product)
 {
@@ -89,7 +39,7 @@
 		return NULL;
 
 	vector_foreach_slot (conf->mptable, mpe, i)
-		if (mpe->wwid && strcmp(mpe->wwid, wwid) == 0)
+		if (mpe->wwid && !strcmp(mpe->wwid, wwid))
 			return mpe;
 
 	return NULL;
@@ -192,10 +142,22 @@
 	vector_free(mptable);
 }
 
+struct mpentry *
+alloc_mpe (void)
+{
+	struct mpentry * mpe = (struct mpentry *)
+				MALLOC(sizeof(struct mpentry));
+
+	return mpe;
+}
+
 static struct hwentry *
 alloc_hwe (void)
 {
-	return (struct hwentry *)MALLOC(sizeof(struct hwentry));
+	struct hwentry * hwe = (struct hwentry *)
+				MALLOC(sizeof(struct hwentry));
+
+	return hwe;
 }
 
 static char *
@@ -245,13 +207,10 @@
 	if (pgp)
 		hwe->pgpolicy = pgp;
 
-	if (getuid) {
+	if (getuid)
 		hwe->getuid = set_param_str(getuid);
-		push_callout(getuid);
-	} else {
+	else
 		hwe->getuid = set_default(DEFAULT_GETUID);
-		push_callout(DEFAULT_GETUID);
-	}
 
 	if (!hwe->getuid)
 		goto out;
@@ -291,21 +250,17 @@
 	if (pgp)
 		hwe->pgpolicy = pgp;
 
-	if (getuid) {
+	if (getuid)
 		hwe->getuid = set_param_str(getuid);
-		push_callout(getuid);
-	} else {
+	else
 		hwe->getuid = set_default(DEFAULT_GETUID);
-		push_callout(DEFAULT_GETUID);
-	}
 
 	if (!hwe->getuid)
 		goto out;
 	
-	if (getprio) {
+	if (getprio)
 		hwe->getprio = set_param_str(getprio);
-		push_callout(getprio);
-	} else
+	else
 		hwe->getprio = NULL;
 
 	if (hwhandler)	
@@ -378,7 +333,6 @@
 	free_blacklist(conf->blist);
 	free_mptable(conf->mptable);
 	free_hwtable(conf->hwtable);
-	free_strvec(conf->binvec);
 
 	FREE(conf);
 }
@@ -386,7 +340,8 @@
 int
 load_config (char * file)
 {
-	conf = alloc_config();
+	if (!conf)
+		conf = alloc_config();
 
 	if (!conf)
 		return 1;
@@ -394,8 +349,9 @@
 	/*
 	 * internal defaults
 	 */
-	conf->verbosity = 2;
-	conf->signal = 1;		/* 1 == Send a signal to multipathd */
+	if (!conf->verbosity)
+		conf->verbosity = 2;
+
 	conf->dev_type = DEV_NONE;
 	conf->minio = 1000;
 

Modified: multipath-tools/trunk/libmultipath/config.h
==============================================================================
--- multipath-tools/trunk/libmultipath/config.h	(original)
+++ multipath-tools/trunk/libmultipath/config.h	Mon Sep 19 12:43:52 2005
@@ -16,6 +16,8 @@
 	int selector_args;
 	int pgpolicy;
 	int checker_index;
+	int pgfailback;
+	int rr_weight;
 
 	char * vendor;
 	char * product;
@@ -29,6 +31,8 @@
 struct mpentry {
 	int selector_args;
 	int pgpolicy;
+	int pgfailback;
+	int rr_weight;
 
 	char * wwid;
 	char * selector;
@@ -40,7 +44,6 @@
 	int verbosity;
 	int dry_run;
 	int list;
-	int signal;
 	int pgpolicy_flag;
 	int with_sysfs;
 	int default_selector_args;
@@ -48,6 +51,10 @@
 	int dev_type;
 	int minio;
 	int checkint;
+	int max_checkint;
+	int pgfailback;
+	int remove;
+	int rr_weight;
 
 	char * dev;
 	char * multipath;
@@ -61,17 +68,16 @@
 	vector mptable;
 	vector hwtable;
 	vector blist;
-	vector binvec;
 };
 
 struct config * conf;
 
-extern int push_callout(char * callout);
-
 struct hwentry * find_hwe (vector hwtable, char * vendor, char * product);
 struct mpentry * find_mpe (char * wwid);
 char * get_mpe_wwid (char * alias);
 
+struct mpentry * alloc_mpe (void);
+
 void free_hwe (struct hwentry * hwe);
 void free_hwtable (vector hwtable);
 void free_mpe (struct mpentry * mpe);

Modified: multipath-tools/trunk/libmultipath/debug.c
==============================================================================
--- multipath-tools/trunk/libmultipath/debug.c	(original)
+++ multipath-tools/trunk/libmultipath/debug.c	Mon Sep 19 12:43:52 2005
@@ -2,23 +2,29 @@
 #include <stdlib.h>
 #include <stdarg.h>
 
+#if DAEMON
+#include "../multipathd/log_pthread.h"
+#endif
+
 #include "config.h"
 
-void condlog (int prio, char * fmt, ...)
+void dlog (int sink, int prio, char * fmt, ...)
 {
 	va_list ap;
 	int thres;
 
-	if (!conf)
-		thres = 0;
-	else
-		thres = conf->verbosity;
-
 	va_start(ap, fmt);
+	thres = (conf) ? conf->verbosity : 0;
 
 	if (prio <= thres) {
-		vfprintf(stdout, fmt, ap);
-		fprintf(stdout, "\n");
+		if (!sink) {
+			vfprintf(stdout, fmt, ap);
+			fprintf(stdout, "\n");
+		}
+#if DAEMON
+		else
+			log_safe(prio + 3, fmt, ap);
+#endif
 	}
 	va_end(ap);
 }

Modified: multipath-tools/trunk/libmultipath/debug.h
==============================================================================
--- multipath-tools/trunk/libmultipath/debug.h	(original)
+++ multipath-tools/trunk/libmultipath/debug.h	Mon Sep 19 12:43:52 2005
@@ -1,8 +1,19 @@
-void condlog (int prio, char * fmt, ...);
+void dlog (int sink, int prio, char * fmt, ...);
 
 #if DAEMON
+
 #include <pthread.h>
+#include <stdarg.h>
 #include "../multipathd/log_pthread.h"
+
+int logsink;
+
 #define condlog(prio, fmt, args...) \
-	log_safe(prio + 3, fmt, ##args)
-#endif
+	dlog(logsink, prio, fmt, ##args)
+
+#else /* DAEMON */
+
+#define condlog(prio, fmt, args...) \
+	dlog(0, prio, fmt, ##args)
+
+#endif /* DAEMON */

Modified: multipath-tools/trunk/libmultipath/defaults.h
==============================================================================
--- multipath-tools/trunk/libmultipath/defaults.h	(original)
+++ multipath-tools/trunk/libmultipath/defaults.h	Mon Sep 19 12:43:52 2005
@@ -7,7 +7,7 @@
 
 #define DEFAULT_TARGET		"multipath"
 #define DEFAULT_PIDFILE		"/var/run/multipathd.pid"
-#define DEFAULT_RUNFILE		"/var/run/multipath.run"
+#define DEFAULT_SOCKET		"/var/run/multipathd.sock"
 #define DEFAULT_CONFIGFILE	"/etc/multipath.conf"
 
 char * set_default (char * str);

Modified: multipath-tools/trunk/libmultipath/devmapper.c
==============================================================================
--- multipath-tools/trunk/libmultipath/devmapper.c	(original)
+++ multipath-tools/trunk/libmultipath/devmapper.c	Mon Sep 19 12:43:52 2005
@@ -4,46 +4,60 @@
 #include <libdevmapper.h>
 #include <ctype.h>
 #include <linux/kdev_t.h>
+#include <unistd.h>
 
 #include "vector.h"
 #include "structs.h"
 #include "debug.h"
 #include "memory.h"
+#include "devmapper.h"
+
+#define MAX_WAIT 5
+#define LOOPS_PER_SEC 5
 
 extern int
 dm_prereq (char * str, int x, int y, int z)
 {
-	int r = 1;
+	int r = 2;
 	struct dm_task *dmt;
 	struct dm_versions *target;
 	struct dm_versions *last_target;
 
 	if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
-		return 1;
+		return 3;
 
 	dm_task_no_open_count(dmt);
 
-	if (!dm_task_run(dmt))
+	if (!dm_task_run(dmt)) {
+		condlog(0, "Can not communicate with kernel DM");
 		goto out;
+	}
 
 	target = dm_task_get_versions(dmt);
 
-	/* Fetch targets and print 'em */
 	do {
 		last_target = target;
 
-		if (!strncmp(str, target->name, strlen(str)) &&
-		    /* dummy prereq on multipath version */
-		    target->version[0] >= x &&
-		    target->version[1] >= y &&
-		    target->version[2] >= z
-		   )
-			r = 0;
+		if (!strncmp(str, target->name, strlen(str))) {
+			r--;
+			
+			if (target->version[0] >= x &&
+			    target->version[1] >= y &&
+			    target->version[2] >= z)
+				r--;
+
+			break;
+		}
 
 		target = (void *) target + target->next;
 	} while (last_target != target);
 
-	out:
+	if (r == 2)
+		condlog(0, "DM multipath kernel driver not loaded");
+	else if (r == 1)
+		condlog(0, "DM multipath kernel driver version too old");
+
+out:
 	dm_task_destroy(dmt);
 	return r;
 }
@@ -70,7 +84,7 @@
 
 extern int
 dm_addmap (int task, const char *name, const char *target,
-	   const char *params, unsigned long size) {
+	   const char *params, unsigned long long size) {
 	int r = 0;
 	struct dm_task *dmt;
 
@@ -121,7 +135,7 @@
 }
 
 extern int
-dm_get_map(char * name, unsigned long * size, char * outparams)
+dm_get_map(char * name, unsigned long long * size, char * outparams)
 {
 	int r = 1;
 	struct dm_task *dmt;
@@ -183,6 +197,9 @@
 	if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE)
 		r = 0;
 out:
+	if (r)
+		condlog(0, "%s: error getting map status string", name);
+
 	dm_task_destroy(dmt);
 	return r;
 }
@@ -220,6 +237,35 @@
 	return r;
 }
 
+static int
+dm_dev_t (char * mapname, char * dev_t, int len)
+{
+	int r = 1;
+	struct dm_task *dmt;
+	struct dm_info info;
+
+	if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+		return 0;
+
+	if (!dm_task_set_name(dmt, mapname))
+		goto out;
+
+	if (!dm_task_run(dmt))
+		goto out;
+
+	if (!dm_task_get_info(dmt, &info))
+		goto out;
+
+	r = info.open_count;
+	if (snprintf(dev_t, len, "%i:%i", info.major, info.minor) > len)
+		    goto out;
+
+	r = 0;
+out:
+	dm_task_destroy(dmt);
+	return r;
+}
+	
 int
 dm_get_opencount (char * mapname)
 {
@@ -245,6 +291,57 @@
 	return r;
 }
 	
+int
+dm_get_minor (char * mapname)
+{
+	int r = -1;
+	struct dm_task *dmt;
+	struct dm_info info;
+
+	if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+		return 0;
+
+	if (!dm_task_set_name(dmt, mapname))
+		goto out;
+
+	if (!dm_task_run(dmt))
+		goto out;
+
+	if (!dm_task_get_info(dmt, &info))
+		goto out;
+
+	r = info.minor;
+out:
+	dm_task_destroy(dmt);
+	return r;
+}
+	
+extern int
+dm_flush_map (char * mapname, char * type)
+{
+	int r;
+
+	if (!dm_map_present(mapname))
+		return 0;
+
+	if (!dm_type(mapname, type))
+		return 1;
+
+	if (dm_remove_partmaps(mapname))
+		return 1;
+
+	if (dm_get_opencount(mapname))
+		return 1;
+
+	r = dm_simplecmd(DM_DEVICE_REMOVE, mapname);
+
+	if (r) {
+		condlog(4, "multipath map %s removed", mapname);
+		return 0;
+	}
+	return 1;
+}
+
 extern int
 dm_flush_maps (char * type)
 {
@@ -268,11 +365,7 @@
 		goto out;
 
 	do {
-		if (dm_type(names->name, type) &&
-		    dm_get_opencount(names->name) == 0 &&
-		    !dm_simplecmd(DM_DEVICE_REMOVE, names->name))
-			r++;
-
+		r += dm_flush_map(names->name, type);
 		next = names->next;
 		names = (void *) names + next;
 	} while (next);
@@ -348,8 +441,8 @@
 	return r;
 }
 
-int
-dm_switchgroup(char * mapname, int index)
+static int
+dm_groupmsg (char * msg, char * mapname, int index)
 {
 	int r = 0;
 	struct dm_task *dmt;
@@ -364,8 +457,7 @@
 	if (!dm_task_set_sector(dmt, 0))
 		goto out;
 
-	snprintf(str, 24, "switch_group %i\n", index);
-	condlog(3, "message %s 0 %s", mapname, str);
+	snprintf(str, 24, "%s_group %i\n", msg, index);
 
 	if (!dm_task_set_message(dmt, str))
 		goto out;
@@ -375,15 +467,37 @@
 	if (!dm_task_run(dmt))
 		goto out;
 
+	condlog(3, "message %s 0 %s", mapname, str);
 	r = 1;
 
 	out:
+	if (!r)
+		condlog(3, "message %s 0 %s failed", mapname, str);
+
 	dm_task_destroy(dmt);
 
 	return r;
 }
 
 int
+dm_switchgroup(char * mapname, int index)
+{
+	return dm_groupmsg("switch", mapname,index);
+}
+
+int
+dm_enablegroup(char * mapname, int index)
+{
+	return dm_groupmsg("enable", mapname,index);
+}
+
+int
+dm_disablegroup(char * mapname, int index)
+{
+	return dm_groupmsg("disable", mapname,index);
+}
+
+int
 dm_get_maps (vector mp, char * type)
 {
 	struct multipath * mpp;
@@ -392,7 +506,7 @@
 	struct dm_names *names;
 	unsigned next = 0;
 
-	if (!type)
+	if (!type || !mp)
 		return 1;
 
 	if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
@@ -413,8 +527,7 @@
 
 	do {
 		if (dm_type(names->name, type)) {
-			mpp = (struct multipath *)
-				MALLOC(sizeof(struct multipath));
+			mpp = alloc_multipath();
 
 			if (!mpp)
 				goto out;
@@ -483,3 +596,152 @@
 
 	return info.event_nr;
 }
+
+char *
+dm_mapname(int major, int minor)
+{
+	char * response;
+	struct dm_task *dmt;
+	int r;
+	int loop = MAX_WAIT * LOOPS_PER_SEC;
+
+	if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
+		return NULL;
+
+	if (!dm_task_set_major(dmt, major) ||
+	    !dm_task_set_minor(dmt, minor))
+		goto bad;
+
+	dm_task_no_open_count(dmt);
+
+	/*
+	 * device map might not be ready when we get here from
+	 * uevent trigger
+	 */
+	while (--loop) {
+		r = dm_task_run(dmt);
+
+		if (r)
+			break;
+
+		usleep(1000 * 1000 / LOOPS_PER_SEC);
+	}
+
+	if (!r) {
+		condlog(0, "%i:%i: timeout fetching map name", major, minor);
+		goto bad;
+	}
+
+	response = STRDUP((char *)dm_task_get_name(dmt));
+	dm_task_destroy(dmt);
+	return response;
+bad:
+	dm_task_destroy(dmt);
+	condlog(0, "%i:%i: error fetching map name", major, minor);
+	return NULL;
+}
+
+int
+dm_remove_partmaps (char * mapname)
+{
+	struct dm_task *dmt;
+	struct dm_names *names;
+	unsigned next = 0;
+	char params[PARAMS_SIZE];
+	unsigned long long size;
+	char dev_t[32];
+	int r = 1;
+
+	if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+		return 1;
+
+	dm_task_no_open_count(dmt);
+
+	if (!dm_task_run(dmt))
+		goto out;
+
+	if (!(names = dm_task_get_names(dmt)))
+		goto out;
+
+	if (!names->dev) {
+		r = 0; /* this is perfectly valid */
+		goto out;
+	}
+
+	if (dm_dev_t(mapname, &dev_t[0], 32))
+		goto out;
+
+	do {
+		if (
+		    /*
+		     * if devmap target is "linear"
+		     */
+		    dm_type(names->name, "linear") &&
+
+		    /*
+		     * and the multipath mapname and the part mapname start
+		     * the same
+		     */
+		    !strncmp(names->name, mapname, strlen(mapname)) &&
+
+		    /*
+		     * and the opencount is 0 for us to allow removal
+		     */
+		    !dm_get_opencount(names->name) &&
+
+		    /*
+		     * and we can fetch the map table from the kernel
+		     */
+		    !dm_get_map(names->name, &size, &params[0]) &&
+
+		    /*
+		     * and the table maps over the multipath map
+		     */
+		    strstr(params, dev_t)
+		   ) {
+		    		/*
+				 * then it's a kpartx generated partition.
+				 * remove it.
+				 */
+				condlog(4, "partition map %s removed",
+					names->name);
+				dm_simplecmd(DM_DEVICE_REMOVE, names->name);
+		   }
+
+		next = names->next;
+		names = (void *) names + next;
+	} while (next);
+
+	r = 0;
+out:
+	dm_task_destroy (dmt);
+	return r;
+}
+
+#if 0
+int
+dm_rename (char * old, char * new)
+{
+	int r = 1;
+	struct dm_task *dmt;
+
+	if (!(dmt = dm_task_create(DM_DEVICE_RENAME)))
+		return 0;
+
+	if (!dm_task_set_name(dmt, old))
+		goto out;
+
+	if (!dm_task_set_newname(dmt, new))
+		goto out;
+	
+	dm_task_no_open_count(dmt);
+
+	if (!dm_task_run(dmt))
+		goto out;
+
+	r = 0;
+out:
+	dm_task_destroy(dmt);
+	return r;
+}
+#endif

Modified: multipath-tools/trunk/libmultipath/devmapper.h
==============================================================================
--- multipath-tools/trunk/libmultipath/devmapper.h	(original)
+++ multipath-tools/trunk/libmultipath/devmapper.h	Mon Sep 19 12:43:52 2005
@@ -1,13 +1,24 @@
 int dm_prereq (char *, int, int, int);
 int dm_simplecmd (int, const char *);
-int dm_addmap (int, const char *, const char *, const char *, unsigned long);
+int dm_addmap (int, const char *, const char *, const char *,
+	       unsigned long long);
 int dm_map_present (char *);
-int dm_get_map(char *, unsigned long *, char *);
+int dm_get_map(char *, unsigned long long *, char *);
 int dm_get_status(char *, char *);
 int dm_type(char *, char *);
+int dm_flush_map (char *, char *);
 int dm_flush_maps (char *);
 int dm_fail_path(char * mapname, char * path);
 int dm_reinstate(char * mapname, char * path);
 int dm_switchgroup(char * mapname, int index);
+int dm_enablegroup(char * mapname, int index);
+int dm_disablegroup(char * mapname, int index);
 int dm_get_maps (vector mp, char * type);
 int dm_geteventnr (char *name);
+int dm_get_minor (char *name);
+char * dm_mapname(int major, int minor);
+int dm_remove_partmaps (char * mapname);
+
+#if 0
+int dm_rename (char * old, char * new);
+#endif

Modified: multipath-tools/trunk/libmultipath/dict.c
==============================================================================
--- multipath-tools/trunk/libmultipath/dict.c	(original)
+++ multipath-tools/trunk/libmultipath/dict.c	Mon Sep 19 12:43:52 2005
@@ -21,7 +21,7 @@
 	if (!conf->multipath)
 		return 1;
 
-	return push_callout(conf->multipath);
+	return 0;
 }
 
 static int
@@ -31,6 +31,7 @@
 
 	buff = VECTOR_SLOT(strvec, 1);
 	conf->checkint = atoi(buff);
+	conf->max_checkint = conf->checkint << 2;
 
 	return 0;
 }
@@ -81,7 +82,7 @@
 	if (!conf->default_getuid)
 		return 1;
 	
-	return push_callout(conf->default_getuid);
+	return 0;
 }
 
 static int
@@ -92,7 +93,12 @@
 	if (!conf->default_getprio)
 		return 1;
 	
-	return push_callout(conf->default_getprio);
+	if (!strncmp(conf->default_getprio, "none", 4)) {
+		FREE(conf->default_getprio);
+		conf->default_getprio = NULL;
+	}
+		
+	return 0;
 }
 
 static int
@@ -122,6 +128,44 @@
 	return 0;
 }
 
+static int
+def_weight_handler(vector strvec)
+{
+	char * buff;
+
+	buff = set_value(strvec);
+
+	if (!buff)
+		return 1;
+
+	if (strlen(buff) == 10 &&
+	    !strcmp(buff, "priorities"))
+		conf->rr_weight = RR_WEIGHT_PRIO;
+
+	FREE(buff);
+
+	return 0;
+}
+
+static int
+default_failback_handler(vector strvec)
+{
+	char * buff;
+
+	buff = set_value(strvec);
+
+	if (!strncmp(buff, "manual", 6))
+		conf->pgfailback = -FAILBACK_MANUAL;
+	else if (!strncmp(buff, "immediate", 9))
+		conf->pgfailback = -FAILBACK_IMMEDIATE;
+	else
+		conf->pgfailback = atoi(buff);
+
+	FREE(buff);
+
+	return 0;
+}
+
 /*
  * blacklist block handlers
  */
@@ -245,7 +289,7 @@
 	if (!hwe->getuid)
 		return 1;
 
-	return push_callout(hwe->getuid);
+	return 0;
 }
 
 static int
@@ -329,7 +373,58 @@
 	if (!hwe->getprio)
 		return 1;
 
-	return push_callout(hwe->getprio);
+	if (!strncmp(hwe->getprio, "none", 4)) {
+		FREE(hwe->getprio);
+		hwe->getprio = NULL;
+	}
+
+	return 0;
+}
+
+static int
+hw_failback_handler(vector strvec)
+{
+	struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+	char * buff;
+
+	if (!hwe)
+		return 1;
+
+	buff = set_value(strvec);
+
+	if (!strncmp(buff, "manual", 6))
+		hwe->pgfailback = -FAILBACK_MANUAL;
+	else if (!strncmp(buff, "immediate", 9))
+		hwe->pgfailback = -FAILBACK_IMMEDIATE;
+	else
+		hwe->pgfailback = atoi(buff);
+
+	FREE(buff);
+
+	return 0;
+}
+
+static int
+hw_weight_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) == 10 &&
+	    !strcmp(buff, "priorities"))
+		hwe->rr_weight = RR_WEIGHT_PRIO;
+
+	FREE(buff);
+
+	return 0;
 }
 
 /*
@@ -433,6 +528,52 @@
 	return 0;
 }
 
+static int
+mp_failback_handler(vector strvec)
+{
+	struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
+	char * buff;
+
+	if (!mpe)
+		return 1;
+
+	buff = set_value(strvec);
+
+	if (!strncmp(buff, "manual", 6))
+		mpe->pgfailback = -FAILBACK_MANUAL;
+	else if (!strncmp(buff, "immediate", 9))
+		mpe->pgfailback = -FAILBACK_IMMEDIATE;
+	else
+		mpe->pgfailback = atoi(buff);
+
+	FREE(buff);
+
+	return 0;
+}
+
+static int
+mp_weight_handler(vector strvec)
+{
+	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) == 10 &&
+	    !strcmp(buff, "priorities"))
+		mpe->rr_weight = RR_WEIGHT_PRIO;
+
+	FREE(buff);
+
+	return 0;
+}
+
 vector
 init_keywords(void)
 {
@@ -447,7 +588,9 @@
 	install_keyword("default_getuid_callout", &def_getuid_callout_handler);
 	install_keyword("default_prio_callout", &def_prio_callout_handler);
 	install_keyword("default_features", &def_features_handler);
+	install_keyword("failback", &default_failback_handler);
 	install_keyword("rr_min_io", &def_minio_handler);
+	install_keyword("rr_weight", &def_weight_handler);
 	
 	install_keyword_root("devnode_blacklist", &blacklist_handler);
 	install_keyword("devnode", &ble_handler);
@@ -465,6 +608,8 @@
 	install_keyword("features", &hw_features_handler);
 	install_keyword("hardware_handler", &hw_handler_handler);
 	install_keyword("prio_callout", &prio_callout_handler);
+	install_keyword("failback", &hw_failback_handler);
+	install_keyword("rr_weight", &hw_weight_handler);
 	install_sublevel_end();
 
 	install_keyword_root("multipaths", &multipaths_handler);
@@ -474,6 +619,8 @@
 	install_keyword("alias", &alias_handler);
 	install_keyword("path_grouping_policy", &mp_pgpolicy_handler);
 	install_keyword("path_selector", &mp_selector_handler);
+	install_keyword("failback", &mp_failback_handler);
+	install_keyword("rr_weight", &mp_weight_handler);
 	install_sublevel_end();
 
 	return keywords;

Modified: multipath-tools/trunk/libmultipath/discovery.c
==============================================================================
--- multipath-tools/trunk/libmultipath/discovery.c	(original)
+++ multipath-tools/trunk/libmultipath/discovery.c	Mon Sep 19 12:43:52 2005
@@ -2,6 +2,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <errno.h>
 
 #include <sysfs/dlist.h>
 #include <sysfs/libsysfs.h>
@@ -21,17 +23,43 @@
 #define readattr(a,b) \
 	sysfs_read_attribute_value(a, b, sizeof(b))
 
+struct path *
+store_pathinfo (vector pathvec, vector hwtable, char * devname, int flag)
+{
+	struct path * pp;
+
+	pp = alloc_path();
+
+	if (!pp)
+		return NULL;
+
+	if(safe_sprintf(pp->dev, "%s", devname)) {
+		condlog(0, "pp->dev too small");
+		goto out;
+	}
+	if (pathinfo(pp, hwtable, flag))
+		goto out;
+
+	if (store_path(pathvec, pp))
+		goto out;
+
+	return pp;
+out:
+	free_path(pp);
+	return NULL;
+}
+
 int
 path_discovery (vector pathvec, struct config * conf, int flag)
 {
 	struct sysfs_directory * sdir;
 	struct sysfs_directory * devp;
 	char path[FILE_NAME_SIZE];
-	struct path * curpath;
+	struct path * pp;
 	int r = 1;
 
 	if(safe_sprintf(path, "%s/block", sysfs_path)) {
-		fprintf(stderr, "path too small\n");
+		condlog(0, "path too small");
 		exit(1);
 	}
 	sdir = sysfs_open_directory(path);
@@ -43,36 +71,30 @@
 
 		if(safe_sprintf(path, "%s/block/%s/device", sysfs_path,
 				devp->name)) {
-			fprintf(stderr, "path too small\n");
+			condlog(0, "path too small");
 			exit(1);
 		}
 				
 		if (!filepresent(path))
 			continue;
 
-		curpath = find_path_by_dev(pathvec, devp->name);
-
-		if (!curpath) {
-			curpath = alloc_path();
-
-			if (!curpath)
-				goto out;
+		pp = find_path_by_dev(pathvec, devp->name);
 
-			if (store_path(pathvec, curpath)) {
-				free_path(curpath);
+		if (!pp) {
+			/*
+			 * new path : alloc, store and fetch info
+			 * the caller wants
+			 */
+			if (!store_pathinfo(pathvec, conf->hwtable,
+					   devp->name, flag))
 				goto out;
-			}
-			if(safe_sprintf(curpath->dev, "%s", devp->name)) {
-				fprintf(stderr, "curpath->dev too small\n");
-				exit(1);
-			}
-			devinfo(curpath, conf->hwtable, DI_ALL);
 		} else {
 			/*
-			 * path already known,
+			 * path already known :
 			 * refresh only what the caller wants
 			 */
-			devinfo(curpath, conf->hwtable, flag);
+			if (pathinfo(pp, conf->hwtable, flag))
+				goto out;
 		}
 	}
 	r = 0;
@@ -81,6 +103,26 @@
 	return r;
 }
 
+#define WAIT_MAX_SECONDS 5
+#define WAIT_LOOP_PER_SECOND 5
+
+static int
+wait_sysfs_attr (char * filename)
+{
+	int loop;
+	struct stat stats;
+	
+	loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
+	
+	while (--loop) {
+		if (stat(filename, &stats) == 0)
+			return 0;
+
+		usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
+	}
+	return 1;
+}	
+			
 #define declare_sysfs_get_str(fname, fmt) \
 extern int \
 sysfs_get_##fname (char * sysfs_path, char * dev, char * buff, int len) \
@@ -89,8 +131,12 @@
 	char attr_buff[SYSFS_PATH_SIZE]; \
 	int attr_len; \
 \
-	if(safe_sprintf(attr_path, fmt, sysfs_path, dev)) \
+	if (safe_sprintf(attr_path, fmt, sysfs_path, dev)) \
+		return 1; \
+\
+	if (wait_sysfs_attr(attr_path)) \
 		return 1; \
+\
 	if (0 > sysfs_read_attribute_value(attr_path, attr_buff, sizeof(attr_buff))) \
 		return 1; \
 \
@@ -109,50 +155,77 @@
 declare_sysfs_get_str(dev, "%s/block/%s/dev");
 
 #define declare_sysfs_get_val(fname, fmt) \
-extern unsigned long  \
+extern unsigned long long  \
 sysfs_get_##fname (char * sysfs_path, char * dev) \
 { \
 	char attr_path[SYSFS_PATH_SIZE]; \
 	char attr_buff[SYSFS_PATH_SIZE]; \
+	int r; \
+	unsigned long long val; \
+\
+	if (safe_sprintf(attr_path, fmt, sysfs_path, dev)) \
+		return 0; \
 \
-	if(safe_sprintf(attr_path, fmt, sysfs_path, dev)) \
+	if (wait_sysfs_attr(attr_path)) \
 		return 0; \
+\
 	if (0 > sysfs_read_attribute_value(attr_path, attr_buff, sizeof(attr_buff))) \
 		return 0; \
 \
-	return strtoul(attr_buff, NULL, 0); \
+	r = sscanf(attr_buff, "%llu\n", &val); \
+\
+	if (r != 1) \
+		return 0; \
+	else \
+		return (val); \
 }
 
 declare_sysfs_get_val(size, "%s/block/%s/size");
 
+/*
+ * udev might be slow creating node files : wait
+ */
 static int
 opennode (char * dev, int mode)
 {
 	char devpath[FILE_NAME_SIZE];
 	int fd;
+	int loop;
 
 	if (safe_sprintf(devpath, "%s/%s", conf->udev_dir, dev)) {
-		fprintf(stderr, "devpath too small\n");
+		condlog(0, "devpath too small");
 		return -1;
 	}
-	fd = open(devpath, mode);
 
-	if (fd <= 0)
-		condlog(0, "open(%s) failed", devpath);
+	loop = WAIT_MAX_SECONDS * WAIT_LOOP_PER_SECOND;
 	
-	return fd;
+	while (--loop) {
+		fd = open(devpath, mode);
+
+		if (fd <= 0 && errno != ENOENT) {
+			condlog(3, "open error (%s)\n", strerror(errno));
+			return fd;
+		}
+		if (fd > 0)
+			return fd;
+
+		usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND);
+	}
+	condlog(0, "failed to open %s", devpath);
+	return -1;
 }
 
-#if 0
 int
-get_claimed(int fd)
+get_claimed(char * devname)
 {
-	/*
-	 * FIXME : O_EXCL always fails ?
-	 */
+	int fd = opennode(devname, O_EXCL);
+
+	if (fd < 0)
+		return 1;
+
+	close(fd);
 	return 0;
 }	
-#endif
 
 extern int
 devt2devname (char *devname, char *devt)
@@ -165,7 +238,7 @@
 	int len;
 
 	if(safe_sprintf(block_path, "%s/block", sysfs_path)) {
-		fprintf(stderr, "block_path too small\n");
+		condlog(0, "block_path too small");
 		exit(1);
 	}
 	sdir = sysfs_open_directory(block_path);
@@ -174,7 +247,7 @@
 	dlist_for_each_data (sdir->subdirs, devp, struct sysfs_directory) {
 		if(safe_sprintf(attr_path, "%s/%s/dev",
 				block_path, devp->name)) {
-			fprintf(stderr, "attr_path too small\n");
+			condlog(0, "attr_path too small");
 			exit(1);
 		}
 		sysfs_read_attribute_value(attr_path, attr_value,
@@ -189,7 +262,7 @@
 		    strncmp(attr_value, devt, len) == 0) {
 			if(safe_sprintf(attr_path, "%s/%s",
 					block_path, devp->name)) {
-				fprintf(stderr, "attr_path too small\n");
+				condlog(0, "attr_path too small");
 				exit(1);
 			}
 			sysfs_get_name_from_path(attr_path, devname,
@@ -267,15 +340,48 @@
 		len = buff[3];
 		if (len > 0) {
 			memcpy(str, buff + 4, len);
-			buff[len] = '\0';
+			str[len] = '\0';
 		}
 		return 1;
 	}
         return 0;
 }
 
-extern int
-sysfs_devinfo(struct path * curpath)
+static void
+sysfs_get_bus (char * sysfs_path, struct path * curpath)
+{
+	struct sysfs_device *sdev;
+	char attr_path[FILE_NAME_SIZE];
+	char attr_buff[FILE_NAME_SIZE];
+
+	curpath->bus = SYSFS_BUS_UNDEF;
+
+	/*
+	 * This is ugly : we should be able to do a simple
+	 * get_link("%s/block/%s/device/bus", ...) but it just
+	 * won't work
+	 */
+	if(safe_sprintf(attr_path, "%s/block/%s/device",
+			sysfs_path, curpath->dev)) {
+		condlog(0, "attr_path too small");
+		return;
+	}
+
+	if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff)))
+		return;
+
+	sdev = sysfs_open_device_path(attr_buff);
+
+	if (!strncmp(sdev->bus, "scsi", 4))
+		curpath->bus = SYSFS_BUS_SCSI;
+	else if (!strncmp(sdev->bus, "ide", 3))
+		curpath->bus = SYSFS_BUS_IDE;
+
+	return;
+}
+
+static int
+scsi_sysfs_pathinfo (struct path * curpath)
 {
 	char attr_path[FILE_NAME_SIZE];
 	char attr_buff[FILE_NAME_SIZE];
@@ -283,40 +389,34 @@
 	if (sysfs_get_vendor(sysfs_path, curpath->dev,
 			     curpath->vendor_id, SCSI_VENDOR_SIZE))
 		return 1;
+
 	condlog(3, "vendor = %s", curpath->vendor_id);
 
 	if (sysfs_get_model(sysfs_path, curpath->dev,
 			    curpath->product_id, SCSI_PRODUCT_SIZE))
 		return 1;
+
 	condlog(3, "product = %s", curpath->product_id);
 
 	if (sysfs_get_rev(sysfs_path, curpath->dev,
 			  curpath->rev, SCSI_REV_SIZE))
 		return 1;
-	condlog(3, "rev = %s", curpath->rev);
-
-	if (sysfs_get_dev(sysfs_path, curpath->dev,
-			  curpath->dev_t, BLK_DEV_SIZE))
-		return 1;
-	condlog(3, "dev_t = %s", curpath->dev_t);
-
-	curpath->size = sysfs_get_size(sysfs_path, curpath->dev);
 
-	if (curpath->size == 0)
-		return 1;
-	condlog(3, "size = %lu", curpath->size);
+	condlog(3, "rev = %s", curpath->rev);
 
 	/*
 	 * host / bus / target / lun
 	 */
 	if(safe_sprintf(attr_path, "%s/block/%s/device",
 			sysfs_path, curpath->dev)) {
-		fprintf(stderr, "attr_path too small\n");
+		condlog(0, "attr_path too small");
 		return 1;
 	}
 	if (0 > sysfs_get_link(attr_path, attr_buff, sizeof(attr_buff)))
 		return 1;
+	
 	basename(attr_buff, attr_path);
+
 	sscanf(attr_path, "%i:%i:%i:%i",
 			&curpath->sg_id.host_no,
 			&curpath->sg_id.channel,
@@ -337,7 +437,7 @@
 			curpath->sg_id.host_no,
 			curpath->sg_id.channel,
 			curpath->sg_id.scsi_id)) {
-		fprintf(stderr, "attr_path too small\n");
+		condlog(0, "attr_path too small");
 		return 1;
 	}
 	if (0 <= readattr(attr_path, attr_buff) && strlen(attr_buff) > 0)
@@ -349,6 +449,44 @@
 }
 
 static int
+common_sysfs_pathinfo (struct path * curpath)
+{
+
+	sysfs_get_bus(sysfs_path, curpath);
+	condlog(3, "bus = %i", curpath->bus);
+
+	if (sysfs_get_dev(sysfs_path, curpath->dev,
+			  curpath->dev_t, BLK_DEV_SIZE))
+		return 1;
+
+	condlog(3, "dev_t = %s", curpath->dev_t);
+
+	curpath->size = sysfs_get_size(sysfs_path, curpath->dev);
+
+	if (curpath->size == 0)
+		return 1;
+
+	condlog(3, "size = %llu", curpath->size);
+
+	return 0;
+}
+
+extern int
+sysfs_pathinfo(struct path * curpath)
+{
+	if (common_sysfs_pathinfo(curpath))
+		return 1;
+
+	if (curpath->bus == SYSFS_BUS_UNDEF)
+		return 0;
+	else if (curpath->bus == SYSFS_BUS_SCSI)
+		if (scsi_sysfs_pathinfo(curpath))
+			return 1;
+
+	return 0;
+}
+
+static int
 apply_format (char * string, char * cmd, struct path * pp)
 {
 	char * pos;
@@ -427,8 +565,19 @@
 	return 0;
 }
 
+static int
+scsi_ioctl_pathinfo (struct path * pp, int mask)
+{
+	if (mask & DI_SERIAL) {
+		get_serial(pp->serial, pp->fd);
+		condlog(3, "serial = %s", pp->serial);
+	}
+
+	return 0;
+}
+
 extern int
-devinfo (struct path *pp, vector hwtable, int mask)
+pathinfo (struct path *pp, vector hwtable, int mask)
 {
 	char buff[CALLOUT_MAX_SIZE];
 	char prio[16];
@@ -438,28 +587,25 @@
 	/*
 	 * fetch info available in sysfs
 	 */
-	if (mask & DI_SYSFS && sysfs_devinfo(pp))
+	if (mask & DI_SYSFS && sysfs_pathinfo(pp))
 		return 1;
 
 	/*
 	 * then those not available through sysfs
 	 */
+	if (mask & DI_CLAIMED) {
+		pp->claimed = get_claimed(pp->dev);
+		condlog(3, "claimed = %i", pp->claimed);
+	}
 	if (pp->fd <= 0)
 		pp->fd = opennode(pp->dev, O_RDONLY);
 
 	if (pp->fd <= 0)
 		return 1;
 
-	if (mask & DI_SERIAL) {
-		get_serial(pp->serial, pp->fd);
-		condlog(3, "serial = %s", pp->serial);
-	}
-#if 0
-	if (mask & DI_CLAIMED) {
-		pp->claimed = get_claimed(pp->fd);
-		condlog(3, "claimed = %i", pp->claimed);
-	}
-#endif
+	if (pp->bus == SYSFS_BUS_SCSI)
+		if (scsi_ioctl_pathinfo(pp, mask))
+			return 1;
 
 	/* get and store hwe pointer */
 	pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id);
@@ -467,9 +613,10 @@
 	/*
 	 * get path state, no message collection, no context
 	 */
-	select_checkfn(pp);
-
 	if (mask & DI_CHECKER) {
+		if (!pp->checkfn)
+			select_checkfn(pp);
+
 		pp->state = pp->checkfn(pp->fd, NULL, NULL);
 		condlog(3, "state = %i", pp->state);
 	}
@@ -478,9 +625,12 @@
 	 * get path prio
 	 */
 	if (mask & DI_PRIO) {
-		select_getprio(pp);
+		if (!pp->getprio)
+			select_getprio(pp);
 
-		if (apply_format(pp->getprio, &buff[0], pp)) {
+		if (!pp->getprio) {
+			pp->priority = 1;
+		} else if (apply_format(pp->getprio, &buff[0], pp)) {
 			condlog(0, "error formatting prio callout command");
 			pp->priority = -1;
 		} else if (execute_program(buff, prio, 16)) {
@@ -496,7 +646,8 @@
 	 * get path uid
 	 */
 	if (mask & DI_WWID && !strlen(pp->wwid)) {
-		select_getuid(pp);
+		if (!pp->getuid)
+			select_getuid(pp);
 
 		if (apply_format(pp->getuid, &buff[0], pp)) {
 			condlog(0, "error formatting uid callout command");

Modified: multipath-tools/trunk/libmultipath/discovery.h
==============================================================================
--- multipath-tools/trunk/libmultipath/discovery.h	(original)
+++ multipath-tools/trunk/libmultipath/discovery.h	Mon Sep 19 12:43:52 2005
@@ -26,18 +26,16 @@
 int sysfs_get_rev (char * sysfs_path, char * dev, char * buff, int len);
 int sysfs_get_dev (char * sysfs_path, char * dev, char * buff, int len);
 
-unsigned long sysfs_get_size (char * sysfs_path, char * dev);
+unsigned long long sysfs_get_size (char * sysfs_path, char * dev);
 int path_discovery (vector pathvec, struct config * conf, int flag);
 
 void basename (char *, char *);
 int get_serial (char * buff, int fd);
 int do_tur (char *);
 int devt2devname (char *, char *);
-int devinfo (struct path *, vector hwtable, int mask);
-
-#if 0
-int get_claimed(int fd);
-#endif
+int pathinfo (struct path *, vector hwtable, int mask);
+struct path * store_pathinfo (vector pathvec, vector hwtable,
+			      char * devname, int flag);
 
 /*
  * discovery bitmask

Modified: multipath-tools/trunk/libmultipath/dmparser.c
==============================================================================
--- multipath-tools/trunk/libmultipath/dmparser.c	(original)
+++ multipath-tools/trunk/libmultipath/dmparser.c	Mon Sep 19 12:43:52 2005
@@ -16,45 +16,6 @@
 #define WORD_SIZE 64
 
 static int
-get_word (char * sentence, char ** word)
-{
-	char * p;
-	int len;
-	int skip = 0;
-	
-	while (*sentence ==  ' ') {
-		sentence++;
-		skip++;
-	}
-	if (*sentence == '\0')
-		return 0;
-
-	p = sentence;
-
-	while (*p !=  ' ' && *p != '\0')
-		p++;
-
-	len = (int) (p - sentence);
-
-	if (!word)
-		return skip + len;
-
-	*word = MALLOC(len + 1);
-
-	if (!*word) {
-		condlog(0, "get_word : oom\n");
-		return 0;
-	}
-	strncpy(*word, sentence, len);
-	condlog(4, "*word = %s, len = %i", *word, len);
-
-	if (*p == '\0')
-		return 0;
-
-	return skip + len;
-}
-
-static int
 merge_words (char ** dst, char * word, int space)
 {
 	char * p;
@@ -255,11 +216,23 @@
 			if (store_path(pgp->paths, pp))
 				goto out;
 
-			pgp->id ^= (long)pp;
-
+			/*
+			 * Update wwid for multipaths which are not setup
+			 * in the get_dm_mpvec() code path
+			 */
 			if (!strlen(mpp->wwid))
 				strncpy(mpp->wwid, pp->wwid, WWID_SIZE);
 
+			/*
+			 * Update wwid for paths which may not have been
+			 * active at the time the getuid callout was run
+			 */
+			else if (!strlen(pp->wwid))
+				strncpy(pp->wwid, mpp->wwid, WWID_SIZE);
+
+			pgp->id ^= (long)pp;
+			pp->pgindex = i + 1;
+
 			for (k = 0; k < num_paths_args; k++)
 				p += get_word(p, NULL);
 		}
@@ -367,7 +340,7 @@
 			pgp->status = PGSTATE_ENABLED;
 			break;
 		default:
-			pgp->status = PGSTATE_RESERVED;
+			pgp->status = PGSTATE_UNDEF;
 			break;
 		}
 		FREE(word);

Modified: multipath-tools/trunk/libmultipath/hwtable.c
==============================================================================
--- multipath-tools/trunk/libmultipath/hwtable.c	(original)
+++ multipath-tools/trunk/libmultipath/hwtable.c	Mon Sep 19 12:43:52 2005
@@ -12,17 +12,18 @@
 	int r = 0;
 
 	r += store_hwe(hw, "3PARdata", "VV", MULTIBUS, DEFAULT_GETUID);
-	r += store_hwe(hw, "COMPAQ", "HSV110 (C)COMPAQ", MULTIBUS, DEFAULT_GETUID);
-	r += store_hwe(hw, "COMPAQ", "MSA1000", MULTIBUS, DEFAULT_GETUID);
-	r += store_hwe(hw, "COMPAQ", "MSA1000 VOLUME", MULTIBUS, DEFAULT_GETUID);
+	r += store_hwe(hw, "COMPAQ", "HSV110 (C)COMPAQ", GROUP_BY_SERIAL, DEFAULT_GETUID);
+	r += store_hwe(hw, "COMPAQ", "MSA1000", GROUP_BY_SERIAL, DEFAULT_GETUID);
+	r += store_hwe(hw, "COMPAQ", "MSA1000 VOLUME", GROUP_BY_SERIAL, DEFAULT_GETUID);
 	r += store_hwe(hw, "DDN", "SAN DataDirector", MULTIBUS, DEFAULT_GETUID);
-	r += store_hwe(hw, "DEC", "HSG80", MULTIBUS, DEFAULT_GETUID);
+	r += store_hwe(hw, "DEC", "HSG80", GROUP_BY_SERIAL, DEFAULT_GETUID);
 	r += store_hwe(hw, "EMC", "SYMMETRIX", MULTIBUS, DEFAULT_GETUID);
-	r += store_hwe(hw, "FSC", "CentricStor", MULTIBUS, DEFAULT_GETUID);
+	r += store_hwe(hw, "FSC", "CentricStor", GROUP_BY_SERIAL, DEFAULT_GETUID);
 	r += store_hwe(hw, "HITACHI", "DF400", MULTIBUS, DEFAULT_GETUID);
 	r += store_hwe(hw, "HITACHI", "DF500", MULTIBUS, DEFAULT_GETUID);
 	r += store_hwe(hw, "HITACHI", "DF600", MULTIBUS, DEFAULT_GETUID);
-	r += store_hwe(hw, "HP", "HSV110", MULTIBUS, DEFAULT_GETUID);
+	r += store_hwe(hw, "HP", "HSV110", GROUP_BY_SERIAL, DEFAULT_GETUID);
+	r += store_hwe(hw, "HP", "HSV210", MULTIBUS, DEFAULT_GETUID);
 	r += store_hwe(hw, "HP", "A6189A", MULTIBUS, DEFAULT_GETUID);
 	r += store_hwe(hw, "HP", "OPEN-", MULTIBUS, DEFAULT_GETUID);
 	r += store_hwe(hw, "IBM", "ProFibre 4000R", MULTIBUS, DEFAULT_GETUID);
@@ -34,7 +35,8 @@
 	r += store_hwe(hw, "SUN", "T4", MULTIBUS, DEFAULT_GETUID);
 
 	r += store_hwe_ext(hw, "DGC", "*", GROUP_BY_PRIO, DEFAULT_GETUID,
-		   "/sbin/pp_emc /dev/%n", "1 emc", "0", "emc_clariion");
+		   "/sbin/mpath_prio_emc /dev/%n", "1 emc",
+		   "1 queue_if_no_path", "emc_clariion");
 	r += store_hwe_ext(hw, "IBM", "3542", GROUP_BY_SERIAL, DEFAULT_GETUID,
 		   NULL, "0", "0", "tur");
 	r += store_hwe_ext(hw, "SGI", "TP9400", MULTIBUS, DEFAULT_GETUID,

Modified: multipath-tools/trunk/libmultipath/memory.c
==============================================================================
--- multipath-tools/trunk/libmultipath/memory.c	(original)
+++ multipath-tools/trunk/libmultipath/memory.c	Mon Sep 19 12:43:52 2005
@@ -133,6 +133,49 @@
 	return buf;
 }
 
+char *
+dbg_strdup(char *str, char *file, char *function, int line)
+{
+	void *buf;
+	int i = 0;
+	long check;
+	long size;
+
+	size = strlen(str) + 1;
+	buf = zalloc(size + sizeof (long));
+	strcat(buf, str);
+
+	check = 0xa5a5 + size;
+	*(long *) ((char *) buf + size) = check;
+
+	while (i < number_alloc_list) {
+		if (alloc_list[i].type == 0)
+			break;
+		i++;
+	}
+
+	if (i == number_alloc_list)
+		number_alloc_list++;
+
+	assert(number_alloc_list < MAX_ALLOC_LIST);
+
+	alloc_list[i].ptr = buf;
+	alloc_list[i].size = size;
+	alloc_list[i].file = file;
+	alloc_list[i].func = function;
+	alloc_list[i].line = line;
+	alloc_list[i].csum = check;
+	alloc_list[i].type = 9;
+
+	if (debug & 1)
+		printf("strdup[%3d:%3d], %p, %4ld at %s, %3d, %s\n",
+		       i, number_alloc_list, buf, size, file, line,
+		       function);
+
+	n++;
+	return buf;
+}
+
 
 
 /* Display a buffer into a HEXA formated output */

Modified: multipath-tools/trunk/libmultipath/memory.h
==============================================================================
--- multipath-tools/trunk/libmultipath/memory.h	(original)
+++ multipath-tools/trunk/libmultipath/memory.h	Mon Sep 19 12:43:52 2005
@@ -51,11 +51,14 @@
                       (__FILE__), (char *)(__FUNCTION__), (__LINE__)) )
 #define REALLOC(b,n) ( dbg_realloc((b), (n), \
                       (__FILE__), (char *)(__FUNCTION__), (__LINE__)) )
+#define STRDUP(n)    ( dbg_strdup((n), \
+                      (__FILE__), (char *)(__FUNCTION__), (__LINE__)) )
 
 /* Memory debug prototypes defs */
 extern char *dbg_malloc(unsigned long, char *, char *, int);
 extern int dbg_free(void *, char *, char *, int);
 extern void *dbg_realloc(void *, unsigned long, char *, char *, int);
+extern char *dbg_strdup(char *, char *, char *, int);
 extern void dbg_free_final(char *);
 
 #else
@@ -63,6 +66,7 @@
 #define MALLOC(n)    (zalloc(n))
 #define FREE(p)      (xfree(p))
 #define REALLOC(p,n) (realloc((p),(n)))
+#define STRDUP(n)    (strdup(n))
 
 #endif
 

Modified: multipath-tools/trunk/libmultipath/pgpolicies.c
==============================================================================
--- multipath-tools/trunk/libmultipath/pgpolicies.c	(original)
+++ multipath-tools/trunk/libmultipath/pgpolicies.c	Mon Sep 19 12:43:52 2005
@@ -243,7 +243,7 @@
 {
 	struct pathgroup * pgp;
 
-	if (VECTOR_SIZE(pgp->paths) < 0)
+	if (VECTOR_SIZE(mp->paths) < 0)
 		return 0;
 
 	if (!mp->pg)

Modified: multipath-tools/trunk/libmultipath/pgpolicies.h
==============================================================================
--- multipath-tools/trunk/libmultipath/pgpolicies.h	(original)
+++ multipath-tools/trunk/libmultipath/pgpolicies.h	Mon Sep 19 12:43:52 2005
@@ -11,7 +11,7 @@
 
 /* Storage controlers capabilities */
 enum iopolicies { 
-	IOPOLICY_RESERVED,
+	IOPOLICY_UNDEF,
 	FAILOVER,
 	MULTIBUS,
 	GROUP_BY_SERIAL,

Added: multipath-tools/trunk/libmultipath/print.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libmultipath/print.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,261 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "vector.h"
+#include "structs.h"
+#include "print.h"
+#include "dmparser.h"
+
+#include "../libcheckers/path_state.h"
+
+#define MAX_FIELD_LEN 64
+
+#define MAX(x,y) (x > y) ? x : y;
+
+void
+get_path_layout (struct path_layout * pl, vector pathvec)
+{
+	int i;
+	int hbtl_len, dev_len, dev_t_len;
+	char buff[MAX_FIELD_LEN];
+	struct path * pp;
+
+	/* reset max col lengths */
+	pl->hbtl_len = 0;
+	pl->dev_len = 0;
+	pl->dev_t_len = 0;
+
+	vector_foreach_slot (pathvec, pp, i) {
+		hbtl_len = snprintf(buff, MAX_FIELD_LEN, "%i:%i:%i:%i",
+					pp->sg_id.host_no,
+					pp->sg_id.channel,
+					pp->sg_id.scsi_id,
+					pp->sg_id.lun);
+		dev_len = strlen(pp->dev);
+		dev_t_len = strlen(pp->dev_t);
+
+		pl->hbtl_len = MAX(hbtl_len, pl->hbtl_len);
+		pl->dev_len = MAX(dev_len, pl->dev_len);
+		pl->dev_t_len = MAX(dev_t_len, pl->dev_t_len);
+	}
+	return;
+}
+
+void
+get_map_layout (struct map_layout * ml, vector mpvec)
+{
+	int i;
+	char buff[MAX_FIELD_LEN];
+	int mapname_len, mapdev_len;
+	struct multipath * mpp;
+
+	/* reset max col lengths */
+	ml->mapname_len = 0;
+	ml->mapdev_len = 0;
+
+	vector_foreach_slot (mpvec, mpp, i) {
+		mapname_len = (mpp->alias) ?
+				strlen(mpp->alias) : strlen(mpp->wwid);
+		ml->mapname_len = MAX(mapname_len, ml->mapname_len);
+
+		mapdev_len = snprintf(buff, MAX_FIELD_LEN,
+			       	      "dm-%i", mpp->minor);
+		ml->mapdev_len = MAX(mapdev_len, ml->mapdev_len);
+	}
+	return;
+}
+
+#define TAIL   (line + len - 1 - c)
+#define PAD(x) while ((int)(c - s) < (x)) *c++ = ' '; s = c
+#define NOPAD  s = c
+#define PRINT(var, size, format, args...)      \
+	        fwd = snprintf(var, size, format, ##args); \
+		c += (fwd >= size) ? size : fwd;
+
+int
+snprint_map (char * line, int len, char * format,
+	     struct multipath * mpp, struct map_layout * ml)
+{
+	char * c = line;   /* line cursor */
+	char * s = line;   /* for padding */
+	char * f = format; /* format string cursor */
+	int i, j;
+	int fwd;
+
+	do {
+		if (!TAIL)
+			break;
+
+		if (*f != '%') {
+			*c++ = *f;
+			NOPAD;
+			continue;
+		}
+		f++;
+		switch (*f) {
+		case 'w':	
+			if (mpp->alias) {
+				PRINT(c, TAIL, "%s", mpp->alias);
+			} else {
+				PRINT(c, TAIL, "%s", mpp->wwid);
+			}
+			PAD(ml->mapname_len);
+			break;
+		case 'd':
+			PRINT(c, TAIL, "dm-%i", mpp->minor);
+			PAD(ml->mapdev_len);
+			break;
+		case 'F':
+			if (!mpp->failback_tick) {
+				PRINT(c, TAIL, "[no scheduled failback]");
+				NOPAD;
+				break;
+			}
+			i = mpp->failback_tick;
+			j = mpp->pgfailback - mpp->failback_tick;
+
+			while (i-- > 0) {
+				PRINT(c, TAIL, "X");
+			}
+			while (j-- > 0) {
+				PRINT(c, TAIL, ".");
+			}
+			PRINT(c, TAIL, " %i/%i",
+				     mpp->failback_tick, mpp->pgfailback);
+			NOPAD;
+			break;
+		default:
+			break;
+		}
+	} while (*f++);
+
+	line[c - line - 1] = '\n';
+	line[c - line] = '\0';
+
+	return (c - line);
+}
+
+int
+snprint_path (char * line, int len, char * format, struct path * pp,
+	    struct path_layout * pl)
+{
+	char * c = line;   /* line cursor */
+	char * s = line;   /* for padding */
+	char * f = format; /* format string cursor */
+	int i, j;
+	int fwd;
+
+	do {
+		if (!TAIL)
+			break;
+
+		if (*f != '%') {
+			*c++ = *f;
+			NOPAD;
+			continue;
+		}
+		f++;
+		switch (*f) {
+		case 'w':	
+			PRINT(c, TAIL, "%s ", pp->wwid);
+			NOPAD;
+			break;
+		case 'i':
+			if (pp->sg_id.host_no < 0) {
+				PRINT(c, TAIL, "#:#:#:# ");
+			} else {
+				PRINT(c, TAIL, "%i:%i:%i:%i",
+					pp->sg_id.host_no,
+					pp->sg_id.channel,
+					pp->sg_id.scsi_id,
+					pp->sg_id.lun);
+			}
+			PAD(pl->hbtl_len);
+			break;
+		case 'd':
+			PRINT(c, TAIL, "%s", pp->dev);
+			PAD(pl->dev_len);
+			break;
+		case 'D':
+			PRINT(c, TAIL, "%s", pp->dev_t);
+			PAD(pl->dev_t_len);
+			break;
+		case 'T':
+			switch (pp->state) {
+			case PATH_UP:
+				PRINT(c, TAIL, "[ready]");
+				break;
+			case PATH_DOWN:
+				PRINT(c, TAIL, "[faulty]");
+				break;
+			case PATH_SHAKY:
+				PRINT(c, TAIL, "[shaky]");
+				break;
+			case PATH_GHOST:
+				PRINT(c, TAIL, "[ghost]");
+				break;
+			default:
+				break;
+			}
+			NOPAD;
+			break;
+		case 't':
+			switch (pp->dmstate) {
+			case PSTATE_ACTIVE:
+				PRINT(c, TAIL, "[active]");
+				break;
+			case PSTATE_FAILED:
+				PRINT(c, TAIL, "[failed]");
+				break;
+			default:
+				break;
+			}
+			NOPAD;
+			break;
+		case 'c':
+			if (pp->claimed && pp->dmstate == PSTATE_UNDEF) {
+				PRINT(c, TAIL, "[claimed]");
+			}
+			NOPAD;
+			break;
+		case 's':
+			PRINT(c, TAIL, "%s/%s/%s",
+				      pp->vendor_id, pp->product_id, pp->rev);
+			NOPAD;
+			break;
+		case 'C':
+			if (!pp->mpp) {
+				PRINT(c, TAIL, "[orphan]");
+				NOPAD;
+				break;
+			}
+			i = pp->tick;
+			j = pp->checkint - pp->tick;
+
+			while (i-- > 0) {
+				PRINT(c, TAIL, "X");
+			}
+			while (j-- > 0) {
+				PRINT(c, TAIL, ".");
+			}
+			PRINT(c, TAIL, " %i/%i",
+				      pp->tick, pp->checkint);
+			NOPAD;
+			break;
+		case 'p':
+			if (pp->priority) {
+				PRINT(c, TAIL, "%i", pp->priority);
+			}
+			NOPAD;
+			break;
+		default:
+			break;
+		}
+	} while (*f++);
+
+	line[c - line - 1] = '\n';
+	line[c - line] = '\0';
+
+	return (c - line);
+}
+

Added: multipath-tools/trunk/libmultipath/print.h
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libmultipath/print.h	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,43 @@
+/*
+ * path format magics :
+ * 
+ * %w : multipath uid
+ * %i : scsi tuple
+ * %d : device name
+ * %D : device major:minor
+ * %t : device mapper path status
+ * %T : checker path status
+ * %s : scsi strings
+ * %c : claimed
+ * %p : priority
+ * 
+ * map format magics :
+ * 
+ * %w : multipath uid
+ * %d : DM device name
+ * %F : failback countdown
+ * %C : checker countdown
+ */
+#define PRINT_PATH_LONG      "%w %i %d %D %p %t%T%c %s"
+#define PRINT_PATH_INDENT    " \\_ %i %d %D %t%T%c"
+#define PRINT_PATH_CHECKER   "%i %d %D %p %t%T %C"
+#define PRINT_MAP_FAILBACK   "%w %d %F"
+
+#define MAX_LINE_LEN 80
+
+struct path_layout {
+	int hbtl_len;
+	int dev_len;
+	int dev_t_len;
+};
+
+struct map_layout {
+	int mapname_len;
+	int mapdev_len;
+};
+
+
+void get_path_layout (struct path_layout * pl, vector pathvec);
+void get_map_layout (struct map_layout * pl, vector mpvec);
+int snprint_path (char *, int, char *, struct path *, struct path_layout *);
+int snprint_map (char *, int, char *,struct multipath *, struct map_layout *);

Modified: multipath-tools/trunk/libmultipath/propsel.c
==============================================================================
--- multipath-tools/trunk/libmultipath/propsel.c	(original)
+++ multipath-tools/trunk/libmultipath/propsel.c	Mon Sep 19 12:43:52 2005
@@ -14,6 +14,58 @@
  * stop at first explicit setting found
  */
 extern int
+select_rr_weight (struct multipath * mp)
+{
+	if (mp->mpe && mp->mpe->rr_weight) {
+		mp->rr_weight = mp->mpe->rr_weight;
+		condlog(3, "rr_weight = %i (LUN setting)",
+			mp->rr_weight);
+		return 0;
+	}
+	if (mp->hwe && mp->hwe->rr_weight) {
+		mp->rr_weight = mp->hwe->rr_weight;
+		condlog(3, "rr_weight = %i (controler setting)",
+			mp->rr_weight);
+		return 0;
+	}
+	if (conf->rr_weight) {
+		mp->rr_weight = conf->rr_weight;
+		condlog(3, "rr_weight = %i (config file default)",
+			mp->rr_weight);
+		return 0;
+	}
+	mp->rr_weight = RR_WEIGHT_NONE;
+	condlog(3, "rr_weight = %i (internal default)",
+		mp->rr_weight);
+	return 0;
+}
+
+extern int
+select_pgfailback (struct multipath * mp)
+{
+	if (mp->mpe && mp->mpe->pgfailback != FAILBACK_UNDEF) {
+		mp->pgfailback = mp->mpe->pgfailback;
+		condlog(3, "pgfailback = %i (LUN setting)", mp->pgfailback);
+		return 0;
+	}
+	if (mp->hwe && mp->hwe->pgfailback != FAILBACK_UNDEF) {
+		mp->pgfailback = mp->hwe->pgfailback;
+		condlog(3, "pgfailback = %i (controler setting)",
+			mp->pgfailback);
+		return 0;
+	}
+	if (conf->pgfailback != FAILBACK_UNDEF) {
+		mp->pgfailback = conf->pgfailback;
+		condlog(3, "pgfailback = %i (config file default)",
+			mp->pgfailback);
+		return 0;
+	}
+	mp->pgfailback = -FAILBACK_MANUAL;
+	condlog(3, "pgfailover = %i (internal default)", mp->pgfailback);
+	return 0;
+}
+
+extern int
 select_pgpolicy (struct multipath * mp)
 {
 	struct path * pp;

Modified: multipath-tools/trunk/libmultipath/propsel.h
==============================================================================
--- multipath-tools/trunk/libmultipath/propsel.h	(original)
+++ multipath-tools/trunk/libmultipath/propsel.h	Mon Sep 19 12:43:52 2005
@@ -1,3 +1,5 @@
+int select_rr_weight (struct multipath * mp);
+int select_pgfailback (struct multipath * mp);
 int select_pgpolicy (struct multipath * mp);
 int select_selector (struct multipath * mp);
 int select_alias (struct multipath * mp);

Modified: multipath-tools/trunk/libmultipath/structs.c
==============================================================================
--- multipath-tools/trunk/libmultipath/structs.c	(original)
+++ multipath-tools/trunk/libmultipath/structs.c	Mon Sep 19 12:43:52 2005
@@ -11,7 +11,17 @@
 struct path *
 alloc_path (void)
 {
-	return (struct path *)MALLOC(sizeof(struct path));
+	struct path * pp;
+	
+	pp = (struct path *)MALLOC(sizeof(struct path));
+
+	if (pp) {
+		pp->sg_id.host_no = -1;
+		pp->sg_id.channel = -1;
+		pp->sg_id.scsi_id = -1;
+		pp->sg_id.lun = -1;
+	}
+	return pp;
 }
 
 void
@@ -21,7 +31,7 @@
 		return;
 
 	if (pp->checker_context)
-		FREE(pp->checker_context);
+		free(pp->checker_context);
 
 	if (pp->fd > 0)
 		close(pp->fd);
@@ -91,7 +101,14 @@
 struct multipath *
 alloc_multipath (void)
 {
-	return (struct multipath *)MALLOC(sizeof(struct multipath));
+	struct multipath * mpp;
+
+	mpp = (struct multipath *)MALLOC(sizeof(struct multipath));
+
+	if (mpp)
+		mpp->nextpg = 1;
+
+	return mpp;
 }
 
 void
@@ -164,6 +181,32 @@
 }
 
 struct multipath *
+find_mp_by_minor (vector mp, int minor)
+{
+	int i;
+	struct multipath * mpp;
+	
+	vector_foreach_slot (mp, mpp, i)
+		if (mpp->minor == minor)
+			return mpp;
+
+	return NULL;
+}
+
+struct multipath *
+find_mp_by_wwid (vector mp, char * wwid)
+{
+	int i;
+	struct multipath * mpp;
+	
+	vector_foreach_slot (mp, mpp, i)
+		if (!strncmp(mpp->wwid, wwid, WWID_SIZE))
+			return mpp;
+
+	return NULL;
+}
+
+struct multipath *
 find_mp (vector mp, char * alias)
 {
 	int i;

Modified: multipath-tools/trunk/libmultipath/structs.h
==============================================================================
--- multipath-tools/trunk/libmultipath/structs.h	(original)
+++ multipath-tools/trunk/libmultipath/structs.h	Mon Sep 19 12:43:52 2005
@@ -14,17 +14,37 @@
 #define SCSI_PRODUCT_SIZE	17
 #define SCSI_REV_SIZE		5
 
-#define KEEP_PATHS		0
-#define FREE_PATHS		1
+enum free_path_switch {
+	KEEP_PATHS,
+	FREE_PATHS
+};
+
+enum rr_weight_mode {
+	RR_WEIGHT_UNDEF,
+	RR_WEIGHT_NONE,
+	RR_WEIGHT_PRIO
+};
+
+enum failback_mode {
+	FAILBACK_UNDEF,
+	FAILBACK_MANUAL,
+	FAILBACK_IMMEDIATE
+};
+
+enum sysfs_buses {
+	SYSFS_BUS_UNDEF,
+	SYSFS_BUS_SCSI,
+	SYSFS_BUS_IDE
+};
 
 enum pathstates {
-	PSTATE_RESERVED,
+	PSTATE_UNDEF,
 	PSTATE_FAILED,
 	PSTATE_ACTIVE
 };
 
 enum pgstates {
-	PGSTATE_RESERVED,
+	PGSTATE_UNDEF,
 	PGSTATE_ENABLED,
 	PGSTATE_DISABLED,
 	PGSTATE_ACTIVE
@@ -64,12 +84,16 @@
 	char rev[SCSI_REV_SIZE];
 	char serial[SERIAL_SIZE];
 	char tgt_node_name[NODE_NAME_SIZE];
-	unsigned long size;
+	unsigned long long size;
+	unsigned int checkint;
+	unsigned int tick;
 	int state;
+	int bus;
 	int dmstate;
 	int failcount;
-	unsigned int priority;
+	int priority;
 	int claimed;
+	int pgindex;
 	char * getuid;
 	char * getprio;
 	int (*checkfn) (int, char *, void **);
@@ -83,11 +107,15 @@
 
 struct multipath {
 	char wwid[WWID_SIZE];
+	int minor;
 	int pgpolicy;
 	int nextpg;
 	int queuedio;
 	int action;
-	unsigned long size;
+	int pgfailback;
+	int failback_tick;
+	int rr_weight;
+	unsigned long long size;
 	vector paths;
 	vector pg;
 	char params[PARAMS_SIZE];
@@ -100,6 +128,9 @@
 	char * hwhandler;
 	struct mpentry * mpe;
 	struct hwentry * hwe;
+
+	/* daemon store a data blob for DM event waiter threads */
+	void * waiter;
 };
 
 struct pathgroup {
@@ -123,6 +154,9 @@
 int store_pathgroup (vector pgvec, struct pathgroup * pgp);
 
 struct multipath * find_mp (vector mp, char * alias);
+struct multipath * find_mp_by_wwid (vector mp, char * wwid);
+struct multipath * find_mp_by_minor (vector mp, int minor);
+	
 struct path * find_path_by_devt (vector pathvec, char * devt);
 struct path * find_path_by_dev (vector pathvec, char * dev);
 

Added: multipath-tools/trunk/libmultipath/switchgroup.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libmultipath/switchgroup.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,24 @@
+#include "vector.h"
+#include "structs.h"
+#include "switchgroup.h"
+#include "../libcheckers/path_state.h"
+
+extern void
+select_path_group (struct multipath * mpp)
+{
+	int i, j;
+	int highest = 0;
+	struct pathgroup * pgp;
+	struct path * pp;
+	
+	vector_foreach_slot (mpp->pg, pgp, i) {
+		vector_foreach_slot (pgp->paths, pp, j) {
+			if (pp->state != PATH_DOWN)
+				pgp->priority += pp->priority;
+		}
+		if (pgp->priority > highest) {
+			highest = pgp->priority;
+			mpp->nextpg = i + 1;
+		}
+	}
+}

Added: multipath-tools/trunk/libmultipath/switchgroup.h
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libmultipath/switchgroup.h	Mon Sep 19 12:43:52 2005
@@ -0,0 +1 @@
+void select_path_group (struct multipath * mpp);

Added: multipath-tools/trunk/libmultipath/uevent.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libmultipath/uevent.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,134 @@
+/*
+ * uevent.c - trigger upon netlink uevents from the kernel
+ *
+ *	Only kernels from version 2.6.10* on provide the uevent netlink socket.
+ *	Until the libc-kernel-headers are updated, you need to compile with:
+ *
+ *	  gcc -I /lib/modules/`uname -r`/build/include -o uevent_listen uevent_listen.c
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers at vrfy.org>
+ *
+ *	This program is free software; you can redistribute it and/or modify it
+ *	under the terms of the GNU General Public License as published by the
+ *	Free Software Foundation version 2 of the License.
+ *
+ *	This program is distributed in the hope that it will be useful, but
+ *	WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *	General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License along
+ *	with this program; if not, write to the Free Software Foundation, Inc.,
+ *	675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/user.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+
+#include "memory.h"
+#include "debug.h"
+#include "uevent.h"
+
+struct uevent * alloc_uevent (void)
+{
+	return (struct uevent *)MALLOC(sizeof(struct uevent));
+}
+
+int uevent_listen(int (*uev_trigger)(struct uevent *, void * trigger_data),
+		  void * trigger_data)
+{
+	int sock;
+	struct sockaddr_nl snl;
+	int retval;
+
+	memset(&snl, 0x00, sizeof(struct sockaddr_nl));
+	snl.nl_family = AF_NETLINK;
+	snl.nl_pid = getpid();
+	snl.nl_groups = 0xffffffff;
+
+	sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+	if (sock == -1) {
+		condlog(0, "error getting socket, exit\n");
+		exit(1);
+	}
+
+	retval = bind(sock, (struct sockaddr *) &snl,
+		      sizeof(struct sockaddr_nl));
+	if (retval < 0) {
+		condlog(0, "bind failed, exit\n");
+		goto exit;
+	}
+
+	while (1) {
+		static char buffer[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE];
+		int i;
+		char *pos;
+		size_t bufpos;
+		ssize_t buflen;
+		struct uevent *uev;
+
+		buflen = recv(sock, &buffer, sizeof(buffer), 0);
+		if (buflen <  0) {
+			condlog(0, "error receiving message\n");
+			continue;
+		}
+
+		if ((size_t)buflen > sizeof(buffer)-1)
+			buflen = sizeof(buffer)-1;
+
+		buffer[buflen] = '\0';
+		uev = alloc_uevent();
+
+		if (!uev) {
+			condlog(1, "lost uevent, oom");
+			continue;
+		}
+
+		/* save start of payload */
+		bufpos = strlen(buffer) + 1;
+
+		/* action string */
+		uev->action = buffer;
+		pos = strchr(buffer, '@');
+		if (!pos)
+			continue;
+		pos[0] = '\0';
+
+		/* sysfs path */
+		uev->devpath = &pos[1];
+
+		/* hotplug events have the environment attached - reconstruct envp[] */
+		for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) {
+			int keylen;
+			char *key;
+
+			key = &buffer[bufpos];
+			keylen = strlen(key);
+			uev->envp[i] = key;
+			bufpos += keylen + 1;
+		}
+		uev->envp[i] = NULL;
+
+		condlog(3, "uevent '%s' from '%s'\n", uev->action, uev->devpath);
+
+		/* print payload environment */
+		for (i = 0; uev->envp[i] != NULL; i++)
+			condlog(3, "%s\n", uev->envp[i]);
+
+		if (uev_trigger && uev_trigger(uev, trigger_data))
+			condlog(0, "uevent trigger error");
+	}
+
+exit:
+	close(sock);
+	return 1;
+}

Added: multipath-tools/trunk/libmultipath/uevent.h
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libmultipath/uevent.h	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,17 @@
+/* environment buffer, the kernel's size in lib/kobject_uevent.c should fit in */
+#define HOTPLUG_BUFFER_SIZE		1024
+#define HOTPLUG_NUM_ENVP		32
+#define OBJECT_SIZE			512
+
+#ifndef NETLINK_KOBJECT_UEVENT
+#define NETLINK_KOBJECT_UEVENT		15
+#endif
+
+struct uevent {
+	char *devpath;
+	char *action;
+	char *envp[HOTPLUG_NUM_ENVP];
+};
+
+int uevent_listen(int (*store_uev)(struct uevent *, void * trigger_data),
+		  void * trigger_data);

Modified: multipath-tools/trunk/libmultipath/util.c
==============================================================================
--- multipath-tools/trunk/libmultipath/util.c	(original)
+++ multipath-tools/trunk/libmultipath/util.c	Mon Sep 19 12:43:52 2005
@@ -4,6 +4,9 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "debug.h"
+#include "memory.h"
+
 #define PARAMS_SIZE 255
 
 int
@@ -49,3 +52,45 @@
 	return 0;
 }
 
+int
+get_word (char * sentence, char ** word)
+{
+	char * p;
+	int len;
+	int skip = 0;
+	
+	if (word)
+		*word = NULL;
+
+	while (*sentence ==  ' ') {
+		sentence++;
+		skip++;
+	}
+	if (*sentence == '\0')
+		return 0;
+
+	p = sentence;
+
+	while (*p !=  ' ' && *p != '\0')
+		p++;
+
+	len = (int) (p - sentence);
+
+	if (!word)
+		return skip + len;
+
+	*word = MALLOC(len + 1);
+
+	if (!*word) {
+		condlog(0, "get_word : oom\n");
+		return 0;
+	}
+	strncpy(*word, sentence, len);
+	condlog(4, "*word = %s, len = %i", *word, len);
+
+	if (*p == '\0')
+		return 0;
+
+	return skip + len;
+}
+

Modified: multipath-tools/trunk/libmultipath/util.h
==============================================================================
--- multipath-tools/trunk/libmultipath/util.h	(original)
+++ multipath-tools/trunk/libmultipath/util.h	Mon Sep 19 12:43:52 2005
@@ -4,6 +4,8 @@
 int strcmp_chomp(char *, char *);
 void basename (char * src, char * dst);
 int filepresent (char * run);
+int get_word (char * sentence, char ** word);
+
 
 #define safe_sprintf(var, format, args...)	\
 	snprintf(var, sizeof(var), format, ##args) >= sizeof(var)

Added: multipath-tools/trunk/libmultipath/uxsock.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libmultipath/uxsock.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+
+#include "memory.h"
+#include "uxsock.h"
+
+/*
+ * connect to a unix domain socket
+ */
+int ux_socket_connect(const char *name)
+{
+	int fd;
+        struct sockaddr_un addr;
+
+        memset(&addr, 0, sizeof(addr));
+        addr.sun_family = AF_UNIX;
+        strncpy(addr.sun_path, name, sizeof(addr.sun_path));
+
+	fd = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (fd == -1) {
+		return -1;
+	}
+	
+	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+/*
+ * create a unix domain socket and start listening on it
+ * return a file descriptor open on the socket 
+ */
+int ux_socket_listen(const char *name)
+{
+	int fd;
+        struct sockaddr_un addr;
+
+	/* get rid of any old socket */
+	unlink(name);
+
+	fd = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (fd == -1) return -1;
+
+        memset(&addr, 0, sizeof(addr));
+        addr.sun_family = AF_UNIX;
+        strncpy(addr.sun_path, name, sizeof(addr.sun_path));
+
+        if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+		close(fd);
+		return -1;
+	}	
+
+        if (listen(fd, 10) == -1) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+/*
+ * keep writing until its all sent
+ */
+int write_all(int fd, const void *buf, size_t len)
+{
+	size_t total = 0;
+	while (len) {
+		int n = write(fd, buf, len);
+		if (n <= 0) return total;
+		buf = n + (char *)buf;
+		len -= n;
+		total += n;
+	}
+	return total;
+}
+
+/*
+ * keep reading until its all read
+ */
+int read_all(int fd, void *buf, size_t len)
+{
+	size_t total = 0;
+	while (len) {
+		int n = read(fd, buf, len);
+		if (n <= 0) return total;
+		buf = n + (char *)buf;
+		len -= n;
+		total += n;
+	}
+	return total;
+}
+
+/*
+ * send a packet in length prefix format
+ */
+int send_packet(int fd, const char *buf, size_t len)
+{
+	if (write_all(fd, &len, sizeof(len)) != sizeof(len)) return -1;
+	if (write_all(fd, buf, len) != len) return -1;	
+	return 0;
+}
+
+/*
+ * receive a packet in length prefix format
+ */
+int recv_packet(int fd, char **buf, size_t *len)
+{
+	if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) return -1;
+	(*buf) = MALLOC(*len);
+	if (read_all(fd, *buf, *len) != *len) {
+		FREE(*buf);
+		return -1;
+	}
+	return 0;
+}

Added: multipath-tools/trunk/libmultipath/uxsock.h
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/libmultipath/uxsock.h	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,6 @@
+/* some prototypes */
+int ux_socket_connect(const char *name);
+int ux_socket_listen(const char *name);
+int send_packet(int fd, const char *buf, size_t len);
+int recv_packet(int fd, char **buf, size_t *len);
+

Modified: multipath-tools/trunk/libmultipath/vector.c
==============================================================================
--- multipath-tools/trunk/libmultipath/vector.c	(original)
+++ multipath-tools/trunk/libmultipath/vector.c	Mon Sep 19 12:43:52 2005
@@ -63,6 +63,18 @@
 	return v->slot[slot];
 }
 
+int
+find_slot(vector v, void * addr)
+{
+	int i;
+
+	for (i = 0; i < (v->allocated / VECTOR_DEFAULT_SIZE); i++)
+		if (v->slot[i] == addr)
+			return i;
+
+	return -1;
+}
+
 void
 vector_del_slot(vector v, int slot)
 {
@@ -76,12 +88,27 @@
 
 	v->allocated -= VECTOR_DEFAULT_SIZE;
 
-	if (!v->allocated)
+	if (!v->allocated) {
+		FREE(v->slot);
 		v->slot = NULL;
+	}
 	else
 		v = REALLOC(v->slot, sizeof (void *) * v->allocated);
 }
 
+void
+vector_repack(vector v)
+{
+	int i;
+
+	if (!v->allocated)
+		return;
+
+	for (i = 0; i < (v->allocated / VECTOR_DEFAULT_SIZE); i++)
+		if (i > 0 && v->slot[i] == NULL)
+			vector_del_slot(v, i--);
+}
+
 /* Free memory vector allocation */
 void
 vector_free(vector v)
@@ -104,8 +131,8 @@
 	if (!strvec)
 		return;
 
-	for (i = 0; i < VECTOR_SIZE(strvec); i++)
-		if ((str = VECTOR_SLOT(strvec, i)) != NULL)
+	vector_foreach_slot (strvec, str, i)
+		if (str)
 			FREE(str);
 
 	vector_free(strvec);

Modified: multipath-tools/trunk/libmultipath/vector.h
==============================================================================
--- multipath-tools/trunk/libmultipath/vector.h	(original)
+++ multipath-tools/trunk/libmultipath/vector.h	Mon Sep 19 12:43:52 2005
@@ -46,6 +46,8 @@
 extern void vector_set_slot(vector v, void *value);
 extern void vector_del_slot(vector v, int slot);
 extern void *vector_insert_slot(vector v, int slot, void *value);
+int find_slot(vector v, void * addr);
+extern void vector_repack(vector v);
 extern void vector_dump(vector v);
 extern void dump_strvec(vector strvec);
 

Modified: multipath-tools/trunk/multipath.conf.annotated
==============================================================================
--- multipath-tools/trunk/multipath.conf.annotated	(original)
+++ multipath-tools/trunk/multipath.conf.annotated	Mon Sep 19 12:43:52 2005
@@ -1,342 +1,279 @@
+##
+## This is a template multipath-tools configuration file
+## Uncomment the lines relevent to your environment
+##
 #
-# name  : defaults
-# desc  : multipath-tools default settings
+##
+## name  : defaults
+## desc  : multipath-tools default settings
+##
+#defaults {
+#	#
+#	# name    : multipath_tool
+#	# scope   : multipathd
+#	# desc    : the tool in charge of configuring the multipath device maps
+#	# default : "/sbin/multipath -v0"
+#	#
+#	multipath_tool	"/sbin/multipath -v0"
 #
-defaults {
-	#
-	# name    : multipath_tool
-	# scope   : multipathd
-	# desc    : the tool in charge of configuring the multipath device maps
-	# default : "/sbin/multipath -v 0 -S"
-	#
-	multipath_tool	"/sbin/multipath -v 0 -S"
-
-	#
-	# 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
-	# default : 5
-	#
-	polling_interval 10
-
-	#
-	# name    : default_selector
-	# scope   : multipath
-	# 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"
-	#
-	default_selector	"round-robin 0"
-
-	#
-	# name    : default_path_grouping_policy
-	# scope   : multipath
-	# desc    : the default path grouping policy to apply to unspecified
-	#           multipaths
-	# default : multibus
-	#
-	default_path_grouping_policy	multibus
-
-	#
-	# name    : default_getuid_callout
-	# scope   : multipath
-	# desc    : the default program and args to callout to obtain a unique 
-	#           path identifier. Absolute path required
-	# default : /sbin/scsi_id -g -u -s
-	#
-	default_getuid_callout	"/sbin/scsi_id -g -u -s /block/%n"
-
-	#
-	# name    : default_prio_callout
-	# scope   : multipath
-	# desc    : the default program and args to callout to obtain a path 
-	#           priority value. The ALUA bits in SPC-3 provide an
-	#           exploitable prio value for example
-	# default : /bin/false
-	#
-	default_prio_callout	"/bin/false"
-
-	#
-	# name    : rr_min_io
-	# scope   : multipath
-	# desc    : the number of IO to route to a path before switching
-	#           to the next in the same path group
-	# default : 1000
-	#
-	r_min_io	100
-}
-	
-#
-# name    : blacklist
-# scope   : multipath & multipathd
-# desc    : list of device names to discard as not multipath candidates
-# default : cciss, fd, hd, md, dm, sr, scd, st, ram, raw, loop
-#
-blacklist {
-        wwid 26353900f02796769
-	devnode "(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
-	devnode "hd[a-z][[0-9]*]"
-	devnode "cciss!c[0-9]d[0-9]*[p[0-9]*]"
-}
-
-#
-# name    : multipaths
-# scope   : multipath & multipathd
-# desc    : list of multipaths finest-grained settings
-#
-multipaths {
-	#
-	# name  : multipath
-	# scope : multipath & multipathd
-	# desc  : container for settings that apply to one specific multipath
-	#
-	multipath {
-		#
-		# name  : wwid
-		# scope : multipath & multipathd
-		# desc  : index of the container
-		#
-		wwid			3600508b4000156d700012000000b0000
-
-		#
-		# name    : alias
-		# scope   : multipath
-		# desc    : symbolic name for the multipath
-		#
-		alias			yellow
-
-		#
-		# name    : path_grouping_policy
-		# scope   : multipath
-		# desc    : path grouping policy to apply to this multipath
-		# values  : failover, multibus, group_by_serial
-		# default : failover
-		#
-		path_grouping_policy	multibus
-
-		#
-		# name    : path_checker
-		# scope   : multipathd
-		# desc    : path checking alorithm to use to check path state
-		# values  : readsector0, tur
-		# default : readsector0
-		#
-		# path_checker		readsector0
-		
-		#
-		# name    : path_selector
-		# desc    : the path selector algorithm to use for this mpath
-		#           these algo are offered by the kernel mpath target
-		# values  : "round-robin 0"
-		# default : "round-robin 0"
-		#
-		path_selector		"round-robin 0"
-	}
-	multipath {
-		wwid	1DEC_____321816758474
-		alias	red
-	}
-}
-
-#
-# name  : devices
-# scope : multipath & multipathd
-# desc  : list of per storage controler settings
-#	  overrides default settings (device_maps block)
-#         overriden by per multipath settings (multipaths block)
-#
-devices {
-	#
-	# name  : device
-	# scope : multipath & multipathd
-	# desc  : settings for this specific storage controler
-	#
-	device {
-		#
-		# name  : vendor, product
-		# scope : multipath & multipathd
-		# desc  : index for the block
-		#
-		vendor			"COMPAQ  "
-		product			"HSV110 (C)COMPAQ"
-
-		#
-		# name    : path_grouping_policy
-		# scope   : multipath
-		# desc    : path grouping policy to apply to multipath hosted
-		#           by this storage controler
-		# values  : failover        = 1 path per priority group
-		#           multibus        = all valid paths in 1 priority
-		#                             group
-		#           group_by_serial = 1 priority group per detected
-		#                             serial number
-		# default : failover
-		#
-		path_grouping_policy	multibus
-
-		#
-		# name    : getuid_callout
-		# scope   : multipath
-		# desc    : the program and args to callout to obtain a unique 
-		#           path identifier. Absolute path required
-		# default : /sbin/scsi_id -g -u -s
-		#
-		getuid_callout          "/sbin/scsi_id -g -u -s /block/%n"
-
-		#
-		# name    : prio_callout
-		# scope   : multipath
-		# desc    : the program and args to callout to obtain a path 
-		#           weight. Weights are summed for each path group to
-		#	    determine the next PG to use case of failure.
-		# default : no callout, all paths equals
-		#
-		prio_callout          "/sbin/pp_balance_units %d"
-
-		#
-		# name    : path_checker
-		# scope   : multipathd
-		# desc    : path checking alorithm to use to check path state
-		# values  : readsector0, tur
-		# default : readsector0
-		#
-		path_checker		readsector0
-
-		#
-		# name    : path_selector
-		# desc    : the path selector algorithm to use for this mpath
-		#           these algo are offered by the kernel mpath target
-		# values  : "round-robin 0"
-		# default : "round-robin 0"
-		#
-		path_selector		"round-robin 0"
-	}
-	device {
-		vendor			"COMPAQ  "
-		product			"MSA1000         "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"COMPAQ  "
-		product			"MSA1000 VOLUME  "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"DEC     "
-		product			"HSG80           "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"HP      "
-		product			"HSV100          "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"3PARdata"
-		product			"VV              "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"DDN     "
-		product			"SAN DataDirector"
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"FSC     "
-		product			"CentricStor     "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"HITACHI "
-		product			"DF400           "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"HITACHI "
-		product			"DF500           "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"HITACHI "
-		product			"DF600           "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"IBM     "
-		product			"ProFibre 4000R  "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"IBM     "
-		product			"3542            "
-		path_grouping_policy	group_by_serial
-		path_checker		tur
-	}
-	device {
-		vendor			"SGI     "
-		product			"TP9100          "
-		vendor			"COMPAQ  "
-		product			"MSA1000         "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"SGI     "
-		product			"TP9300          "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"SGI     "
-		product			"TP9400          "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"SGI     "
-		product			"TP9500          "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		# all paths active but with a switchover latency
-		# LSI controlers
-		vendor			"STK     "
-		product			"OPENstorage D280"
-		path_grouping_policy	group_by_serial
-		path_checker		tur
-	}
-	device {
-		# assymmetric array
-		vendor			"SUN     "
-		product			"StorEdge 3510   "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		# symmetric array
-		vendor			"SUN     "
-		product			"T4              "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-}
+#	#
+#	# 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
+#	# default : 5
+#	#
+#	polling_interval 10
+#
+#	#
+#	# name    : default_selector
+#	# scope   : multipath
+#	# 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"
+#	#
+#	default_selector	"round-robin 0"
+#
+#	#
+#	# name    : default_path_grouping_policy
+#	# scope   : multipath
+#	# desc    : the default path grouping policy to apply to unspecified
+#	#           multipaths
+#	# default : multibus
+#	#
+#	default_path_grouping_policy	multibus
+#
+#	#
+#	# name    : default_getuid_callout
+#	# scope   : multipath
+#	# desc    : the default program and args to callout to obtain a unique 
+#	#           path identifier. Absolute path required
+#	# default : /sbin/scsi_id -g -u -s
+#	#
+#	default_getuid_callout	"/sbin/scsi_id -g -u -s /block/%n"
+#
+#	#
+#	# name    : default_prio_callout
+#	# scope   : multipath
+#	# desc    : the default program and args to callout to obtain a path 
+#	#           priority value. The ALUA bits in SPC-3 provide an
+#	#           exploitable prio value for example. "none" is a valid value
+#	# default : (null)
+#	#
+#	#default_prio_callout	"/bin/true"
+#
+#	#
+#	# name    : rr_min_io
+#	# scope   : multipath
+#	# desc    : the number of IO to route to a path before switching
+#	#           to the next in the same path group
+#	# default : 1000
+#	#
+#	r_min_io	100
+#
+#	#
+#	# name    : rr_weight
+#	# scope   : multipath
+#	# desc    : if set to priorities the multipath configurator will assign
+#	#	    path weights as "path prio * rr_min_io"
+#	# values  : priorities|uniform
+#	# default : uniform
+#	#
+#	rr_weight		priorities
+#
+#	#
+#	# name    : failback
+#	# scope   : multipathd
+#	# desc    : tell the daemon to manage path group failback, or not to.
+#	#	    0 means immediate failback, values >0 means deffered failback
+#	#	    expressed in seconds.
+#	# values  : manual|immediate|n > 0
+#	# default : immediate
+#	#
+#	failback	manual
+#}
+#	
+##
+## name    : blacklist
+## scope   : multipath & multipathd
+## desc    : list of device names to discard as not multipath candidates
+## default : cciss, fd, hd, md, dm, sr, scd, st, ram, raw, loop
+##
+#blacklist {
+#        wwid 26353900f02796769
+#	devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
+#	devnode "^hd[a-z][[0-9]*]"
+#	devnode "^cciss!c[0-9]d[0-9]*[p[0-9]*]"
+#}
+#
+##
+## name    : multipaths
+## scope   : multipath & multipathd
+## desc    : list of multipaths finest-grained settings
+##
+#multipaths {
+#	#
+#	# name  : multipath
+#	# scope : multipath & multipathd
+#	# desc  : container for settings that apply to one specific multipath
+#	#
+#	multipath {
+#		#
+#		# name  : wwid
+#		# scope : multipath & multipathd
+#		# desc  : index of the container
+#		#
+#		wwid			3600508b4000156d700012000000b0000
+#
+#		#
+#		# name    : alias
+#		# scope   : multipath
+#		# desc    : symbolic name for the multipath
+#		#
+#		alias			yellow
+#
+#		#
+#		# name    : path_grouping_policy
+#		# scope   : multipath
+#		# desc    : path grouping policy to apply to this multipath
+#		# values  : failover, multibus, group_by_serial
+#		# default : failover
+#		#
+#		path_grouping_policy	multibus
+#
+#		#
+#		# name    : path_checker
+#		# scope   : multipathd
+#		# desc    : path checking alorithm to use to check path state
+#		# values  : readsector0, tur
+#		# default : readsector0
+#		#
+#		# path_checker		readsector0
+#		
+#		#
+#		# name    : path_selector
+#		# desc    : the path selector algorithm to use for this mpath
+#		#           these algo are offered by the kernel mpath target
+#		# values  : "round-robin 0"
+#		# default : "round-robin 0"
+#		#
+#		path_selector		"round-robin 0"
+#
+#		#
+#		# name    : failback
+#		# scope   : multipathd
+#		# desc    : tell the daemon to manage path group failback, or not to.
+#		#	    0 means immediate failback, values >0 means deffered failback
+#		#	    expressed in seconds.
+#		# values  : manual|immediate|n > 0
+#		# default : immediate
+#		#
+#		failback		manual
+#	}
+#	multipath {
+#		wwid	1DEC_____321816758474
+#		alias	red
+#		rr_weight		priorities
+#	}
+#}
+#
+##
+## name  : devices
+## scope : multipath & multipathd
+## desc  : list of per storage controler settings
+##	  overrides default settings (device_maps block)
+##         overriden by per multipath settings (multipaths block)
+##
+#devices {
+#	#
+#	# name  : device
+#	# scope : multipath & multipathd
+#	# desc  : settings for this specific storage controler
+#	#
+#	device {
+#		#
+#		# name  : vendor, product
+#		# scope : multipath & multipathd
+#		# desc  : index for the block
+#		#
+#		vendor			"COMPAQ  "
+#		product			"HSV110 (C)COMPAQ"
+#
+#		#
+#		# name    : path_grouping_policy
+#		# scope   : multipath
+#		# desc    : path grouping policy to apply to multipath hosted
+#		#           by this storage controler
+#		# values  : failover        = 1 path per priority group
+#		#           multibus        = all valid paths in 1 priority
+#		#                             group
+#		#           group_by_serial = 1 priority group per detected
+#		#                             serial number
+#		# default : failover
+#		#
+#		path_grouping_policy	multibus
+#
+#		#
+#		# name    : getuid_callout
+#		# scope   : multipath
+#		# desc    : the program and args to callout to obtain a unique 
+#		#           path identifier. Absolute path required
+#		# default : /sbin/scsi_id -g -u -s
+#		#
+#		getuid_callout          "/sbin/scsi_id -g -u -s /block/%n"
+#
+#		#
+#		# name    : prio_callout
+#		# scope   : multipath
+#		# desc    : the program and args to callout to obtain a path 
+#		#           weight. Weights are summed for each path group to
+#		#	    determine the next PG to use case of failure.
+#		#	    "none" is a valid value.
+#		# default : no callout, all paths equals
+#		#
+#		prio_callout          "/sbin/mpath_prio_balance_units %d"
+#
+#		#
+#		# name    : path_checker
+#		# scope   : multipathd
+#		# desc    : path checking alorithm to use to check path state
+#		# values  : readsector0, tur
+#		# default : readsector0
+#		#
+#		path_checker		readsector0
+#
+#		#
+#		# name    : path_selector
+#		# desc    : the path selector algorithm to use for this mpath
+#		#           these algo are offered by the kernel mpath target
+#		# values  : "round-robin 0"
+#		# default : "round-robin 0"
+#		#
+#		path_selector		"round-robin 0"
+#
+#		#
+#		# name    : failback
+#		# scope   : multipathd
+#		# desc    : tell the daemon to manage path group failback, or not to.
+#		#	    0 means immediate failback, values >0 means deffered failback
+#		#	    expressed in seconds.
+#		# values  : manual|immediate|n > 0
+#		# default : immediate
+#		#
+#		failback		30
+#	}
+#	device {
+#		vendor			"COMPAQ  "
+#		product			"MSA1000         "
+#		path_grouping_policy	multibus
+#		path_checker		tur
+#		rr_weight		priorities
+#	}
+#}

Modified: multipath-tools/trunk/multipath.conf.synthetic
==============================================================================
--- multipath-tools/trunk/multipath.conf.synthetic	(original)
+++ multipath-tools/trunk/multipath.conf.synthetic	Mon Sep 19 12:43:52 2005
@@ -1,162 +1,57 @@
-defaults {
-	multipath_tool	"/sbin/multipath -v 0 -S"
-	udev_dir	/dev
-	polling_interval 10
-	default_selector	"round-robin 0"
-	default_path_grouping_policy	multibus
-	default_getuid_callout	"/sbin/scsi_id -g -u -s /block/%n"
-	default_prio_callout	"/bin/false"
-	default_features	"0"
-	rr_wmin_io		100
-}
-devnode_blacklist {
-        wwid 26353900f02796769
-	devnode "(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
-	devnode "hd[a-z][[0-9]*]"
-	devnode "cciss!c[0-9]d[0-9]*[p[0-9]*]"
-}
-multipaths {
-	multipath {
-		wwid			3600508b4000156d700012000000b0000
-		alias			yellow
-		path_grouping_policy	multibus
-		path_checker		readsector0
-		path_selector		"round-robin 0"
-	}
-	multipath {
-		wwid	1DEC_____321816758474
-		alias	red
-	}
-}
-devices {
-	device {
-		vendor			"COMPAQ  "
-		product			"HSV110 (C)COMPAQ"
-		path_grouping_policy	multibus
-		getuid_callout          "/sbin/scsi_id -g -u -s /block/%n"
-		path_checker		readsector0
-		path_selector		"round-robin 0"
-		features		"1 queue_if_no_path"
-		hardware_handler	"0"
-	}
-	device {
-		vendor			"COMPAQ  "
-		product			"MSA1000         "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"COMPAQ  "
-		product			"MSA1000 VOLUME  "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"DEC     "
-		product			"HSG80           "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"HP      "
-		product			"HSV100          "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"3PARdata"
-		product			"VV              "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"IBM     "
-		product			"3542            "
-		path_grouping_policy	group_by_serial
-		path_checker		tur
-	}
-	device {
-		vendor			"DDN     "
-		product			"SAN DataDirector"
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"FSC     "
-		product			"CentricStor     "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"HITACHI "
-		product			"DF400           "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"HITACHI "
-		product			"DF500           "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"HITACHI "
-		product			"DF600           "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"IBM     "
-		product			"ProFibre 4000R  "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"SGI     "
-		product			"TP9100          "
-		vendor			"COMPAQ  "
-		product			"MSA1000         "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"SGI     "
-		product			"TP9300          "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"SGI     "
-		product			"TP9400          "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		vendor			"SGI     "
-		product			"TP9500          "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		# all paths active but with a switchover latency
-		# LSI controlers
-		vendor			"STK     "
-		product			"OPENstorage D280"
-		path_grouping_policy	group_by_serial
-		path_checker		tur
-	}
-	device {
-		# assymmetric array
-		vendor			"SUN     "
-		product			"StorEdge 3510   "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-	device {
-		# symmetric array
-		vendor			"SUN     "
-		product			"T4              "
-		path_grouping_policy	multibus
-		path_checker		tur
-	}
-}
+##
+## This is a template multipath-tools configuration file
+## Uncomment the lines relevent to your environment
+##
+#defaults {
+#	multipath_tool	"/sbin/multipath -v0"
+#	udev_dir	/dev
+#	polling_interval 10
+#	default_selector	"round-robin 0"
+#	default_path_grouping_policy	multibus
+#	default_getuid_callout	"/sbin/scsi_id -g -u -s /block/%n"
+#	default_prio_callout	"/bin/true"
+#	default_features	"0"
+#	rr_min_io		100
+#	rr_weight		priorities
+#	failback		immediate
+#}
+#devnode_blacklist {
+#        wwid 26353900f02796769
+#	devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
+#	devnode "^hd[a-z][[0-9]*]"
+#	devnode "^cciss!c[0-9]d[0-9]*[p[0-9]*]"
+#}
+#multipaths {
+#	multipath {
+#		wwid			3600508b4000156d700012000000b0000
+#		alias			yellow
+#		path_grouping_policy	multibus
+#		path_checker		readsector0
+#		path_selector		"round-robin 0"
+#		failback		manual
+#		rr_weight		priorities
+#	}
+#	multipath {
+#		wwid	1DEC_____321816758474
+#		alias	red
+#	}
+#}
+#devices {
+#	device {
+#		vendor			"COMPAQ  "
+#		product			"HSV110 (C)COMPAQ"
+#		path_grouping_policy	multibus
+#		getuid_callout          "/sbin/scsi_id -g -u -s /block/%n"
+#		path_checker		readsector0
+#		path_selector		"round-robin 0"
+#		features		"1 queue_if_no_path"
+#		hardware_handler	"0"
+#		failback		15
+#		rr_weight		priorities
+#	}
+#	device {
+#		vendor			"COMPAQ  "
+#		product			"MSA1000         "
+#		path_grouping_policy	multibus
+#	}
+#}

Modified: multipath-tools/trunk/multipath/Makefile
==============================================================================
--- multipath-tools/trunk/multipath/Makefile	(original)
+++ multipath-tools/trunk/multipath/Makefile	Mon Sep 19 12:43:52 2005
@@ -21,7 +21,7 @@
 all: $(BUILD)
 
 prepare:
-	make -C $(multipathdir) clean
+	make -C $(multipathdir) prepare
 	rm -f core *.o *.gz
 
 glibc: prepare $(OBJS)
@@ -43,7 +43,6 @@
 install:
 	install -d $(DESTDIR)$(bindir)
 	install -m 755 $(EXEC) $(DESTDIR)$(bindir)/
-	install -d $(DESTDIR)/var/cache/multipath/
 	install -d $(DESTDIR)/etc/dev.d/block/
 	install -m 755 multipath.dev $(DESTDIR)/etc/dev.d/block/
 	install -d $(DESTDIR)/etc/udev/rules.d

Modified: multipath-tools/trunk/multipath/main.c
==============================================================================
--- multipath-tools/trunk/multipath/main.c	(original)
+++ multipath-tools/trunk/multipath/main.c	Mon Sep 19 12:43:52 2005
@@ -20,7 +20,6 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
-#include <signal.h>
 
 #include <parser.h>
 #include <vector.h>
@@ -40,12 +39,17 @@
 #include <propsel.h>
 #include <discovery.h>
 #include <debug.h>
+#include <switchgroup.h>
 #include <sysfs/libsysfs.h>
+#include <print.h>
 
 #include "main.h"
 #include "pgpolicies.h"
 #include "dict.h"
 
+/* for column aligned output */
+struct path_layout pl;
+
 static char *
 get_refwwid (vector pathvec)
 {
@@ -67,13 +71,15 @@
 			if (!pp)
 				return NULL;
 
+			strncpy(pp->dev, buff, FILE_NAME_SIZE);
+
+			if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
+				return NULL;
+
 			if (store_path(pathvec, pp)) {
 				free_path(pp);
 				return NULL;
 			}
-			strncpy(pp->dev, buff, FILE_NAME_SIZE);
-			if (devinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
-				return NULL;
 		}
 
 		refwwid = MALLOC(WWID_SIZE);
@@ -95,18 +101,19 @@
 			if (!pp)
 				return NULL;
 
-			if (store_path(pathvec, pp)) {
-				free_path(pp);
-				return NULL;
-			}
 			devt2devname(conf->dev, buff);
 
 			if(safe_sprintf(pp->dev, "%s", buff)) {
 				fprintf(stderr, "pp->dev too small\n");
 				exit(1);
 			}
-			if (devinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
+			if (pathinfo(pp, conf->hwtable, DI_SYSFS | DI_WWID))
+				return NULL;
+			
+			if (store_path(pathvec, pp)) {
+				free_path(pp);
 				return NULL;
+			}
 		}
 
 		refwwid = MALLOC(WWID_SIZE);
@@ -141,70 +148,20 @@
 	return NULL;
 }
 
-/*
- * print_path styles
- */
-#define PRINT_PATH_ALL		0
-#define PRINT_PATH_SHORT	1
-
 static void
-print_path (struct path * pp, int style)
+print_path (struct path * pp, char * style)
 {
-	if (style != PRINT_PATH_SHORT && pp->wwid)
-		printf ("%s ", pp->wwid);
-	else
-		printf ("  \\_ ");
-
-	printf("%i:%i:%i:%i ",
-	       pp->sg_id.host_no,
-	       pp->sg_id.channel,
-	       pp->sg_id.scsi_id,
-	       pp->sg_id.lun);
-
-	if (pp->dev)
-		printf("%-4s ", pp->dev);
-
-	if (pp->dev_t)
-		printf("%-7s ", pp->dev_t);
-
-	switch (pp->state) {
-	case PATH_UP:
-		printf("[ready ]");
-		break;
-	case PATH_DOWN:
-		printf("[faulty]");
-		break;
-	case PATH_SHAKY:
-		printf("[shaky ]");
-		break;
-	default:
-		printf("[undef ]");
-		break;
-	}
-	switch (pp->dmstate) {
-	case PSTATE_ACTIVE:
-		printf("[active]");
-		break;
-	case PSTATE_FAILED:
-		printf("[failed]");
-		break;
-	default:
-		break;
-	}
-	if (pp->claimed)
-		printf("[claimed]");
-
-	if (style != PRINT_PATH_SHORT && pp->product_id)
-		printf("[%.16s]", pp->product_id);
+	char buff[MAX_LINE_LEN];
 
-	fprintf(stdout, "\n");
+	snprint_path(&buff[0], MAX_LINE_LEN, style, pp, &pl);
+	printf("%s", buff);
 }
 
 static void
 print_map (struct multipath * mpp)
 {
 	if (mpp->size && mpp->params)
-		printf("0 %lu %s %s\n",
+		printf("0 %llu %s %s\n",
 			 mpp->size, DEFAULT_TARGET, mpp->params);
 	return;
 }
@@ -216,7 +173,7 @@
 	struct path * pp;
 
 	vector_foreach_slot (pathvec, pp, i)
-		print_path(pp, PRINT_PATH_ALL);
+		print_path(pp, PRINT_PATH_LONG);
 }
 
 static void
@@ -260,14 +217,14 @@
 
 	printf("\n");
 
-	if (mpp->size < 2000)
-		printf("[size=%lu kB]", mpp->size / 2);
-	else if (mpp->size < (2000 * 1024))
-		printf("[size=%lu MB]", mpp->size / 2 / 1024);
-	else if (mpp->size < (2000 * 1024 * 1024))
-		printf("[size=%lu GB]", mpp->size / 2 / 1024 / 1024);
+	if (mpp->size < (1 << 11))
+		printf("[size=%llu kB]", mpp->size >> 1);
+	else if (mpp->size < (1 << 21))
+		printf("[size=%llu MB]", mpp->size >> 11);
+	else if (mpp->size < (1 << 31))
+		printf("[size=%llu GB]", mpp->size >> 21);
 	else
-		printf("[size=%lu TB]", mpp->size / 2 / 1024 / 1024 / 1024);
+		printf("[size=%llu TB]", mpp->size >> 31);
 
 	if (mpp->features)
 		printf("[features=\"%s\"]", mpp->features);
@@ -283,8 +240,17 @@
 	vector_foreach_slot (mpp->pg, pgp, j) {
 		printf("\\_ ");
 
-		if (mpp->selector)
+		if (mpp->selector) {
 			printf("%s ", mpp->selector);
+#if 0
+			/* align to path status info */
+			for (i = pl.hbtl_len + pl.dev_len + pl.dev_t_len + 4;
+			     i > strlen(mpp->selector); i--)
+				printf(" ");
+#endif
+		}
+		if (pgp->priority)
+			printf("[prio=%i]", pgp->priority);
 
 		switch (pgp->status) {
 		case PGSTATE_ENABLED:
@@ -299,13 +265,10 @@
 		default:
 			break;
 		}
-		if (mpp->nextpg && mpp->nextpg == j + 1)
-			printf("[first]");
-
 		printf("\n");
 
 		vector_foreach_slot (pgp->paths, pp, i)
-			print_path(pp, PRINT_PATH_SHORT);
+			print_path(pp, PRINT_PATH_INDENT);
 	}
 	printf("\n");
 }
@@ -338,6 +301,7 @@
 {
 	int i, j;
 	int shift, freechar;
+	int minio;
 	char * p;
 	struct pathgroup * pgp;
 	struct path * pp;
@@ -368,8 +332,13 @@
 		freechar -= shift;
 
 		vector_foreach_slot (pgp->paths, pp, j) {
+			minio = conf->minio;
+			
+			if (mp->rr_weight == RR_WEIGHT_PRIO && pp->priority)
+				minio *= pp->priority;
+
 			shift = snprintf(p, freechar, " %s %d",
-					 pp->dev_t, conf->minio);
+					 pp->dev_t, minio);
 			if (shift >= freechar) {
 				fprintf(stderr, "mp->params too small\n");
 				return 1;
@@ -394,9 +363,7 @@
 setup_map (struct multipath * mpp)
 {
 	struct path * pp;
-	struct pathgroup * pgp;
-	int i, j;
-	int highest = 0;
+	int i;
 
 	/*
 	 * don't bother if devmap size is unknown
@@ -408,10 +375,10 @@
 
 	/*
 	 * don't bother if a constituant path is claimed
-	 * FIXME : claimed detection broken, always unclaimed for now
+	 * (not by the device mapper driver)
 	 */
 	vector_foreach_slot (mpp->paths, pp, i) {
-		if (pp->claimed) {
+		if (pp->claimed && pp->dmstate == PSTATE_UNDEF) {
 			condlog(3, "%s claimed", pp->dev);
 			return 1;
 		}
@@ -424,6 +391,7 @@
 	select_selector(mpp);
 	select_features(mpp);
 	select_hwhandler(mpp);
+	select_rr_weight(mpp);
 
 	/*
 	 * apply selected grouping policy to valid paths
@@ -455,19 +423,9 @@
 
 	/*
 	 * ponders each path group and determine highest prio pg
+	 * to switch over (default to first)
 	 */
-	mpp->nextpg = 1;
-	vector_foreach_slot (mpp->pg, pgp, i) {
-		vector_foreach_slot (pgp->paths, pp, j) {
-			pgp->id ^= (long)pp;
-			if (pp->state != PATH_DOWN)
-				pgp->priority += pp->priority;
-		}
-		if (pgp->priority > highest) {
-			highest = pgp->priority;
-			mpp->nextpg = i + 1;
-		}
-	}
+	select_path_group(mpp);
 
 	/*
 	 * transform the mp->pg vector of vectors of paths
@@ -495,41 +453,38 @@
 	return count;
 }
 
-/*
- * detect if a path is in the map we are about to create but not in the
- * current one (triggers a valid reload)
- * if a path is in the current map but not in the one we are about to create,
- * don't reload : it may come back latter so save the reload burden
- */
+static void
+compute_pgid(struct pathgroup * pgp)
+{
+	struct path * pp;
+	int i;
+
+	vector_foreach_slot (pgp->paths, pp, i)
+		pgp->id ^= (long)pp;
+}
+
 static int
-pgcmp2 (struct multipath * mpp, struct multipath * cmpp)
+pgcmp (struct multipath * mpp, struct multipath * cmpp)
 {
-	int i, j, k, l;
+	int i, j;
 	struct pathgroup * pgp;
 	struct pathgroup * cpgp;
-	struct path * pp;
-	struct path * cpp;
-	int found = 0;
+	int r = 0;
 
 	vector_foreach_slot (mpp->pg, pgp, i) {
-		vector_foreach_slot (pgp->paths, pp, j) {
-			vector_foreach_slot (cmpp->pg, cpgp, k) {
-				vector_foreach_slot (cpgp->paths, cpp, l) {
-					if (pp == cpp) {
-						found = 1;
-						break;
-					}
-				}
-				if (found)
-					break;
+		compute_pgid(pgp);
+
+		vector_foreach_slot (cmpp->pg, cpgp, j) {
+			if (pgp->id == cpgp->id) {
+				r = 0;
+				break;
 			}
-			if (found)
-				found = 0;
-			else
-				return 1;
+			r++;
 		}
+		if (r)
+			return r;
 	}
-	return 0;
+	return r;
 }
 
 static void
@@ -540,6 +495,13 @@
 	cmpp = find_mp(curmp, mpp->alias);
 
 	if (!cmpp) {
+		cmpp = find_mp_by_wwid(curmp, mpp->wwid);
+
+		if (cmpp && !conf->dry_run) {
+			condlog(2, "remove: %s (dup of %s)",
+				cmpp->alias, mpp->alias);
+			dm_flush_map(cmpp->alias, DEFAULT_TARGET);
+		}
 		mpp->action = ACT_CREATE;
 		return;
 	}
@@ -576,7 +538,7 @@
 		mpp->action = ACT_RELOAD;
 		return;
 	}
-	if (pgcmp2(mpp, cmpp)) {
+	if (pgcmp(mpp, cmpp)) {
 		condlog(3, "different path group topology");
 		mpp->action = ACT_RELOAD;
 		return;
@@ -625,10 +587,14 @@
 	/*
 	 * last chance to quit before touching the devmaps
 	 */
-	if (conf->dry_run || mpp->action == ACT_NOTHING)
+	if (conf->dry_run)
+		return 0;
+
+	switch (mpp->action) {
+	case ACT_NOTHING:
 		return 0;
 
-	if (mpp->action == ACT_SWITCHPG) {
+	case ACT_SWITCHPG:
 		dm_switchgroup(mpp->alias, mpp->nextpg);
 		/*
 		 * we may have avoided reinstating paths because there where in
@@ -637,12 +603,18 @@
 		 */
 		reinstate_paths(mpp);
 		return 0;
-	}
-	if (mpp->action == ACT_CREATE)
+
+	case ACT_CREATE:
 		op = DM_DEVICE_CREATE;
+		break;
 
-	if (mpp->action == ACT_RELOAD)
+	case ACT_RELOAD:
 		op = DM_DEVICE_RELOAD;
+		break;
+
+	default:
+		break;
+	}
 
 		
 	/*
@@ -759,39 +731,10 @@
 }
 
 static void
-signal_daemon (void)
-{
-	FILE *file;
-	pid_t pid;
-	char *buf;
-
-	buf = MALLOC(8);
-
-	if (!buf)
-		return;
-
-	file = fopen(DEFAULT_PIDFILE, "r");
-
-	if (!file) {
-		condlog(1, "cannot signal daemon, pidfile not found");
-		FREE(buf);
-		return;
-	}
-
-	buf = fgets(buf, 8, file);
-	fclose(file);
-
-	pid = (pid_t)atol(buf);
-	FREE(buf);
-
-	kill(pid, SIGHUP);
-}
-
-static void
 usage (char * progname)
 {
 	fprintf (stderr, VERSION_STRING);
-	fprintf (stderr, "Usage: %s\t[-v level] [-d] [-l] [-S]\n",
+	fprintf (stderr, "Usage: %s\t[-v level] [-d] [-l|-ll|-f|-F]\n",
 		progname);
 	fprintf (stderr,
 		"\t\t\t[-p failover|multibus|group_by_serial|group_by_prio]\n" \
@@ -803,8 +746,9 @@
 		"\t   2\t\t\tdefault verbosity\n" \
 		"\t   3\t\t\tprint debug information\n" \
 		"\t-d\t\tdry run, do not create or update devmaps\n" \
-		"\t-l\t\tlist the current multipath topology\n" \
-		"\t-S\t\tinhibit signal sending to multipathd\n"
+		"\t-l\t\tshow multipath topology (sysfs and DM info)\n" \
+		"\t-ll\t\tshow multipath topology (maximum info)\n" \
+		"\t-F\t\tflush a multipath device map\n" \
 		"\t-F\t\tflush all multipath device maps\n" \
 		"\t-p policy\tforce all maps to specified policy :\n" \
 		"\t   failover\t\t1 path per priority group\n" \
@@ -822,25 +766,33 @@
 }
 
 static int
-update_pathvec (vector pathvec)
+update_paths (struct multipath * mpp)
 {
-	int i;
+	int i, j;
+	struct pathgroup * pgp;
 	struct path * pp;
 
-	vector_foreach_slot (pathvec, pp, i) {
-		if (pp->dev && pp->dev_t && strlen(pp->dev) == 0) {
-			devt2devname(pp->dev, pp->dev_t);
-			devinfo(pp, conf->hwtable,
-				DI_SYSFS | DI_CHECKER | DI_SERIAL | DI_PRIO);
+	vector_foreach_slot (mpp->pg, pgp, i) {
+		vector_foreach_slot (pgp->paths, pp, j) {
+			if (!strlen(pp->dev)) {
+				devt2devname(pp->dev, pp->dev_t);
+				pathinfo(pp, conf->hwtable,
+					 DI_SYSFS | DI_CHECKER | \
+					 DI_SERIAL | DI_PRIO);
+				continue;
+			}
+			if (pp->state == PATH_UNCHECKED)
+				pathinfo(pp, conf->hwtable, DI_CHECKER);
+
+			if (!pp->priority)
+				pathinfo(pp, conf->hwtable, DI_PRIO);
 		}
-		if (pp->checkfn && pp->state == PATH_UNCHECKED)
-			pp->state = pp->checkfn(pp->fd, NULL, NULL);
 	}
 	return 0;
 }
 
 static int
-get_current_mp (vector curmp, vector pathvec, char * refwwid)
+get_dm_mpvec (vector curmp, vector pathvec, char * refwwid)
 {
 	int i;
 	struct multipath * mpp;
@@ -853,18 +805,29 @@
 		wwid = get_mpe_wwid(mpp->alias);
 
 		if (wwid) {
-			strncpy(mpp->wwid, wwid, WWID_SIZE);
+			/* out of specified scope */
+			if (refwwid && strncmp(wwid, refwwid, WWID_SIZE))
+				continue;
 			wwid = NULL;
-		} else
-			strncpy(mpp->wwid, mpp->alias, WWID_SIZE);
-
-		if (refwwid && strncmp(mpp->wwid, refwwid, WWID_SIZE))
-			continue;
+		}
 
 		condlog(3, "params = %s", mpp->params);
 		condlog(3, "status = %s", mpp->status);
+
+		/* will set mpp->wwid */
 		disassemble_map(pathvec, mpp->params, mpp);
-		update_pathvec(pathvec);
+
+		/*
+		 * disassemble_map may have added new paths to pathvec.
+		 * If not in "fast list mode", we need to fetch information
+		 * about them
+		 */
+		if (conf->list != 1)
+			update_paths(mpp);
+
+		if (conf->list > 1)
+			select_path_group(mpp);
+
 		disassemble_status(mpp->status, mpp);
 
 		if (conf->list)
@@ -882,15 +845,20 @@
 	vector curmp = NULL;
 	vector pathvec = NULL;
 	int i;
+	int di_flag = 0;
 	int arg;
 	extern char *optarg;
 	extern int optind;
 	char * refwwid = NULL;
 
-	if (dm_prereq(DEFAULT_TARGET, 1, 0, 3)) {
-		condlog(0, "device mapper prerequisites not met");
+	if (getuid() != 0) {
+		fprintf(stderr, "need to be root\n");
 		exit(1);
 	}
+
+	if (dm_prereq(DEFAULT_TARGET, 1, 0, 3))
+		exit(1);
+
 	if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
 		condlog(0, "multipath tools need sysfs mounted");
 		exit(1);
@@ -898,7 +866,7 @@
 	if (load_config(DEFAULT_CONFIGFILE))
 		exit(1);
 
-	while ((arg = getopt(argc, argv, ":qdlFSi:M:v:p:")) != EOF ) {
+	while ((arg = getopt(argc, argv, ":qdl::Ffi:M:v:p:")) != EOF ) {
 		switch(arg) {
 		case 1: printf("optarg : %s\n",optarg);
 			break;
@@ -911,25 +879,26 @@
 			break;
 		case 'd':
 			conf->dry_run = 1;
-			conf->signal = 0;
+			break;
+		case 'f':
+			conf->remove = 1;
 			break;
 		case 'F':
-			dm_flush_maps(DEFAULT_TARGET);
-			goto out;
+			conf->remove = 2;
 			break;
 		case 'l':
 			conf->list = 1;
 			conf->dry_run = 1;
-			conf->signal = 0;
+
+			if (optarg && !strncmp(optarg, "l", 1))
+				conf->list++;
+
 			break;
 		case 'M':
 #if _DEBUG_
 			debug = atoi(optarg);
 #endif
 			break;
-		case 'S':
-			conf->signal = 0;
-			break;
 		case 'p':
 			conf->pgpolicy_flag = get_pgpolicy_id(optarg);
 			if (conf->pgpolicy_flag == -1) {
@@ -964,6 +933,19 @@
 
 	}
 
+	if (conf->remove == FLUSH_ONE) {
+		if (conf->dev_type == DEV_DEVMAP)
+			dm_flush_map(conf->dev, DEFAULT_TARGET);
+		else
+			condlog(0, "must provide a map name to remove");
+
+		goto out;
+	}
+	else if (conf->remove == FLUSH_ALL) {
+		dm_flush_maps(DEFAULT_TARGET);
+		goto out;
+	}
+
 	/*
 	 * allocate core vectors to store paths and multipaths
 	 */
@@ -981,15 +963,31 @@
 	if (conf->dev && blacklist(conf->blist, conf->dev))
 		goto out;
 	
-	if (!cache_cold(CACHE_EXPIRE)) {
-		condlog(3, "load path identifiers cache");
-		cache_load(pathvec);
+	condlog(3, "load path identifiers cache");
+	cache_load(pathvec);
+
+	if (conf->verbosity > 2) {
+		fprintf(stdout, "#\n# all paths in cache :\n#\n");
+		print_all_paths(pathvec);
 	}
 
 	/*
 	 * get a path list
 	 */
-	if (path_discovery(pathvec, conf, DI_CHECKER) || VECTOR_SIZE(pathvec) == 0)
+	if (conf->dev)
+		di_flag = DI_WWID;
+
+	if (conf->list > 1)
+		/* extended path info '-ll' */
+		di_flag |= DI_SYSFS | DI_CHECKER;
+	else if (conf->list)
+		/* minimum path info '-l' */
+		di_flag |= DI_SYSFS;
+	else
+		/* maximum info */
+		di_flag = DI_ALL;
+
+	if (path_discovery(pathvec, conf, di_flag) || VECTOR_SIZE(pathvec) == 0)
 		goto out;
 
 	if (conf->verbosity > 2) {
@@ -998,11 +996,11 @@
 	}
 
 	refwwid = get_refwwid(pathvec);
+	get_path_layout(&pl, pathvec);
 
-	if (get_current_mp(curmp, pathvec, refwwid))
+	if (get_dm_mpvec(curmp, pathvec, refwwid))
 		goto out;
 
-	cache_dump(pathvec);
 	filter_pathvec(pathvec, refwwid);
 
 	if (conf->list)
@@ -1014,18 +1012,14 @@
 	coalesce_paths(curmp, pathvec);
 
 out:
-	/*
-	 * signal multipathd that new devmaps may have come up
-	 */
-	if (conf->signal)
-		signal_daemon();
-	
 	if (refwwid)
 		FREE(refwwid);
 
 	free_multipathvec(curmp, KEEP_PATHS);
 	free_pathvec(pathvec, FREE_PATHS);
 	free_config(conf);
+	dm_lib_release();
+	dm_lib_exit();
 #ifdef _DEBUG_
 	dbg_free_final(NULL);
 #endif

Modified: multipath-tools/trunk/multipath/main.h
==============================================================================
--- multipath-tools/trunk/multipath/main.h	(original)
+++ multipath-tools/trunk/multipath/main.h	Mon Sep 19 12:43:52 2005
@@ -35,13 +35,16 @@
 	ACT_CREATE
 };
 
+#define FLUSH_ONE 1
+#define FLUSH_ALL 2
+
 /*
  * Build version
  */
 #define PROG    "multipath"
 
-#define VERSION_CODE 0x000404
-#define DATE_CODE    0x100405
+#define VERSION_CODE 0x000405
+#define DATE_CODE    0x100605
 
 #define MULTIPATH_VERSION(version)	\
 	(version >> 16) & 0xFF,		\

Modified: multipath-tools/trunk/multipath/multipath.8
==============================================================================
--- multipath-tools/trunk/multipath/multipath.8	(original)
+++ multipath-tools/trunk/multipath/multipath.8	Mon Sep 19 12:43:52 2005
@@ -6,12 +6,9 @@
 .RB [\| \-v\ \c
 .IR verbosity \|]
 .RB [\| \-d \|]
-.RB [\| \-l \|]
-.RB [\| \-i\ \c
-.IR int \|]
+.RB [\| \-l | \-ll | \-f | \-F \|]
 .RB [\| \-p\ \c
 .BR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|]
-.RB [\| -S \|]
 .RB [\| device \|]
 .SH DESCRIPTION
 .B multipath
@@ -36,19 +33,22 @@
 dry run, do not create or update devmaps
 .TP
 .B \-l
-list the current multipath configuration
+show the current multipath topology from information fetched in sysfs and the device mapper
 .TP
+.B \-ll
+show the current multipath topology from all available information (sysfs, the device mapper, path checkers ...)
 .TP
-.BI \-i " interval"
-multipath target param: polling interval
 .TP
 .BI \-D " major:minor"
 update only the devmap the path pointed by
 .I major:minor
 is in
 .TP
+.B \-f
+flush a multipath device map specified as parameter, if unused
+.TP
 .B \-F
-flush all the multipath device maps
+flush all unused multipath device maps
 .TP
 .BI \-p " policy"
 force maps to specified policy:
@@ -70,9 +70,6 @@
 1 priority group per target node name. Target node names are fetched in /sys/class/fc_transport/target*/node_name.
 .RE
 .TP
-.B \-S
-do not send signal to multipathd. -d activate this silently.
-.TP
 .BI device
 update only the devmap the path pointed by
 .I device

Modified: multipath-tools/trunk/multipathd/Makefile
==============================================================================
--- multipath-tools/trunk/multipathd/Makefile	(original)
+++ multipath-tools/trunk/multipathd/Makefile	Mon Sep 19 12:43:52 2005
@@ -15,14 +15,20 @@
 #
 CFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes \
 	 -DDAEMON -I$(multipathdir) -I$(checkersdir)
-LDFLAGS = -lpthread -ldevmapper -lsysfs
+LDFLAGS = -lpthread -ldevmapper -lsysfs -lreadline -lcurses
+
+#
+# debuging stuff
+#
+#CFLAGS += -DLCKDBG
+#CFLAGS += -D_DEBUG_
+#CFLAGS += -DLOGDBG
 
 #
 # object files
 #
-OBJS = main.o copy.o log.o log_pthread.o pidfile.o \
-       $(MULTIPATHLIB)-glibc.a \
-       $(CHECKERSLIB)-glibc.a \
+OBJS = main.o log.o log_pthread.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o \
+       $(MULTIPATHLIB)-glibc.a $(CHECKERSLIB)-glibc.a \
 
 
 #
@@ -37,7 +43,7 @@
 
 $(EXEC): clean $(OBJS)
 	$(CC) $(OBJS) -o $(EXEC) $(LDFLAGS)
-	strip $(EXEC)
+	$(STRIP) $(EXEC)
 	$(GZIP) $(EXEC).8 > $(EXEC).8.gz
 
 $(CHECKERSLIB)-glibc.a:
@@ -59,6 +65,6 @@
 	rm -f $(DESTDIR)$(mandir)/$(EXEC).8.gz
 
 clean:
-	$(MAKE) -C $(multipathdir) clean
+	$(MAKE) -C $(multipathdir) prepare
 	rm -f core *.o $(EXEC) *.gz
 

Added: multipath-tools/trunk/multipathd/cli.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/multipathd/cli.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,346 @@
+#include <memory.h>
+#include <vector.h>
+#include <util.h>
+
+#include "cli.h"
+
+static struct key *
+alloc_key (void)
+{
+	return (struct key *)MALLOC(sizeof(struct key));
+}
+
+static struct handler *
+alloc_handler (void)
+{
+	return (struct handler *)MALLOC(sizeof(struct handler));
+}
+
+static int
+add_key (vector vec, char * str, int code, int has_param)
+{
+	struct key * kw;
+
+	kw = alloc_key();
+
+	if (!kw)
+		return 1;
+
+	kw->code = code;
+	kw->has_param = has_param;
+	kw->str = STRDUP(str);
+
+	if (!kw->str)
+		goto out;
+
+	if (!vector_alloc_slot(vec))
+		goto out1;
+
+	vector_set_slot(vec, kw);
+
+	return 0;
+
+out1:
+	FREE(kw->str);
+out:
+	FREE(kw);
+	return 1;
+}
+
+int
+add_handler (int fp, int (*fn)(void *, char **, int *, void *))
+{
+	struct handler * h;
+
+	h = alloc_handler();
+
+	if (!h)
+		return 1;
+
+	if (!vector_alloc_slot(handlers)) {
+		FREE(h);
+		return 1;
+	}
+
+	vector_set_slot(handlers, h);
+	h->fingerprint = fp;
+	h->fn = fn;
+
+	return 0;
+}
+
+static void
+free_key (struct key * kw)
+{
+	if (kw->str)
+		FREE(kw->str);
+
+	if (kw->param)
+		FREE(kw->param);
+
+	FREE(kw);
+}
+
+void
+free_keys (vector vec)
+{
+	int i;
+	struct key * kw;
+
+	vector_foreach_slot (vec, kw, i)
+		free_key(kw);
+
+	vector_free(vec);
+}
+
+void
+free_handlers (vector vec)
+{
+	int i;
+	struct handler * h;
+
+	vector_foreach_slot (vec, h, i)
+		FREE(h);
+
+	vector_free(vec);
+}
+
+int
+load_keys (void)
+{
+	int r = 0;
+	keys = vector_alloc();
+
+	if (!keys)
+		return 1;
+
+	r += add_key(keys, "list", LIST, 0);
+	r += add_key(keys, "show", LIST, 0);
+	r += add_key(keys, "add", ADD, 0);
+	r += add_key(keys, "remove", DEL, 0);
+	r += add_key(keys, "del", DEL, 0);
+	r += add_key(keys, "switch", SWITCH, 0);
+	r += add_key(keys, "switchgroup", SWITCH, 0);
+	r += add_key(keys, "paths", PATHS, 0);
+	r += add_key(keys, "maps", MAPS, 0);
+	r += add_key(keys, "path", PATH, 1);
+	r += add_key(keys, "map", MAP, 1);
+	r += add_key(keys, "group", GROUP, 1);
+	r += add_key(keys, "dump", DUMP, 0);
+	r += add_key(keys, "pathvec", PATHVEC, 0);
+	r += add_key(keys, "reconfigure", RECONFIGURE, 0);
+
+	if (r) {
+		free_keys(keys);
+		keys = NULL;
+		return 1;
+	}
+
+	return 0;
+}
+
+static struct key *
+find_key (char * str)
+{
+	int i;
+	struct key * kw = NULL;
+
+	vector_foreach_slot (keys, kw, i)
+		if (strlen(str) == strlen(kw->str) &&
+		    !strcmp(kw->str, str))
+				return kw;
+
+	return NULL;
+}
+		
+static struct handler *
+find_handler (int fp)
+{
+	int i;
+	struct handler *h;
+
+	vector_foreach_slot (handlers, h, i)
+		if (h->fingerprint == fp)
+			return h;
+
+	return NULL;
+}
+
+static vector
+get_cmdvec (char * cmd)
+{
+	int fwd = 1;
+	char * p = cmd;
+	char * buff;
+	struct key * kw = NULL;
+	struct key * cmdkw = NULL;
+	vector cmdvec;
+
+	cmdvec = vector_alloc();
+
+	if (!cmdvec)
+		return NULL;
+
+	while (fwd) {
+		fwd = get_word(p, &buff);
+
+		if (!buff)
+			break;
+
+		p += fwd;
+		kw = find_key(buff);
+		FREE(buff);
+
+		if (!kw)
+			goto out; /* synthax error */
+
+		cmdkw = alloc_key();
+
+		if (!cmdkw)
+			goto out;
+
+		if (!vector_alloc_slot(cmdvec)) {
+			FREE(cmdkw);
+			goto out;
+		}
+
+		vector_set_slot(cmdvec, cmdkw);
+		cmdkw->code = kw->code;
+		cmdkw->has_param = kw->has_param;
+		
+		if (kw->has_param) {
+			if (*p == '\0')
+				goto out;
+
+			fwd = get_word(p, &buff);
+
+			if (!buff)
+				goto out;
+
+			p += fwd;
+			cmdkw->param = buff;
+		}
+	}
+
+	return cmdvec;
+
+out:
+	free_keys(cmdvec);
+	return NULL;
+}
+
+static int 
+fingerprint(vector vec)
+{
+	int i;
+	int fp = 0;
+	struct key * kw;
+
+	vector_foreach_slot(vec, kw, i)
+		fp += kw->code;
+
+	return fp;
+}
+
+int
+alloc_handlers (void)
+{
+	handlers = vector_alloc();
+
+	if (!handlers)
+		return 1;
+
+	return 0;
+}
+
+static int
+genhelp_sprint_aliases (char * reply, vector keys, struct key * refkw)
+{
+	int i, fwd = 0;
+	struct key * kw;
+
+	vector_foreach_slot (keys, kw, i)
+		if (kw->code == refkw->code && kw != refkw)
+			fwd += sprintf(reply, "|%s", kw->str);
+
+	return fwd;
+}
+
+static char *
+genhelp_handler (void)
+{
+	int i, j;
+	int fp;
+	struct handler * h;
+	struct key * kw;
+	char * reply;
+	char * p;
+
+	reply = MALLOC(MAX_REPLY_LEN);
+
+	if (!reply)
+		return NULL;
+
+	p = reply;
+	p += sprintf(p, "\n");
+
+	vector_foreach_slot (handlers, h, i) {
+		fp = h->fingerprint;
+		vector_foreach_slot (keys, kw, j) {
+			if ((kw->code & fp)) {
+				fp -= kw->code;
+				p += sprintf(p, " %s", kw->str);
+				p += genhelp_sprint_aliases(p, keys, kw);
+
+				if (kw->has_param)
+					p += sprintf(p, " $%s", kw->str);
+			}
+		}
+		p += sprintf(p, "\n");
+	}
+
+	return reply;
+}
+
+int
+parse_cmd (char * cmd, char ** reply, int * len, void * data)
+{
+	int r;
+	struct handler * h;
+	vector cmdvec = get_cmdvec(cmd);
+
+	if (!cmdvec) {
+		*reply = genhelp_handler();
+		*len = strlen(*reply);
+		return 0;
+	}
+
+	h = find_handler(fingerprint(cmdvec));
+
+	if (!h) {
+		*reply = genhelp_handler();
+		*len = strlen(*reply) + 1;
+		return 0;
+	}
+
+	/*
+	 * execute handler
+	 */
+	r = h->fn(cmdvec, reply, len, data);
+	free_keys(cmdvec);
+
+	return r;
+}
+
+char *
+get_keyparam (vector v, int code)
+{
+	struct key * kw;
+	int i;
+
+	vector_foreach_slot(v, kw, i)
+		if (kw->code == code)
+			return kw->param;
+
+	return NULL;
+}

Added: multipath-tools/trunk/multipathd/cli.h
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/multipathd/cli.h	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,37 @@
+#define LIST		1
+#define ADD		(1 << 1)
+#define DEL		(1 << 2)
+#define SWITCH		(1 << 3)
+#define PATHS		(1 << 4)
+#define MAPS		(1 << 5)
+#define PATH		(1 << 6)
+#define MAP		(1 << 7)
+#define GROUP		(1 << 8)
+#define DUMP		(1 << 9)
+#define PATHVEC		(1 << 10)
+#define RECONFIGURE	(1 << 11)
+
+#define MAX_REPLY_LEN 1000
+
+struct key {
+	char * str;
+	char * param;
+	int code;
+	int has_param;
+};
+
+struct handler {
+	int fingerprint;
+	int (*fn)(void *, char **, int *, void *);
+};
+
+vector keys;
+vector handlers;
+
+int alloc_handlers (void);
+int add_handler (int fp, int (*fn)(void *, char **, int *, void *));
+int parse_cmd (char * cmd, char ** reply, int * len, void *);
+int load_keys (void);
+char * get_keyparam (vector v, int code);
+void free_keys (vector vec);
+void free_handlers (vector vec);

Added: multipath-tools/trunk/multipathd/cli_handlers.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/multipathd/cli_handlers.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,91 @@
+#include <memory.h>
+#include <vector.h>
+#include <structs.h>
+#include <devmapper.h>
+#include <config.h>
+#include <blacklist.h>
+
+#include "main.h"
+#include "cli.h"
+
+int
+cli_list_paths (void * v, char ** reply, int * len, void * data)
+{
+	struct paths * allpaths = (struct paths *)data;
+
+	return show_paths(reply, len, allpaths);
+}
+
+int
+cli_list_maps (void * v, char ** reply, int * len, void * data)
+{
+	struct paths * allpaths = (struct paths *)data;
+
+	return show_maps(reply, len, allpaths);
+}
+
+int
+cli_add_path (void * v, char ** reply, int * len, void * data)
+{
+	struct paths * allpaths = (struct paths *)data;
+	char * param = get_keyparam(v, PATH);
+
+	if (blacklist(conf->blist, param)) {
+		*reply = strdup("blacklisted");
+		*len = strlen(*reply) + 1;
+		return 0;
+	}
+	return uev_add_path(param, allpaths);
+}
+
+int
+cli_del_path (void * v, char ** reply, int * len, void * data)
+{
+	struct paths * allpaths = (struct paths *)data;
+	char * param = get_keyparam(v, PATH);
+
+	return uev_remove_path(param, allpaths);
+}
+
+int
+cli_add_map (void * v, char ** reply, int * len, void * data)
+{
+	struct paths * allpaths = (struct paths *)data;
+	char * param = get_keyparam(v, MAP);
+
+	return uev_add_map(param, allpaths);
+}
+
+int
+cli_del_map (void * v, char ** reply, int * len, void * data)
+{
+	struct paths * allpaths = (struct paths *)data;
+	char * param = get_keyparam(v, MAP);
+
+	return uev_remove_map(param, allpaths);
+}
+
+int
+cli_switch_group(void * v, char ** reply, int * len, void * data)
+{
+	char * mapname = get_keyparam(v, MAP);
+	int groupnum = atoi(get_keyparam(v, GROUP));
+	
+	return dm_switchgroup(mapname, groupnum);
+}
+
+int
+cli_dump_pathvec(void * v, char ** reply, int * len, void * data)
+{
+	struct paths * allpaths = (struct paths *)data;
+			
+	return dump_pathvec(reply, len, allpaths);
+}
+
+int
+cli_reconfigure(void * v, char ** reply, int * len, void * data)
+{
+	struct paths * allpaths = (struct paths *)data;
+			
+	return reconfigure(allpaths);
+}

Added: multipath-tools/trunk/multipathd/cli_handlers.h
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/multipathd/cli_handlers.h	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,9 @@
+int cli_list_paths (void * v, char ** reply, int * len, void * data);
+int cli_list_maps (void * v, char ** reply, int * len, void * data);
+int cli_add_path (void * v, char ** reply, int * len, void * data);
+int cli_del_path (void * v, char ** reply, int * len, void * data);
+int cli_add_map (void * v, char ** reply, int * len, void * data);
+int cli_del_map (void * v, char ** reply, int * len, void * data);
+int cli_switch_group(void * v, char ** reply, int * len, void * data);
+int cli_dump_pathvec(void * v, char ** reply, int * len, void * data);
+int cli_reconfigure(void * v, char ** reply, int * len, void * data);

Modified: multipath-tools/trunk/multipathd/log.c
==============================================================================
--- multipath-tools/trunk/multipathd/log.c	(original)
+++ multipath-tools/trunk/multipathd/log.c	Mon Sep 19 12:43:52 2005
@@ -4,8 +4,12 @@
 #include <string.h>
 #include <syslog.h>
 
+#include <memory.h>
+
 #include "log.h"
 
+#define ALIGN(len, s) (((len)+(s)-1)/(s)*(s))
+
 #if LOGDBG
 static void dump_logarea (void)
 {
@@ -30,7 +34,7 @@
 static int logarea_init (int size)
 {
 	logdbg(stderr,"enter logarea_init\n");
-	la = malloc(sizeof(struct logarea));
+	la = (struct logarea *)MALLOC(sizeof(struct logarea));
 	
 	if (!la)
 		return 1;
@@ -38,11 +42,11 @@
 	if (size < MAX_MSG_SIZE)
 		size = DEFAULT_AREA_SIZE;
 
-	la->start = malloc(size);
+	la->start = MALLOC(size);
 	memset(la->start, 0, size);
 
 	if (!la->start) {
-		free(la);
+		FREE(la);
 		return 1;
 	}
 
@@ -51,11 +55,11 @@
 	la->head = la->start;
 	la->tail = la->start;
 
-	la->buff = malloc(MAX_MSG_SIZE + sizeof(struct logmsg));
+	la->buff = MALLOC(MAX_MSG_SIZE + sizeof(struct logmsg));
 
 	if (!la->buff) {
-		free(la->start);
-		free(la);
+		FREE(la->start);
+		FREE(la);
 		return 1;
 	}
 	return 0;
@@ -75,9 +79,9 @@
 
 void free_logarea (void)
 {
-	free(la->start);
-	free(la->buff);
-	free(la);
+	FREE(la->start);
+	FREE(la->buff);
+	FREE(la);
 	return;
 }
 
@@ -101,21 +105,23 @@
 	if (!la->empty) {
 		fwd = sizeof(struct logmsg) + 
 		      strlen((char *)&lastmsg->str) * sizeof(char) + 1;
-		la->tail += fwd;
+		la->tail += ALIGN(fwd, sizeof(void *));
 	}
 	vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
-	len = strlen(buff) * sizeof(char) + 1;
+	len = ALIGN(sizeof(struct logmsg) + strlen(buff) * sizeof(char) + 1,
+		    sizeof(void *));
 
 	/* not enough space on tail : rewind */
-	if (la->head <= la->tail &&
-	    (len + sizeof(struct logmsg)) > (la->end - la->tail)) {
+	if (la->head <= la->tail && len > (la->end - la->tail)) {
 		logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail);
 		la->tail = la->start;
+
+		if (la->empty)
+			la->head = la->start;
 	}
 
 	/* not enough space on head : drop msg */
-	if (la->head > la->tail &&
-	    (len + sizeof(struct logmsg)) > (la->head - la->tail)) {
+	if (la->head > la->tail && len >= (la->head - la->tail)) {
 		logdbg(stderr, "enqueue: log area overrun, drop msg\n");
 
 		if (!la->empty)
@@ -128,7 +134,7 @@
 	la->empty = 0;
 	msg = (struct logmsg *)la->tail;
 	msg->prio = prio;
-	memcpy((void *)&msg->str, buff, len);
+	memcpy((void *)&msg->str, buff, strlen(buff));
 	lastmsg->next = la->tail;
 	msg->next = la->head;
 
@@ -149,7 +155,7 @@
 
 	if (la->empty)
 		return 1;
-	
+
 	int len = strlen((char *)&src->str) * sizeof(char) +
 		  sizeof(struct logmsg) + 1;
 

Modified: multipath-tools/trunk/multipathd/log.h
==============================================================================
--- multipath-tools/trunk/multipathd/log.h	(original)
+++ multipath-tools/trunk/multipathd/log.h	Mon Sep 19 12:43:52 2005
@@ -1,7 +1,7 @@
 #ifndef LOG_H
 #define LOG_H
 
-#define DEFAULT_AREA_SIZE 4096
+#define DEFAULT_AREA_SIZE 8192
 #define MAX_MSG_SIZE 128
 
 #ifndef LOGLEVEL

Modified: multipath-tools/trunk/multipathd/log_pthread.c
==============================================================================
--- multipath-tools/trunk/multipathd/log_pthread.c	(original)
+++ multipath-tools/trunk/multipathd/log_pthread.c	Mon Sep 19 12:43:52 2005
@@ -4,15 +4,15 @@
 #include <pthread.h>
 #include <sys/mman.h>
 
+#include <memory.h>
+
 #include "log_pthread.h"
 #include "log.h"
 
-void log_safe (int prio, char * fmt, ...)
+void log_safe (int prio, char * fmt, va_list ap)
 {
-	va_list ap;
-
 	pthread_mutex_lock(logq_lock);
-	va_start(ap, fmt);
+	//va_start(ap, fmt);
 	log_enqueue(prio, fmt, ap);
 	va_end(ap);
 	pthread_mutex_unlock(logq_lock);

Modified: multipath-tools/trunk/multipathd/log_pthread.h
==============================================================================
--- multipath-tools/trunk/multipathd/log_pthread.h	(original)
+++ multipath-tools/trunk/multipathd/log_pthread.h	Mon Sep 19 12:43:52 2005
@@ -7,7 +7,7 @@
 pthread_mutex_t *logev_lock;
 pthread_cond_t *logev_cond;
 
-void log_safe(int prio, char * fmt, ...);
+void log_safe(int prio, char * fmt, va_list ap);
 void log_thread_start(void);
 void log_thread_stop(void);
 

Modified: multipath-tools/trunk/multipathd/main.c
==============================================================================
--- multipath-tools/trunk/multipathd/main.c	(original)
+++ multipath-tools/trunk/multipathd/main.c	Mon Sep 19 12:43:52 2005
@@ -1,18 +1,7 @@
-#include <string.h>
-#include <pthread.h>
-#include <stdio.h>
 #include <unistd.h>
-#include <linux/unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
 #include <sys/stat.h>
-#include <fcntl.h>
 #include <libdevmapper.h>
-#include <signal.h>
 #include <wait.h>
-#include <sched.h>
-#include <errno.h>
-#include <sys/mount.h>
 #include <sys/mman.h>
 
 /*
@@ -46,21 +35,25 @@
 #include <discovery.h>
 #include <debug.h>
 #include <propsel.h>
+#include <uevent.h>
+#include <switchgroup.h>
+#include <path_state.h>
+#include <print.h>
 
 #include "main.h"
-#include "copy.h"
-#include "clone_platform.h"
 #include "pidfile.h"
+#include "uxlsnr.h"
+#include "uxclnt.h"
+#include "cli.h"
+#include "cli_handlers.h"
 
 #define FILE_NAME_SIZE 256
 #define CMDSIZE 160
 
-#define CALLOUT_DIR "/var/cache/multipathd"
-
 #define LOG_MSG(a,b) \
-	if (strlen(a)) { \
-		log_safe(LOG_WARNING, "%s: %s", b, a); \
-		memset(a, 0, MAX_CHECKER_MSG_SIZE); \
+	if (strlen(b)) { \
+		condlog(a, "%s: %s", pp->dev_t, b); \
+		memset(b, 0, MAX_CHECKER_MSG_SIZE); \
 	}
 
 #ifdef LCKDBG
@@ -75,229 +68,312 @@
 #define unlock(a) pthread_mutex_unlock(a)
 #endif
 
-/*
- * global vars
- */
-int pending_event = 0;
-int from_sighup;
-pthread_mutex_t *event_lock;
-pthread_cond_t *event;
+pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER;
+pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 /*
  * structs
  */
-struct paths {
-	pthread_mutex_t *lock;
-	vector pathvec;
-};
-
 struct event_thread {
-	pthread_t *thread;
-	pthread_mutex_t *waiter_lock;
-	int lease;
+	struct dm_task *dmt;
+	pthread_t thread;
 	int event_nr;
 	char mapname[WWID_SIZE];
 	struct paths *allpaths;
 };
 
-/*
- * helpers functions
- */
+static struct event_thread *
+alloc_waiter (void)
+{
+
+	struct event_thread * wp;
+
+	wp = (struct event_thread *)MALLOC(sizeof(struct event_thread));
+
+	return wp;
+}
+
 static void
-strvec_free (vector vec)
+cleanup_lock (void * data)
+{
+	unlock((pthread_mutex_t *)data);
+}
+
+static void
+set_paths_owner (struct paths * allpaths, struct multipath * mpp)
 {
 	int i;
-	char * str;
+	struct path * pp;
 
-	vector_foreach_slot (vec, str, i)
-		if (str)
-			FREE(str);
+	if (!mpp)
+		return;
 
-	vector_free(vec);
+	vector_foreach_slot (allpaths->pathvec, pp, i) {
+		if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) {
+			condlog(4, "%s ownership set", pp->dev_t);
+			pp->mpp = mpp;
+		}
+	}
+}
+
+static void
+unset_paths_owner (struct paths * allpaths, struct multipath * mpp)
+{
+	int i;
+	struct path * pp;
+
+	vector_foreach_slot (allpaths->pathvec, pp, i) {
+		if (pp->mpp == mpp) {
+			condlog(4, "%s is orphaned", pp->dev_t);
+			pp->mpp = NULL;
+		}
+	}
 }
 
 static int
-exit_daemon (int status)
+update_multipath_table (struct multipath *mpp, vector pathvec)
 {
-	if (status != 0)
-		fprintf(stderr, "bad exit status. see daemon.log\n");
+	if (!mpp)
+		return 1;
 
-	log_safe(LOG_INFO, "umount ramfs");
-	umount(CALLOUT_DIR);
+	if (dm_get_map(mpp->alias, &mpp->size, mpp->params))
+		return 1;
 
-	log_safe(LOG_INFO, "unlink pidfile");
-	unlink(DEFAULT_PIDFILE);
+	if (disassemble_map(pathvec, mpp->params, mpp))
+		return 1;
 
-	log_safe(LOG_NOTICE, "--------shut down-------");
-	log_thread_stop();
-	exit(status);
+	return 0;
 }
 
-static void *
-get_devmaps (void)
+static int
+update_multipath_status (struct multipath *mpp)
 {
-	char *devmap;
-	struct dm_task *dmt;
-	struct dm_names *names = NULL;
-	unsigned next = 0;
-	void *nexttgt;
-	vector devmaps;
-
-	devmaps = vector_alloc();
+	if (!mpp)
+		return 1;
 
-	if (!devmaps)
-		return NULL;
+	if(dm_get_status(mpp->alias, mpp->status))
+		return 1;
 
-	if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
-		goto out;
+	if (disassemble_status(mpp->status, mpp))
+		return 1;
 
-	dm_task_no_open_count(dmt);
+	return 0;
+}
 
-	if (!dm_task_run(dmt))
-		goto out1;
+static int
+update_multipath_strings (struct multipath *mpp, vector pathvec)
+{
+	if (mpp->selector) {
+		FREE(mpp->selector);
+		mpp->selector = NULL;
+	}
 
-	if (!(names = dm_task_get_names(dmt)))
-		goto out1;
+	if (mpp->features) {
+		FREE(mpp->features);
+		mpp->features = NULL;
+	}
 
-	if (!names->dev) {
-		log_safe(LOG_WARNING, "no devmap found");
-		goto out1;
+	if (mpp->hwhandler) {
+		FREE(mpp->hwhandler);
+		mpp->hwhandler = NULL;
 	}
-	do {
-		names = (void *) names + next;
-		nexttgt = NULL;
 
-		/*
-		 * keep only multipath maps
-		 */
-		log_safe(LOG_DEBUG, "devmap %s", names->name);
-		
-		if (!dm_type(names->name, "multipath")) {
-			log_safe(LOG_DEBUG,
-			       "   skip non multipath target");
-			goto out2;
-		}
+	free_pgvec(mpp->pg, KEEP_PATHS);
+	mpp->pg = NULL;
 
-		/*
-		 * new map
-		 */
-		devmap = MALLOC(WWID_SIZE);
+	if (update_multipath_table(mpp, pathvec))
+		return 1;
 
-		if (!devmap)
-			goto out2;
+	if (update_multipath_status(mpp))
+		return 1;
 
-		strcpy(devmap, names->name);
-			
-		if (!vector_alloc_slot(devmaps)) {
-			free(devmap);
-			goto out2;
-		}
-		vector_set_slot(devmaps, devmap);
-out2:
-		next = names->next;
-	} while (next);
+	return 0;
+}
 
-	dm_task_destroy(dmt);
-	return devmaps;
-out1:
-	dm_task_destroy(dmt);
-out:
-	strvec_free(devmaps);
-	return NULL;
+static void
+set_multipath_wwid (struct multipath * mpp)
+{
+	char * wwid;
+
+	wwid = get_mpe_wwid(mpp->alias);
+
+	if (wwid) {
+		strncpy(mpp->wwid, wwid, WWID_SIZE);
+		wwid = NULL;
+	} else
+		strncpy(mpp->wwid, mpp->alias, WWID_SIZE);
 }
 
 static int
-updatepaths (struct paths *allpaths, char *sysfs_path)
+setup_multipath (struct paths * allpaths, struct multipath * mpp)
 {
-	lock(allpaths->lock);
-	path_discovery(allpaths->pathvec, conf, 0);
-	unlock(allpaths->lock);
+	int i;
+
+	set_multipath_wwid(mpp);
+	mpp->mpe = find_mpe(mpp->wwid);
+	condlog(4, "discovered map %s", mpp->alias);
+
+	if (update_multipath_strings(mpp, allpaths->pathvec))
+		goto out;
+
+	set_paths_owner(allpaths, mpp);
+	select_pgfailback(mpp);
 
 	return 0;
+out:
+	/*
+	 * purge the multipath vector
+	 */
+	if ((i = find_slot(allpaths->mpvec, (void *)mpp)) != -1)
+		vector_del_slot(allpaths->mpvec, i);
+
+	free_multipath(mpp, KEEP_PATHS);
+	condlog(0, "failed to setup multipath");
+	return 1;
 }
 
 static int
-mark_failed_path (struct paths *allpaths, char *mapname)
+need_switch_pathgroup (struct multipath * mpp, int refresh)
 {
-	struct multipath *mpp;
-	struct pathgroup  *pgp;
-	struct path *pp;
-	struct path *app;
+	struct pathgroup * pgp;
+	struct path * pp;
 	int i, j;
-	int r = 1;
 
-	if (!dm_map_present(mapname))
+	if (!mpp || mpp->pgfailback == -FAILBACK_MANUAL)
 		return 0;
 
-	mpp = alloc_multipath();
+	/*
+	 * Refresh path priority values
+	 */
+	if (refresh)
+		vector_foreach_slot (mpp->pg, pgp, i)
+			vector_foreach_slot (pgp->paths, pp, j)
+				pathinfo(pp, conf->hwtable, DI_PRIO);
 
-	if (!mpp)
+	select_path_group(mpp); /* sets mpp->nextpg */
+	pgp = VECTOR_SLOT(mpp->pg, mpp->nextpg - 1);
+
+	if (pgp && pgp->status != PGSTATE_ACTIVE)
 		return 1;
 
-	if (dm_get_map(mapname, &mpp->size, (char *)mpp->params))
-		goto out;
+	return 0;
+}
 
-	if (dm_get_status(mapname, mpp->status))
-		goto out;
+static void
+switch_pathgroup (struct multipath * mpp)
+{
+	struct pathgroup * pgp;
 	
-	lock(allpaths->lock);
-	r = disassemble_map(allpaths->pathvec, mpp->params, mpp);
-	unlock(allpaths->lock);
+	pgp = VECTOR_SLOT(mpp->pg, mpp->nextpg - 1);
 	
-	if (r)
-		goto out;
+	if (pgp && pgp->status != PGSTATE_ACTIVE) {
+		dm_switchgroup(mpp->alias, mpp->nextpg);
+		condlog(2, "%s: switch to path group #%i",
+			 mpp->alias, mpp->nextpg);
+	}
+}
 
-	r = disassemble_status(mpp->status, mpp);
+static int
+update_multipath (struct paths *allpaths, char *mapname)
+{
+	struct multipath *mpp;
+	struct pathgroup  *pgp;
+	struct path *pp;
+	int i, j;
+	int r = 1;
 
-	if (r)
+	mpp = find_mp(allpaths->mpvec, mapname);
+
+	if (!mpp)
 		goto out;
 
-	r = 0; /* can't fail from here on */
-	lock(allpaths->lock);
+	free_pgvec(mpp->pg, KEEP_PATHS);
+	mpp->pg = NULL;
+
+	if (setup_multipath(allpaths, mpp))
+		goto out; /* mpp freed in setup_multipath */
 
+	/*
+	 * compare checkers states with DM states
+	 */
 	vector_foreach_slot (mpp->pg, pgp, i) {
 		vector_foreach_slot (pgp->paths, pp, j) {
 			if (pp->dmstate != PSTATE_FAILED)
 				continue;
 
-			app = find_path_by_devt(allpaths->pathvec, pp->dev_t);
+			if (pp->state != PATH_DOWN) {
+				condlog(2, "%s: mark as failed", pp->dev_t);
+				pp->state = PATH_DOWN;
 
-			if (app && app->state != PATH_DOWN) {
-				log_safe(LOG_NOTICE, "mark %s as failed",
-					pp->dev_t);
-				app->state = PATH_DOWN;
+				/*
+				 * if opportune,
+				 * schedule the next check earlier
+				 */
+				if (pp->tick > conf->checkint)
+					pp->tick = conf->checkint;
 			}
 		}
 	}
-	unlock(allpaths->lock);
+	r = 0;
 out:
-	free_multipath(mpp, KEEP_PATHS);
+	if (r)
+		condlog(0, "failed to update multipath");
 
 	return r;
 }
 
-static void *
-waiteventloop (struct event_thread * waiter, char * cmd)
+static void
+free_waiter (void * data)
 {
-	//char buff[1];
-	struct dm_task *dmt;
+	struct event_thread * wp = (struct event_thread *)data;
+
+	if (wp->dmt)
+		dm_task_destroy(wp->dmt);
+	FREE(wp);
+}
+
+static sigset_t unblock_sighup(void)
+{
+	sigset_t set, old;
+
+	sigemptyset(&set);
+	sigaddset(&set, SIGHUP);
+	pthread_sigmask(SIG_UNBLOCK, &set, &old);
+	return old;
+}
+
+/*
+ * returns the reschedule delay
+ * negative means *stop*
+ */
+static int
+waiteventloop (struct event_thread * waiter)
+{
+	sigset_t set;
 	int event_nr;
+	int r;
 
 	if (!waiter->event_nr)
 		waiter->event_nr = dm_geteventnr(waiter->mapname);
 
-	if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
-		return 0;
+	if (!(waiter->dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
+		return 1;
 
-	if (!dm_task_set_name(dmt, waiter->mapname))
-		goto out;
+	if (!dm_task_set_name(waiter->dmt, waiter->mapname))
+		return 1;
 
-	if (waiter->event_nr && !dm_task_set_event_nr(dmt, waiter->event_nr))
-		goto out;
+	if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt,
+						      waiter->event_nr))
+		return 1;
 
-	dm_task_no_open_count(dmt);
+	dm_task_no_open_count(waiter->dmt);
 
-	dm_task_run(dmt);
+	set = unblock_sighup();
+	dm_task_run(waiter->dmt);
+	pthread_sigmask(SIG_SETMASK, &set, NULL);
+	pthread_testcancel();
+	dm_task_destroy(waiter->dmt);
+	waiter->dmt = NULL;
 
 	waiter->event_nr++;
 
@@ -305,269 +381,657 @@
 	 * upon event ...
 	 */
 	while (1) {
-		log_safe(LOG_DEBUG, "%s", cmd);
-		log_safe(LOG_NOTICE, "devmap event (%i) on %s",
-				waiter->event_nr, waiter->mapname);
+		condlog(3, "%s: devmap event #%i",
+				waiter->mapname, waiter->event_nr);
 
-		mark_failed_path(waiter->allpaths, waiter->mapname);
-		//execute_program(cmd, buff, 1);
+		/*
+		 * event might be :
+		 *
+		 * 1) a table reload, which means our mpp structure is
+		 *    obsolete : refresh it through update_multipath()
+		 * 2) a path failed by DM : mark as such through
+		 *    update_multipath()
+		 * 3) map has gone away : stop the thread.
+		 * 4) a path reinstate : nothing to do
+		 * 5) a switch group : nothing to do
+		 */
+		pthread_cleanup_push(cleanup_lock, waiter->allpaths->lock);
+		lock(waiter->allpaths->lock);
+
+		r = update_multipath(waiter->allpaths, waiter->mapname);
+		pthread_cleanup_pop(1);
+
+		if (r)
+			return -1; /* stop the thread */
 
 		event_nr = dm_geteventnr(waiter->mapname);
 
 		if (waiter->event_nr == event_nr)
-			break;
+			return 1; /* upon problem reschedule 1s later */
 
 		waiter->event_nr = event_nr;
 	}
-
-out:
-	dm_task_destroy(dmt);
-
-	/*
-	 * tell waiterloop we have an event
-	 */
-	lock(event_lock);
-	pending_event++;
-	pthread_cond_signal(event);
-	unlock(event_lock);
-	
-	return NULL;
+	return -1; /* never reach there */
 }
 
 static void *
 waitevent (void * et)
 {
+	int r;
 	struct event_thread *waiter;
-	char cmd[CMDSIZE];
 
 	mlockall(MCL_CURRENT | MCL_FUTURE);
 
 	waiter = (struct event_thread *)et;
-	lock(waiter->waiter_lock);
-	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+	pthread_cleanup_push(free_waiter, et);
+
+	while (1) {
+		r = waiteventloop(waiter);
 
-	if (safe_snprintf(cmd, CMDSIZE, "%s %s",
-			  conf->multipath, waiter->mapname)) {
-		log_safe(LOG_ERR, "command too long, abord reconfigure");
+		if (r < 0)
+			break;
+
+		pthread_testcancel();
+		sleep(r);
+		pthread_testcancel();
+	}
+
+	pthread_cleanup_pop(1);
+	return NULL;
+}
+
+static int
+stop_waiter_thread (struct multipath * mpp, struct paths * allpaths)
+{
+	struct event_thread * wp = (struct event_thread *)mpp->waiter;
+	pthread_t thread = wp->thread;
+	int r;
+
+	if (!wp)
+		return 1;
+
+	condlog(2, "%s: reap event checker", wp->mapname);
+
+	if ((r = pthread_cancel(thread)))
+		return r;
+
+	pthread_kill(thread, SIGHUP);
+	return 0;
+}
+
+static int
+start_waiter_thread (struct multipath * mpp, struct paths * allpaths)
+{
+	pthread_attr_t attr;
+	struct event_thread * wp;
+
+	if (!mpp)
+		return 0;
+
+	if (pthread_attr_init(&attr))
+		goto out;
+
+	pthread_attr_setstacksize(&attr, 32 * 1024);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+	wp = alloc_waiter();
+
+	if (!wp)
 		goto out;
+
+	mpp->waiter = (void *)wp;
+	strncpy(wp->mapname, mpp->alias, WWID_SIZE);
+	wp->allpaths = allpaths;
+
+	if (pthread_create(&wp->thread, &attr, waitevent, wp)) {
+		condlog(0, "%s: cannot create event checker", wp->mapname);
+		goto out1;
 	}
-	while (1)
-		waiteventloop(waiter, cmd);
+	condlog(2, "%s: event checker started", wp->mapname);
 
+	return 0;
+out1:
+	free_waiter(wp);
+	mpp->waiter = NULL;
 out:
+	condlog(0, "failed to start waiter thread");
+	return 1;
+}
+
+static void
+remove_map (struct multipath * mpp, struct paths * allpaths)
+{
+	int i;
+
+	/*
+	 * stop the DM event waiter thread
+	 */
+	if (stop_waiter_thread(mpp, allpaths)) {
+		condlog(0, "%s: error canceling waiter thread", mpp->alias);
+		/*
+		 * warrior mode
+		 */
+		free_waiter(mpp->waiter);
+	}
+
 	/*
-	 * release waiter_lock so that waiterloop knows we are gone
+	 * clear references to this map
 	 */
-	unlock(waiter->waiter_lock);
-	pthread_exit(waiter->thread);
+	unset_paths_owner(allpaths, mpp);
 
-	return NULL;
+	/*
+	 * purge the multipath vector
+	 */
+	i = find_slot(allpaths->mpvec, (void *)mpp);
+	vector_del_slot(allpaths->mpvec, i);
+
+	/*
+	 * final free
+	 */
+	free_multipath(mpp, KEEP_PATHS);
+	mpp = NULL;
 }
 
-static void *
-alloc_waiter (void)
+static void
+remove_maps (struct paths * allpaths)
 {
+	int i;
+	struct multipath * mpp;
 
-	struct event_thread * wp;
+	vector_foreach_slot (allpaths->mpvec, mpp, i)
+		remove_map(mpp, allpaths);
 
-	wp = MALLOC(sizeof(struct event_thread));
+	vector_free(allpaths->mpvec);
+	allpaths->mpvec = NULL;
+}
 
-	if (!wp)
-		return NULL;
+int
+uev_add_map (char * devname, struct paths * allpaths)
+{
+	int major, minor, i;
+	char dev_t[BLK_DEV_SIZE];
+	char * alias;
+	struct multipath * mpp;
 
-	wp->thread = MALLOC(sizeof(pthread_t));
+	if (sysfs_get_dev(sysfs_path, devname, dev_t, BLK_DEV_SIZE))
+		return 1;
 
-	if (!wp->thread)
-		goto out;
+	if (sscanf(dev_t, "%d:%d", &major, &minor) != 2)
+		return 1;
+
+	alias = dm_mapname(major, minor);
 		
-	wp->waiter_lock = (pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t));
+	if (!alias)
+		return 1;
+	
+	if (!dm_type(alias, DEFAULT_TARGET)) {
+		condlog(4, "%s: not a multipath map", alias);
+		FREE(alias);
+		return 0;
+	}
 
-	if (!wp->waiter_lock)
-		goto out1;
+	mpp = find_mp(allpaths->mpvec, alias);
 
-	pthread_mutex_init(wp->waiter_lock, NULL);
-	return wp;
+	if (mpp) {
+		/*
+		 * this should not happen,
+		 * we missed a remove map event (not sent ?)
+		 */
+		condlog(2, "%s: already registered", alias);
+		remove_map(mpp, allpaths);
+	}
+
+	/*
+	 * now we can allocate
+	 */
+	mpp = alloc_multipath();
+
+	if (!mpp)
+		return 1;
+
+	mpp->minor = minor;
+	mpp->alias = alias;
+
+	if (setup_multipath(allpaths, mpp))
+		return 1; /* mpp freed in setup_multipath */
+
+	if (!vector_alloc_slot(allpaths->mpvec))
+		goto out;
+
+	vector_set_slot(allpaths->mpvec, mpp);
+	set_paths_owner(allpaths, mpp);
+
+	if (start_waiter_thread(mpp, allpaths))
+		goto out;
+
+	return 0;
+out:
+	condlog(2, "%s: add devmap failed", mpp->alias);
+	/*
+	 * purge the multipath vector
+	 */
+	if ((i = find_slot(allpaths->mpvec, (void *)mpp)) != -1)
+		vector_del_slot(allpaths->mpvec, i);
+
+	free_multipath(mpp, KEEP_PATHS);
+	return 1;
+}
+
+int
+uev_remove_map (char * devname, struct paths * allpaths)
+{
+	int minor;
+	struct multipath * mpp;
+
+	if (sscanf(devname, "dm-%d", &minor) != 1)
+		return 1;
+
+	mpp = find_mp_by_minor(allpaths->mpvec, minor);
+
+	if (!mpp) {
+		condlog(3, "%s: devmap not registered, can't remove",
+			devname);
+		return 1;
+	}
+
+	condlog(2, "remove %s devmap", mpp->alias);
+	remove_map(mpp, allpaths);
+
+	return 0;
+}
+
+int
+uev_add_path (char * devname, struct paths * allpaths)
+{
+	struct path * pp;
+
+	pp = find_path_by_dev(allpaths->pathvec, devname);
+
+	if (pp) {
+		condlog(3, "%s: already in pathvec");
+		return 1;
+	}
+	pp = store_pathinfo(allpaths->pathvec, conf->hwtable,
+		       devname, DI_SYSFS | DI_WWID);
+
+	if (!pp) {
+		condlog(0, "%s: failed to store path info", devname);
+		return 1;
+	}
+
+	condlog(2, "%s: path checker registered", devname);
+	pp->mpp = find_mp_by_wwid(allpaths->mpvec, pp->wwid);
+
+	if (pp->mpp)
+		condlog(4, "%s: ownership set to %s",
+				pp->dev_t, pp->mpp->alias);
+	else
+		condlog(4, "%s: orphaned", pp->dev_t);
+
+	return 0;
+}
+
+int
+uev_remove_path (char * devname, struct paths * allpaths)
+{
+	int i;
+	struct path * pp;
+
+	pp = find_path_by_dev(allpaths->pathvec, devname);
+
+	if (!pp) {
+		condlog(3, "%s: not in pathvec");
+		return 1;
+	}
+	condlog(2, "remove %s path checker", devname);
+	i = find_slot(allpaths->pathvec, (void *)pp);
+	vector_del_slot(allpaths->pathvec, i);
+	free_path(pp);
+
+	return 0;
+}
+
+int
+show_paths (char ** r, int * len, struct paths * allpaths)
+{
+	int i;
+	struct path * pp;
+	char * c;
+	char * reply;
+	struct path_layout pl;
+
+	get_path_layout(&pl, allpaths->pathvec);
+	reply = MALLOC(MAX_REPLY_LEN);
+
+	if (!reply)
+		return 1;
+
+	c = reply;
+	c += sprintf(c, "\n");
+
+	vector_foreach_slot(allpaths->pathvec, pp, i)
+		c += snprint_path(c, reply + MAX_REPLY_LEN - c,
+			       	  PRINT_PATH_CHECKER, pp, &pl);
+
+	*r = reply;
+	*len = (int)(c - reply + 1);
+	return 0;
+}
+
+int
+show_maps (char ** r, int *len, struct paths * allpaths)
+{
+	int i;
+	struct multipath * mpp;
+	char * c;
+	char * reply;
+	struct map_layout ml;
+
+	get_map_layout(&ml, allpaths->mpvec);
+	reply = MALLOC(MAX_REPLY_LEN);
+
+	if (!reply)
+		return 1;
+
+	c = reply;
+	c += sprintf(c, "\n");
+
+	vector_foreach_slot(allpaths->mpvec, mpp, i)
+		c += snprint_map(c, reply + MAX_REPLY_LEN - c,
+			       	 PRINT_MAP_FAILBACK, mpp, &ml);
+
+	*r = reply;
+	*len = (int)(c - reply + 1);
+	return 0;
+}
+
+int
+dump_pathvec (char ** r, int * len, struct paths * allpaths)
+{
+	int i;
+	struct path * pp;
+	char * reply;
+	char * p;
+
+	*len = VECTOR_SIZE(allpaths->pathvec) * sizeof(struct path);
+	reply = (char *)MALLOC(*len);
+	*r = reply;
+
+	if (!reply)
+		return 1;
+
+	p = reply;
+
+	vector_foreach_slot (allpaths->pathvec, pp, i) {
+		memcpy((void *)p, pp, sizeof(struct path));
+		p += sizeof(struct path);
+	}
+
+	return 0;
+}
+
+static int
+get_dm_mpvec (struct paths * allpaths)
+{
+	int i;
+	struct multipath * mpp;
+
+	if (dm_get_maps(allpaths->mpvec, "multipath"))
+		return 1;
+
+	vector_foreach_slot (allpaths->mpvec, mpp, i) {
+		if (setup_multipath(allpaths, mpp))
+			return 1;
+		mpp->minor = dm_get_minor(mpp->alias);
+		start_waiter_thread(mpp, allpaths);
+	}
+
+	return 0;
+}
+
+int
+reconfigure (struct paths * allpaths)
+{
+	struct config * old = conf;
+	struct multipath * mpp;
+	struct path * pp;
+	int i;
+
+	conf = NULL;
+
+	if (load_config(DEFAULT_CONFIGFILE)) {
+		conf = old;
+		condlog(2, "reconfigure failed, continue with old config");
+		return 1;
+	}
+	conf->verbosity = old->verbosity;
+	free_config(old);
+
+	vector_foreach_slot (allpaths->mpvec, mpp, i) {
+		mpp->mpe = find_mpe(mpp->wwid);
+		set_paths_owner(allpaths, mpp);
+	}
+	vector_foreach_slot (allpaths->pathvec, pp, i) {
+		select_checkfn(pp);
+		select_getuid(pp);
+		select_getprio(pp);
+	}
+	condlog(2, "reconfigured");
+	return 0;
+}
+
+int
+uxsock_trigger (char * str, char ** reply, int * len, void * trigger_data)
+{
+	struct paths * allpaths;
+	int r;
+	
+	*reply = NULL;
+	*len = 0;
+	allpaths = (struct paths *)trigger_data;
+
+	pthread_cleanup_push(cleanup_lock, allpaths->lock);
+	lock(allpaths->lock);
+
+	r = parse_cmd(str, reply, len, allpaths);
+
+	if (r) {
+		*reply = STRDUP("fail\n");
+		*len = strlen(*reply) + 1;
+		r = 1;
+	}
+	else if (*len == 0) {
+		*reply = STRDUP("ok\n");
+		*len = strlen(*reply) + 1;
+		r = 0;
+	}
+
+	pthread_cleanup_pop(1);
+	return r;
+}
+
+int 
+uev_trigger (struct uevent * uev, void * trigger_data)
+{
+	int r = 0;
+	char devname[32];
+	struct paths * allpaths;
+
+	allpaths = (struct paths *)trigger_data;
+	pthread_cleanup_push(cleanup_lock, allpaths->lock);
+	lock(allpaths->lock);
+
+	if (strncmp(uev->devpath, "/block", 6))
+		goto out;
+
+	basename(uev->devpath, devname);
+
+	/*
+	 * device map add/remove event
+	 */
+	if (!strncmp(devname, "dm-", 3)) {
+		if (!strncmp(uev->action, "add", 3)) {
+			r = uev_add_map(devname, allpaths);
+			goto out;
+		}
+#if 0
+		if (!strncmp(uev->action, "remove", 6)) {
+			r = uev_remove_map(devname, allpaths);
+			goto out;
+		}
+#endif
+		goto out;
+	}
+	
+	/*
+	 * path add/remove event
+	 */
+	if (blacklist(conf->blist, devname))
+		goto out;
+
+	if (!strncmp(uev->action, "add", 3)) {
+		r = uev_add_path(devname, allpaths);
+		goto out;
+	}
+	if (!strncmp(uev->action, "remove", 6)) {
+		r = uev_remove_path(devname, allpaths);
+		goto out;
+	}
 
-out1:
-	free(wp->thread);
 out:
-	free(wp);
-	return NULL;
+	FREE(uev);
+	pthread_cleanup_pop(1);
+	return r;
 }
 
-static void
-free_waiter (struct event_thread * wp)
+static void *
+ueventloop (void * ap)
 {
-	pthread_mutex_destroy(wp->waiter_lock);
-	free(wp->thread);
-	free(wp);
+	uevent_listen(&uev_trigger, ap);
+
+	return NULL;
 }
 
 static void *
-waiterloop (void *ap)
+uxlsnrloop (void * ap)
 {
-	struct paths *allpaths;
-	vector devmaps = NULL;
-	char *devmap;
-	vector waiters;
-	struct event_thread *wp;
-	pthread_attr_t attr;
-	int r;
-	char buff[1];
-	int i, j;
-
-	mlockall(MCL_CURRENT | MCL_FUTURE);
-	log_safe(LOG_NOTICE, "start DM events thread");
-
-	if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
-		log_safe(LOG_ERR, "can not find sysfs mount point");
+	if (load_keys())
 		return NULL;
-	}
-
-	/*
-	 * inits
-	 */
-	allpaths = (struct paths *)ap;
-	waiters = vector_alloc();
-
-	if (!waiters)
+	
+	if (alloc_handlers())
 		return NULL;
 
-	if (pthread_attr_init(&attr))
-		return NULL;
+	add_handler(LIST+PATHS, cli_list_paths);
+	add_handler(LIST+MAPS, cli_list_maps);
+	add_handler(ADD+PATH, cli_add_path);
+	add_handler(DEL+PATH, cli_del_path);
+	add_handler(ADD+MAP, cli_add_map);
+	add_handler(DEL+MAP, cli_del_map);
+	add_handler(SWITCH+MAP+GROUP, cli_switch_group);
+	add_handler(DUMP+PATHVEC, cli_dump_pathvec);
+	add_handler(RECONFIGURE, cli_reconfigure);
 
-	pthread_attr_setstacksize(&attr, 32 * 1024);
+	uxsock_listen(&uxsock_trigger, ap);
 
-	log_safe(LOG_NOTICE, "initial reconfigure multipath maps");
-	execute_program(conf->multipath, buff, 1);
+	return NULL;
+}
 
-	while (1) {
-		/*
-		 * revoke the leases
-		 */
-		vector_foreach_slot (waiters, wp, i)
-			wp->lease = 0;
+static int
+exit_daemon (int status)
+{
+	if (status != 0)
+		fprintf(stderr, "bad exit status. see daemon.log\n");
 
-		/*
-		 * update devmap list
-		 */
-		log_safe(LOG_INFO, "refresh devmaps list");
+	condlog(3, "unlink pidfile");
+	unlink(DEFAULT_PIDFILE);
 
-		if (devmaps)
-			free_strvec(devmaps);
+	lock(&exit_mutex);
+	pthread_cond_signal(&exit_cond);
+	unlock(&exit_mutex);
 
-		while ((devmaps = get_devmaps()) == NULL) {
-			/*
-			 * we're not allowed to fail here
-			 */
-			log_safe(LOG_ERR, "can't get devmaps ... retry");
-			sleep(5);
-		}
+	return status;
+}
 
-		/*
-		 * update paths list
-		 */
-		log_safe(LOG_INFO, "refresh paths list");
+static void
+fail_path (struct path * pp)
+{
+	if (!pp->mpp)
+		return;
 
-		while(updatepaths(allpaths, sysfs_path)) {
-			log_safe(LOG_ERR, "can't update path list ... retry");
-			sleep(5);
-		}
+	condlog(2, "checker failed path %s in map %s",
+		 pp->dev_t, pp->mpp->alias);
 
-		/*
-		 * start waiters on all devmaps
-		 */
-		log_safe(LOG_INFO, "start up event loops");
+	dm_fail_path(pp->mpp->alias, pp->dev_t);
+}
 
-		vector_foreach_slot (devmaps, devmap, i) {
-			/*
-			 * find out if devmap already has
-			 * a running waiter thread
-			 */
-			vector_foreach_slot (waiters, wp, j)
-				if (!strcmp(wp->mapname, devmap))
-					break;
-					
-			/*
-			 * no event_thread struct : init it
-			 */
-			if (j == VECTOR_SIZE(waiters)) {
-				wp = alloc_waiter();
+/*
+ * caller must have locked the path list before calling that function
+ */
+static void
+reinstate_path (struct path * pp)
+{
+	if (pp->mpp) {
+		if (dm_reinstate(pp->mpp->alias, pp->dev_t))
+			condlog(0, "%s: reinstate failed", pp->dev_t);
+		else
+			condlog(2, "%s: reinstated", pp->dev_t);
+	}
+}
 
-				if (!wp)
-					continue;
+static void
+enable_group(struct path * pp)
+{
+	struct pathgroup * pgp;
 
-				strncpy(wp->mapname, devmap, WWID_SIZE);
-				wp->allpaths = allpaths;
+	/*
+	 * if path is added through uev_add_path, pgindex can be unset.
+	 * next update_strings() will set it, upon map reload event.
+	 *
+	 * we can safely return here, because upon map reload, all
+	 * PG will be enabled.
+ 	 */
+	if (!pp->pgindex)
+		return;
 
-				if (!vector_alloc_slot(waiters)) {
-					free_waiter(wp);
-					continue;
-				}
-				vector_set_slot(waiters, wp);
-			}
-			
-			/*
-			 * event_thread struct found
-			 */
-			else if (j < VECTOR_SIZE(waiters)) {
-				r = pthread_mutex_trylock(wp->waiter_lock);
+	pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1);
+	
+	if (pgp->status == PGSTATE_DISABLED) {
+		condlog(2, "%s: enable group #%i", pp->mpp->alias, pp->pgindex);
+		dm_enablegroup(pp->mpp->alias, pp->pgindex);
+	}
+}
 
-				/*
-				 * thread already running : next devmap
-				 */
-				if (r) {
-					log_safe(LOG_DEBUG,
-						 "event checker running : %s",
-						 wp->mapname);
+static void
+mpvec_garbage_collector (struct paths * allpaths)
+{
+	struct multipath * mpp;
+	int i;
 
-					/*
-					 * renew the lease
-					 */
-					wp->lease = 1;
-					continue;
-				}
-				pthread_mutex_unlock(wp->waiter_lock);
-			}
-			
-			if (pthread_create(wp->thread, &attr, waitevent, wp)) {
-				log_safe(LOG_ERR,
-					 "cannot create event checker : %s",
-					 wp->mapname);
-				free_waiter(wp);
-				vector_del_slot(waiters, j);
-				continue;
-			}
-			wp->lease = 1;
-			log_safe(LOG_NOTICE, "event checker started : %s",
-					wp->mapname);
-		}
-		vector_foreach_slot (waiters, wp, i) {
-			if (wp->lease == 0) {
-				log_safe(LOG_NOTICE, "reap event checker : %s",
-					wp->mapname);
-
-				pthread_cancel(*wp->thread);
-				free_waiter(wp);
-				vector_del_slot(waiters, i);
-				i--;
-			}
+	vector_foreach_slot (allpaths->mpvec, mpp, i) {
+		if (!dm_map_present(mpp->alias)) {
+			condlog(2, "%s: remove dead map", mpp->alias);
+			remove_map(mpp, allpaths);
 		}
-		/*
-		 * wait event condition
-		 */
-		lock(event_lock);
+	}
+}
 
-		if (pending_event > 0)
-			pending_event--;
+static void
+defered_failback_tick (vector mpvec)
+{
+	struct multipath * mpp;
+	int i;
 
-		log_safe(LOG_INFO, "%i pending event(s)", pending_event);
-		if(pending_event == 0)
-			pthread_cond_wait(event, event_lock);
+	vector_foreach_slot (mpvec, mpp, i) {
+		/*
+		 * defered failback getting sooner
+		 */
+		if (mpp->pgfailback > 0 && mpp->failback_tick > 0) {
+			mpp->failback_tick--;
 
-		unlock(event_lock);
+			if (!mpp->failback_tick && need_switch_pathgroup(mpp, 1))
+				switch_pathgroup(mpp);
+		}
 	}
-	return NULL;
 }
 
 static void *
@@ -575,10 +1039,8 @@
 {
 	struct paths *allpaths;
 	struct path *pp;
-	int i;
+	int i, count = 0;
 	int newstate;
-	char buff[1];
-	char cmd[CMDSIZE];
 	char checker_msg[MAX_CHECKER_MSG_SIZE];
 
 	mlockall(MCL_CURRENT | MCL_FUTURE);
@@ -586,21 +1048,38 @@
 	memset(checker_msg, 0, MAX_CHECKER_MSG_SIZE);
 	allpaths = (struct paths *)ap;
 
-	log_safe(LOG_NOTICE, "path checkers start up");
+	condlog(2, "path checkers start up");
 
 	while (1) {
+		pthread_cleanup_push(cleanup_lock, allpaths->lock);
 		lock(allpaths->lock);
-		log_safe(LOG_DEBUG, "checking paths");
+		condlog(4, "tick");
 
 		vector_foreach_slot (allpaths->pathvec, pp, i) {
+			if (!pp->mpp)
+				continue;
+
+			if (pp->tick) {
+				/*
+				 * don't check this path yet
+				 */
+				pp->tick--;
+				continue;
+			}
+
+			/*
+			 * provision a next check soonest,
+			 * in case we exit abnormaly from here
+			 */
+			pp->tick = conf->checkint;
+			
 			if (!pp->checkfn) {
-				devinfo(pp, conf->hwtable, DI_SYSFS);
+				pathinfo(pp, conf->hwtable, DI_SYSFS);
 				select_checkfn(pp);
 			}
 
 			if (!pp->checkfn) {
-				log_safe(LOG_ERR, "%s: checkfn is void",
-					 pp->dev);
+				condlog(0, "%s: checkfn is void", pp->dev);
 				continue;
 			}
 			newstate = pp->checkfn(pp->fd, checker_msg,
@@ -608,166 +1087,145 @@
 			
 			if (newstate != pp->state) {
 				pp->state = newstate;
-				LOG_MSG(checker_msg, pp->dev_t);
+				LOG_MSG(1, checker_msg);
 
 				/*
-				 * don't trigger map reconfiguration for
-				 * path going down. It will be handled
-				 * in due time by DM event waiters
+				 * upon state change, reset the checkint
+				 * to the shortest delay
 				 */
+				pp->checkint = conf->checkint;
+
 				if (newstate == PATH_DOWN ||
-				    newstate == PATH_SHAKY)
+				    newstate == PATH_SHAKY) {
+					/*
+					 * proactively fail path in the DM
+					 */
+					fail_path(pp);
+
+					/*
+					 * cancel scheduled failback
+					 */
+					pp->mpp->failback_tick = 0;
+
 					continue;
+				}
 
 				/*
-				 * reconfigure map now
+				 * reinstate this path
 				 */
-				if (safe_snprintf(cmd, CMDSIZE, "%s %s",
-						  conf->multipath, pp->dev_t)) {
-					log_safe(LOG_ERR, "command too long,"
-							" abord reconfigure");
-				} else {
-					log_safe(LOG_DEBUG, "%s", cmd);
-					log_safe(LOG_INFO,
-						"reconfigure %s multipath",
-						pp->dev_t);
-					execute_program(cmd, buff, 1);
-				}
+				reinstate_path(pp);
+
+				/*
+				 * need to switch group ?
+				 */
+				update_multipath_strings(pp->mpp,
+							 allpaths->pathvec);
+
+				/*
+				 * schedule defered failback
+				 */
+				if (pp->mpp->pgfailback > 0)
+					pp->mpp->failback_tick =
+						pp->mpp->pgfailback;
+				else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE &&
+				    need_switch_pathgroup(pp->mpp, 1))
+					switch_pathgroup(pp->mpp);
 
 				/*
-				 * tell waiterloop we have an event
+				 * if at least one path is up in a group, and
+				 * the group is disabled, re-enable it
+				 */
+				if (newstate == PATH_UP)
+					enable_group(pp);
+			}
+			else if (newstate == PATH_UP || newstate == PATH_GHOST) {
+				LOG_MSG(4, checker_msg);
+				/*
+				 * double the next check delay.
+				 * max at conf->max_checkint
 				 */
-				lock (event_lock);
-				pending_event++;
-				pthread_cond_signal(event);
-				unlock (event_lock);
+				if (pp->checkint < (conf->max_checkint / 2))
+					pp->checkint = 2 * pp->checkint;
+				else
+					pp->checkint = conf->max_checkint;
+
+				pp->tick = pp->checkint;
+				condlog(4, "%s: delay next check %is",
+						pp->dev_t, pp->tick);
+
 			}
 			pp->state = newstate;
+
+			/*
+			 * path prio refreshing
+			 */
+			condlog(4, "path prio refresh");
+			pathinfo(pp, conf->hwtable, DI_PRIO);
+
+			if (need_switch_pathgroup(pp->mpp, 0)) {
+				if (pp->mpp->pgfailback > 0)
+					pp->mpp->failback_tick =
+						pp->mpp->pgfailback;
+				else if (pp->mpp->pgfailback ==
+						-FAILBACK_IMMEDIATE)
+					switch_pathgroup(pp->mpp);
+			}
+		}
+		defered_failback_tick(allpaths->mpvec);
+
+		if (count)
+			count--;
+		else {
+			condlog(4, "map garbage collection");
+			mpvec_garbage_collector(allpaths);
+			count = MAPGCINT;
 		}
-		unlock(allpaths->lock);
-		sleep(conf->checkint);
+		
+		pthread_cleanup_pop(1);
+		sleep(1);
 	}
 	return NULL;
 }
 
 static struct paths *
-initpaths (void)
+init_paths (void)
 {
-	struct paths *allpaths;
+	struct paths * allpaths;
+
+	allpaths = (struct paths *)MALLOC(sizeof(struct paths));
+
+	if (!allpaths)
+		return NULL;
 
-	allpaths = MALLOC (sizeof (struct paths));
 	allpaths->lock = 
-		(pthread_mutex_t *) MALLOC (sizeof (pthread_mutex_t));
-	allpaths->pathvec = vector_alloc();
-	pthread_mutex_init (allpaths->lock, NULL);
+		(pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t));
 
-	event = (pthread_cond_t *) MALLOC (sizeof (pthread_cond_t));
-	pthread_cond_init (event, NULL);
-	event_lock = (pthread_mutex_t *) MALLOC (sizeof (pthread_mutex_t));
-	pthread_mutex_init (event_lock, NULL);
-	
-	return (allpaths);
-}
+	if (!allpaths->lock)
+		goto out;
 
-/*
- * this logic is all about keeping callouts working in case of
- * system disk outage (think system over SAN)
- * this needs the clone syscall, so don't bother if not present
- * (Debian Woody)
- */
-#ifdef CLONE_NEWNS
-static int
-prepare_namespace(void)
-{
-	mode_t mode = S_IRWXU;
-	struct stat *buf;
-	char ramfs_args[64];
-	int i;
-	int fd;
-	char * bin;
-	size_t size = 10;
-	struct stat statbuf;
-	
-	buf = MALLOC(sizeof(struct stat));
+	allpaths->pathvec = vector_alloc();
 
-	/*
-	 * create a temp mount point for ramfs
-	 */
-	if (stat(CALLOUT_DIR, buf) < 0) {
-		if (mkdir(CALLOUT_DIR, mode) < 0) {
-			log_safe(LOG_ERR, "cannot create " CALLOUT_DIR);
-			return -1;
-		}
-		log_safe(LOG_DEBUG, "created " CALLOUT_DIR);
-	}
+	if (!allpaths->pathvec)
+		goto out1;
+		
+	allpaths->mpvec = vector_alloc();
 
-	/*
-	 * compute the optimal ramdisk size
-	 */
-	vector_foreach_slot (conf->binvec, bin,i) {
-		if ((fd = open(bin, O_RDONLY)) < 0) {
-			log_safe(LOG_ERR, "cannot open %s", bin);
-			return -1;
-		}
-		if (fstat(fd, &statbuf) < 0) {
-			log_safe(LOG_ERR, "cannot stat %s", bin);
-			return -1;
-		}
-		size += statbuf.st_size;
-		close(fd);
-	}
-	log_safe(LOG_INFO, "ramfs maxsize is %u", (unsigned int) size);
+	if (!allpaths->mpvec)
+		goto out2;
 	
-	/*
-	 * mount the ramfs
-	 */
-	if (safe_sprintf(ramfs_args, "maxsize=%u", (unsigned int) size)) {
-		fprintf(stderr, "ramfs_args too small\n");
-		return -1;
-	}
-	if (mount(NULL, CALLOUT_DIR, "ramfs", MS_SYNCHRONOUS, ramfs_args) < 0) {
-		log_safe(LOG_ERR, "cannot mount ramfs on " CALLOUT_DIR);
-		return -1;
-	}
-	log_safe(LOG_DEBUG, "mount ramfs on " CALLOUT_DIR);
-
-	/*
-	 * populate the ramfs with callout binaries
-	 */
-	vector_foreach_slot (conf->binvec, bin,i) {
-		if (copytodir(bin, CALLOUT_DIR) < 0) {
-			log_safe(LOG_ERR, "cannot copy %s in ramfs", bin);
-			exit_daemon(1);
-		}
-		log_safe(LOG_DEBUG, "cp %s in ramfs", bin);
-	}
-	strvec_free(conf->binvec);
+	pthread_mutex_init(allpaths->lock, NULL);
 
-	/*
-	 * bind the ramfs to :
-	 * /sbin : default home of multipath ...
-	 * /bin  : default home of scsi_id ...
-	 * /tmp  : home of scsi_id temp files
-	 */
-	if (mount(CALLOUT_DIR, "/sbin", NULL, MS_BIND, NULL) < 0) {
-		log_safe(LOG_ERR, "cannot bind ramfs on /sbin");
-		return -1;
-	}
-	log_safe(LOG_DEBUG, "bind ramfs on /sbin");
-	if (mount(CALLOUT_DIR, "/bin", NULL, MS_BIND, NULL) < 0) {
-		log_safe(LOG_ERR, "cannot bind ramfs on /bin");
-		return -1;
-	}
-	log_safe(LOG_DEBUG, "bind ramfs on /bin");
-	if (mount(CALLOUT_DIR, "/tmp", NULL, MS_BIND, NULL) < 0) {
-		log_safe(LOG_ERR, "cannot bind ramfs on /tmp");
-		return -1;
-	}
-	log_safe(LOG_DEBUG, "bind ramfs on /tmp");
+	return allpaths;
 
-	return 0;
+out2:
+	vector_free(allpaths->pathvec);
+out1:
+	FREE(allpaths->lock);
+out:
+	FREE(allpaths);
+	condlog(0, "failed to init paths");
+	return NULL;
 }
-#endif
 
 static void *
 signal_set(int signo, void (*func) (int))
@@ -791,24 +1249,11 @@
 static void
 sighup (int sig)
 {
-	log_safe(LOG_NOTICE, "SIGHUP received from multipath or operator");
+	condlog(3, "SIGHUP received");
 
 #ifdef _DEBUG_
-	        dbg_free_final(NULL);
+	dbg_free_final(NULL);
 #endif
-
-	/*
-	 * signal updatepaths() that we come from SIGHUP
-	 */
-	from_sighup = 1;
-
-	/*
-	 * ask for allpaths refresh
-	 */
-	lock(event_lock);
-	pending_event++;
-	pthread_cond_signal(event);
-	unlock(event_lock);
 }
 
 static void
@@ -837,7 +1282,7 @@
         res = sched_setscheduler (0, SCHED_RR, &sched_param);
 
         if (res == -1)
-                log_safe(LOG_WARNING, "Could not set SCHED_RR at priority 99");
+                condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99");
 	return;
 }
 
@@ -858,85 +1303,102 @@
 static int
 child (void * param)
 {
-	pthread_t wait_thr, check_thr;
+	pthread_t check_thr, uevent_thr, uxlsnr_thr;
 	pthread_attr_t attr;
-	struct paths *allpaths;
+	struct paths * allpaths;
 
 	mlockall(MCL_CURRENT | MCL_FUTURE);
 
-	log_thread_start();
-	log_safe(LOG_NOTICE, "--------start up--------");
+	if (logsink)
+		log_thread_start();
 
-	if (pidfile_create(DEFAULT_PIDFILE, getpid())) {
-		log_thread_stop();
+	condlog(2, "--------start up--------");
+	condlog(2, "read " DEFAULT_CONFIGFILE);
+
+	if (load_config(DEFAULT_CONFIGFILE))
 		exit(1);
-	}
-	signal_init();
-	setscheduler();
-	set_oom_adj(-17);
-	allpaths = initpaths();
-	
-	conf->checkint = CHECKINT;
 
 	setlogmask(LOG_UPTO(conf->verbosity + 3));
 
-	condlog(2, "read " DEFAULT_CONFIGFILE);
-	init_data(DEFAULT_CONFIGFILE, init_keywords);
-
 	/*
 	 * fill the voids left in the config file
 	 */
-	if (conf->binvec == NULL) {
-		conf->binvec = vector_alloc();
-		push_callout("/sbin/scsi_id");
-	}
-	if (conf->multipath == NULL) {
-		conf->multipath = MULTIPATH;
-		push_callout(conf->multipath);
-	}
-	if (conf->hwtable == NULL) {
-		conf->hwtable = vector_alloc();
-		setup_default_hwtable(conf->hwtable);
+	if (!conf->checkint) {
+		conf->checkint = CHECKINT;
+		conf->max_checkint = MAX_CHECKINT;
 	}
-	if (conf->blist == NULL) {
-		conf->blist = vector_alloc();
-		setup_default_blist(conf->blist);
-	}
-	if (conf->default_selector == NULL)
-		conf->default_selector = set_default(DEFAULT_SELECTOR);
-
-	if (conf->udev_dir == NULL)
-		conf->udev_dir = set_default(DEFAULT_UDEVDIR);
 
-	if (conf->default_getuid == NULL)
-		conf->default_getuid = set_default(DEFAULT_GETUID);
-
-	if (conf->default_features == NULL)
-		conf->default_features = set_default(DEFAULT_FEATURES);
+	if (pidfile_create(DEFAULT_PIDFILE, getpid())) {
+		if (logsink)
+			log_thread_stop();
 
-	if (conf->default_hwhandler == NULL)
-		conf->default_hwhandler = set_default(DEFAULT_HWHANDLER);
+		exit(1);
+	}
+	signal_init();
+	setscheduler();
+	set_oom_adj(-17);
+	allpaths = init_paths();
 
+	if (!allpaths)
+		exit(1);
 
-#ifdef CLONE_NEWNS
-	if (prepare_namespace() < 0) {
-		log_safe(LOG_ERR, "cannot prepare namespace");
-		exit_daemon(1);
+	if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
+		condlog(0, "can not find sysfs mount point");
+		exit(1);
 	}
-#endif
+
+	/*
+	 * fetch paths and multipaths lists
+	 * no paths and/or no multipaths are valid scenarii
+	 * vectors maintenance will be driven by events
+	 */
+	path_discovery(allpaths->pathvec, conf, DI_SYSFS | DI_WWID);
+	get_dm_mpvec(allpaths);
 
 	/*
 	 * start threads
 	 */
 	pthread_attr_init(&attr);
 	pthread_attr_setstacksize(&attr, 64 * 1024);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 	
-	pthread_create(&wait_thr, &attr, waiterloop, allpaths);
 	pthread_create(&check_thr, &attr, checkerloop, allpaths);
-	pthread_join(wait_thr, NULL);
-	pthread_join(check_thr, NULL);
+	pthread_create(&uevent_thr, &attr, ueventloop, allpaths);
+	pthread_create(&uxlsnr_thr, &attr, uxlsnrloop, allpaths);
 
-	return 0;
+	pthread_cond_wait(&exit_cond, &exit_mutex);
+
+	/*
+	 * exit path
+	 */
+	lock(allpaths->lock);
+	remove_maps(allpaths);
+	free_pathvec(allpaths->pathvec, FREE_PATHS);
+
+	pthread_cancel(check_thr);
+	pthread_cancel(uevent_thr);
+	pthread_cancel(uxlsnr_thr);
+
+	free_keys(keys);
+	free_handlers(handlers);
+	free_polls();
+
+	unlock(allpaths->lock);
+	pthread_mutex_destroy(allpaths->lock);
+	FREE(allpaths->lock);
+	FREE(allpaths);
+	free_config(conf);
+
+	condlog(2, "--------shut down-------");
+	
+	if (logsink)
+		log_thread_stop();
+
+#ifdef _DEBUG_
+	dbg_free_final(NULL);
+#endif
+
+	exit(0);
 }
 
 int
@@ -946,10 +1408,11 @@
 	extern int optind;
 	int arg;
 	int err;
-	void * child_stack;
 	
+	logsink = 1;
+
 	if (getuid() != 0) {
-		fprintf(stderr, "need to be root, exit");
+		fprintf(stderr, "need to be root\n");
 		exit(1);
 	}
 
@@ -957,20 +1420,17 @@
 	chdir("/");
 	umask(umask(077) | 022);
 
-	child_stack = (void *)malloc(CHILD_STACK_SIZE);
-
-	if (!child_stack)
-		exit(1);
-
 	conf = alloc_config();
 
 	if (!conf)
 		exit(1);
 
-	conf->verbosity = 2;
-
-	while ((arg = getopt(argc, argv, ":qdlFSi:v:p:")) != EOF ) {
+	while ((arg = getopt(argc, argv, ":dv:k::")) != EOF ) {
 	switch(arg) {
+		case 'd':
+			logsink = 0;
+			//debug=1; /* ### comment me out ### */
+			break;
 		case 'v':
 			if (sizeof(optarg) > sizeof(char *) ||
 			    !isdigit(optarg[0]))
@@ -978,33 +1438,26 @@
 
 			conf->verbosity = atoi(optarg);
 			break;
+		case 'k':
+			uxclnt(optarg);
+			exit(0);
 		default:
 			;
 		}
 	}
 
-#ifdef CLONE_NEWNS	/* recent systems have clone() */
-
-#    if defined(__hppa__) || defined(__powerpc64__)
-	err = clone(child, child_stack, CLONE_NEWNS, NULL);
-#    elif defined(__ia64__)
-	err = clone2(child, child_stack,
-		     CHILD_STACK_SIZE, CLONE_NEWNS, NULL,
-		     NULL, NULL, NULL);
-#    else
-	err = clone(child, child_stack + CHILD_STACK_SIZE, CLONE_NEWNS, NULL);
-#    endif
-	if (err < 0)
-		exit (1);
-
-	exit(0);
-#else			/* older system fallback to fork() */
-	err = fork();
+	if (!logsink)
+		err = 0;
+	else
+		err = fork();
 	
 	if (err < 0)
-		exit (1);
-
-	return (child(child_stack));
-#endif
-
+		/* error */
+		exit(1);
+	else if (err > 0)
+		/* parent dies */
+		exit(0);
+	else
+		/* child lives */
+		return (child(NULL));
 }

Modified: multipath-tools/trunk/multipathd/main.h
==============================================================================
--- multipath-tools/trunk/multipathd/main.h	(original)
+++ multipath-tools/trunk/multipathd/main.h	Mon Sep 19 12:43:52 2005
@@ -1,8 +1,24 @@
-#ifndef HWTABLE_H
-#define HWTABLE_H
+#ifndef MAIN_H
+#define MAIN_H
 
 #define DAEMON 1
 #define CHECKINT 5
-#define MULTIPATH "/sbin/multipath -v0 -S"
+#define MAPGCINT 5
+#define MAX_CHECKINT CHECKINT << 2
 
-#endif
+struct paths {
+	pthread_mutex_t *lock;
+	vector pathvec;
+	vector mpvec;
+};
+
+int reconfigure (struct paths *);
+int show_paths (char **, int *, struct paths *);
+int show_maps (char **, int *, struct paths *);
+int dump_pathvec (char **, int *, struct paths * allpaths);
+int uev_add_path (char *, struct paths *);
+int uev_remove_path (char *, struct paths *);
+int uev_add_map (char *, struct paths *);
+int uev_remove_map (char *, struct paths *);
+
+#endif /* MAIN_H */

Modified: multipath-tools/trunk/multipathd/multipathd.init.redhat
==============================================================================
--- multipath-tools/trunk/multipathd/multipathd.init.redhat	(original)
+++ multipath-tools/trunk/multipathd/multipathd.init.redhat	Mon Sep 19 12:43:52 2005
@@ -5,7 +5,7 @@
 #
 # Starts the multipath daemon
 #
-# chkconfig: 2345 13 87
+# chkconfig: - 13 87
 # description: Manage device-mapper multipath devices
 # processname: multipathd
 

Added: multipath-tools/trunk/multipathd/uxclnt.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/multipathd/uxclnt.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,73 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <uxsock.h>
+#include <memory.h>
+#include <defaults.h>
+
+/*
+ * process the client 
+ */
+static void process(int fd)
+{
+	char *line;
+	char *reply;
+
+	while ((line = readline("multipathd> "))) {
+		size_t len;
+
+		if (send_packet(fd, line, strlen(line) + 1) != 0) break;
+		if (recv_packet(fd, &reply, &len) != 0) break;
+
+		printf("%s\n", reply);
+
+		if (line && *line)
+			add_history(line);
+
+		free(line);
+		FREE(reply);
+	}
+}
+
+static void process_req(int fd, char * inbuf)
+{
+	char *reply;
+	size_t len;
+
+	send_packet(fd, inbuf, strlen(inbuf) + 1);
+	recv_packet(fd, &reply, &len);
+
+	printf("%s\n", reply);
+	FREE(reply);
+}
+	
+/*
+ * entry point
+ */
+int uxclnt(char * inbuf)
+{
+	int fd;
+
+	fd = ux_socket_connect(DEFAULT_SOCKET);
+	if (fd == -1) {
+		perror("ux_socket_connect");
+		exit(1);
+	}
+
+	if (inbuf)
+		process_req(fd, inbuf);
+	else
+		process(fd);
+	
+	return 0;
+}

Added: multipath-tools/trunk/multipathd/uxclnt.h
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/multipathd/uxclnt.h	Mon Sep 19 12:43:52 2005
@@ -0,0 +1 @@
+int uxclnt(char * inbuf);

Added: multipath-tools/trunk/multipathd/uxlsnr.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/multipathd/uxlsnr.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,164 @@
+/*
+ * A simple domain socket listener
+ * Original author : tridge at samba.org, January 2002
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+
+#include <memory.h>
+#include <debug.h>
+#include <vector.h>
+#include <structs.h>
+#include <uxsock.h>
+#include <defaults.h>
+
+#include "uxlsnr.h"
+
+#define SLEEP_TIME 5000
+
+struct client {
+	int fd;
+	struct client *next, *prev;
+};
+
+static struct client *clients;
+static unsigned num_clients;
+
+/*
+ * handle a new client joining
+ */
+static void new_client(int ux_sock)
+{
+	struct client *c;
+	struct sockaddr addr;
+	socklen_t len = sizeof(addr);
+	int fd;
+
+	fd = accept(ux_sock, &addr, &len);
+	
+	if (fd == -1)
+		return;
+
+	/* put it in our linked list */
+	c = (struct client *)MALLOC(sizeof(*c));
+	memset(c, 0, sizeof(*c));
+	c->fd = fd;
+	c->next = clients;
+	if (c->next) c->next->prev = c;
+	clients = c;
+	num_clients++;
+}
+
+/*
+ * kill off a dead client
+ */
+static void dead_client(struct client *c)
+{
+	close(c->fd);
+	if (c->prev) c->prev->next = c->next;
+	if (c->next) c->next->prev = c->prev;
+	if (c == clients) clients = c->next;
+	FREE(c);
+	num_clients--;
+}
+
+void free_polls (void)
+{
+	FREE(polls);
+}
+
+/*
+ * entry point
+ */
+void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *),
+			void * trigger_data)
+{
+	int ux_sock;
+	size_t len;
+	int rlen;
+	char *inbuf;
+	char *reply;
+
+	ux_sock = ux_socket_listen(DEFAULT_SOCKET);
+
+	if (ux_sock == -1) {
+		condlog(0, "ux_socket_listen error");
+		exit(1);
+	}
+
+	polls = (struct pollfd *)MALLOC(0);
+
+	while (1) {
+		struct client *c;
+		int i, poll_count;
+
+		/* setup for a poll */
+		polls = REALLOC(polls, (1+num_clients) * sizeof(*polls));
+		polls[0].fd = ux_sock;
+		polls[0].events = POLLIN;
+
+		/* setup the clients */
+		for (i=1, c = clients; c; i++, c = c->next) {
+			polls[i].fd = c->fd;
+			polls[i].events = POLLIN;
+		}
+
+		/* most of our life is spent in this call */
+		poll_count = poll(polls, i, SLEEP_TIME);
+		
+		if (poll_count == -1) {
+			if (errno == EINTR)
+				continue;
+
+			/* something went badly wrong! */
+			condlog(0, "poll");
+			exit(1);
+		}
+
+		if (poll_count == 0)
+			continue;
+
+		/* see if a client wants to speak to us */
+		for (i=1, c = clients; c; i++) {
+			struct client *next = c->next;
+
+			if (polls[i].revents & POLLIN) {
+				if (recv_packet(c->fd, &inbuf, &len) != 0) {
+					dead_client(c);
+				} else {
+					inbuf[len - 1] = 0;
+					condlog(4, "Got request [%s]", inbuf);
+					uxsock_trigger(inbuf, &reply, &rlen,
+							trigger_data);
+
+					if (reply) {
+						if (send_packet(c->fd, reply,
+						     rlen) != 0) {
+							dead_client(c);
+						}
+						FREE(reply);
+						reply = NULL;
+					}
+					FREE(inbuf);
+				}
+			}
+			c = next;
+		}
+
+		/* see if we got a new client */
+		if (polls[0].revents & POLLIN) {
+			new_client(ux_sock);
+		}
+	}
+
+	return NULL;
+}

Added: multipath-tools/trunk/multipathd/uxlsnr.h
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/multipathd/uxlsnr.h	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,7 @@
+struct pollfd *polls;
+
+void free_polls(void);
+void * uxsock_listen(int (*uxsock_trigger)
+			(char *, char **, int *, void *),
+			void * trigger_data);
+

Modified: multipath-tools/trunk/path_priority/pp_alua/Makefile
==============================================================================
--- multipath-tools/trunk/path_priority/pp_alua/Makefile	(original)
+++ multipath-tools/trunk/path_priority/pp_alua/Makefile	Mon Sep 19 12:43:52 2005
@@ -12,7 +12,7 @@
 #
 # This file is released under the GPL.
 #==============================================================================
-EXEC		= pp_alua
+EXEC		= mpath_prio_alua
 BUILD		= glibc
 DEBUG		= 0
 DEBUG_DUMPHEX	= 0
@@ -39,17 +39,23 @@
 	$(CC) -static -o $(EXEC) $(OBJS)
 	$(STRIP) $(EXEC)
 
-install: $(EXEC)
+install: $(BUILD) $(EXEC).8.gz
 	install -m 755 $(EXEC) $(DESTDIR)$(bindir)/$(EXEC)
+	install -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
 
 uninstall:
 	rm $(DESTDIR)$(bindir)/$(EXEC)
+	rm $(DESTDIR)$(mandir)/$(EXEC).8.gz
+
 clean:	
-	rm -f *.o $(EXEC)
+	rm -f *.o *.gz $(EXEC)
 
 %.o:	%.c
 	$(CC) $(CFLAGS) -c -o $@ $<
 
+$(EXEC).8.gz:	$(EXEC).8
+	$(GZIP) $< >$@
+
 main.o:	main.c rtpg.h spc3.h
 
 rtpg.o:	rtpg.c rtpg.h spc3.h

Modified: multipath-tools/trunk/path_priority/pp_alua/main.c
==============================================================================
--- multipath-tools/trunk/path_priority/pp_alua/main.c	(original)
+++ multipath-tools/trunk/path_priority/pp_alua/main.c	Mon Sep 19 12:43:52 2005
@@ -12,13 +12,18 @@
  * 
  * This file is released under the GPL.
  */
+#include <linux/kdev_t.h>
+
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#include <unistd.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
 
 #include "rtpg.h"
 
@@ -30,7 +35,7 @@
 #define ALUA_PRIO_GETAAS_FAILED			5
 
 #define ALUA_PRIO_MAJOR				0
-#define ALUA_PRIO_MINOR				4
+#define ALUA_PRIO_MINOR				6
 
 #define PRINT_ERROR(f, a...) \
 		if (verbose) \
@@ -59,11 +64,23 @@
 		basename(command));
 	printf("Options are:\n");
 
+	printf("\t-d <device directory>\n");
+	printf("\t\tSets the directory prefix for relative path names and");
+	printf(" created\n\t\tpath names. (default = \"/dev\")\n");
+
+	printf("\t-h\n");
+	printf("\t\tPrint this help.\n");
+
 	printf("\t-v\n");
 	printf("\t\tTurn on verbose output.\n");
 
 	printf("\t-V\n");
 	printf("\t\tPrints the version number and exits.\n");
+
+	printf("\nDevice may be an absolute or relative path to a device ");
+	printf("node or a major and\nminor number seperated by a colon (:).");
+	printf(" In this case a temporary device node\nwill be created in ");
+	printf("the device directory.\n");
 }
 
 void
@@ -168,13 +185,20 @@
 int
 main (int argc, char **argv)
 {
-	char *		s_opts = "hvV";
+	char		devicepath[PATH_MAX];
+	char *		devicedir;
+	char *		s_opts = "d:hvV";
+	char *		pos;
 	int		fd;
 	int		rc;
 	int		c;
 
+	devicedir = "/dev";
 	while ((c = getopt(argc, argv, s_opts)) >= 0) {
 		switch(c) {
+			case 'd':
+				devicedir = optarg;
+				break;
 			case 'h':
 				print_help(argv[0]);
 				return ALUA_PRIO_SUCCESS;
@@ -200,8 +224,32 @@
 
 	rc = ALUA_PRIO_SUCCESS;
 	for(c = optind; c < argc && !rc; c++) {
-		fd = open_block_device(argv[c]);
+		if (argv[c][0] == '/') {
+			pos = NULL;
+			sprintf(devicepath, "%s", argv[c]);
+		} else if ((pos = index(argv[c], ':')) == NULL) {
+			sprintf(devicepath, "%s/%s", devicedir, argv[c]);
+		} else {
+			int major;
+			int minor;
+
+			major = atoi(argv[c]);
+			minor = atoi(++pos);
+			sprintf(devicepath, "%s/tmpdev-%u:%u-%u",
+				devicedir, major, minor, getpid()
+			);
+			mknod(
+				devicepath,
+				S_IFBLK|S_IRUSR|S_IWUSR,
+				MKDEV(major, minor)
+			);
+			
+		}
+
+		fd = open_block_device(devicepath);
 		if (fd < 0) {
+			if (pos != NULL)
+				unlink(devicepath);
 			return -fd;
 		}
 		rc = get_alua_info(fd);
@@ -223,6 +271,10 @@
 			rc = ALUA_PRIO_SUCCESS;
 		}
 		close_block_device(fd);
+
+		/* The path was created before. */
+		if (pos != NULL)
+			unlink(devicepath);
 	}
 
 	return -rc;

Added: multipath-tools/trunk/path_priority/pp_alua/mpath_prio_alua.8
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/path_priority/pp_alua/mpath_prio_alua.8	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,162 @@
+.TH MPATH_PRIO_ALUA 8 "7. June 2005" "multipath-tools" \
+"Linux Administrator's Manual"
+.SH NAME
+mpath_prio_alua \- Path priority tool based on Asymmetric LUn Access
+.SH SYNOPSIS
+.B mpath_prio_alua
+.RB [\| \-d\ \c
+.IR directory \|]
+.RB [\| \-h \|]
+.RB [\| \-v \|]
+.RB [\| \-V \|]
+.IR device " \|[ " device " \|[ " ... " \|]\|]"
+.SH DESCRIPTION
+.B mpath_prio_alua
+is used as a priority callout for the multipath command. It returns a number
+that is used by multipath to group devices with the same priority together.
+.SH OPTIONS
+.TP
+.BI \-d " directory"
+target directory for devices given as relative device names or devices given
+as
+.IR major : minor \c
+ number.
+Default is "/dev".
+.TP
+.B \-h
+displays the command line help.
+.TP
+.B  \-v
+turns on verbose output. This shows all results in human readable format.
+This includes information about the port group the device is in and its
+current state.
+.TP
+.B  \-V
+shows the version number and exits.
+.TP
+.BI device
+specifies the device to query (the device must be a SCSI device that supports
+the \*[lq]Report Target Port Groups\*[rq] command).
+One of the following three formats may be used:
+.RS
+.IP \(bu 2
+The full path name that starts with '/' (e.g. /dev/sda).
+.IP \(bu
+The device name only. This will prefix the directory name given by the
+\-d option (e.g. sda).
+.IP \(bu
+The major and minor number of the device separated by ':'. This will
+create a temporary device node in the device directory (e.g. 8:0). The
+temporary name will be
+.RB \*[lq] tmpdev-<major>:<minor>-<pid> \*[rq].
+.SH "RETURN VALUE"
+The mpath_prio_alua command returns the following values:
+.IP \fB0
+on success. In this case the priority for the device is printed to
+stdout. The priority value is:
+.RS
+.IP \fB50\fP
+for devices that are in the active, optimized group
+.IP \fB10
+for devices that are in an active but non-optimized group
+.IP \fB1
+for devices that are in the standby group
+.IP \fB0
+for all other groups
+.RE
+.IP ""
+The reason for the widely spaced priority values is the way multipath handles
+them. It will multiply the number of paths in a group with the priority value
+and select the group with the highest result. Thus, if there are six paths in
+the active, non-optimized group and only one in the active, optimized one,
+the non-optimized group would be used.
+.IP \fB1
+Indicates an error parsing the command line.
+.IP \fB2
+The given devices could not be opened for reading.
+.IP \fB3
+The device does not support target port groups.
+.IP \fB4
+The inquiry command did not return a target port group for the given device.
+.IP \fB5
+The report target port group command failed or did not return a target
+port group that was obtained from the inquiry command.
+.SH "EXAMPLES"
+This example queries a device directly and returns the priority string:
+.P
+.RS
+.B #> mpath_prio_alua /dev/sda
+.br
+50
+.RE
+.P
+Now the major and minor number is used to specify the device and verbose
+output is selected:
+.P
+.RS
+.B #> mpath_prio_alua -v 8:0
+.br
+Target port groups are implicitly supported.
+.br
+Reported target port group is 0 [active/optimized]
+.br
+50
+.RE
+.P
+The following example shows the entries in the devices section of the
+.RI "multipath-tool configuration file (" /etc/multipath.conf )
+to support an IBM DS6000 storage system:
+.P
+.RS
+.PD 0
+device {
+.RS
+.TP 22
+.B  vendor
+"IBM       "
+.TP
+.B  product
+"1750500         "
+.TP
+.B  path_grouping_policy
+group_by_prio
+.TP
+.B  prio_callout
+"/sbin/mpath_prio_alua -d/tmp %d"
+.TP
+.B  features
+"1 queue_if_no_path"
+.TP
+.B  path_checker
+tur
+.RE
+}
+.PD
+.RE
+.TP
+.B  Notes:
+.IP \(bu 2
+Depending on your default configuration not all keywords are required
+.RB "(e.g. if your " default_path_checker " is set to tur you don't have to"
+.RB "use the " path_checker " statement in the device section)."
+.IP \(bu
+.RB "The entries for " vendor " and " product " must be strings that are 8"
+.RB "characters long (for " vendor ") and 16 characters long (for " product ")."
+The strings have to be padded with blanks if necessary.
+.IP \(bu
+If you are working with hotpluggable devices whose device nodes are created
+by udev you should use the %d flag in the
+.BR prio_callout " statement."
+This is because a short time elapses between the devices being available
+and udev creating the device nodes.
+.IP \(bu
+If under certain circumstances your storage subsystem temporarily reports
+.RB "failures on all paths, you should use the " features " statement showed"
+in the example.
+This will configure the multipath volume to requeue I/O until a path becomes
+available again, instead of reporting failures in that case.
+.SH "SEE ALSO"
+.BR multipath (8),
+.SH AUTHORS
+.B mpath_prio_alua
+was developed by Jan Kunigk and adapted by Stefan Bader <shbader at de.ibm.com>

Modified: multipath-tools/trunk/path_priority/pp_alua/rtpg.c
==============================================================================
--- multipath-tools/trunk/path_priority/pp_alua/rtpg.c	(original)
+++ multipath-tools/trunk/path_priority/pp_alua/rtpg.c	Mon Sep 19 12:43:52 2005
@@ -123,7 +123,7 @@
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.op = OPERATION_CODE_INQUIRY;
 	if (evpd) {
-		cmd.evpd = 1;
+		inquiry_command_set_evpd(&cmd);
 		cmd.page = codepage;
 	}
 	set_uint16(cmd.length, resplen);
@@ -166,7 +166,7 @@
 
 	rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq));
 	if (!rc) {
-		rc = inq.tpgs;
+		rc = inquiry_data_get_tpgs(&inq);
 	}
 
 	return rc;
@@ -189,7 +189,7 @@
 			if ((((char *) dscr) - ((char *) vpd83)) > sizeof(buf))
 				break;
 
-			if (dscr->id_type == IDTYPE_TARGET_PORT_GROUP) {
+			if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) {
 				struct vpd83_tpg_dscr *	p;
 
 				if (rc != -RTPG_NO_TPG_IDENTIFIER) {
@@ -221,7 +221,7 @@
 
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.op			= OPERATION_CODE_RTPG;
-	cmd.service_action	= SERVICE_ACTION_RTPG;
+	rtpg_command_set_service_action(&cmd);
 	set_uint32(cmd.length, resplen);
 	PRINT_HEX((unsigned char *) &cmd, sizeof(cmd));
 
@@ -270,7 +270,7 @@
 					"group.\n");
 			} else {
 				PRINT_DEBUG("pref=%i\n", dscr->pref);
-				rc = dscr->aas;
+				rc = rtpg_tpg_dscr_get_aas(dscr);
 			}
 		}
 	}

Modified: multipath-tools/trunk/path_priority/pp_alua/spc3.h
==============================================================================
--- multipath-tools/trunk/path_priority/pp_alua/spc3.h	(original)
+++ multipath-tools/trunk/path_priority/pp_alua/spc3.h	Mon Sep 19 12:43:52 2005
@@ -57,14 +57,20 @@
 
 struct inquiry_command {
 	unsigned char	op;
-	unsigned char	reserved1			: 6;
-	unsigned char	obsolete1			: 1;
-	unsigned char	evpd				: 1;
+	unsigned char	b1;		/* xxxxxx.. = reserved               */
+					/* ......x. = obsolete               */
+					/* .......x = evpd                   */
 	unsigned char	page;
 	unsigned char	length[2];
 	unsigned char	control;
 } __attribute__((packed));
 
+static inline void
+inquiry_command_set_evpd(struct inquiry_command *ic)
+{
+	ic->b1 |= 1;
+}
+
 /*-----------------------------------------------------------------------------
  * Data returned by the standard inquiry command.
  *-----------------------------------------------------------------------------
@@ -109,64 +115,58 @@
 #define TPGS_BOTH					0x3
 
 struct inquiry_data {
-	unsigned char	peripheral_qualifier		: 3;
-	unsigned char	peripheral_device_type		: 5;
-	/* Removable Medium Bit (1 == removable) */
-	unsigned char	rmb				: 1;
-	unsigned char	reserved1			: 7;
+	unsigned char	b0;		/* xxx..... = peripheral_qualifier   */
+					/* ...xxxxx = peripheral_device_type */
+	unsigned char	b1;             /* x....... = removable medium       */
+					/* .xxxxxxx = reserverd              */
 	unsigned char	version;
-	unsigned char	obsolete1			: 2;
-	/* Normal ACA Supported */
-	unsigned char	norm_aca			: 1;
-	/* Hierarchical LUN assignment support */
-	unsigned char	hi_sup				: 1;
-	/* If 2 then response data is as defined in SPC-3. */
-	unsigned char	response_data_format		: 4;
+	unsigned char	b3;		/* xx...... = obsolete               */
+					/* ..x..... = normal aca supported   */
+					/* ...x.... = hirarchichal lun supp. */
+					/* ....xxxx = response format        */
+					/*            2 is spc-3 format      */
 	unsigned char	length;
-	/* Storage Controller Component Supported. */
-	unsigned char	sccs				: 1;
-	/* Access Controls Cordinator. */
-	unsigned char	acc				: 1;
-	/* Target Port Group Support */
-	unsigned char	tpgs				: 2;
-	/* Third Party Copy support. */
-	unsigned char	tpc				: 1;
-	unsigned char	reserved2			: 2;
-	/* PROTECTion information supported. */
-	unsigned char	protect				: 1;
-	/* Basic task management model supported (CmdQue must be 0). */
-	unsigned char	bque				: 1;
-	/* ENClosure SERVices supported. */
-	unsigned char	encserv				: 1;
-	unsigned char	vs1				: 1;
-	/* MULTIPort support. */
-	unsigned char	multip				: 1;
-	/* Medium CHaNGeR. */
-	unsigned char	mchngr				: 1;
-	unsigned char	obsolete2			: 2;
-	unsigned char	addr16				: 1;
-	unsigned char	obsolete3			: 2;
-	unsigned char	wbus16				: 1;
-	unsigned char	sync				: 1;
-	/* LINKed commands supported. */
-	unsigned char	link				: 1;
-	unsigned char	obsolete4			: 1;
-	unsigned char	cmdque				: 1;
-	unsigned char	vs2				: 1;
+	unsigned char	b5;		/* x....... = storage controller     */
+					/*            component supported    */
+					/* .x...... = access controls coord. */
+					/* ..xx.... = target port group supp.*/
+					/* ....x... = third party copy supp. */
+					/* .....xx. = reserved               */
+					/* .......x = protection info supp.  */
+	unsigned char	b6;		/* x....... = bque                   */
+					/* .x...... = enclosure services sup.*/
+					/* ..x..... = vs1                    */
+					/* ...x.... = multiport support      */
+					/* ....x... = medium changer         */
+					/* .....xx. = obsolete               */
+					/* .......x = add16                  */
+	unsigned char	b7;		/* xx...... = obsolete               */
+					/* ..x..... = wbus16                 */
+					/* ...x.... = sync                   */
+					/* ....x... = linked commands supp.  */
+					/* .....x.. = obsolete               */
+					/* ......x. = command queue support  */
+					/* .......x = vs2                    */
 	unsigned char	vendor_identification[8];
 	unsigned char	product_identification[8];
 	unsigned char	product_revision[4];
 	unsigned char	vendor_specific[20];
-	unsigned char	reserved3			: 4;
-	unsigned char	clocking			: 2;
-	unsigned char	qas				: 1;
-	unsigned char	ius				: 1;
+	unsigned char	b48;		/* xxxx.... = reserved               */
+					/* ....xx.. = clocking               */
+					/* ......x. = qas                    */
+					/* .......x = ius                    */
 	unsigned char	reserved4;
 	unsigned char	version_descriptor[8][2];
 	unsigned char	reserved5[22];
 	unsigned char	vendor_parameters[0];
 } __attribute__((packed));
 
+static inline int
+inquiry_data_get_tpgs(struct inquiry_data *id)
+{
+	return (id->b5 >> 4) & 3;
+}
+
 /*-----------------------------------------------------------------------------
  * Inquiry data returned when requesting vital product data page 0x83.
  *-----------------------------------------------------------------------------
@@ -195,23 +195,28 @@
 } __attribute__((packed));
 
 struct vpd83_dscr {
-	unsigned char		protocol_id			: 4;
-	unsigned char		codeset				: 4;
-	/* Set if the protocol_id field is valid. */
-	unsigned char		piv				: 1;
-	unsigned char		reserved1			: 1;
-	unsigned char		association			: 2;
-	unsigned char		id_type				: 4;
+	unsigned char		b0;	/* xxxx.... = protocol id            */
+					/* ....xxxx = codeset                */
+	unsigned char		b1;	/* x....... = protocol id valid      */
+					/* .x...... = reserved               */
+					/* ..xx.... = association            */
+					/* ....xxxx = id type                */
 	unsigned char		reserved2;
-	unsigned char		length;				/* size-4 */
+	unsigned char		length;				/* size-4    */
 	unsigned char		data[0];
 } __attribute__((packed));
 
+static inline int
+vpd83_dscr_istype(struct vpd83_dscr *d, unsigned char type)
+{
+	return ((d->b1 & 7) == type);
+}
+
 struct vpd83_data {
-	unsigned char		peripheral_qualifier		: 3;
-	unsigned char		peripheral_device_type		: 5;
-	unsigned char		page_code;			/* 0x83 */
-	unsigned char		length[2];			/* size-4 */
+	unsigned char		b0;	/* xxx..... = peripheral_qualifier   */
+					/* ...xxxxx = peripheral_device_type */
+	unsigned char		page_code;			/* 0x83      */
+	unsigned char		length[2];			/* size-4    */
 	struct vpd83_dscr	data[0];
 } __attribute__((packed));
 
@@ -243,15 +248,21 @@
 #define SERVICE_ACTION_RTPG		0x0a
 
 struct rtpg_command {
-	unsigned char			op;			/* 0xa3 */
-	unsigned char			reserved1	: 3;
-	unsigned char			service_action	: 5;	/* 0x0a */
+	unsigned char		op;	/* 0xa3                              */
+	unsigned char		b1;	/* xxx..... = reserved               */
+					/* ...xxxxx = service action (0x0a)  */
 	unsigned char			reserved2[4];
 	unsigned char			length[4];
 	unsigned char			reserved3;
 	unsigned char			control;
 } __attribute__((packed));
 
+static inline void
+rtpg_command_set_service_action(struct rtpg_command *cmd)
+{
+	cmd->b1 = (cmd->b1 & 0xe0) | SERVICE_ACTION_RTPG;
+}
+
 struct rtpg_tp_dscr {
 	unsigned char			obsolete1[2];
 	/* The Relative Target Port Identifier of a target port. */
@@ -269,14 +280,14 @@
 #define TPG_STATUS_IMPLICIT_CHANGE	0x2
 
 struct rtpg_tpg_dscr {
-	unsigned char			pref		: 1;
-	unsigned char			reserved1	: 3;
-	unsigned char			aas		: 4;
-	unsigned char			reserved2	: 4;
-	unsigned char			u_sup		: 1;
-	unsigned char			s_sup		: 1;
-	unsigned char			an_sup		: 1;
-	unsigned char			ao_sup		: 1;
+	unsigned char	b0;		/* x....... = pref(ered) port        */
+					/* .xxx.... = reserved               */
+					/* ....xxxx = asymetric access state */
+	unsigned char	b1;		/* xxxx.... = reserved               */
+					/* ....x... = unavailable support    */
+					/* .....x.. = standby support        */
+					/* ......x. = non-optimized support  */
+					/* .......x = optimized support      */
 	unsigned char			tpg[2];
 	unsigned char			reserved3;
 	unsigned char			status;
@@ -285,6 +296,12 @@
 	struct rtpg_tp_dscr		data[0];
 } __attribute__((packed));
 
+static inline int
+rtpg_tpg_dscr_get_aas(struct rtpg_tpg_dscr *d)
+{
+	return (d->b0 & 0x0f);
+}
+
 struct rtpg_data {
 	unsigned char			length[4];		/* size-4 */
 	struct rtpg_tpg_dscr		data[0];

Added: multipath-tools/trunk/path_priority/pp_balance_units/Makefile
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/path_priority/pp_balance_units/Makefile	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,47 @@
+# Makefile
+#
+# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui at free.fr>
+#
+BUILD = glibc
+DEBUG = 0
+
+TOPDIR	= ../..
+include $(TOPDIR)/Makefile.inc
+
+ifeq ($(strip $(BUILD)),klibc)
+	CFLAGS = -I/usr/include -DDEBUG=$(DEBUG)
+	OBJS = pp_balance_units.o $(MULTIPATHLIB)-$(BUILD).a
+else
+	CFLAGS = -pipe -g -Wall -Wunused -Wstrict-prototypes \
+		 -I$(multipathdir) -DDEBUG=$(DEBUG)
+	LDFLAGS = -ldevmapper
+	OBJS = pp_balance_units.o $(MULTIPATHLIB)-$(BUILD).a
+endif
+
+EXEC = pp_balance_units
+
+all: $(BUILD)
+
+prepare:
+	rm -f core *.o *.gz
+
+glibc: prepare $(OBJS)
+	$(CC) -o $(EXEC) $(OBJS) $(LDFLAGS)
+	$(STRIP) $(EXEC)
+	
+klibc: prepare $(OBJS)
+	$(CC) -static -o $(EXEC) $(CRT0) $(OBJS) $(KLIBC) $(LIBGCC)
+	$(STRIP) $(EXEC)
+
+$(MULTIPATHLIB)-$(BUILD).a:
+	make -C $(multipathdir) BUILD=$(BUILD) $(BUILD)
+
+install:
+	install -d $(DESTDIR)$(bindir)
+	install -m 755 $(EXEC) $(DESTDIR)$(bindir)/
+
+uninstall:
+	rm $(DESTDIR)$(bindir)/$(EXEC)
+
+clean:
+	rm -f core *.o $(EXEC) *.gz

Added: multipath-tools/trunk/path_priority/pp_balance_units/pp_balance_units.c
==============================================================================
--- (empty file)
+++ multipath-tools/trunk/path_priority/pp_balance_units/pp_balance_units.c	Mon Sep 19 12:43:52 2005
@@ -0,0 +1,474 @@
+/*
+ * Christophe Varoqui (2004)
+ * This code is GPLv2, see license file
+ *
+ * This path prioritizer aims to balance logical units over all
+ * controlers available. The logic is :
+ *
+ * - list all paths in all primary path groups
+ * - for each path, get the controler's serial
+ * - compute the number of active paths attached to each controler
+ * - compute the max number of paths attached to the same controler
+ * - if sums are already balanced or if the path passed as parameter is
+ *   attached to controler with less active paths, then return 
+ *   (max_path_attached_to_one_controler - number_of_paths_on_this_controler)
+ * - else, or if anything goes wrong, return 1 as a default prio
+ *
+ */
+#define __user
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <libdevmapper.h>
+#include <vector.h>
+#include <memory.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <scsi/sg.h>
+
+#define SERIAL_SIZE 255
+#define WORD_SIZE 255
+#define PARAMS_SIZE 255
+#define FILE_NAME_SIZE 255
+#define INQUIRY_CMDLEN  6
+#define INQUIRY_CMD     0x12
+#define SENSE_BUFF_LEN  32
+#define DEF_TIMEOUT     60000
+#define RECOVERED_ERROR 0x01
+#define MX_ALLOC_LEN    255
+#define SCSI_CHECK_CONDITION    0x2
+#define SCSI_COMMAND_TERMINATED 0x22
+#define SG_ERR_DRIVER_SENSE     0x08
+
+#if DEBUG
+#define debug(format, arg...) fprintf(stderr, format "\n", ##arg)
+#else
+#define debug(format, arg...) do {} while(0)
+#endif
+
+#define safe_sprintf(var, format, args...)	\
+	snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
+#define safe_snprintf(var, size, format, args...)      \
+	snprintf(var, size, format, ##args) >= size
+
+struct path {
+	char dev_t[WORD_SIZE];
+	char serial[SERIAL_SIZE];
+};
+
+struct controler {
+	char serial[SERIAL_SIZE];
+	int path_count;
+};
+
+static int
+exit_tool (int ret)
+{
+	printf("1\n");
+	exit(ret);
+}
+
+static int
+opennode (char * devt, int mode)
+{
+	char devpath[FILE_NAME_SIZE];
+	unsigned int major;
+	unsigned int minor;
+	int fd;
+
+	sscanf(devt, "%u:%u", &major, &minor);
+	memset(devpath, 0, FILE_NAME_SIZE);
+	
+	if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode",
+			 major, minor)) {
+		fprintf(stderr, "devpath too small\n");
+		return -1;
+	}
+	unlink (devpath);
+	mknod(devpath, S_IFBLK|S_IRUSR|S_IWUSR, makedev(major, minor));
+	fd = open(devpath, mode);
+	
+	if (fd < 0)
+		unlink(devpath);
+
+	return fd;
+
+}
+
+static void
+closenode (char * devt, int fd)
+{
+	char devpath[FILE_NAME_SIZE];
+	unsigned int major;
+	unsigned int minor;
+
+	if (fd >= 0)
+		close(fd);
+
+	sscanf(devt, "%u:%u", &major, &minor);
+	if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode",
+			 major, minor)) {
+		fprintf(stderr, "devpath too small\n");
+		return;
+	}
+	unlink(devpath);
+}
+
+static int
+do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
+       void *resp, int mx_resp_len, int noisy)
+{
+        unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
+            { INQUIRY_CMD, 0, 0, 0, 0, 0 };
+        unsigned char sense_b[SENSE_BUFF_LEN];
+        struct sg_io_hdr io_hdr;
+                                                                                                                 
+        if (cmddt)
+                inqCmdBlk[1] |= 2;
+        if (evpd)
+                inqCmdBlk[1] |= 1;
+        inqCmdBlk[2] = (unsigned char) pg_op;
+	inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff);
+	inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff);
+        memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof (inqCmdBlk);
+        io_hdr.mx_sb_len = sizeof (sense_b);
+        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+        io_hdr.dxfer_len = mx_resp_len;
+        io_hdr.dxferp = resp;
+        io_hdr.cmdp = inqCmdBlk;
+        io_hdr.sbp = sense_b;
+        io_hdr.timeout = DEF_TIMEOUT;
+ 
+        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
+                return -1;
+ 
+        /* treat SG_ERR here to get rid of sg_err.[ch] */
+        io_hdr.status &= 0x7e;
+        if ((0 == io_hdr.status) && (0 == io_hdr.host_status) &&
+            (0 == io_hdr.driver_status))
+                return 0;
+        if ((SCSI_CHECK_CONDITION == io_hdr.status) ||
+            (SCSI_COMMAND_TERMINATED == io_hdr.status) ||
+            (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) {
+                if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) {
+                        int sense_key;
+                        unsigned char * sense_buffer = io_hdr.sbp;
+                        if (sense_buffer[0] & 0x2)
+                                sense_key = sense_buffer[1] & 0xf;
+                        else
+                                sense_key = sense_buffer[2] & 0xf;
+                        if(RECOVERED_ERROR == sense_key)
+                                return 0;
+                }
+        }
+        return -1;
+}
+
+static int
+get_serial (char * str, char * devt)
+{
+	int fd;
+        int len;
+        char buff[MX_ALLOC_LEN + 1];
+
+	fd = opennode(devt, O_RDONLY);
+
+	if (fd < 0)
+                return 0;
+
+	if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) {
+		len = buff[3];
+		if (len > 0) {
+			memcpy(str, buff + 4, len);
+			buff[len] = '\0';
+		}
+		close(fd);
+		return 1;
+	}
+
+	closenode(devt, fd);
+        return 0;
+}
+
+static void *
+get_params (void)
+{
+	struct dm_task *dmt, *dmt1;
+	struct dm_names *names = NULL;
+	unsigned next = 0;
+	void *nexttgt;
+	uint64_t start, length;
+	char *target_type = NULL;
+	char *params;
+	char *pp;
+	vector paramsvec = NULL;
+
+	if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+		return NULL;
+
+	if (!dm_task_run(dmt))
+		goto out;
+
+	if (!(names = dm_task_get_names(dmt)))
+		goto out;
+
+	if (!names->dev) {
+		debug("no devmap found");
+		goto out;
+	}
+	do {
+		/*
+		 * keep only multipath maps
+		 */
+		names = (void *) names + next;
+		nexttgt = NULL;
+		debug("devmap %s :", names->name);
+
+		if (!(dmt1 = dm_task_create(DM_DEVICE_TABLE)))
+			goto out;
+
+		if (!dm_task_set_name(dmt1, names->name))
+			goto out1;
+
+		if (!dm_task_run(dmt1))
+			goto out1;
+
+		do {
+			nexttgt = dm_get_next_target(dmt1, nexttgt,
+						     &start,
+						     &length,
+						     &target_type,
+						     &params);
+			debug("\\_ %lu %lu %s", (unsigned long) start,
+				(unsigned long) length,
+				target_type);
+
+			if (!target_type) {
+				debug("unknown target type");
+				goto out1;
+			}
+
+			if (!strncmp(target_type, "multipath", 9)) {
+				if (!paramsvec)
+					paramsvec = vector_alloc();
+
+				pp = malloc(PARAMS_SIZE);
+				strncpy(pp, params, PARAMS_SIZE);
+				vector_alloc_slot(paramsvec);
+				vector_set_slot(paramsvec, pp);
+			} else
+				debug("skip non multipath target");
+		} while (nexttgt);
+out1:
+		dm_task_destroy(dmt1);
+		next = names->next;
+	} while (next);
+out:
+	dm_task_destroy(dmt);
+	return paramsvec;
+}
+
+static int
+get_word (char *sentence, char *word)
+{
+	char *p;
+	int skip = 0;
+	
+	while (*sentence ==  ' ') {
+		sentence++;
+		skip++;
+	}
+	p = sentence;
+
+	while (*p !=  ' ' && *p != '\0')
+		p++;
+
+	skip += (p - sentence);
+
+	if (p - sentence > WORD_SIZE) {
+		fprintf(stderr, "word too small\n");
+		exit_tool(1);
+	}
+	strncpy(word, sentence, WORD_SIZE);
+	word += p - sentence;
+	*word = '\0';
+
+	if (*p == '\0')
+		return 0;
+
+	return skip;
+}
+
+static int
+is_path (char * word)
+{
+	char *p;
+
+	if (!word)
+		return 0;
+
+	p = word;
+
+	while (*p != '\0') {
+		if (*p == ':')
+			return 1;
+		p++;
+	}
+	return 0;
+}
+
+static int
+get_paths (vector pathvec)
+{
+	vector paramsvec = NULL;
+	char * str;
+	struct path * pp;
+	int i;
+	enum where {BEFOREPG, INPG, AFTERPG};
+	int pos = BEFOREPG;
+
+	if (!pathvec)
+		return 1;
+
+	if (!(paramsvec = get_params()))
+		exit_tool(0);
+
+	vector_foreach_slot (paramsvec, str, i) {
+		debug("params %s", str);
+		while (pos != AFTERPG) {
+			pp = zalloc(sizeof(struct path));
+			str += get_word(str, pp->dev_t);
+
+			if (!is_path(pp->dev_t)) {
+				debug("skip \"%s\"", pp->dev_t);
+				free(pp);
+
+				if (pos == INPG)
+					pos = AFTERPG;
+				
+				continue;
+			}
+			if (pos == BEFOREPG)
+				pos = INPG;
+
+			get_serial(pp->serial, pp->dev_t);
+			vector_alloc_slot(pathvec);
+			vector_set_slot(pathvec, pp);
+			debug("store %s [%s]",
+				pp->dev_t, pp->serial);
+		}
+		pos = BEFOREPG;
+	}
+	return 0;
+}
+
+static void *
+find_controler (vector controlers, char * serial)
+{
+	int i;
+	struct controler * cp;
+
+	if (!controlers)
+		return NULL;
+
+	vector_foreach_slot (controlers, cp, i)
+		if (!strncmp(cp->serial, serial, SERIAL_SIZE))
+				return cp;
+	return NULL;
+}
+
+static void
+get_controlers (vector controlers, vector pathvec)
+{
+	int i;
+	struct path * pp;
+	struct controler * cp;
+	
+	if (!controlers)
+		return;
+
+	vector_foreach_slot (pathvec, pp, i) {
+		if (!pp || !strlen(pp->serial))
+			continue;
+		
+		cp = find_controler(controlers, pp->serial);
+
+		if (!cp) {
+			cp = zalloc(sizeof(struct controler));
+			vector_alloc_slot(controlers);
+			vector_set_slot(controlers, cp);
+			strncpy(cp->serial, pp->serial, SERIAL_SIZE);
+		}
+		cp->path_count++;	
+	}
+}
+
+static int
+get_max_path_count (vector controlers)
+{
+	int i;
+	int max = 0;
+	struct controler * cp;
+
+	if (!controlers)
+		return 0;
+
+	vector_foreach_slot (controlers, cp, i) {
+		debug("controler %s : %i paths", cp->serial, cp->path_count);
+		if(cp->path_count > max)
+			max = cp->path_count;
+	}
+	debug("max_path_count = %i", max);
+	return max;
+}
+
+int
+main (int argc, char **argv)
+{
+	vector pathvec = NULL;
+	vector controlers = NULL;
+	struct path * ref_path = NULL;
+	struct controler * cp = NULL;
+	int max_path_count = 0;
+
+	ref_path = zalloc(sizeof(struct path));
+
+	if (!ref_path)
+		exit_tool(1);
+
+	if (argc != 2)
+		exit_tool(1);
+
+	if (optind<argc)
+		strncpy(ref_path->dev_t, argv[optind], WORD_SIZE);
+
+	get_serial(ref_path->serial, ref_path->dev_t);
+
+	if (!ref_path->serial || !strlen(ref_path->serial))
+		exit_tool(0);
+
+	pathvec = vector_alloc();
+	controlers = vector_alloc();
+
+	get_paths(pathvec);
+	get_controlers(controlers, pathvec);
+	max_path_count = get_max_path_count(controlers);
+	cp = find_controler(controlers, ref_path->serial);
+
+	if (!cp) {
+		debug("no other active path on serial %s\n",
+			ref_path->serial);
+		exit_tool(0);
+	}
+
+	printf("%i\n", max_path_count - cp->path_count + 1);
+
+	return(0);
+}

Modified: multipath-tools/trunk/path_priority/pp_emc/Makefile
==============================================================================
--- multipath-tools/trunk/path_priority/pp_emc/Makefile	(original)
+++ multipath-tools/trunk/path_priority/pp_emc/Makefile	Mon Sep 19 12:43:52 2005
@@ -1,4 +1,4 @@
-EXEC		= pp_emc
+EXEC		= mpath_prio_emc
 BUILD		= glibc
 OBJS		= pp_emc.o
 



More information about the pkg-lvm-commits mailing list