summaryrefslogtreecommitdiff
path: root/extras/multipath-tools/multipath/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'extras/multipath-tools/multipath/main.c')
-rw-r--r--extras/multipath-tools/multipath/main.c895
1 files changed, 895 insertions, 0 deletions
diff --git a/extras/multipath-tools/multipath/main.c b/extras/multipath-tools/multipath/main.c
new file mode 100644
index 0000000000..8f06a8f94c
--- /dev/null
+++ b/extras/multipath-tools/multipath/main.c
@@ -0,0 +1,895 @@
+/*
+ * Soft: multipath device mapper target autoconfig
+ *
+ * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $
+ *
+ * Author: Copyright (C) 2003 Christophe Varoqui
+ *
+ * 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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/kdev_t.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <dlist.h>
+#include <sysfs/libsysfs.h>
+#include "../libdevmapper/libdevmapper.h"
+#include "main.h"
+#include "devinfo.h"
+
+/* White list switch */
+static int
+get_unique_id(struct path * mypath)
+{
+ int i;
+ static struct {
+ char * vendor;
+ char * product;
+ int iopolicy;
+ int (*getuid) (char *, char *);
+ } wlist[] = {
+ {"COMPAQ ", "HSV110 (C)COMPAQ", GROUP_BY_TUR, &get_evpd_wwid},
+ {"COMPAQ ", "MSA1000 ", GROUP_BY_TUR, &get_evpd_wwid},
+ {"COMPAQ ", "MSA1000 VOLUME ", GROUP_BY_TUR, &get_evpd_wwid},
+ {"DEC ", "HSG80 ", GROUP_BY_TUR, &get_evpd_wwid},
+ {"HP ", "HSV100 ", GROUP_BY_TUR, &get_evpd_wwid},
+ {"HP ", "A6189A ", MULTIBUS, &get_evpd_wwid},
+ {"HP ", "OPEN- ", MULTIBUS, &get_evpd_wwid},
+ {"DDN ", "SAN DataDirector", MULTIBUS, &get_evpd_wwid},
+ {"FSC ", "CentricStor ", MULTIBUS, &get_evpd_wwid},
+ {"HITACHI ", "DF400 ", MULTIBUS, &get_evpd_wwid},
+ {"HITACHI ", "DF500 ", MULTIBUS, &get_evpd_wwid},
+ {"HITACHI ", "DF600 ", MULTIBUS, &get_evpd_wwid},
+ {"IBM ", "ProFibre 4000R ", MULTIBUS, &get_evpd_wwid},
+ {"SGI ", "TP9100 ", MULTIBUS, &get_evpd_wwid},
+ {"SGI ", "TP9300 ", MULTIBUS, &get_evpd_wwid},
+ {"SGI ", "TP9400 ", MULTIBUS, &get_evpd_wwid},
+ {"SGI ", "TP9500 ", MULTIBUS, &get_evpd_wwid},
+ {NULL, NULL, 0, NULL},
+ };
+
+ for (i = 0; wlist[i].vendor; i++) {
+ if (strncmp(mypath->vendor_id, wlist[i].vendor, 8) == 0 &&
+ strncmp(mypath->product_id, wlist[i].product, 16) == 0) {
+ mypath->iopolicy = wlist[i].iopolicy;
+ if (!wlist[i].getuid(mypath->sg_dev, mypath->wwid))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+sysfsdevice2devname (char *devname, char *device)
+{
+ char sysfs_path[FILE_NAME_SIZE];
+ char block_path[FILE_NAME_SIZE];
+ char link_path[FILE_NAME_SIZE];
+ int r;
+
+ if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
+ fprintf(stderr, "[device] feature available with sysfs only\n");
+ exit (1);
+ }
+
+ sprintf(link_path, "%s%s/block", sysfs_path, device);
+
+ r = sysfs_get_link(link_path, block_path, FILE_NAME_SIZE);
+
+ if (r != 0)
+ return 1;
+
+ sysfs_get_name_from_path(block_path, devname, FILE_NAME_SIZE);
+
+ return 0;
+}
+
+
+static int
+devt2devname (char *devname, int major, int minor)
+{
+ struct sysfs_directory * sdir;
+ struct sysfs_directory * devp;
+ char sysfs_path[FILE_NAME_SIZE];
+ char block_path[FILE_NAME_SIZE];
+ char attr_path[FILE_NAME_SIZE];
+ char attr_value[16];
+ char attr_ref_value[16];
+
+ if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
+ fprintf(stderr, "-D feature available with sysfs only\n");
+ exit (1);
+ }
+
+ sprintf(attr_ref_value, "%i:%i\n", major, minor);
+ sprintf(block_path, "%s/block", sysfs_path);
+ sdir = sysfs_open_directory(block_path);
+ sysfs_read_directory(sdir);
+
+ dlist_for_each_data(sdir->subdirs, devp, struct sysfs_directory) {
+ sprintf(attr_path, "%s/%s/dev", block_path, devp->name);
+ sysfs_read_attribute_value(attr_path, attr_value, 16);
+
+ if (!strcmp(attr_value, attr_ref_value)) {
+ sprintf(attr_path, "%s/%s", block_path, devp->name);
+ sysfs_get_name_from_path(attr_path, devname, FILE_NAME_SIZE);
+ break;
+ }
+ }
+
+ sysfs_close_directory(sdir);
+
+ return 0;
+}
+
+static int
+blacklist (char * dev) {
+ int i;
+ static struct {
+ char * headstr;
+ int lengh;
+ } blist[] = {
+ {"cciss", 5},
+ {"hd", 2},
+ {"md", 2},
+ {"dm", 2},
+ {"sr", 2},
+ {"scd", 3},
+ {"ram", 3},
+ {"raw", 3},
+ {NULL, 0},
+ };
+
+ for (i = 0; blist[i].lengh; i++) {
+ if (strncmp(dev, blist[i].headstr, blist[i].lengh) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+devinfo (struct path *curpath)
+{
+ get_lun_strings(curpath->vendor_id,
+ curpath->product_id,
+ curpath->rev,
+ curpath->sg_dev);
+ get_serial(curpath->serial, curpath->sg_dev);
+ curpath->tur = do_tur(curpath->sg_dev);
+ if (!get_unique_id(curpath))
+ return 1;
+
+ return 0;
+}
+
+
+static int
+get_all_paths_sysfs(struct env * conf, struct path * all_paths)
+{
+ int k=0;
+ struct sysfs_directory * sdir;
+ struct sysfs_directory * devp;
+ struct sysfs_link * linkp;
+ char refwwid[WWID_SIZE];
+ char empty_buff[WWID_SIZE];
+ char buff[FILE_NAME_SIZE];
+ char path[FILE_NAME_SIZE];
+ struct path curpath;
+
+ memset(empty_buff, 0, WWID_SIZE);
+ memset(refwwid, 0, WWID_SIZE);
+
+ /* if called from hotplug, only consider the paths that relate
+ to the device pointed by conf.hotplugdev */
+
+ if (strncmp("/devices", conf->hotplugdev, 8) == 0) {
+ if (sysfsdevice2devname (buff, conf->hotplugdev))
+ return 0;
+
+ sprintf(curpath.sg_dev, "/dev/%s", buff);
+
+ if (devinfo(&curpath))
+ return 0;
+
+ strcpy(refwwid, curpath.wwid);
+ memset(&curpath, 0, sizeof(path));
+ }
+
+ /* if major/minor specified on the cmd line,
+ only consider affiliated paths */
+
+ if (conf->major >= 0 && conf->minor >= 0) {
+ if (devt2devname(buff, conf->major, conf->minor))
+ return 0;
+
+ sprintf(curpath.sg_dev, "/dev/%s", buff);
+
+ if (devinfo(&curpath))
+ return 0;
+
+ strcpy(refwwid, curpath.wwid);
+ memset(&curpath, 0, sizeof(path));
+ }
+
+
+ sprintf(path, "%s/block", conf->sysfs_path);
+ sdir = sysfs_open_directory(path);
+ sysfs_read_directory(sdir);
+
+ dlist_for_each_data(sdir->subdirs, devp, struct sysfs_directory) {
+ if (blacklist(devp->name))
+ continue;
+
+ sysfs_read_directory(devp);
+
+ if(devp->links == NULL)
+ continue;
+
+ dlist_for_each_data(devp->links, linkp, struct sysfs_link) {
+ if (!strncmp(linkp->name, "device", 6))
+ break;
+ }
+
+ if (linkp == NULL) {
+ continue;
+ }
+
+ basename(devp->path, buff);
+ sprintf(curpath.sg_dev, "/dev/%s", buff);
+
+ if(devinfo(&curpath)) {
+ memset(&curpath, 0, sizeof(path));
+ continue;
+ }
+
+ if (memcmp(empty_buff, refwwid, WWID_SIZE) != 0 &&
+ strncmp(curpath.wwid, refwwid, WWID_SIZE) != 0) {
+ memset(&curpath, 0, sizeof(path));
+ continue;
+ }
+
+ strcpy(all_paths[k].sg_dev, curpath.sg_dev);
+ strcpy(all_paths[k].dev, curpath.sg_dev);
+ strcpy(all_paths[k].wwid, curpath.wwid);
+ strcpy(all_paths[k].vendor_id, curpath.vendor_id);
+ strcpy(all_paths[k].product_id, curpath.product_id);
+ all_paths[k].iopolicy = curpath.iopolicy;
+ all_paths[k].tur = curpath.tur;
+
+ /* done with curpath, zero for reuse */
+ memset(&curpath, 0, sizeof(path));
+
+ basename(linkp->target, buff);
+ sscanf(buff, "%i:%i:%i:%i",
+ &all_paths[k].sg_id.host_no,
+ &all_paths[k].sg_id.channel,
+ &all_paths[k].sg_id.scsi_id,
+ &all_paths[k].sg_id.lun);
+ k++;
+ }
+ sysfs_close_directory(sdir);
+ return 0;
+}
+
+static int
+get_all_paths_nosysfs(struct env * conf, struct path * all_paths,
+ struct scsi_dev * all_scsi_ids)
+{
+ int k, i, fd;
+ char buff[FILE_NAME_SIZE];
+ char file_name[FILE_NAME_SIZE];
+
+ for (k = 0; k < conf->max_devs; k++) {
+ strcpy(file_name, "/dev/sg");
+ sprintf(buff, "%d", k);
+ strncat(file_name, buff, FILE_NAME_SIZE);
+ strcpy(all_paths[k].sg_dev, file_name);
+
+ get_lun_strings(all_paths[k].vendor_id,
+ all_paths[k].product_id,
+ all_paths[k].rev,
+ all_paths[k].sg_dev);
+ get_serial(all_paths[k].serial, all_paths[k].sg_dev);
+ if (!get_unique_id(&all_paths[k]))
+ continue;
+
+ if ((fd = open(all_paths[k].sg_dev, O_RDONLY)) < 0)
+ return 0;
+
+ if (0 > ioctl(fd, SG_GET_SCSI_ID, &(all_paths[k].sg_id)))
+ printf("device %s failed on sg ioctl, skip\n",
+ file_name);
+
+ close(fd);
+
+ for (i = 0; i < conf->max_devs; i++) {
+ if ((all_paths[k].sg_id.host_no ==
+ all_scsi_ids[i].host_no)
+ && (all_paths[k].sg_id.scsi_id ==
+ (all_scsi_ids[i].scsi_id.dev_id & 0xff))
+ && (all_paths[k].sg_id.lun ==
+ ((all_scsi_ids[i].scsi_id.dev_id >> 8) & 0xff))
+ && (all_paths[k].sg_id.channel ==
+ ((all_scsi_ids[i].scsi_id.
+ dev_id >> 16) & 0xff))) {
+ strcpy(all_paths[k].dev, all_scsi_ids[i].dev);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+get_all_scsi_ids(struct env * conf, struct scsi_dev * all_scsi_ids)
+{
+ int k, big, little, res, host_no, fd;
+ char buff[64];
+ char fname[FILE_NAME_SIZE];
+ struct scsi_idlun my_scsi_id;
+
+ for (k = 0; k < conf->max_devs; k++) {
+ strcpy(fname, "/dev/sd");
+ if (k < 26) {
+ buff[0] = 'a' + (char) k;
+ buff[1] = '\0';
+ strcat(fname, buff);
+ } else if (k <= 255) {
+ /* assumes sequence goes x,y,z,aa,ab,ac etc */
+ big = k / 26;
+ little = k - (26 * big);
+ big = big - 1;
+
+ buff[0] = 'a' + (char) big;
+ buff[1] = 'a' + (char) little;
+ buff[2] = '\0';
+ strcat(fname, buff);
+ } else
+ strcat(fname, "xxxx");
+
+ if ((fd = open(fname, O_RDONLY)) < 0) {
+ if (conf->verbose)
+ fprintf(stderr, "can't open %s. mknod ?",
+ fname);
+ continue;
+ }
+
+ res = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &my_scsi_id);
+ if (res < 0) {
+ close(fd);
+ printf("Could not get scsi idlun\n");
+ continue;
+ }
+
+ res = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
+ if (res < 0) {
+ close(fd);
+ printf("Could not get host_no\n");
+ continue;
+ }
+
+ close(fd);
+
+ strcpy(all_scsi_ids[k].dev, fname);
+ all_scsi_ids[k].scsi_id = my_scsi_id;
+ all_scsi_ids[k].host_no = host_no;
+ }
+ return 0;
+}
+
+/* print_path style */
+#define ALL 0
+#define NOWWID 1
+
+static void
+print_path(struct path * all_paths, int k, int style)
+{
+ if (style != NOWWID)
+ printf("%s ", all_paths[k].wwid);
+ else
+ printf(" \\_");
+ printf("(%i %i %i %i) ",
+ all_paths[k].sg_id.host_no,
+ all_paths[k].sg_id.channel,
+ all_paths[k].sg_id.scsi_id, all_paths[k].sg_id.lun);
+ if(0 != strcmp(all_paths[k].sg_dev, all_paths[k].dev))
+ printf("%s ", all_paths[k].sg_dev);
+ printf("%s ", all_paths[k].dev);
+ printf("[%.16s]\n", all_paths[k].product_id);
+}
+
+static void
+print_all_path(struct env * conf, struct path * all_paths)
+{
+ int k;
+ char empty_buff[WWID_SIZE];
+
+ memset(empty_buff, 0, WWID_SIZE);
+ for (k = 0; k < conf->max_devs; k++) {
+ if (memcmp(empty_buff, all_paths[k].wwid, WWID_SIZE) == 0)
+ continue;
+ print_path(all_paths, k, ALL);
+ }
+}
+
+static void
+print_all_mp(struct path * all_paths, struct multipath * mp, int nmp)
+{
+ int k, i;
+
+ for (k = 0; k <= nmp; k++) {
+ printf("%s\n", mp[k].wwid);
+ for (i = 0; i <= mp[k].npaths; i++)
+ print_path(all_paths, PINDEX(k,i), NOWWID);
+ }
+}
+
+static int
+coalesce_paths(struct env * conf, struct multipath * mp,
+ struct path * all_paths)
+{
+ int k, i, nmp, np, already_done;
+ char empty_buff[WWID_SIZE];
+
+ nmp = -1;
+ already_done = 0;
+ memset(empty_buff, 0, WWID_SIZE);
+
+ for (k = 0; k < conf->max_devs - 1; k++) {
+ /* skip this path for some reason */
+
+ /* 1. if path has no unique id */
+ if (memcmp(empty_buff, all_paths[k].wwid, WWID_SIZE) == 0)
+ continue;
+
+ /* 2. if mp with this uid already instanciated */
+ for (i = 0; i <= nmp; i++) {
+ if (0 == strcmp(mp[i].wwid, all_paths[k].wwid))
+ already_done = 1;
+ }
+ if (already_done) {
+ already_done = 0;
+ continue;
+ }
+
+ /* at this point, we know we really got a new mp */
+ np = 0;
+ nmp++;
+ strcpy(mp[nmp].wwid, all_paths[k].wwid);
+ PINDEX(nmp,np) = k;
+
+ if (mp[nmp].size == 0)
+ mp[nmp].size = get_disk_size(all_paths[k].dev);
+
+ for (i = k + 1; i < conf->max_devs; i++) {
+ if (0 == strcmp(all_paths[k].wwid, all_paths[i].wwid)) {
+ np++;
+ PINDEX(nmp,np) = i;
+ mp[nmp].npaths = np;
+ }
+ }
+ }
+ return nmp;
+}
+
+static void
+group_by_tur(struct multipath * mp, struct path * all_paths, char * str) {
+ int left_path_count = 0;
+ int right_path_count = 0;
+ int i;
+ char left_path_buff[FILE_NAME_SIZE], right_path_buff[FILE_NAME_SIZE];
+ char * left_path_buff_p = &left_path_buff[0];
+ char * right_path_buff_p = &right_path_buff[0];
+
+ for (i = 0; i <= mp->npaths; i++) {
+ if (all_paths[mp->pindex[i]].tur) {
+ left_path_buff_p += sprintf(left_path_buff_p, " %s", all_paths[mp->pindex[i]].dev);
+ left_path_count++;
+ } else {
+ right_path_buff_p += sprintf(right_path_buff_p, " %s", all_paths[mp->pindex[i]].dev);
+ right_path_count++;
+ }
+ }
+ if (!left_path_count)
+ sprintf(str, " 1 round-robin %i 0 %s", right_path_count, right_path_buff);
+ else if (!right_path_count)
+ sprintf(str, " 1 round-robin %i 0 %s", left_path_count, left_path_buff);
+ else
+ sprintf(str, " 2 round-robin %i 0 %s round-robin %i 0 %s",
+ left_path_count, left_path_buff,
+ right_path_count, right_path_buff);
+}
+
+static void
+group_by_serial(struct multipath * mp, struct path * all_paths, char * str) {
+ int path_count, pg_count = 0;
+ int i, k;
+ int * bitmap;
+ char path_buff[FILE_NAME_SIZE];
+ char pg_buff[FILE_NAME_SIZE];
+ char * path_buff_p = &path_buff[0];
+ char * pg_buff_p = &pg_buff[0];
+
+ /* init the bitmap */
+ bitmap = malloc((mp->npaths + 1) * sizeof(int));
+ memset(bitmap, 0, (mp->npaths + 1) * sizeof(int));
+
+ for (i = 0; i <= mp->npaths; i++) {
+ if (bitmap[i])
+ continue;
+
+ /* here, we really got a new pg */
+ pg_count++;
+ path_count = 1;
+ memset(&path_buff, 0, FILE_NAME_SIZE * sizeof(char));
+ path_buff_p = &path_buff[0];
+
+ path_buff_p += sprintf(path_buff_p, " %s", all_paths[mp->pindex[i]].dev);
+ bitmap[i] = 1;
+
+ for (k = i + 1; k <= mp->npaths; k++) {
+ if (bitmap[k])
+ continue;
+ if (0 == strcmp(all_paths[mp->pindex[i]].serial,
+ all_paths[mp->pindex[k]].serial)) {
+ path_buff_p += sprintf(path_buff_p, " %s", all_paths[mp->pindex[k]].dev);
+ bitmap[k] = 1;
+ path_count++;
+ }
+ }
+ pg_buff_p += sprintf(pg_buff_p, " round-robin %i 0%s",
+ path_count, path_buff);
+ }
+ sprintf(str, " %i%s", pg_count, pg_buff);
+ free(bitmap);
+}
+
+static int
+dm_simplecmd(int task, const char *name) {
+ int r = 0;
+ struct dm_task *dmt;
+
+ if (!(dmt = dm_task_create(task)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+
+ r = dm_task_run(dmt);
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int
+dm_addmap(int task, const char *name, const char *params, long size) {
+ struct dm_task *dmt;
+
+ if (!(dmt = dm_task_create(task)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, name))
+ goto addout;
+
+ if (!dm_task_add_target(dmt, 0, size, DM_TARGET, params))
+ goto addout;
+
+ if (!dm_task_run(dmt))
+ goto addout;
+
+ addout:
+ dm_task_destroy(dmt);
+ return 1;
+}
+
+static int
+setup_map(struct env * conf, struct path * all_paths,
+ struct multipath * mp, int index, int op)
+{
+ char params[255];
+ char * params_p;
+ int i, np;
+
+ /* defaults for multipath target */
+ char * dm_ps_name = "round-robin";
+ int dm_ps_nr_args = 0;
+
+ params_p = &params[0];
+
+ np = 0;
+ for (i=0; i<=mp[index].npaths; i++) {
+ if (0 == all_paths[PINDEX(index,i)].sg_id.scsi_type)
+ np++;
+ }
+
+ if (np < 1)
+ return 0;
+
+ if ((all_paths[PINDEX(index,0)].iopolicy == MULTIBUS &&
+ conf->iopolicy == -1) || conf->iopolicy == MULTIBUS) {
+ params_p += sprintf(params_p, " 1 %s %i %i",
+ dm_ps_name, np, dm_ps_nr_args);
+
+ for (i=0; i<=mp[index].npaths; i++) {
+ if (0 != all_paths[PINDEX(index,i)].sg_id.scsi_type)
+ continue;
+ params_p += sprintf(params_p, " %s",
+ all_paths[PINDEX(index,i)].dev);
+ }
+ }
+
+ if ((all_paths[PINDEX(index,0)].iopolicy == FAILOVER &&
+ conf->iopolicy == -1) || conf->iopolicy == FAILOVER) {
+ params_p += sprintf(params_p, " %i", mp[index].npaths + 1);
+ for (i=0; i<=mp[index].npaths; i++) {
+ if (0 != all_paths[PINDEX(index,i)].sg_id.scsi_type)
+ continue;
+ params_p += sprintf(params_p, " %s ",
+ dm_ps_name);
+ params_p += sprintf(params_p, "1 %i",
+ dm_ps_nr_args);
+ params_p += sprintf(params_p, " %s",
+ all_paths[PINDEX(index,i)].dev);
+ }
+ }
+
+ if ((all_paths[PINDEX(index,0)].iopolicy == GROUP_BY_SERIAL &&
+ conf->iopolicy == -1) || conf->iopolicy == GROUP_BY_SERIAL) {
+ group_by_serial(&mp[index], all_paths, params_p);
+ }
+
+ if ((all_paths[PINDEX(index,0)].iopolicy == GROUP_BY_TUR &&
+ conf->iopolicy == -1) || conf->iopolicy == GROUP_BY_TUR) {
+ group_by_tur(&mp[index], all_paths, params_p);
+ }
+
+ if (mp[index].size < 0)
+ return 0;
+
+ if (!conf->quiet) {
+ if (op == DM_DEVICE_RELOAD)
+ printf("U:");
+ if (op == DM_DEVICE_CREATE)
+ printf("N:");
+ printf("%s:0 %li %s %s\n",
+ mp[index].wwid, mp[index].size, DM_TARGET, params);
+ }
+
+ if (op == DM_DEVICE_RELOAD)
+ dm_simplecmd(DM_DEVICE_SUSPEND, mp[index].wwid);
+
+ dm_addmap(op, mp[index].wwid, params, mp[index].size);
+
+ if (op == DM_DEVICE_RELOAD)
+ dm_simplecmd(DM_DEVICE_RESUME, mp[index].wwid);
+
+ return 1;
+}
+
+static int
+map_present(char * str)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct dm_names *names;
+ unsigned next = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+ return 0;
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ if (!(names = dm_task_get_names(dmt)))
+ goto out;
+
+ if (!names->dev) {
+ goto out;
+ }
+
+ do {
+ if (0 == strcmp(names->name, str))
+ r = 1;
+ next = names->next;
+ names = (void *) names + next;
+ } while (next);
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static void
+signal_daemon (void)
+{
+ FILE *file;
+ pid_t pid;
+ char *buf;
+
+ buf = malloc (8);
+
+ file = fopen (PIDFILE, "r");
+
+ if (!file) {
+ fprintf(stderr, "cannot signal daemon, pidfile not found\n");
+ return;
+ }
+
+ buf = fgets (buf, 8, file);
+ fclose (file);
+
+ pid = (pid_t) atol (buf);
+ free (buf);
+
+ kill(pid, SIGHUP);
+}
+
+static int
+filepresent(char * run) {
+ struct stat buf;
+
+ if(!stat(run, &buf))
+ return 1;
+ return 0;
+}
+
+static void
+usage(char * progname)
+{
+ fprintf(stderr, VERSION_STRING);
+ fprintf(stderr, "Usage: %s [-v|-q] [-d] [-m max_devs]\n",
+ progname);
+ fprintf(stderr, " [-p failover|multibus|group_by_serial]\n" \
+ " [device]\n" \
+ "\n" \
+ "\t-v\t\tverbose, print all paths and multipaths\n" \
+ "\t-q\t\tquiet, no output at all\n" \
+ "\t-d\t\tdry run, do not create or update devmaps\n" \
+ "\t-m max_devs\tscan {max_devs} devices at most\n" \
+ "\n" \
+ "\t-p policy\tforce maps to specified policy :\n" \
+ "\t failover\t\t- 1 path per priority group\n" \
+ "\t multibus\t\t- all paths in 1 priority group\n" \
+ "\t group_by_serial\t- 1 priority group per serial\n" \
+ "\t group_by_tur\t\t- 1 priority group per TUR state\n" \
+ "\n" \
+ "\t-D maj min\tlimit scope to the device's multipath\n" \
+ "\t\t\t(major minor device reference)\n"
+ "\tdevice\t\tlimit scope to the device's multipath\n" \
+ "\t\t\t(hotplug-style $DEVPATH reference)\n"
+ );
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct multipath * mp;
+ struct path * all_paths;
+ struct scsi_dev * all_scsi_ids;
+ struct env conf;
+ int i, k, nmp;
+ int try = 0;
+
+ /* Don't run in parallel */
+ while (filepresent(RUN) && try++ < MAXTRY)
+ usleep(100000);
+
+ if (filepresent(RUN)) {
+ fprintf(stderr, "waited for to long. exiting\n");
+ exit (1);
+ }
+
+ /* Our turn */
+ if (!open(RUN, O_CREAT)) {
+ fprintf(stderr, "can't create runfile\n");
+ exit (1);
+ }
+
+ /* Default behaviour */
+ conf.max_devs = MAX_DEVS;
+ conf.dry_run = 0; /* 1 == Do not Create/Update devmaps */
+ conf.verbose = 0; /* 1 == Print all_paths and mp */
+ conf.quiet = 0; /* 1 == Do not even print devmaps */
+ conf.iopolicy = -1; /* Apply the defaults in get_unique_id() */
+ conf.major = -1;
+ conf.minor = -1;
+
+ for (i = 1; i < argc; ++i) {
+ if (0 == strcmp("-v", argv[i])) {
+ if (conf.quiet == 1)
+ usage(argv[0]);
+ conf.verbose = 1;
+ } else if (0 == strcmp("-m", argv[i])) {
+ conf.max_devs = atoi(argv[++i]);
+ if (conf.max_devs < 2)
+ usage(argv[0]);
+ } else if (0 == strcmp("-D", argv[i])) {
+ conf.major = atoi(argv[++i]);
+ conf.minor = atoi(argv[++i]);
+ } else if (0 == strcmp("-q", argv[i])) {
+ if (conf.verbose == 1)
+ usage(argv[0]);
+ conf.quiet = 1;
+ } else if (0 == strcmp("-d", argv[i]))
+ conf.dry_run = 1;
+ else if (0 == strcmp("-p", argv[i])) {
+ i++;
+ if (!strcmp(argv[i], "failover"))
+ conf.iopolicy = FAILOVER;
+ if (!strcmp(argv[i], "multibus"))
+ conf.iopolicy = MULTIBUS;
+ if (!strcmp(argv[i], "group_by_serial"))
+ conf.iopolicy = GROUP_BY_SERIAL;
+ if (!strcmp(argv[i], "group_by_tur"))
+ conf.iopolicy = GROUP_BY_TUR;
+ } else if (*argv[i] == '-') {
+ fprintf(stderr, "Unknown switch: %s\n", argv[i]);
+ usage(argv[0]);
+ } else
+ strncpy(conf.hotplugdev, argv[i], FILE_NAME_SIZE);
+ }
+
+ /* dynamic allocations */
+ mp = malloc(conf.max_devs * sizeof(struct multipath));
+ all_paths = malloc(conf.max_devs * sizeof(struct path));
+ all_scsi_ids = malloc(conf.max_devs * sizeof(struct scsi_dev));
+ if (mp == NULL || all_paths == NULL || all_scsi_ids == NULL)
+ exit(1);
+
+ if (sysfs_get_mnt_path(conf.sysfs_path, FILE_NAME_SIZE)) {
+ get_all_scsi_ids(&conf, all_scsi_ids);
+ get_all_paths_nosysfs(&conf, all_paths, all_scsi_ids);
+ } else {
+ get_all_paths_sysfs(&conf, all_paths);
+ }
+
+ nmp = coalesce_paths(&conf, mp, all_paths);
+
+ if (conf.verbose) {
+ print_all_path(&conf, all_paths);
+ fprintf(stdout, "\n");
+ print_all_mp(all_paths, mp, nmp);
+ fprintf(stdout, "\n");
+ }
+
+ if (conf.dry_run)
+ exit(0);
+
+ for (k=0; k<=nmp; k++) {
+ if (map_present(mp[k].wwid)) {
+ setup_map(&conf, all_paths, mp, k, DM_DEVICE_RELOAD);
+ } else {
+ setup_map(&conf, all_paths, mp, k, DM_DEVICE_CREATE);
+ }
+ }
+
+ /* signal multipathd that new devmaps may have come up */
+ signal_daemon();
+
+ /* free allocs */
+ free(mp);
+ free(all_paths);
+ free(all_scsi_ids);
+
+ /* release runfile */
+ unlink(RUN);
+
+ exit(0);
+}