When building an initrd for mulitpath root, do not hardcode the wwid
into the initrd.

Instead add a command to nash that uses the root LABEL or UUID on the
kernel command line to find a slave node, and extracts the wwid from
that in the same manner as multipathd (i.e. using /sbin/scsi_id).


diff -r 8efe3223aca6 nash/block.c
--- a/nash/block.c	Wed Feb 17 14:23:58 2010 +0000
+++ b/nash/block.c	Thu Feb 18 10:32:28 2010 +0000
@@ -454,6 +454,24 @@
     return path;
 }
 
+char *
+nashFindSysfsByDevno(nashContext *c, dev_t devno)
+{
+    nashBdevIter biter;
+    nashBdev dev = NULL;
+    char *path = NULL;
+
+    biter = nashBdevIterNew(c, "/sys/block");
+    while (nashBdevIterNext(biter, &dev) >= 0) {
+        if (dev->devno == devno) {
+            path = strdup(dev->sysfs_path);
+            break;
+        }
+    }
+    nashBdevIterEnd(&biter);
+    return path;
+}
+
 static char *
 block_find_fs_by_keyvalue(nashContext *c, const char *key, const char *value)
 {
diff -r 8efe3223aca6 nash/include/nash/block.h
--- a/nash/include/nash/block.h	Wed Feb 17 14:23:58 2010 +0000
+++ b/nash/include/nash/block.h	Thu Feb 18 10:32:28 2010 +0000
@@ -30,6 +30,7 @@
 extern char *nashFindFsByUUID(nashContext *, const char *uuid);
 extern char *nashFindFsByName(nashContext *, const char *name);
 extern char *nashFindDeviceByDevno(nashContext *, dev_t devno);
+extern char *nashFindSysfsByDevno(nashContext *c, dev_t devno);
 
 extern int nashParseSysfsDevno(const char *path, dev_t *dev);
 
diff -r 8efe3223aca6 nash/nash.c
--- a/nash/nash.c	Wed Feb 17 14:23:58 2010 +0000
+++ b/nash/nash.c	Thu Feb 18 10:32:28 2010 +0000
@@ -1565,6 +1565,152 @@
     return nashMkPathBySpec(_nash_context, root, "/dev/root") < 0 ? 1 : 0;
 }
 
