summaryrefslogtreecommitdiff
path: root/extras/multipath-tools/multipathd
diff options
context:
space:
mode:
Diffstat (limited to 'extras/multipath-tools/multipathd')
-rw-r--r--extras/multipath-tools/multipathd/Makefile31
-rw-r--r--extras/multipath-tools/multipathd/checkers.c62
-rw-r--r--extras/multipath-tools/multipathd/checkers.h2
-rw-r--r--extras/multipath-tools/multipathd/devinfo.c82
-rw-r--r--extras/multipath-tools/multipathd/devinfo.h15
-rw-r--r--extras/multipath-tools/multipathd/main.c674
-rw-r--r--extras/multipath-tools/multipathd/multipathd.init42
-rw-r--r--extras/multipath-tools/multipathd/sg_include.h43
8 files changed, 951 insertions, 0 deletions
diff --git a/extras/multipath-tools/multipathd/Makefile b/extras/multipath-tools/multipathd/Makefile
new file mode 100644
index 0000000000..5bfd58a837
--- /dev/null
+++ b/extras/multipath-tools/multipathd/Makefile
@@ -0,0 +1,31 @@
+EXEC = multipathd
+
+CC = gcc
+GZIP = /bin/gzip -9 -c
+
+bindir = /usr/bin
+mandir = /usr/share/man/man8
+rcdir = /etc/init.d
+
+CFLAGS = -pipe -g -O2 -Wall -Wunused -Wstrict-prototypes -DDEBUG=1
+LDFLAGS = -lpthread -ldevmapper -lsysfs
+
+OBJS = main.o devinfo.o checkers.o
+
+$(EXEC): $(OBJS)
+ $(CC) $(LDFLAGS) $(OBJS) -o $(EXEC)
+ strip $(EXEC)
+
+install:
+ install -d $(bindir)
+ install -m 755 $(EXEC) $(bindir)
+ install -d $(rcdir)
+ install -m 755 multipathd.init $(rcdir)/$(EXEC)
+
+uninstall:
+ rm -f $(bindir)/$(EXEC)
+ rm -f $(rcdir)/$(EXEC)
+
+clean:
+ rm -f core *.o $(EXEC) *.gz
+
diff --git a/extras/multipath-tools/multipathd/checkers.c b/extras/multipath-tools/multipathd/checkers.c
new file mode 100644
index 0000000000..7ad32b04e9
--- /dev/null
+++ b/extras/multipath-tools/multipathd/checkers.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "sg_include.h"
+
+#define TUR_CMD_LEN 6
+
+/*
+ * test IO functions : add yours here
+ *
+ * returns 0 : path gone valid
+ * returns 1 : path still failed
+ */
+
+int readsector0 (char *devnode)
+{
+ int fd, r;
+ char buf;
+
+ fd = open (devnode, O_RDONLY);
+ if (read (fd, &buf, 1) != 1)
+ r = 0;
+ else
+ r = 1;
+
+ close (fd);
+
+ return r;
+}
+
+int tur(char *devnode)
+{
+ unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 };
+ struct sg_io_hdr io_hdr;
+ unsigned char sense_buffer[32];
+ int fd;
+
+ fd = open (devnode, O_RDONLY);
+
+ 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) {
+ close(fd);
+ return 0;
+ }
+ if (io_hdr.info & SG_INFO_OK_MASK) {
+ return 0;
+ }
+ return 1;
+}
diff --git a/extras/multipath-tools/multipathd/checkers.h b/extras/multipath-tools/multipathd/checkers.h
new file mode 100644
index 0000000000..057c319d1b
--- /dev/null
+++ b/extras/multipath-tools/multipathd/checkers.h
@@ -0,0 +1,2 @@
+int readsector0 (char *);
+int tur (char *);
diff --git a/extras/multipath-tools/multipathd/devinfo.c b/extras/multipath-tools/multipathd/devinfo.c
new file mode 100644
index 0000000000..46505470f4
--- /dev/null
+++ b/extras/multipath-tools/multipathd/devinfo.c
@@ -0,0 +1,82 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include "devinfo.h"
+#include "sg_include.h"
+
+#define FILE_NAME_SIZE 255
+
+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[4] = (unsigned char) mx_resp_len;
+ 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) {
+ perror("SG_IO (inquiry) error");
+ 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;
+}
+
+int
+get_lun_strings(char * vendor_id, char * product_id, char * rev, char * devname)
+{
+ int fd;
+ char buff[36];
+
+ /* ioctl style */
+ if ((fd = open(devname, O_RDONLY)) < 0)
+ return 1;
+ if (0 != do_inq(fd, 0, 0, 0, buff, 36, 1))
+ return 1;
+ memcpy(vendor_id, &buff[8], 8);
+ memcpy(product_id, &buff[16], 16);
+ memcpy(rev, &buff[32], 4);
+ close(fd);
+
+ return 0;
+}
diff --git a/extras/multipath-tools/multipathd/devinfo.h b/extras/multipath-tools/multipathd/devinfo.h
new file mode 100644
index 0000000000..46b2894051
--- /dev/null
+++ b/extras/multipath-tools/multipathd/devinfo.h
@@ -0,0 +1,15 @@
+#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 WWID_SIZE 33
+#define BLKGETSIZE _IO(0x12,96)
+
+/* exerpt from "sg_err.h" */
+#define SCSI_CHECK_CONDITION 0x2
+#define SCSI_COMMAND_TERMINATED 0x22
+#define SG_ERR_DRIVER_SENSE 0x08
+
+int get_lun_strings (char *, char *, char *, char *);
diff --git a/extras/multipath-tools/multipathd/main.c b/extras/multipath-tools/multipathd/main.c
new file mode 100644
index 0000000000..7b5ccfca23
--- /dev/null
+++ b/extras/multipath-tools/multipathd/main.c
@@ -0,0 +1,674 @@
+#include <string.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libdevmapper.h>
+#include <syslog.h>
+#include <signal.h>
+#include <wait.h>
+
+#include "devinfo.h"
+#include "checkers.h"
+
+#define CHECKINT 5
+#define MAXPATHS 2048
+#define FILENAMESIZE 256
+#define MAPNAMESIZE 64
+#define TARGETTYPESIZE 16
+#define PARAMSSIZE 2048
+#define MAXMAPS 512
+
+#define MULTIPATH "/sbin/multipath"
+#define PIDFILE "/var/run/multipathd.pid"
+
+#ifndef DEBUG
+#define DEBUG 1
+#endif
+#define LOG(x, y, z...) if (DEBUG >= x) syslog(x, y, ##z)
+
+struct path
+{
+ int major;
+ int minor;
+ char mapname[MAPNAMESIZE];
+ int (*checkfn) (char *);
+};
+
+struct paths
+{
+ pthread_mutex_t *lock;
+ struct path *paths_h;
+};
+
+struct event_thread
+{
+ pthread_t *thread;
+ pthread_mutex_t *waiter_lock;
+ char mapname[MAPNAMESIZE];
+};
+
+struct devmap
+{
+ char mapname[MAPNAMESIZE];
+};
+
+/* global var */
+pthread_mutex_t *event_lock;
+pthread_cond_t *event;
+
+int makenode (char *devnode, int major, int minor)
+{
+ dev_t dev;
+
+ dev = makedev (major, minor);
+ unlink (devnode);
+
+ return mknod(devnode, S_IFBLK | S_IRUSR | S_IWUSR, dev);
+}
+
+int select_checkfn(struct path *path_p)
+{
+ char devnode[FILENAMESIZE];
+ char vendor[8];
+ char product[16];
+ char rev[4];
+ int i, r;
+
+ /* default checkfn */
+ path_p->checkfn = &readsector0;
+
+ sprintf (devnode, "/tmp/.select.%i.%i", path_p->major, path_p->minor);
+
+ r = makenode (devnode, path_p->major, path_p->minor);
+
+ if (r < 0) {
+ LOG(2, "[select_checkfn] can not make node %s", devnode);
+ return r;
+ }
+
+ r = get_lun_strings(vendor, product, rev, devnode);
+
+ if (r) {
+ LOG(2, "[select_checkfn] can not get strings");
+ return r;
+ }
+
+ r = unlink (devnode);
+
+ if (r < 0) {
+ LOG(2, "[select_checkfn] can not unlink %s", devnode);
+ return r;
+ }
+
+ static struct {
+ char * vendor;
+ char * product;
+ int (*checkfn) (char *);
+ } wlist[] = {
+ {"COMPAQ ", "HSV110 (C)COMPAQ", &tur},
+ {"COMPAQ ", "MSA1000 ", &tur},
+ {"COMPAQ ", "MSA1000 VOLUME ", &tur},
+ {"DEC ", "HSG80 ", &tur},
+ {"HP ", "HSV100 ", &readsector0},
+ {NULL, NULL, NULL},
+ };
+
+ for (i = 0; wlist[i].vendor; i++) {
+ if (strncmp(vendor, wlist[i].vendor, 8) == 0 &&
+ strncmp(product, wlist[i].product, 16) == 0) {
+ path_p->checkfn = wlist[i].checkfn;
+ }
+ }
+
+ return 0;
+}
+
+int get_devmaps (struct devmap *devmaps)
+{
+ struct devmap *devmaps_p;
+ struct dm_task *dmt, *dmt1;
+ struct dm_names *names = NULL;
+ unsigned next = 0;
+ void *nexttgt;
+ int r = 0;
+ long long start, length;
+ char *target_type = NULL;
+ char *params;
+
+ memset (devmaps, 0, MAXMAPS * sizeof (struct devmap));
+
+ if (!(dmt = dm_task_create(DM_DEVICE_LIST))) {
+ r = 1;
+ goto out;
+ }
+
+ if (!dm_task_run(dmt)) {
+ r = 1;
+ goto out;
+ }
+
+ if (!(names = dm_task_get_names(dmt))) {
+ r = 1;
+ goto out;
+ }
+
+ if (!names->dev) {
+ LOG (1, "[get_devmaps] no devmap found");
+ goto out;
+ }
+
+ devmaps_p = devmaps;
+
+ do {
+ /* keep only multipath maps */
+
+ names = (void *) names + next;
+ nexttgt = NULL;
+ LOG (3, "[get_devmaps] iterate on devmap names : %s", names->name);
+
+ LOG (3, "[get_devmaps] dm_task_create(DM_DEVICE_STATUS)");
+ if (!(dmt1 = dm_task_create(DM_DEVICE_STATUS)))
+ goto out1;
+
+ LOG (3, "[get_devmaps] dm_task_set_name(dmt1, names->name)");
+ if (!dm_task_set_name(dmt1, names->name))
+ goto out1;
+
+ LOG (3, "[get_devmaps] dm_task_run(dmt1)");
+ if (!dm_task_run(dmt1))
+ goto out1;
+ LOG (3, "[get_devmaps] DM_DEVICE_STATUS ioctl done");
+ do {
+ LOG (3, "[get_devmaps] iterate on devmap's targets");
+ nexttgt = dm_get_next_target(dmt1, nexttgt,
+ &start,
+ &length,
+ &target_type,
+ &params);
+
+
+ LOG (3, "[get_devmaps] test target_type existence");
+ if (!target_type)
+ goto out1;
+
+ LOG (3, "[get_devmaps] test target_type is multipath");
+ if (!strncmp (target_type, "multipath", 9)) {
+ strcpy (devmaps_p->mapname, names->name);
+ devmaps_p++;
+
+ /* test vector overflow */
+ if (devmaps_p - devmaps >= MAXMAPS * sizeof (struct devmap)) {
+ LOG (1, "[get_devmaps] devmaps overflow");
+ dm_task_destroy(dmt1);
+ r = 1;
+ goto out;
+ }
+ }
+
+ } while (nexttgt);
+
+out1:
+ dm_task_destroy(dmt1);
+ next = names->next;
+
+ } while (next);
+
+out:
+ dm_task_destroy(dmt);
+
+ LOG (3, "[get_devmaps] done");
+ return r;
+}
+
+int checkpath (struct path *path_p)
+{
+ char devnode[FILENAMESIZE];
+ int r;
+
+ LOG (2, "[checkpath] checking path %i:%i", path_p->major, path_p->minor);
+ sprintf (devnode, "/tmp/.checker.%i.%i", path_p->major, path_p->minor);
+
+ if (path_p->checkfn == NULL) {
+ LOG (1, "[checkpath] test function not set for path %i:%i",
+ path_p->major, path_p->minor);
+ return 1;
+ }
+
+ r = makenode (devnode, path_p->major, path_p->minor);
+
+ if (r < 0) {
+ LOG (2, "[checkpath] can not make node for %s", devnode);
+ return r;
+ }
+
+ r = path_p->checkfn(devnode);
+ unlink (devnode);
+
+ return r;
+}
+
+int updatepaths (struct devmap *devmaps, struct paths *failedpaths)
+{
+ struct path *path_p;
+ struct devmap *devmaps_p;
+ void *next;
+ struct dm_task *dmt;
+ long long start, length;
+ char *target_type = NULL;
+ char *params, *p1, *p2;
+ char word[6];
+ int i;
+
+ path_p = failedpaths->paths_h;
+
+ pthread_mutex_lock (failedpaths->lock);
+ memset (failedpaths->paths_h, 0, MAXPATHS * sizeof (struct path));
+
+ /* first pass */
+ /* ask DM the failed path list */
+
+ devmaps_p = devmaps;
+
+ while (*devmaps_p->mapname != 0x0) {
+ next = NULL;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
+ break;
+
+ if (!dm_task_set_name(dmt, devmaps_p->mapname))
+ goto out;
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ do {
+ next = dm_get_next_target(dmt, next, &start, &length,
+ &target_type, &params);
+
+ /* begin ugly parser */
+ p1 = params;
+ p2 = params;
+ while (*p1) {
+ /* p2 lags at the begining of the word p1 parses */
+ while (*p1 != ' ') {
+ /* if the current word is a path */
+ if (*p1 == ':') {
+ /* p1 jumps to path state */
+ while (*p1 != 'A' && *p1 != 'F')
+ p1++;
+
+ /* store path info */
+
+ path_p->checkfn = NULL;
+
+ i = 0;
+ memset (&word, 'O', 6 * sizeof (char));
+ while (*p2 != ':') {
+ word[i++] = *p2;
+ p2++;
+ }
+ path_p->major = atoi (word);
+
+ p2++;
+ i = 0;
+ memset (&word, 'O', 6 * sizeof (char));
+ while (*p2 != ' ') {
+ word[i++] = *p2;
+ p2++;
+ }
+ path_p->minor = atoi (word);
+
+ strcpy (path_p->mapname, devmaps_p->mapname);
+
+ /*
+ * discard active paths
+ * don't trust the A status flag : double check
+ */
+ if (*p1 == 'A' &&
+ !select_checkfn (path_p) &&
+ checkpath (path_p)) {
+ LOG(2, "[updatepaths] discard %i:%i as valid path",
+ path_p->major, path_p->minor);
+ p1++;
+ memset (path_p, 0, sizeof(struct path));
+ continue;
+ }
+
+ path_p++;
+
+ /* test vector overflow */
+ if (path_p - failedpaths->paths_h >= MAXPATHS * sizeof (struct path)) {
+ LOG (1, "[updatepaths] path_h overflow");
+ pthread_mutex_unlock (failedpaths->lock);
+ return 1;
+ }
+ }
+ p1++;
+ }
+ p2 = p1;
+ p1++;
+ }
+ } while (next);
+
+out:
+ dm_task_destroy(dmt);
+ devmaps_p++;
+
+ }
+
+ pthread_mutex_unlock (failedpaths->lock);
+ return 0;
+}
+
+int geteventnr (char *name)
+{
+ struct dm_task *dmt;
+ struct dm_info info;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ if (!dm_task_get_info(dmt, &info))
+ return 0;
+
+ if (!info.exists) {
+ LOG(1, "Device %s does not exist", name);
+ return 0;
+ }
+
+out:
+ dm_task_destroy(dmt);
+
+ return info.event_nr;
+}
+
+void *waitevent (void * et)
+{
+ int event_nr;
+ struct event_thread *waiter;
+
+ waiter = (struct event_thread *)et;
+ pthread_mutex_lock (waiter->waiter_lock);
+
+ event_nr = geteventnr (waiter->mapname);
+
+ struct dm_task *dmt;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, waiter->mapname))
+ goto out;
+
+ if (event_nr && !dm_task_set_event_nr(dmt, event_nr))
+ goto out;
+
+ dm_task_run(dmt);
+
+out:
+ dm_task_destroy(dmt);
+
+ /* tell waiterloop we have an event */
+ pthread_mutex_lock (event_lock);
+ pthread_cond_signal(event);
+ pthread_mutex_unlock (event_lock);
+
+ /* release waiter_lock so that waiterloop knows we are gone */
+ pthread_mutex_unlock (waiter->waiter_lock);
+ pthread_exit(waiter->thread);
+
+ return (NULL);
+}
+
+void *waiterloop (void *ap)
+{
+ struct paths *failedpaths;
+ struct devmap *devmaps, *devmaps_p;
+ struct event_thread *waiters, *waiters_p;
+ int r;
+
+ /* inits */
+ failedpaths = (struct paths *)ap;
+ devmaps = malloc (MAXMAPS * sizeof (struct devmap));
+ waiters = malloc (MAXMAPS * sizeof (struct event_thread));
+ memset (waiters, 0, MAXMAPS * sizeof (struct event_thread));
+
+ while (1) {
+
+ /* update devmap list */
+ LOG (1, "[event thread] refresh devmaps list");
+ get_devmaps (devmaps);
+
+ /* update failed paths list */
+ LOG (1, "[event thread] refresh failpaths list");
+ updatepaths (devmaps, failedpaths);
+
+ /* start waiters on all devmaps */
+ LOG (1, "[event thread] start up event loops");
+ waiters_p = waiters;
+ devmaps_p = devmaps;
+
+ while (*devmaps_p->mapname != 0x0) {
+
+ /* find out if devmap already has a running waiter thread */
+ while (*waiters_p->mapname != 0x0) {
+ if (!strcmp (waiters_p->mapname, devmaps_p->mapname))
+ break;
+ waiters_p++;
+ }
+
+ /* no event_thread struct : init it */
+ if (*waiters_p->mapname == 0x0) {
+ strcpy (waiters_p->mapname, devmaps_p->mapname);
+ waiters_p->thread = malloc (sizeof (pthread_t));
+ waiters_p->waiter_lock = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
+ pthread_mutex_init (waiters_p->waiter_lock, NULL);
+ }
+
+ /* event_thread struct found */
+ if (*waiters_p->mapname != 0x0) {
+ r = pthread_mutex_trylock (waiters_p->waiter_lock);
+ /* thread already running : out */
+
+ if (r)
+ goto out;
+
+ pthread_mutex_unlock (waiters_p->waiter_lock);
+ }
+
+ LOG (1, "[event thread] create event thread for %s", waiters_p->mapname);
+ pthread_create (waiters_p->thread, NULL, waitevent, waiters_p);
+out:
+ waiters_p = waiters;
+ devmaps_p++;
+ }
+
+ /* wait event condition */
+ pthread_mutex_lock (event_lock);
+ pthread_cond_wait(event, event_lock);
+ pthread_mutex_unlock (event_lock);
+
+ LOG (1, "[event thread] event caught");
+ }
+
+ return (NULL);
+}
+
+void *checkerloop (void *ap)
+{
+ struct paths *failedpaths;
+ struct path *path_p;
+ char *cmdargs[4] = {MULTIPATH, "-D", NULL, NULL};
+ char major[5];
+ char minor[5];
+ int status;
+
+ failedpaths = (struct paths *)ap;
+
+ LOG (1, "[checker thread] path checkers start up");
+
+ while (1) {
+ path_p = failedpaths->paths_h;
+ pthread_mutex_lock (failedpaths->lock);
+ LOG (2, "[checker thread] checking paths");
+ while (path_p->major != 0) {
+
+ if (checkpath (path_p)) {
+ LOG (1, "[checker thread] reconfigure %s\n", path_p->mapname);
+ snprintf (major, 5, "%i", path_p->major);
+ snprintf (minor, 5, "%i", path_p->minor);
+ cmdargs[2] = major;
+ cmdargs[3] = minor;
+ if (fork () == 0)
+ execve (cmdargs[0], cmdargs, NULL);
+
+ wait (&status);
+ /* MULTIPATH will ask for failedpaths refresh (SIGHUP) */
+ }
+
+ path_p++;
+
+ /* test vector overflow */
+ if (path_p - failedpaths->paths_h >= MAXPATHS * sizeof (struct path)) {
+ LOG (1, "[checker thread] path_h overflow");
+ pthread_mutex_unlock (failedpaths->lock);
+ return (NULL);
+ }
+ }
+ pthread_mutex_unlock (failedpaths->lock);
+ sleep(CHECKINT);
+ }
+
+ return (NULL);
+}
+
+struct paths *initpaths (void)
+{
+ struct paths *failedpaths;
+
+ failedpaths = malloc (sizeof (struct paths));
+ failedpaths->paths_h = malloc (MAXPATHS * sizeof (struct path));
+ failedpaths->lock = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
+ pthread_mutex_init (failedpaths->lock, NULL);
+ 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 (failedpaths);
+}
+
+void pidfile (pid_t pid)
+{
+ FILE *file;
+ struct stat *buf;
+
+ buf = malloc (sizeof (struct stat));
+
+ if (!stat (PIDFILE, buf)) {
+ LOG(1, "[master thread] already running : out");
+ free (buf);
+ exit (1);
+ }
+
+ umask (022);
+ pid = setsid ();
+
+ if (pid < -1) {
+ LOG(1, "[master thread] setsid() error");
+ exit (1);
+ }
+
+ file = fopen (PIDFILE, "w");
+ fprintf (file, "%d\n", pid);
+ fclose (file);
+ free (buf);
+}
+
+void *
+signal_set(int signo, void (*func) (int))
+{
+ int r;
+ struct sigaction sig;
+ struct sigaction osig;
+
+ sig.sa_handler = func;
+ sigemptyset(&sig.sa_mask);
+ sig.sa_flags = 0;
+
+ r = sigaction(signo, &sig, &osig);
+
+ if (r < 0)
+ return (SIG_ERR);
+ else
+ return (osig.sa_handler);
+}
+
+void sighup (int sig)
+{
+ LOG (1, "[master thread] SIGHUP caught : refresh devmap list");
+
+ /* ask for failedpaths refresh */
+ pthread_mutex_lock (event_lock);
+ pthread_cond_signal(event);
+ pthread_mutex_unlock (event_lock);
+}
+
+void sigend (int sig)
+{
+ LOG (1, "[master thread] unlink pidfile");
+ unlink (PIDFILE);
+ LOG (1, "[master thread] --------shut down-------");
+ exit (0);
+}
+
+void signal_init(void)
+{
+ signal_set(SIGHUP, sighup);
+ signal_set(SIGINT, sigend);
+ signal_set(SIGTERM, sigend);
+ signal_set(SIGKILL, sigend);
+}
+
+int main (int argc, char *argv[])
+{
+ pthread_t wait, check;
+ struct paths *failedpaths;
+ pid_t pid;
+
+ pid = fork ();
+
+ /* can't fork */
+ if (pid < 0)
+ exit (1);
+
+ /* let the parent die happy */
+ if (pid > 0)
+ exit (0);
+
+ /* child's play */
+ openlog (argv[0], 0, LOG_DAEMON);
+ LOG (1, "[master thread] --------start up--------");
+
+ pidfile (pid);
+ signal_init ();
+
+ failedpaths = initpaths ();
+
+ pthread_create (&wait, NULL, waiterloop, failedpaths);
+ pthread_create (&check, NULL, checkerloop, failedpaths);
+ pthread_join (wait, NULL);
+ pthread_join (check, NULL);
+
+ return 0;
+}
diff --git a/extras/multipath-tools/multipathd/multipathd.init b/extras/multipath-tools/multipathd/multipathd.init
new file mode 100644
index 0000000000..860b2b11db
--- /dev/null
+++ b/extras/multipath-tools/multipathd/multipathd.init
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+DAEMON=/usr/bin/multipathd
+PIDFILE=/var/run/multipathd.pid
+
+test -x $DAEMON || exit 0
+
+case "$1" in
+ start)
+ echo -n "Starting multipath daemon: multipathd"
+ if start-stop-daemon --quiet --stop --signal 0 --pidfile $PIDFILE --name multipathd
+ then
+ echo " already running."
+ exit
+ fi
+ /sbin/start-stop-daemon --start --quiet --exec $DAEMON
+ echo "."
+ ;;
+ stop)
+ echo -n "Stopping multipath daemon: multipathd"
+ if start-stop-daemon --quiet --stop --signal 0 --pidfile $PIDFILE --name multipathd
+ then
+ PID=`cat $PIDFILE`
+ start-stop-daemon --quiet --stop --exec $DAEMON --pidfile $PIDFILE --name multipathd
+ # Now we wait for it to die
+ while kill -0 $PID 2>/dev/null; do sleep 1; done
+ echo "."
+ else
+ echo " not running.";
+ fi
+ ;;
+ force-reload|restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ echo "Usage: /etc/init.d/multipathd {start|stop|restart|force-reload}"
+ exit 1
+esac
+
+exit 0
diff --git a/extras/multipath-tools/multipathd/sg_include.h b/extras/multipath-tools/multipathd/sg_include.h
new file mode 100644
index 0000000000..460506826e
--- /dev/null
+++ b/extras/multipath-tools/multipathd/sg_include.h
@@ -0,0 +1,43 @@
+#ifdef SG_KERNEL_INCLUDES
+ #define __user
+ typedef unsigned char u8;
+ #include "/usr/src/linux/include/scsi/sg.h"
+ #include "/usr/src/linux/include/scsi/scsi.h"
+#else
+ #ifdef SG_TRICK_GNU_INCLUDES
+ #include <linux/../scsi/sg.h>
+ #include <linux/../scsi/scsi.h>
+ #else
+ #include <scsi/sg.h>
+ #endif
+#endif
+
+/*
+ Getting the correct include files for the sg interface can be an ordeal.
+ In a perfect world, one would just write:
+ #include <scsi/sg.h>
+ #include <scsi/scsi.h>
+ This would include the files found in the /usr/include/scsi directory.
+ Those files are maintained with the GNU library which may or may not
+ agree with the kernel and version of sg driver that is running. Any
+ many cases this will not matter. However in some it might, for example
+ glibc 2.1's include files match the sg driver found in the lk 2.2
+ series. Hence if glibc 2.1 is used with lk 2.4 then the additional
+ sg v3 interface will not be visible.
+ If this is a problem then defining SG_KERNEL_INCLUDES will access the
+ kernel supplied header files (assuming they are in the normal place).
+ The GNU library maintainers and various kernel people don't like
+ this approach (but it does work).
+ The technique selected by defining SG_TRICK_GNU_INCLUDES worked (and
+ was used) prior to glibc 2.2 . Prior to that version /usr/include/linux
+ was a symbolic link to /usr/src/linux/include/linux .
+
+ There are other approaches if this include "mixup" causes pain. These
+ would involve include files being copied or symbolic links being
+ introduced.
+
+ Sorry about the inconvenience. Typically neither SG_KERNEL_INCLUDES
+ nor SG_TRICK_GNU_INCLUDES is defined.
+
+ dpg 20010415, 20030522
+*/