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