+/* XenServer: getrootwwid finds a slave device using the
+ * root=LABEL or root=UUID option on the kernel command line,
+ * and then uses /sbin/scsi_id to translate this into a multipath-tools
+ * World Wide IDentifier.
+ * It can optionally then append the wwid to a callback command and
+ * exec that.
+ */
+static int
+getrootwwidCommand(char *cmd, char *end)
+{
+    char *chptr = NULL, *root;
+	char *callout = NULL;
+	char *node = NULL;
+    struct stat sb;
+    char *sysfs_dir;
+    char *sysfs_dir_no_mountpoint;
+    int i;
+	char *args[128];
+	pid_t pid;
+	int wpid;
+	int status = 0;
+	int pipefd[2];
+	char buf[256];
+
+    root = getKernelArg("root");
+    if (root) {
+        char c;
+        chptr = root;
+        while (*chptr && !isspace(*chptr))
+            chptr++;
+        c = *chptr;
+        *chptr = '\0';
+        root = strdupa(root);
+        *chptr = c;
+        chptr = NULL;
+    }
+
+    i = 0;
+    while ((cmd = getArg(cmd, end, &chptr))) {
+        if (!strcmp(chptr, "--exec")) {
+            cmd = getArg(cmd, end, &callout);
+            if (!cmd) {
+                eprintf("getrootwwid: expected callout\n");
+                return 1;
+            }
+		} else if (root) {
+            if (i) {
+                eprintf("getrootwwid: unexpected arguments\n");
+                eprintf("cmd: %p end: %p\n", cmd, end);
+                eprintf("cmd: %s\n", cmd);
+                return 1;
+            }
+            /* if we get here, we got root from the kernel command line,
+               so we don't _really_ care that there wasn't one on the
+               getrootwwid command line. */
+            i++;
+        } else {
+            root = chptr;
+            i++;
+        }
+    }
+    if (!root) {
+        eprintf("getrootwwid: expected device name\n");
+        return 1;
+    }
+
+    node = nashGetPathBySpec(_nash_context, root);
+	if (!node || stat(node, &sb) < 0) {
+        eprintf("getrootwwid: Could not find node for root disk\n");
+        return 1;
+    }
+
+    if (!(sysfs_dir = nashFindSysfsByDevno(NULL, sb.st_rdev))) {
+        eprintf("getrootwwid: Could not find sysfs node for root disk\n");
+        return 1;
+    }
+
+	/* remove mountpoint prefix, i.e. "/sys" */
+	sysfs_dir_no_mountpoint = strdupa(sysfs_dir + 4);
+	free (sysfs_dir);
+	
+	/* create Pipe for getting output of scsi_id */
+	if (pipe(pipefd) == -1) {
+		eprintf("getrootwwid: failed to create pipe\n");
+		return 1;
+	}
+	
+	if (!(pid = fork())) {
+            /* child */
+		close(pipefd[0]);  /* close read end */
+		close(1);
+		dup(pipefd[1]);    /* replace stdout with pipe write end */
+		args[0] = "/sbin/scsi_id";
+		args[1] = "-g";
+		args[2] = "-u";
+		args[3] = "-s";
+		args[4] = sysfs_dir_no_mountpoint;
+		args[5] = NULL;
+		execve(args[0], args, env);
+		eprintf("ERROR: failed in exec of %s: %m\n", args[0]);
+		return 1;
+	}
+
+	close(pipefd[1]); /* close write end */
+	wpid = waitpid(pid, &status, 0);
+
+	if (read(pipefd[0], buf, sizeof(buf)) <= 0) {
+		eprintf("ERROR: failed to read scsi_id output\n");
+		return 1;
+	}
+	if ((chptr = strchr(buf, '\n')))
+		*chptr = 0;
+
+	close(pipefd[0]); /* close read end */
+
+	if (!callout) {
+		printf("%s\n", buf); 
+	} else {
+		char ** nextArg = args;
+		char *co_cmd = callout;
+		char *co_end = callout + strlen(callout);
+		
+		while (co_cmd && co_cmd < co_end) {
+			co_cmd = getArg(co_cmd, co_end, nextArg);
+			nextArg++;
+		}
+		*nextArg++ = buf; /* Add the uid to the end of the command a la 'dmsetup ls --exec' */
+		*nextArg = NULL;
+
+        if (!(pid = fork())) {
+            /* child */
+            execve(args[0], args, env);
+            eprintf("ERROR: failed in exec of %s: %m\n", args[0]);
+            return 1;
+        }
+		
+		wpid = waitpid(pid, &status, 0);
+		if (wpid == -1) {
+			eprintf("ERROR: Failed to wait for process %d: %m\n", wpid);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
 static int
 mkdirCommand(char * cmd, char * end)
 {
@@ -2525,6 +2671,7 @@
     { "mkdmnod", mkDMNodCommand },
     { "mknod", mknodCommand },
     { "mkrootdev", mkrootdevCommand },
+    { "getrootwwid", getrootwwidCommand },
     { "mount", mountCommand },
     { "netname", netnameCommand },
     { "network", networkCommand },
--- ./mkinitrd.orig	2010-02-18 05:18:28.000000000 -0500
+++ ./mkinitrd	2010-02-18 05:32:15.000000000 -0500
@@ -1968,12 +1968,26 @@ emit_modules $MODULES
 #fi
 
 if [ "$use_multipath" == "1" ]; then
-    emit "echo Creating multipath devices"
-    emit "/bin/multipath -v 0"
-    if [ "$use_xdr" == "1" ]; then
-        emit "echo Setting up XDR dm device"
-        emit "/bin/multipath -v 0"
-    fi
+    # XenServer: The original mkinitrd code below creates all possible 
+    # multipath nodes.  We want to create only the one corresponding
+    # to the root disk to speed up boot time and reduce risk of failure.
+    # Also these other disks may correspond to SRs that we don't want
+    # multipathed.
+
+#    emit "echo Creating multipath devices"
+#    emit "/bin/multipath -v 0"
+#    if [ "$use_xdr" == "1" ]; then
+#        emit "echo Setting up XDR dm device"
+#        emit "/bin/multipath -v 0"
+#    fi
+
+    # XenServer: getrootwwid is a nash extension that finds a
+    # slave device using the root=LABEL or root=UUID option on the
+    # kernel command line, and then uses /sbin/scsi_id to translate
+    # this into a multipath-tools World Wide IDentifier.
+    # The wwid is then appended to the callback command and exec'd.
+    emit "getrootwwid --exec '/sbin/multipath -v 0 '"
+
     emit "dmsetup ls --target multipath --exec 'kpartx -a -p p'"
 fi
 
