xref: /titanic_52/usr/src/cmd/stmsboot/stmsboot_util.c (revision 148434217c040ea38dc844384f6ba68d9b325906)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <stropts.h>
36 #include <strings.h>
37 #include <sys/param.h>
38 #include <libdevinfo.h>
39 #include <locale.h>
40 #include <libintl.h>
41 #include <devid.h>
42 #include <sys/libdevid.h>
43 #include <sys/modctl.h> /* for MAXMODCONFNAME */
44 #include <sys/scsi/adapters/scsi_vhci.h>
45 
46 /*
47  * SAVE_DIR is the directory in which system files are saved.
48  * SAVE_DIR must be under the root filesystem, as this program is
49  * typically run before any other filesystems are mounted.
50  */
51 #define	SAVE_DIR	"/etc/mpxio"
52 #define	VHCI_CTL_NODE	"/devices/scsi_vhci:devctl"
53 
54 /* nvlist property names, these are ALL string types */
55 #define	NVL_DEVID 	"nvl-devid"
56 #define	NVL_PATH 	"nvl-path"
57 #define	NVL_PHYSPATH	"nvl-physpath"
58 #define	NVL_MPXPATH	"nvl-mpxiopath"
59 #define	NVL_MPXEN	"nvl-mpxioenabled"
60 
61 #define	MPX_LIST		0x01
62 #define	MPX_MAP			0x02
63 #define	MPX_CAPABLE_CTRL	0x04
64 #define	MPX_INIT		0x08
65 #define	MPX_PHYSICAL		0x10
66 #define	MPX_BOOTPATH		0x20
67 #define	MPX_UPDATEVFSTAB	0x40
68 #define	MPX_USAGE		0x80
69 #define	MSG_INFO		0x01
70 #define	MSG_ERROR		0x02
71 #define	MSG_PANIC		0x04
72 
73 #define	BOOT			0x01
74 #define	NONBOOT			0x00
75 
76 static di_node_t devinfo_root = DI_NODE_NIL;
77 static char *ondiskname = "/etc/mpxio/devid_path.cache";
78 
79 /*
80  * We use devid-keyed nvlists to keep track of the guid, traditional and
81  * MPxIO-enabled /dev/rdsk paths. Each of these nvlists is eventually
82  * added to our global nvlist and our on-disk nvlist.
83  */
84 static nvlist_t *mapnvl;
85 static int mpxenabled = 0;
86 static int limctrl = -1;
87 static int mpxprop = 0;
88 static int guid = 0;
89 static char *drvlimit;
90 static int globarg = 0;
91 static int debugflag = 0;
92 static char *devicep;
93 static int readonlyroot = 0;
94 static int cap_N_option = 0;
95 
96 static void print_mpx_capable(di_node_t curnode);
97 static int popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl,
98     char *strdevid);
99 static int mpxio_nvl_boilerplate(di_node_t curnode);
100 static int validate_devnvl();
101 static void report_map(char *argdev, int physpath);
102 static void list_devs(int listguids, int ctrl);
103 static void logmsg(int level, const char *msg, ...);
104 static char *find_link(di_node_t cnode);
105 static void usage();
106 static void parse_args(int argc, char *argv[]);
107 static void get_devid(di_node_t node, ddi_devid_t *thisdevid);
108 static int print_bootpath();
109 static void vhci_to_phci(char *devpath, char *physpath);
110 static int update_vfstab();
111 
112 int
113 main(int argc, char **argv)
114 {
115 	struct stat cachestat;
116 	int mapfd = 0;
117 	int rv = 0;
118 	char *ondiskbuf;
119 	size_t newsz = 0;
120 
121 	parse_args(argc, argv);
122 	errno = 0;
123 	devinfo_root = di_init("/", DINFOCPYALL|DINFOFORCE);
124 	logmsg(MSG_INFO, "errno = %d after "
125 	    "di_init(/,DINFOCPYALL|DINFOFORCE)\n", errno);
126 	if (devinfo_root == NULL) {
127 		logmsg(MSG_ERROR,
128 		    gettext("Unable to take device tree snapshot "
129 		    "(%s: %d)\n"), strerror(errno), errno);
130 		return (-1);
131 	}
132 	logmsg(MSG_INFO, "opened root di_node\n");
133 
134 	if (globarg == MPX_CAPABLE_CTRL) {
135 		/* we just want to find MPxIO-capable controllers and exit */
136 		if (drvlimit != NULL) {
137 			print_mpx_capable(di_drv_first_node(drvlimit,
138 			    devinfo_root));
139 		} else {
140 			print_mpx_capable(di_drv_first_node("fp",
141 			    devinfo_root));
142 			print_mpx_capable(di_drv_first_node("mpt",
143 			    devinfo_root));
144 		}
145 		di_fini(devinfo_root);
146 		return (0);
147 	}
148 
149 	mapfd = open(ondiskname, O_RDWR|O_CREAT|O_SYNC, S_IRUSR | S_IWUSR);
150 	if (mapfd < 0) {
151 		/* we could be in single-user, so try for RO */
152 		if ((mapfd = open(ondiskname, O_RDONLY)) < 0) {
153 			logmsg(MSG_ERROR,
154 			    gettext("Unable to open or create %s:%s\n"),
155 			    ondiskname, strerror(errno));
156 			return (errno);
157 		}
158 		readonlyroot = 1;
159 	}
160 
161 	if (stat(ondiskname, &cachestat) != 0) {
162 		logmsg(MSG_ERROR,
163 		    gettext("Unable to stat() %s: %s\n"),
164 		    ondiskname, strerror(errno));
165 		return (errno);
166 	}
167 	ondiskbuf = calloc(1, cachestat.st_size);
168 	if (ondiskbuf == NULL) {
169 		logmsg(MSG_ERROR,
170 		    gettext("Unable to allocate memory for the devid "
171 		    "cache file: %s\n"), strerror(errno));
172 		return (errno);
173 	}
174 	rv = read(mapfd, ondiskbuf, cachestat.st_size);
175 	if (rv != cachestat.st_size) {
176 		logmsg(MSG_ERROR,
177 		    gettext("Unable to read all of devid cache file (got %d "
178 		    "from expected %d bytes): %s\n"),
179 		    rv, cachestat.st_size, strerror(errno));
180 		return (errno);
181 	}
182 	errno = 0;
183 	rv = nvlist_unpack(ondiskbuf, cachestat.st_size, &mapnvl, 0);
184 	if (rv) {
185 		logmsg(MSG_INFO,
186 		    "Unable to unpack devid cache file %s: %s (%d)\n",
187 		    ondiskname, strerror(rv), rv);
188 		if (nvlist_alloc(&mapnvl, NV_UNIQUE_NAME, 0) != 0) {
189 			logmsg(MSG_ERROR,
190 			    gettext("Unable to allocate root property"
191 			    "list\n"));
192 			return (errno);
193 		}
194 	}
195 	free(ondiskbuf);
196 
197 	if (validate_devnvl() < 0) {
198 		logmsg(MSG_ERROR,
199 		    gettext("unable to validate kernel with on-disk devid "
200 		    "cache file\n"));
201 		return (errno);
202 	}
203 
204 	/*
205 	 * If we're in single-user mode or maintenance mode, we won't
206 	 * necessarily have a writable root device (ZFSroot; ufs root is
207 	 * different in that we _do_ have a writable root device.
208 	 * This causes problems for the devlink calls (see
209 	 * $SRC/lib/libdevinfo/devinfo_devlink.c) and we do not try to
210 	 * write out the devnvl if root is readonly.
211 	 */
212 	if (!readonlyroot) {
213 		rv = nvlist_size(mapnvl, &newsz, NV_ENCODE_NATIVE);
214 		if (rv) {
215 			logmsg(MSG_ERROR,
216 			    gettext("Unable to determine size of packed "
217 			    "on-disk devid cache file %s: %s (%d).\n"),
218 			    ondiskname, strerror(rv), rv);
219 			logmsg(MSG_ERROR, gettext("Terminating\n"));
220 			nvlist_free(mapnvl);
221 			(void) close(mapfd);
222 			return (rv);
223 		}
224 
225 		if ((ondiskbuf = calloc(1, newsz)) == NULL) {
226 			logmsg(MSG_ERROR,
227 			    "Unable to allocate space for writing out new "
228 			    "on-disk devid cache file: %s\n", strerror(errno));
229 			(void) close(mapfd);
230 			nvlist_free(mapnvl);
231 			return (errno);
232 		}
233 
234 		rv = nvlist_pack(mapnvl, &ondiskbuf, &newsz,
235 		    NV_ENCODE_NATIVE, 0);
236 		if (rv) {
237 			logmsg(MSG_ERROR,
238 			    gettext("Unable to pack on-disk devid cache "
239 			    "file: %s (%d)\n"), strerror(rv), rv);
240 			(void) close(mapfd);
241 			free(ondiskbuf);
242 			nvlist_free(mapnvl);
243 			return (rv);
244 		}
245 
246 		rv = lseek(mapfd, 0, 0);
247 		if (rv == -1) {
248 			logmsg(MSG_ERROR,
249 			    gettext("Unable to seek to start of devid cache "
250 			    "file: %s (%d)\n"), strerror(errno), errno);
251 			(void) close(mapfd);
252 			free(ondiskbuf);
253 			nvlist_free(mapnvl);
254 			return (-1);
255 		}
256 
257 		if (write(mapfd, ondiskbuf, newsz) != newsz) {
258 			logmsg(MSG_ERROR,
259 			    gettext("Unable to completely write out "
260 			    "on-disk devid cache file: %s\n"), strerror(errno));
261 			(void) close(mapfd);
262 			nvlist_free(mapnvl);
263 			free(ondiskbuf);
264 			return (errno);
265 		}
266 	} /* !readonlyroot */
267 
268 	/* Now we can process the command line args */
269 	if (globarg == MPX_PHYSICAL) {
270 		report_map(devicep, BOOT);
271 	} else if (globarg == MPX_BOOTPATH) {
272 		rv = print_bootpath();
273 		di_fini(devinfo_root);
274 		return (rv);
275 	} else if (globarg == MPX_UPDATEVFSTAB) {
276 		rv = update_vfstab();
277 		di_fini(devinfo_root);
278 		return (rv);
279 	} else if (globarg != MPX_INIT) {
280 		if (globarg & MPX_LIST)
281 			list_devs(guid, limctrl);
282 
283 		if (globarg == MPX_MAP)
284 			report_map(devicep, NONBOOT);
285 	} else {
286 		logmsg(MSG_INFO, "\nprivate devid cache file initialised\n");
287 	}
288 
289 	nvlist_free(mapnvl);
290 	di_fini(devinfo_root);
291 	return (0);
292 }
293 
294 static void
295 usage()
296 {
297 	(void) fprintf(stderr,
298 	    gettext("usage: stmsboot_util -b | -m devname | "
299 	    "-l <ctrl> | -L | [-g] | -n | -N | -i | -p devname\n"));
300 	(void) fprintf(stderr, "\n\n");
301 	(void) fprintf(stderr, gettext("\t-h\tprint this usage message\n"));
302 	(void) fprintf(stderr, gettext("\t-b\tretrieve the system's bootpath "
303 	    "setting\n"));
304 	(void) fprintf(stderr, gettext("\t-m devname\n"));
305 	(void) fprintf(stderr, gettext("\t\tReports the current mapping for "
306 	    "devname\n"));
307 	(void) fprintf(stderr, gettext("\t-g\tprint the GUID for MPxIO-capable "
308 	    "devices. This\n"));
309 	(void) fprintf(stderr, gettext("\t\toption is only valid with the -L "
310 	    "or -l options\n"));
311 	(void) fprintf(stderr, gettext("\t-L | -l <ctrl>\n"));
312 	(void) fprintf(stderr, gettext("\t\tList the 'native' to 'MPxIO' "
313 	    "device mappings. If <ctrl>\n"));
314 	(void) fprintf(stderr, gettext("\t\tis specified, only print mappings "
315 	    "for those devices\n"));
316 	(void) fprintf(stderr, gettext("\t\tattached via the specified "
317 	    "controller.\n"));
318 	(void) fprintf(stderr, gettext("\t-i\tinitialise the private devid "
319 	    "cache file and exit\n"));
320 	(void) fprintf(stderr, gettext("\t\tThis option excludes all "
321 	    "others.\n"));
322 	(void) fprintf(stderr, gettext("\t-n\tprint the devfs paths for "
323 	    "multipath-capable\n"));
324 	(void) fprintf(stderr, gettext("\t\tcontroller ports.\n"));
325 	(void) fprintf(stderr, gettext("\t-N\tprint the device aliases of "
326 	    "multipath-capable\n"));
327 	(void) fprintf(stderr, gettext("\t\tcontroller ports.\n"));
328 	(void) fprintf(stderr, gettext("\t-p\tdevname\n"));
329 	(void) fprintf(stderr, gettext("\t\tThis option provides the physical "
330 	    "devfs path for\n"));
331 	(void) fprintf(stderr, gettext("\t\ta specific device (devname). Used "
332 	    "to set the bootpath\n"));
333 	(void) fprintf(stderr, gettext("\t\tvariable on x86/x64 systems\n"));
334 	(void) fprintf(stderr, gettext("\t-u\ttranslates device mappings in "
335 	    "/etc/vfstab as \n"));
336 	(void) fprintf(stderr, gettext("\t\trequired. The output is written "
337 	    "to /etc/mpxio/vfstab.new\n\n"));
338 	exit(2);
339 }
340 
341 static void
342 parse_args(int argc, char *argv[])
343 {
344 	char opt;
345 
346 	if (argc == 1)
347 		usage();
348 
349 	/*
350 	 * -b	prints the bootpath property
351 	 * -d	turns on debug mode for this utility (copious output!)
352 	 * -D drvname
353 	 *	if supplied, indicates that we're going to operate on
354 	 *	devices attached to this driver.
355 	 * -g	if (-l or -L), prints guids for devices rather than paths
356 	 * -h	prints the usage() help text.
357 	 * -i	initialises the cache file and exits.
358 	 * -l controller
359 	 *	list non-STMS to STMS device name mappings for the specific
360 	 *	controller, when MPxIO is enabled only.
361 	 * -L	list non-STMS to STMS device name mappings for all controllers
362 	 *	when MPxIO is enabled only.
363 	 * -m devname
364 	 *	prints the device path (/dev/rdsk) that devname maps to
365 	 *	in the currently-running system.
366 	 * -n
367 	 *	if supplied, returns name of STMS-capable controller nodes.
368 	 *	If the -D drvname option is specified as well, we only report
369 	 *	nodes attached with drvname.
370 	 * -N
371 	 *	same as the -n option, except that we only print the
372 	 *	node-name (dev_info :: devi_node_name). Multiple instances
373 	 *	through the libdevinfo snapshot are uniqified and separated
374 	 *	by the "|" character for direct use by egrep(1).
375 	 * -p devname
376 	 *	prints the physical devfs path for devname. Only used to
377 	 *	determine the bootpath.
378 	 * -u
379 	 *	remaps devices in /etc/vfstab, saving the newly generated
380 	 *	file to /etc/mpxio/vfstab.new. If we have any remapped
381 	 *	devices, exit with status 0, otherwise -1 for error.
382 	 */
383 	while ((opt = getopt(argc, argv, "bdD:ghil:Lm:nNp:u")) != EOF) {
384 		switch (opt) {
385 		case 'b':
386 			globarg = MPX_BOOTPATH;
387 			break;
388 		case 'd':
389 			debugflag = 1;
390 			break;
391 		case 'D':
392 			if ((drvlimit = calloc(1, MAXMODCONFNAME)) == NULL) {
393 				logmsg(MSG_ERROR,
394 				    gettext("Unable to allocate memory for a "
395 				    "driver name: %s\n"), strerror(errno));
396 				exit(errno);
397 			}
398 			bcopy(optarg, drvlimit, strlen(optarg));
399 			/* update this if adding support for a new driver */
400 			if ((strncmp(drvlimit, "fp", 2) == NULL) &&
401 			    (strncmp(drvlimit, "mpt", 3) == NULL)) {
402 				logmsg(MSG_ERROR,
403 				    gettext("invalid parent driver (%s) "
404 				    "specified"), drvlimit);
405 				usage();
406 			}
407 			break;
408 		case 'h':
409 			/* Just drop out and print the usage() output */
410 			globarg = MPX_USAGE;
411 			break;
412 		case 'i':
413 			globarg = MPX_INIT;
414 			break;
415 		case 'l':
416 			globarg |= MPX_LIST;
417 			limctrl = (int)atol(optarg);
418 			if (limctrl < 0) {
419 				logmsg(MSG_INFO,
420 				    gettext("invalid controller number "
421 				    "(%d), checking all controllers\n"),
422 				    limctrl);
423 			}
424 			break;
425 		case 'L':
426 			globarg |= MPX_LIST;
427 			break;
428 		case 'g':
429 			guid = 1;
430 			break;
431 		case 'm':
432 			globarg = MPX_MAP;
433 			if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
434 				logmsg(MSG_ERROR,
435 				    gettext("Unable to allocate space for a "
436 				    "device name\n"));
437 				exit(errno);
438 			}
439 			devicep = strdup(optarg);
440 			break;
441 		case 'N':
442 			cap_N_option = 1;
443 			globarg = MPX_CAPABLE_CTRL;
444 			break;
445 		case 'n':
446 			globarg = MPX_CAPABLE_CTRL;
447 			break;
448 		case 'p':
449 			globarg = MPX_PHYSICAL;
450 			if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
451 				logmsg(MSG_ERROR,
452 				    gettext("Unable to allocate space for a "
453 				    "device name\n"));
454 				exit(errno);
455 			}
456 			devicep = strdup(optarg);
457 			break;
458 		case 'u':
459 			globarg = MPX_UPDATEVFSTAB;
460 			break;
461 		default:
462 			logmsg(MSG_ERROR,
463 			    gettext("Invalid command line option (%c)\n"),
464 			    opt);
465 			usage();
466 		}
467 	}
468 
469 	if ((globarg >= MPX_USAGE) || (guid && (globarg != MPX_LIST)))
470 		usage();
471 
472 	if ((drvlimit != NULL) &&
473 	    ((globarg != MPX_LIST) &&
474 	    (globarg != MPX_CAPABLE_CTRL)))
475 		usage();
476 }
477 
478 static void
479 logmsg(int level, const char *msg, ...)
480 {
481 	va_list ap;
482 
483 	if ((level >= MSG_ERROR) ||
484 	    ((debugflag > 0) && (level >= MSG_INFO))) {
485 		(void) fprintf(stdout, "stmsboot: ");
486 		va_start(ap, msg);
487 		(void) vfprintf(stdout, msg, ap);
488 		va_end(ap);
489 	}
490 }
491 
492 /*
493  * It's up to the caller to do any sorting or pretty-printing of the device
494  * mappings we report. Since we're storing the device links as just the cXtYdZ
495  * part, we'll add /dev/rdsk/ back on when we print the listing so we maintain
496  * compatibility with previous versions of this tool. There's a little bit
497  * of footwork involved to make sure that we show all the paths to a device
498  * rather than just the first one we stashed away.
499  */
500 static void
501 list_devs(int listguids, int ctrl)
502 {
503 	nvlist_t *thisdevnvl;
504 	nvpair_t *pair;
505 	char *diskpath, *livepath, *key, *querydev;
506 	char *matchctrl = NULL;
507 	char checkctrl[MAXPATHLEN];
508 	int rv;
509 
510 	if (!mpxenabled) {
511 		if (mpxprop) {
512 			logmsg(MSG_ERROR, gettext("MPXIO disabled\n"));
513 		} else {
514 			logmsg(MSG_ERROR, gettext("No STMS devices have "
515 			    "been found\n"));
516 		}
517 		return;
518 	}
519 
520 	if (listguids) {
521 		(void) printf(gettext("non-STMS device name\t\t\tGUID\n"
522 		    "------------------------------------------"
523 		    "------------------------\n"));
524 	} else {
525 		(void) printf(gettext("non-STMS device name\t\t\t"
526 		    "STMS device name\n"
527 		    "------------------------------------------"
528 		    "------------------------\n"));
529 	}
530 
531 	bzero(checkctrl, MAXPATHLEN);
532 	pair = NULL;
533 	while ((pair = nvlist_next_nvpair(mapnvl, pair))
534 	    != NULL) {
535 		boolean_t livescsivhcip = B_FALSE;
536 
537 		if ((((rv = nvpair_value_string(pair, &querydev)) < 0) ||
538 		    ((key = nvpair_name(pair)) == NULL)) ||
539 		    ((strstr(key, "/pci") != NULL) ||
540 		    (strstr(key, "/sbus") != NULL) ||
541 		    (strstr(key, "/scsi_vhci") != NULL) ||
542 		    (strncmp(key, "id1", 3) == 0))) {
543 			logmsg(MSG_INFO,
544 			    "list_devs: rv = %d; (%s) is not a devlink, "
545 			    "continuing.\n", rv,
546 			    (key != NULL) ? key : "null");
547 			querydev = NULL;
548 			continue;
549 		}
550 
551 		(void) nvlist_lookup_nvlist(mapnvl, querydev, &thisdevnvl);
552 		(void) nvlist_lookup_boolean_value(thisdevnvl, NVL_MPXEN,
553 		    &livescsivhcip);
554 		(void) nvlist_lookup_string(thisdevnvl, NVL_MPXPATH,
555 		    &livepath);
556 
557 		if ((!livescsivhcip) ||
558 		    (livescsivhcip &&
559 		    (strncmp(key, livepath, strlen(key)) == 0)))
560 			continue;
561 
562 		(void) nvlist_lookup_string(thisdevnvl, NVL_PATH,
563 		    &diskpath);
564 
565 		logmsg(MSG_INFO,
566 		    "list_devs: %s :: %s ::%s :: MPXEN (%s)\n",
567 		    key, diskpath, livepath,
568 		    ((livescsivhcip) ? "TRUE" : "FALSE"));
569 
570 		if (ctrl > -1) {
571 			(void) sprintf(checkctrl, "c%dt", ctrl);
572 			matchctrl = strstr(key, checkctrl);
573 			if (matchctrl == NULL)
574 				continue;
575 		}
576 		if (listguids != 0) {
577 			char *tempguid;
578 			ddi_devid_t curdevid;
579 			int rv;
580 
581 			rv = devid_str_decode(querydev, &curdevid, NULL);
582 			if (rv == -1) {
583 				logmsg(MSG_INFO, "Unable to decode devid %s\n",
584 				    key);
585 				continue;
586 			}
587 			tempguid = devid_to_guid(curdevid);
588 			if (tempguid != NULL)
589 				(void) printf("/dev/rdsk/%s\t%s\n",
590 				    diskpath, tempguid);
591 
592 			devid_free_guid(tempguid);
593 			devid_free(curdevid);
594 			continue;
595 		}
596 
597 		(void) printf("/dev/rdsk/%s\t/dev/rdsk/%s\n",
598 		    (strstr(key, diskpath) == NULL) ? key : diskpath,
599 		    livepath);
600 	}
601 }
602 
603 /*
604  * We get passed a device name which we search the mapnvl for. If we find
605  * it, we print the mapping as it is found. It is up to the caller of this
606  * utility to do any pretty-printing of the results. If a device listed on
607  * the command line does not exist in the mapnvl, then we print NOT_MAPPED.
608  * Otherwise we print the command-line device name as it maps to what is
609  * stashed in the mapnvl - even if that's a "no change" device mapping.
610  *
611  * Example output (-p maps to physpath=BOOT)
612  * # /lib/mpxio/stmsboot_util -p \
613  *	/pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a
614  * /scsi_vhci/disk@g500000e011e17720:a
615  *
616  * Or the reverse:
617  * # /lib/mpxio/stmsboot_util -p /scsi_vhci/disk@g500000e011e17720:a
618  * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a
619  *
620  * For the -m option, used when we're trying to find the root device mapping:
621  *
622  * # /lib/mpxio/stmsboot_util -m /dev/dsk/c2t0d0s2
623  * /dev/dsk/c3t500000E011637CF0d0s2
624  */
625 static void
626 report_map(char *argdev, int physpath)
627 {
628 	nvlist_t *thisdev;
629 	int rv = 0;
630 	char *thisdevid;
631 	char *mpxpath = NULL;
632 	char *prefixt = NULL;
633 	char *prefixp = NULL;
634 	char *stripdev = NULL;
635 	char *slice = NULL;
636 	boolean_t mpxenp;
637 	uint_t slicelen = 0;
638 
639 	mpxenp = B_FALSE;
640 
641 	if ((prefixt = calloc(1, strlen(argdev) + 1)) == NULL) {
642 		logmsg(MSG_INFO, "Unable to allocate memory\n");
643 		(void) printf("NOT_MAPPED\n");
644 		return;
645 	}
646 
647 	(void) strlcpy(prefixt, argdev, strlen(argdev) + 1);
648 
649 	slice = strrchr(argdev, (physpath == BOOT) ? ':' : 's');
650 	if (slice != NULL) {
651 		slicelen = strlen(slice);
652 		if (slicelen > 3)
653 			/* invalid size - max is 3 chars */
654 			slicelen = 0;
655 	}
656 
657 	if ((stripdev = calloc(1, strlen(prefixt) + 1)) == NULL) {
658 		logmsg(MSG_INFO, "Unable to allocate memory\n");
659 		(void) printf("NOT_MAPPED\n");
660 		free(prefixt);
661 		return;
662 	}
663 
664 	if ((strstr(prefixt, "/scsi_vhci") == NULL) &&
665 	    (strstr(prefixt, "/pci") == NULL) &&
666 	    (strstr(prefixt, "/sbus") == NULL)) {
667 		prefixp = strrchr(prefixt, '/');
668 		(void) strlcpy(stripdev,
669 		    (prefixp == NULL) ? prefixt : prefixp + 1,
670 		    (prefixp == NULL) ?
671 		    strlen(prefixt) + 1: strlen(prefixp) + 1);
672 		if (prefixp != NULL)
673 			prefixt[strlen(argdev) - strlen(prefixp) + 1] = '\0';
674 	} else {
675 		if (physpath != BOOT) {
676 			logmsg(MSG_INFO, "Invalid device path provided\n");
677 			(void) printf("NOT_MAPPED\n");
678 			free(stripdev);
679 			free(prefixt);
680 			return;
681 		}
682 		(void) strlcpy(stripdev, argdev, strlen(argdev) + 1);
683 	}
684 
685 	logmsg(MSG_INFO,
686 	    "stripdev (%s), prefixt(%s), prefixp(%s), slice(%s)\n",
687 	    (stripdev == NULL) ? "null" : stripdev,
688 	    (prefixt == NULL) ? "null" : prefixt,
689 	    (prefixp == NULL) ? "null" : prefixp,
690 	    (slice == NULL) ? "null" : slice);
691 
692 	if (slicelen > 0)
693 		stripdev[strlen(stripdev) - slicelen] = '\0';
694 
695 	/* search for the shortened version */
696 	rv = nvlist_lookup_string(mapnvl, stripdev, &thisdevid);
697 	if (rv) {
698 		if (physpath != BOOT) {
699 			logmsg(MSG_INFO,
700 			    "searched mapnvl for '%s', got %s (%d)\n",
701 			    stripdev, strerror(rv), rv);
702 			(void) printf("NOT_MAPPED\n");
703 			free(stripdev);
704 			free(prefixt);
705 			return;
706 		}
707 	}
708 
709 	logmsg(MSG_INFO, "device %s has devid %s\n", stripdev, thisdevid);
710 
711 	if (nvlist_lookup_nvlist(mapnvl, thisdevid, &thisdev) != 0) {
712 		logmsg(MSG_INFO, "device (%s) in mapnvl but "
713 		    "not mapped!\n", thisdevid);
714 		(void) printf("NOT_MAPPED\n");
715 		free(stripdev);
716 		free(prefixt);
717 		return;
718 	}
719 
720 	/* quick exit */
721 	if (!mpxenabled && (strstr(argdev, "/pci") != NULL ||
722 	    strstr(argdev, "/sbus") != NULL)) {
723 		(void) printf("%s\n", argdev);
724 		free(stripdev);
725 		free(prefixt);
726 		return;
727 	}
728 
729 	(void) nvlist_lookup_boolean_value(thisdev, NVL_MPXEN, &mpxenp);
730 
731 	if (physpath == BOOT) {
732 		(void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath);
733 		if ((strstr(argdev, "/scsi_vhci") != NULL) &&
734 		    (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) {
735 			/* Need to translate vhci to phci */
736 			char *realpath;
737 
738 			if ((realpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
739 				logmsg(MSG_ERROR,
740 				    gettext("Unable to allocate "
741 				    "memory for a path element\n"));
742 				free(stripdev);
743 				free(prefixt);
744 				return;
745 			}
746 			vhci_to_phci(stripdev, realpath);
747 			(void) printf("%s%s\n", realpath,
748 			    ((slicelen > 0) && slice != NULL) ? slice : "");
749 			free(realpath);
750 		} else {
751 			(void) printf("%s%s\n", mpxpath,
752 			    ((slicelen > 0) && slice != NULL) ? slice : "");
753 		}
754 	} else {
755 		(void) nvlist_lookup_string(thisdev,
756 		    ((readonlyroot) ? NVL_PHYSPATH :
757 		    ((mpxenp == B_TRUE) ? NVL_MPXPATH : NVL_PATH)),
758 		    &mpxpath);
759 		logmsg(MSG_INFO, "mpxpath = %s\n",
760 		    (mpxpath == NULL) ? "null" : mpxpath);
761 		if (readonlyroot ||
762 		    (strstr(mpxpath, "/scsi_vhci") != NULL) ||
763 		    (strstr(mpxpath, "/pci") != NULL) ||
764 		    (strstr(mpxpath, "/sbus") != NULL)) {
765 			/*
766 			 * If we see a physical path here it means that
767 			 * devlinks aren't fully initialised yet, so we
768 			 * are still in maintenance/single-user mode.
769 			 */
770 			(void) printf("/devices%s:%c\n", mpxpath,
771 			    slice[1] + '1');
772 		} else {
773 			(void) printf("%s%s%s\n",
774 			    (prefixt[0] == '/') ? prefixt : "",
775 			    mpxpath,
776 			    ((slicelen > 0) && slice != NULL) ? slice : "");
777 		}
778 	}
779 	free(prefixt);
780 	free(stripdev);
781 }
782 
783 /*
784  * Validate the in-kernel and on-disk forms of our devid cache,
785  * returns  -1 for unfixable error and 0 for success.
786  */
787 static int
788 validate_devnvl()
789 {
790 	di_node_t	curnode;
791 	int		rv1 = -1;
792 	int		rv2 = -1;
793 
794 	/*
795 	 * Method: we walk through the kernel's concept of the device tree
796 	 * looking for "ssd" then "sd" nodes.
797 	 * We check to see whether the device's devid is already in our nvlist
798 	 * (on disk) nvlist cache file. If it is, we check that it's components
799 	 * match what we've got already and fill any missing fields.
800 	 * If the devid isn't in our on-disk nvlist already then we add it
801 	 * and populate the property nvpairs.
802 	 *
803 	 * At the end of this function we should have this program's concept
804 	 * of the devid-keyed nvlist matching what is in the ondisk form which
805 	 * is ready to be written out.
806 	 * If we can't do this, then we return -1.
807 	 */
808 	curnode = di_drv_first_node("ssd", devinfo_root);
809 	if (curnode != DI_NODE_NIL)
810 		rv1 = mpxio_nvl_boilerplate(curnode);
811 
812 	curnode = di_drv_first_node("sd", devinfo_root);
813 	if (curnode != DI_NODE_NIL)
814 		rv2 = mpxio_nvl_boilerplate(curnode);
815 
816 	if (rv1 + rv2 == -2)
817 		return (-1);
818 
819 	return (0);
820 }
821 
822 static int
823 mpxio_nvl_boilerplate(di_node_t curnode)
824 {
825 	int		rv;
826 	char		*strdevid;
827 	ddi_devid_t	curdevid;
828 	nvlist_t	*newnvl;
829 
830 	for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
831 		errno = 0;
832 
833 		curdevid = NULL;
834 		get_devid(curnode, &curdevid);
835 		if (curdevid == NULL)
836 			/*
837 			 * There's no devid registered for this device
838 			 * so it's not cool enough to play with us
839 			 */
840 			continue;
841 
842 		strdevid = devid_str_encode(curdevid, NULL);
843 		/* does this exist in the on-disk cache? */
844 		rv = nvlist_lookup_nvlist(mapnvl, strdevid, &newnvl);
845 		if (rv == ENOENT) {
846 			logmsg(MSG_INFO, "nvlist for %s not found\n", strdevid);
847 			/* no, so alloc a new nvl to store it */
848 			if (nvlist_alloc(&newnvl, NV_UNIQUE_NAME, 0) != 0) {
849 				logmsg(MSG_ERROR,
850 				    gettext("Unable to allocate space for "
851 				    "a devid property list: %s\n"),
852 				    strerror(errno));
853 				return (-1);
854 			}
855 		} else {
856 			if ((rv != ENOTSUP) && (rv != EINVAL))
857 				logmsg(MSG_INFO,
858 				    "%s exists in ondisknvl, verifying\n",
859 				    strdevid);
860 		}
861 
862 		if (popcheck_devnvl(curnode, newnvl, strdevid) != 0) {
863 			logmsg(MSG_ERROR,
864 			    gettext("Unable to populate devid nvpair "
865 			    "for device with devid %s\n"),
866 			    strdevid);
867 			devid_str_free(strdevid);
868 			nvlist_free(newnvl);
869 			return (-1);
870 		}
871 
872 		/* Now add newnvl into our cache. */
873 		errno = 0;
874 		rv = nvlist_add_nvlist(mapnvl, strdevid, newnvl);
875 		if (rv) {
876 			logmsg(MSG_ERROR,
877 			    gettext("Unable to add device (devid %s) "
878 			    "to in-kernel nvl: %s (%d)\n"),
879 			    strdevid, strerror(rv), rv);
880 			devid_str_free(strdevid);
881 			nvlist_free(newnvl);
882 			return (-1);
883 		}
884 		logmsg(MSG_INFO,
885 		    gettext("added device (devid %s) to mapnvl\n\n"),
886 		    strdevid);
887 		devid_str_free(strdevid);
888 	}
889 	return (0);
890 }
891 
892 /*
893  * Operates on a single di_node_t, collecting all the device properties
894  * that we need. devnvl is allocated by the caller, and we add our nvpairs
895  * to it if they don't already exist.
896  *
897  * We are _only_ interested in devices which have a devid. We pull in
898  * devices even when they're excluded via stmsboot -D (driver), because
899  * we don't want to miss out on any devid data that might be handy later.
900  */
901 static int
902 popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, char *strdevid)
903 {
904 	char *path = NULL;
905 	char *curpath = NULL;
906 	char *devfspath = NULL;
907 	char *prop = NULL;
908 	int scsivhciparent = 0;
909 	int rv = 0;
910 	boolean_t mpxenp = B_FALSE;
911 
912 	errno = 0;
913 	devfspath = di_devfs_path(thisnode);
914 	if (devfspath == NULL) {
915 		logmsg(MSG_ERROR,
916 		    gettext("Unable to determine devfs path for node: %s\n"),
917 		    strerror(errno));
918 		return (-1);
919 	}
920 
921 	/* Add a convenient devfspath to devid inverse map */
922 	if (nvlist_add_string(mapnvl, devfspath, strdevid) != 0) {
923 		logmsg(MSG_ERROR,
924 		    gettext("Unable to add device path %s with devid "
925 		    "%s to mapnvl\n"), devfspath, strdevid);
926 		return (-1);
927 	}
928 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, di_parent_node(thisnode),
929 	    "mpxio-disable", &prop) >= 0) {
930 		if (strncmp(prop, "yes", 3) == 0) {
931 			if (!mpxprop)
932 				mpxprop++;
933 		}
934 	}
935 
936 	if (strncmp(di_driver_name(di_parent_node(thisnode)),
937 	    "scsi_vhci", 9) == 0) {
938 		scsivhciparent = 1;
939 		if (!mpxenabled)
940 			mpxenabled++;
941 
942 		rv = nvlist_lookup_boolean_value(devnvl, NVL_MPXEN, &mpxenp);
943 		if (rv || (mpxenp == B_FALSE)) {
944 			rv = nvlist_add_boolean_value(devnvl,
945 			    NVL_MPXEN, B_TRUE);
946 			if (rv) {
947 				logmsg(MSG_ERROR,
948 				    gettext("Unable to add property %s "
949 				    "(set to B_TRUE) for device %s: "
950 				    "%s (%d)\n"),
951 				    NVL_MPXEN, devfspath,
952 				    strerror(rv), rv);
953 				return (-1);
954 			}
955 			logmsg(MSG_INFO, "NVL_MPXEN :: (B_FALSE->B_TRUE)\n");
956 		}
957 	} else {
958 		/* turn _off_ the flag if it was enabled */
959 		rv = nvlist_add_boolean_value(devnvl, NVL_MPXEN, B_FALSE);
960 		if (rv) {
961 			logmsg(MSG_ERROR,
962 			    gettext("Unable to add property %s "
963 			    "(set to B_FALSE) for device %s: %s (%d)\n"),
964 			    NVL_MPXEN, devfspath,
965 			    strerror(rv), rv);
966 			return (-1);
967 		}
968 		logmsg(MSG_INFO, "NVL_MPXEN :: (B_TRUE-> B_FALSE)\n");
969 	}
970 
971 	rv = nvlist_add_string(devnvl, NVL_PHYSPATH, devfspath);
972 	if (rv) {
973 		logmsg(MSG_ERROR,
974 		    gettext("Unable to add physical device path (%s) "
975 		    "property to nvl\n"));
976 		return (-1);
977 	}
978 
979 	if ((curpath = calloc(1, MAXPATHLEN)) == NULL) {
980 		logmsg(MSG_ERROR,
981 		    gettext("Unable to allocate space for current path\n"));
982 		return (-1);
983 	}
984 	curpath = find_link(thisnode);
985 	if (curpath == NULL) {
986 		if (readonlyroot) {
987 			return (0);
988 		}
989 		logmsg(MSG_ERROR,
990 		    gettext("Unable to determine device path for node %s\n"),
991 		    devfspath);
992 		return (-1);
993 	}
994 
995 	rv = nvlist_lookup_string(devnvl, NVL_MPXPATH, &path);
996 
997 	if (path == NULL && scsivhciparent)
998 		(void) nvlist_add_string(devnvl, NVL_MPXPATH, curpath);
999 
1000 	if (!scsivhciparent) {
1001 		(void) nvlist_add_string(devnvl, NVL_PATH, curpath);
1002 		path = curpath;
1003 	}
1004 
1005 	/*
1006 	 * This next block provides the path to devid inverse mapping
1007 	 * that other functions require
1008 	 */
1009 	if (path != NULL) {
1010 		if (nvlist_add_string(mapnvl, path, strdevid) != 0) {
1011 			logmsg(MSG_ERROR,
1012 			    gettext("Unable to add device %s with devid "
1013 			    "%s to mapnvl\n"), path, strdevid);
1014 			return (-1);
1015 		}
1016 		logmsg(MSG_INFO, "popcheck_devnvl: added path %s :: %s\n",
1017 		    path, strdevid);
1018 		if (nvlist_add_string(mapnvl, curpath, strdevid) != 0) {
1019 			logmsg(MSG_ERROR,
1020 			    gettext("Unable to add device %s with devid "
1021 			    "%s to mapnvl: %s\n"),
1022 			    curpath, strdevid, strerror(errno));
1023 			return (-1);
1024 		}
1025 		logmsg(MSG_INFO, "popcheck_devnvl: added curpath %s :: %s\n",
1026 		    curpath, strdevid);
1027 	}
1028 	if (scsivhciparent) {
1029 		if (nvlist_add_string(devnvl, NVL_MPXPATH, curpath) != 0) {
1030 			logmsg(MSG_ERROR,
1031 			    gettext("Unable to add property %s for device "
1032 			    "%s: %s\n"),
1033 			    NVL_MPXPATH, devfspath, strerror(errno));
1034 			return (-1);
1035 		} else {
1036 			logmsg(MSG_INFO, "added curpath (%s) as NVL_MPXPATH "
1037 			    "to devnvl for devid %s\n", curpath, strdevid);
1038 		}
1039 	}
1040 	return (0);
1041 }
1042 
1043 static void
1044 print_mpx_capable(di_node_t curnode)
1045 {
1046 	char *prop;
1047 	char *path;
1048 	char *aliases = NULL;
1049 
1050 	if (cap_N_option) {
1051 		aliases = calloc(1, MAXPATHLEN + 1);
1052 		if (aliases == NULL) {
1053 			logmsg(MSG_ERROR,
1054 			    gettext("Unable to allocate memory for a device "
1055 			    "alias list\n"));
1056 			return;
1057 		}
1058 	}
1059 
1060 	for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
1061 		if (di_prop_lookup_strings(DDI_DEV_T_ANY, curnode,
1062 		    "initiator-port", &prop) >= 0) {
1063 			if ((path = di_devfs_path(curnode)) == NULL) {
1064 				logmsg(MSG_INFO,
1065 				    "Unable to find devfs path for device "
1066 				    "%s: %s\n", &curnode, strerror(errno));
1067 				continue;
1068 			}
1069 			if (cap_N_option) {
1070 				char *nodename = di_node_name(curnode);
1071 				/* nodename is never going to be null */
1072 				if (strstr(aliases, nodename) == NULL)
1073 					/* haven't seen this nodename before */
1074 					(void) snprintf(aliases,
1075 					    MAXPATHLEN + 1, "%s|%s",
1076 					    ((aliases != NULL) ? aliases : ""),
1077 					    nodename);
1078 			} else
1079 				(void) printf("%s\n", path);
1080 		}
1081 	}
1082 	if (cap_N_option)
1083 		(void) printf("%s\n", aliases);
1084 }
1085 
1086 static int
1087 link_cb(di_devlink_t devlink, void *arg)
1088 {
1089 	const char *result;
1090 
1091 	result = di_devlink_path(devlink);
1092 	if (result == NULL) {
1093 		arg = (void *)"(null)";
1094 	} else {
1095 		(void) strlcpy(arg, result, strlen(result));
1096 	}
1097 	logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n",
1098 	    ((result != NULL) ? result : "(null)"));
1099 	return (DI_WALK_CONTINUE);
1100 }
1101 
1102 static char *
1103 find_link(di_node_t cnode)
1104 {
1105 	di_minor_t devminor = DI_MINOR_NIL;
1106 	di_devlink_handle_t	hdl;
1107 	char *devfspath = NULL;
1108 	char *minorpath = NULL;
1109 	char *linkname = NULL;
1110 	char *cbresult = NULL;
1111 
1112 	devfspath = di_devfs_path(cnode);
1113 	if (cnode == DI_NODE_NIL) {
1114 		logmsg(MSG_ERROR,
1115 		    gettext("find_ctrl must be called with non-null "
1116 		    "di_node_t\n"));
1117 		return (NULL);
1118 	}
1119 	logmsg(MSG_INFO, "find_link: devfspath %s\n", devfspath);
1120 
1121 	if (((cbresult = calloc(1, MAXPATHLEN)) == NULL) ||
1122 	    ((minorpath = calloc(1, MAXPATHLEN)) == NULL) ||
1123 	    ((linkname = calloc(1, MAXPATHLEN)) == NULL)) {
1124 		logmsg(MSG_ERROR, "unable to allocate space for dev link\n");
1125 		return (NULL);
1126 	}
1127 
1128 	devminor = di_minor_next(cnode, devminor);
1129 	hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK);
1130 	if (hdl == NULL) {
1131 		logmsg((readonlyroot ? MSG_INFO : MSG_ERROR),
1132 		    gettext("unable to take devlink snapshot: %s\n"),
1133 		    strerror(errno));
1134 		return (NULL);
1135 	}
1136 
1137 	linkname = "^dsk/";
1138 	(void) snprintf(minorpath, MAXPATHLEN, "%s:c", devfspath);
1139 
1140 	errno = 0;
1141 	if (di_devlink_walk(hdl, linkname, minorpath, DI_PRIMARY_LINK,
1142 	    (void *)cbresult, link_cb) < 0) {
1143 		logmsg(MSG_ERROR,
1144 		    gettext("Unable to walk devlink snapshot for %s: %s\n"),
1145 		    minorpath, strerror(errno));
1146 		return (NULL);
1147 	}
1148 
1149 	if (di_devlink_fini(&hdl) < 0) {
1150 		logmsg(MSG_ERROR,
1151 		    gettext("Unable to close devlink snapshot: %s\n"),
1152 		    strerror(errno));
1153 	}
1154 	if (strstr(cbresult, "dsk/") == NULL)
1155 		return (devfspath);
1156 
1157 	bzero(minorpath, MAXPATHLEN);
1158 	/* strip off the trailing "s2" */
1159 	bcopy(cbresult, minorpath, strlen(cbresult) - 1);
1160 	/* Now strip off the /dev/dsk/ prefix for output flexibility */
1161 	linkname = strrchr(minorpath, '/');
1162 	return (++linkname);
1163 }
1164 
1165 /*
1166  * handle case where device has been probed but its target driver is not
1167  * attached so enumeration has not quite finished. Opening the /devices
1168  * pathname will force the kernel to finish the enumeration process and
1169  * let us get the data we need.
1170  */
1171 static void
1172 get_devid(di_node_t node, ddi_devid_t *thisdevid)
1173 {
1174 	int fd;
1175 	char realpath[MAXPATHLEN];
1176 	char *openpath = di_devfs_path(node);
1177 
1178 	errno = 0;
1179 	bzero(realpath, MAXPATHLEN);
1180 	if (strstr(openpath, "/devices") == NULL) {
1181 		(void) snprintf(realpath, MAXPATHLEN,
1182 		    "/devices%s:c,raw", openpath);
1183 		fd = open(realpath, O_RDONLY|O_NDELAY);
1184 	} else {
1185 		fd = open(openpath, O_RDONLY|O_NDELAY);
1186 	}
1187 
1188 	if (fd < 0) {
1189 		logmsg(MSG_INFO, "Unable to open path %s: %s\n",
1190 		    openpath, strerror(errno));
1191 		return;
1192 	}
1193 
1194 	if (devid_get(fd, thisdevid) != 0) {
1195 		logmsg(MSG_INFO,
1196 		    "'%s' node (%s) without a devid registered\n",
1197 		    di_driver_name(node), di_devfs_path(node));
1198 	}
1199 	(void) close(fd);
1200 }
1201 
1202 static int
1203 print_bootpath()
1204 {
1205 	char *bootprop = NULL;
1206 
1207 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root,
1208 	    "bootpath", &bootprop) >= 0) {
1209 		(void) printf("%s\n", bootprop);
1210 		return (0);
1211 	} else if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root,
1212 	    "boot-path", &bootprop) >= 0) {
1213 		(void) printf("%s\n", bootprop);
1214 		return (0);
1215 	} else {
1216 		(void) printf("ERROR: no bootpath/boot-path property found\n");
1217 		return (ENOENT);
1218 	}
1219 }
1220 
1221 /*
1222  * We only call this routine if we have a scsi_vhci node and must
1223  * determine the actual physical path of its first online client
1224  * path.
1225  */
1226 static void
1227 vhci_to_phci(char *devpath, char *physpath)
1228 {
1229 	sv_iocdata_t	ioc;
1230 	sv_path_info_t	*pi;
1231 	int		vhci_fd;
1232 	int		rv;
1233 	uint_t		npaths = 0;
1234 
1235 	vhci_fd = open(VHCI_CTL_NODE, O_RDWR);
1236 	if (vhci_fd < 0)
1237 		goto failure;
1238 
1239 	bzero(&ioc, sizeof (sv_iocdata_t));
1240 	ioc.client = devpath;
1241 	ioc.ret_elem = &npaths;
1242 	rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc);
1243 	if (rv || npaths == 0) {
1244 		logmsg(MSG_INFO,
1245 		    "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() failed, "
1246 		    "%s (%d)\n", strerror(rv), rv);
1247 		goto failure;
1248 	}
1249 
1250 	bzero(&ioc, sizeof (sv_iocdata_t));
1251 	ioc.client = devpath;
1252 	ioc.buf_elem = npaths;
1253 	ioc.ret_elem = &npaths;
1254 	if ((ioc.ret_buf = calloc(npaths, sizeof (sv_path_info_t)))
1255 	    == NULL)
1256 		goto failure;
1257 	rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc);
1258 	if (rv || npaths == 0) {
1259 		logmsg(MSG_INFO,
1260 		    "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() (#2) "
1261 		    "failed, %s (%d)\n", strerror(rv), rv);
1262 		goto failure;
1263 	}
1264 
1265 	if (ioc.buf_elem < npaths)
1266 		npaths = ioc.buf_elem;
1267 
1268 	pi = (sv_path_info_t *)ioc.ret_buf;
1269 	while (npaths--) {
1270 		if (pi->ret_state == MDI_PATHINFO_STATE_ONLINE) {
1271 			char nodename[4];
1272 
1273 			bzero(nodename, 4);
1274 			/* A hack, but nicer than a platform-specific ifdef */
1275 			if (strstr(devpath, "ssd") != NULL) {
1276 				(void) snprintf(nodename, 4, "ssd");
1277 			} else {
1278 				(void) snprintf(nodename, 4, "sd");
1279 			}
1280 			(void) snprintf(physpath, MAXPATHLEN, "%s/%s@%s",
1281 			    pi->device.ret_phci, nodename, pi->ret_addr);
1282 			free(ioc.ret_buf);
1283 			return;
1284 		}
1285 		pi++;
1286 	}
1287 
1288 failure:
1289 	(void) snprintf(physpath, MAXPATHLEN, "NOT_MAPPED");
1290 }
1291 
1292 /*
1293  * Write /etc/vfstab to /etc/vfstab.new, with any remapped device
1294  * names substituted.
1295  *
1296  * Returns:
1297  * 	0	successful operation
1298  *	-1	failed
1299  */
1300 static int
1301 update_vfstab()
1302 {
1303 	FILE *fdin, *fdout;
1304 	char *buf, *tmpbuf;
1305 	char fname[MAXPATHLEN];
1306 	int rv = -1, rval = -1;
1307 	char cdev[MAXPATHLEN];
1308 	char bdev[MAXPATHLEN];
1309 	char mntpt[MAXPATHLEN];
1310 	char fstype[512];
1311 	char fsckpass[512];
1312 	char mntboot[512];
1313 	char mntopt[MAXPATHLEN];
1314 	char fmt[80];
1315 	char *prefixt = NULL;
1316 	char *curdev = NULL;
1317 	char *thisdevid = NULL;
1318 	char *slice = NULL;
1319 	nvlist_t *thisdev;
1320 	boolean_t devmpx = B_FALSE;
1321 
1322 	buf = calloc(1, MAXPATHLEN);
1323 	tmpbuf = calloc(1, MAXPATHLEN);
1324 	if (buf == NULL || tmpbuf == NULL)
1325 		return (-1);
1326 
1327 	(void) snprintf(fname, MAXPATHLEN, "/etc/mpxio/vfstab.new");
1328 
1329 	fdin = fopen("/etc/vfstab", "r");
1330 	fdout = fopen(fname, "w+");
1331 	if (fdin == NULL || fdout == NULL) {
1332 		logmsg(MSG_INFO, "Unable to open vfstab or create a backup "
1333 		    "vfstab %s\n");
1334 		return (-1);
1335 	}
1336 
1337 	(void) snprintf(fmt, sizeof (fmt),
1338 	    "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1,
1339 	    sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1,
1340 	    sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1);
1341 
1342 	while (fgets(buf, MAXPATHLEN, fdin) != NULL) {
1343 		if (strlen(buf) == (MAXPATHLEN - 1) &&
1344 		    buf[MAXPATHLEN-2] != '\n') {
1345 			logmsg(MSG_ERROR,
1346 			    gettext("/etc/vfstab line length too long, "
1347 			    "exceeded %2$d: \"%3$s\"\n"),
1348 			    MAXPATHLEN - 2, buf);
1349 			goto out;
1350 		}
1351 
1352 		prefixt = NULL;
1353 		curdev = NULL;
1354 		slice = NULL;
1355 		thisdevid = NULL;
1356 		thisdev = NULL;
1357 
1358 		/* LINTED - variable format specifier */
1359 		rv = sscanf(buf, fmt, bdev, cdev, mntpt, fstype, fsckpass,
1360 		    mntboot, mntopt);
1361 
1362 		/*
1363 		 * Walk through the lines in the input file (/etc/vfstab),
1364 		 * skipping anything which is _not_ a COGD (common or garden
1365 		 * disk), ie all the /devices, /system, /dev/md, /dev/vx and
1366 		 * /dev/zvol and so forth.
1367 		 */
1368 		if ((rv == 7) && (bdev[0] == '/') &&
1369 		    (strstr(bdev, "/dev/dsk"))) {
1370 			slice = strrchr(bdev, 's');
1371 			/* take a copy, strip off /dev/dsk/ */
1372 			prefixt = strrchr(bdev, 'c');
1373 			prefixt[strlen(bdev) - 9 - strlen(slice)] = '\0';
1374 			slice++; /* advance past the s */
1375 			rval = nvlist_lookup_string(mapnvl, prefixt,
1376 			    &thisdevid);
1377 			if (rval) {
1378 				/* Whoa, where did this device go?! */
1379 				logmsg(MSG_INFO,
1380 				    "error looking up device %s\n", prefixt);
1381 				/* Comment-out this line in the new version */
1382 				(void) snprintf(tmpbuf, MAXPATHLEN,
1383 				    "# DEVICE NOT FOUND %s", buf);
1384 				(void) fprintf(fdout, "%s", tmpbuf);
1385 				continue;
1386 			} else {
1387 				/* The device exists in our mapnvl */
1388 				(void) nvlist_lookup_nvlist(mapnvl, thisdevid,
1389 				    &thisdev);
1390 				(void) nvlist_lookup_boolean_value(thisdev,
1391 				    NVL_MPXEN, &devmpx);
1392 				(void) nvlist_lookup_string(thisdev,
1393 				    ((devmpx == B_TRUE)
1394 				    ? NVL_MPXPATH : NVL_PATH),
1395 				    &curdev);
1396 			}
1397 		}
1398 
1399 		if ((prefixt != NULL) && (curdev != NULL) &&
1400 		    (rv = (strncmp(prefixt, curdev, strlen(prefixt)) != 0))) {
1401 			/* Mapping change for this device */
1402 			if (strcmp(fstype, "swap") == 0) {
1403 				(void) snprintf(tmpbuf, MAXPATHLEN,
1404 				    "/dev/dsk/%ss%s\t-\t-\tswap\t"
1405 				    "%s\t%s\t%s\n",
1406 				    curdev, slice, fsckpass, mntboot, mntopt);
1407 			} else {
1408 				(void) snprintf(tmpbuf, MAXPATHLEN,
1409 				    "/dev/dsk/%ss%s\t/dev/rdsk/%ss%s\t"
1410 				    "%s\t%s\t%s\t%s\t%s\n",
1411 				    curdev, slice, curdev, slice,
1412 				    mntpt, fstype, fsckpass, mntboot, mntopt);
1413 			}
1414 			errno = 0;
1415 			(void) fprintf(fdout, "%s", tmpbuf);
1416 		} else {
1417 			(void) fprintf(fdout, "%s", buf);
1418 		}
1419 
1420 		errno = 0;
1421 		if (fflush(fdout) != 0) {
1422 			logmsg(MSG_ERROR,
1423 			    gettext("fprintf failed to write to %s: %s (%d)\n"),
1424 			    fname, strerror(errno), errno);
1425 			goto out;
1426 		}
1427 	}
1428 out:
1429 	(void) fclose(fdin);
1430 	(void) fclose(fdout);
1431 	free(buf);
1432 	free(tmpbuf);
1433 	return (errno);
1434 }
1435