xref: /illumos-gate/usr/src/cmd/stmsboot/stmsboot_util.c (revision d583b39bfb4e2571d3e41097c5c357ffe353ad45)
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 			bcopy(optarg, drvlimit, strlen(optarg));
413 			/* update this if adding support for a new driver */
414 			if ((strncmp(drvlimit, "fp", 2) == NULL) &&
415 			    (strncmp(drvlimit, "mpt", 3) == NULL) &&
416 			    (strncmp(drvlimit, "mpt_sas", 7) == NULL) &&
417 			    (strncmp(drvlimit, "pmcs", 4) == NULL)) {
418 				logmsg(MSG_ERROR,
419 				    gettext("invalid parent driver (%s) "
420 				    "specified"), drvlimit);
421 				usage();
422 			}
423 			break;
424 		case 'h':
425 			/* Just drop out and print the usage() output */
426 			globarg = MPX_USAGE;
427 			break;
428 		case 'i':
429 			globarg = MPX_INIT;
430 			break;
431 		case 'l':
432 			globarg |= MPX_LIST;
433 			limctrl = (int)atol(optarg);
434 			if (limctrl < 0) {
435 				logmsg(MSG_INFO,
436 				    gettext("invalid controller number "
437 				    "(%d), checking all controllers\n"),
438 				    limctrl);
439 			}
440 			break;
441 		case 'L':
442 			globarg |= MPX_LIST;
443 			break;
444 		case 'g':
445 			guid = 1;
446 			break;
447 		case 'm':
448 			globarg = MPX_MAP;
449 			if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
450 				logmsg(MSG_ERROR,
451 				    gettext("Unable to allocate space for a "
452 				    "device name\n"));
453 				exit(errno);
454 			}
455 			devicep = strdup(optarg);
456 			break;
457 		case 'N':
458 			cap_N_option = 1;
459 			globarg = MPX_CAPABLE_CTRL;
460 			break;
461 		case 'n':
462 			globarg = MPX_CAPABLE_CTRL;
463 			break;
464 		case 'o':
465 			globarg = MPX_GETPATH;
466 			if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
467 				logmsg(MSG_ERROR,
468 				    gettext("Unable to allocate space for a "
469 				    "device name\n"));
470 				exit(errno);
471 			}
472 			devicep = strdup(optarg);
473 			break;
474 		case 'p':
475 			globarg = MPX_PHYSICAL;
476 			if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
477 				logmsg(MSG_ERROR,
478 				    gettext("Unable to allocate space for a "
479 				    "device name\n"));
480 				exit(errno);
481 			}
482 			devicep = strdup(optarg);
483 			break;
484 		case 'q':
485 			globarg = MPX_DEV_PATH;
486 			if ((devicep = calloc(1, MAXPATHLEN)) == NULL) {
487 				logmsg(MSG_ERROR,
488 				    gettext("Unable to allocate space for a "
489 				    "device name\n"));
490 				exit(errno);
491 			}
492 			devicep = strdup(optarg);
493 			break;
494 		case 'u':
495 			globarg = MPX_UPDATEVFSTAB;
496 			break;
497 		default:
498 			logmsg(MSG_ERROR,
499 			    gettext("Invalid command line option (%c)\n"),
500 			    opt);
501 			usage();
502 		}
503 	}
504 
505 	if ((globarg >= MPX_USAGE) || (guid && (globarg != MPX_LIST)))
506 		usage();
507 
508 	if ((drvlimit != NULL) &&
509 	    ((globarg != MPX_LIST) &&
510 	    (globarg != MPX_CAPABLE_CTRL)))
511 		usage();
512 }
513 
514 static void
515 logmsg(int level, const char *msg, ...)
516 {
517 	va_list ap;
518 
519 	if ((level >= MSG_ERROR) ||
520 	    ((debugflag > 0) && (level >= MSG_INFO))) {
521 		(void) fprintf(stdout, "stmsboot: ");
522 		va_start(ap, msg);
523 		(void) vfprintf(stdout, msg, ap);
524 		va_end(ap);
525 	}
526 }
527 
528 /*
529  * It's up to the caller to do any sorting or pretty-printing of the device
530  * mappings we report. Since we're storing the device links as just the cXtYdZ
531  * part, we'll add /dev/rdsk/ back on when we print the listing so we maintain
532  * compatibility with previous versions of this tool. There's a little bit
533  * of footwork involved to make sure that we show all the paths to a device
534  * rather than just the first one we stashed away.
535  */
536 static void
537 list_devs(int listguids, int ctrl)
538 {
539 	nvlist_t *thisdevnvl;
540 	nvpair_t *pair;
541 	char *diskpath, *livepath, *key, *querydev;
542 	char *matchctrl = NULL;
543 	char checkctrl[MAXPATHLEN];
544 	int rv;
545 
546 	if (!mpxenabled) {
547 		if (mpxprop) {
548 			logmsg(MSG_ERROR, gettext("MPXIO disabled\n"));
549 		} else {
550 			logmsg(MSG_ERROR, gettext("No STMS devices have "
551 			    "been found\n"));
552 		}
553 		return;
554 	}
555 
556 	if (listguids) {
557 		(void) printf(gettext("non-STMS device name\t\t\tGUID\n"
558 		    "------------------------------------------"
559 		    "------------------------\n"));
560 	} else {
561 		(void) printf(gettext("non-STMS device name\t\t\t"
562 		    "STMS device name\n"
563 		    "------------------------------------------"
564 		    "------------------------\n"));
565 	}
566 
567 	bzero(checkctrl, MAXPATHLEN);
568 	pair = NULL;
569 	while ((pair = nvlist_next_nvpair(mapnvl, pair))
570 	    != NULL) {
571 		boolean_t livescsivhcip = B_FALSE;
572 
573 		if ((((rv = nvpair_value_string(pair, &querydev)) < 0) ||
574 		    ((key = nvpair_name(pair)) == NULL)) ||
575 		    ((strstr(key, "/pci") != NULL) ||
576 		    (strstr(key, "/sbus") != NULL) ||
577 		    (strstr(key, "/scsi_vhci") != NULL) ||
578 		    (strncmp(key, "id1", 3) == 0))) {
579 			logmsg(MSG_INFO,
580 			    "list_devs: rv = %d; (%s) is not a devlink, "
581 			    "continuing.\n", rv,
582 			    (key != NULL) ? key : "null");
583 			querydev = NULL;
584 			continue;
585 		}
586 
587 		(void) nvlist_lookup_nvlist(mapnvl, querydev, &thisdevnvl);
588 		(void) nvlist_lookup_boolean_value(thisdevnvl, NVL_MPXEN,
589 		    &livescsivhcip);
590 		(void) nvlist_lookup_string(thisdevnvl, NVL_MPXPATH,
591 		    &livepath);
592 
593 		if ((!livescsivhcip) ||
594 		    (livescsivhcip &&
595 		    (strncmp(key, livepath, strlen(key)) == 0)))
596 			continue;
597 
598 		(void) nvlist_lookup_string(thisdevnvl, NVL_PATH,
599 		    &diskpath);
600 
601 		logmsg(MSG_INFO,
602 		    "list_devs: %s :: %s ::%s :: MPXEN (%s)\n",
603 		    key, diskpath, livepath,
604 		    ((livescsivhcip) ? "TRUE" : "FALSE"));
605 
606 		if (ctrl > -1) {
607 			(void) sprintf(checkctrl, "c%dt", ctrl);
608 			matchctrl = strstr(key, checkctrl);
609 			if (matchctrl == NULL)
610 				continue;
611 		}
612 		if (listguids != 0) {
613 			char *tempguid;
614 			ddi_devid_t curdevid;
615 			int rv;
616 
617 			rv = devid_str_decode(querydev, &curdevid, NULL);
618 			if (rv == -1) {
619 				logmsg(MSG_INFO, "Unable to decode devid %s\n",
620 				    key);
621 				continue;
622 			}
623 			tempguid = devid_to_guid(curdevid);
624 			if (tempguid != NULL)
625 				(void) printf("/dev/rdsk/%s\t%s\n",
626 				    diskpath, tempguid);
627 
628 			devid_free_guid(tempguid);
629 			devid_free(curdevid);
630 			continue;
631 		}
632 
633 		(void) printf("/dev/rdsk/%s\t/dev/rdsk/%s\n",
634 		    (strstr(key, diskpath) == NULL) ? key : diskpath,
635 		    livepath);
636 	}
637 }
638 
639 /*
640  * We get passed a device name which we search the mapnvl for. If we find
641  * it, we print the mapping as it is found. It is up to the caller of this
642  * utility to do any pretty-printing of the results. If a device listed on
643  * the command line does not exist in the mapnvl, then we print NOT_MAPPED.
644  * Otherwise we print the command-line device name as it maps to what is
645  * stashed in the mapnvl - even if that's a "no change" device mapping.
646  *
647  * Example output (-p maps to physpath=BOOT)
648  * # /lib/mpxio/stmsboot_util -p \
649  *	/pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a
650  * /scsi_vhci/disk@g500000e011e17720:a
651  *
652  * Or the reverse:
653  * # /lib/mpxio/stmsboot_util -p /scsi_vhci/disk@g500000e011e17720:a
654  * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a
655  *
656  * For the -m option, used when we're trying to find the root device mapping:
657  *
658  * # /lib/mpxio/stmsboot_util -m /dev/dsk/c2t0d0s2
659  * /dev/dsk/c3t500000E011637CF0d0s2
660  */
661 static void
662 report_map(char *argdev, int physpath)
663 {
664 	nvlist_t *thisdev;
665 	int rv = 0;
666 	char *thisdevid;
667 	char *mpxpath = NULL;
668 	char *prefixt = NULL;
669 	char *prefixp = NULL;
670 	char *stripdev = NULL;
671 	char *slice = NULL;
672 	boolean_t mpxenp;
673 	uint_t slicelen = 0;
674 
675 	mpxenp = B_FALSE;
676 
677 	if ((prefixt = calloc(1, strlen(argdev) + 1)) == NULL) {
678 		logmsg(MSG_INFO, "Unable to allocate memory\n");
679 		(void) printf("NOT_MAPPED\n");
680 		return;
681 	}
682 
683 	(void) strlcpy(prefixt, argdev, strlen(argdev) + 1);
684 
685 	slice = strrchr(argdev, (physpath == NONBOOT) ? 's' : ':');
686 	if (slice != NULL) {
687 		slicelen = strlen(slice);
688 		if (slicelen > 3)
689 			/* invalid size - max is 3 chars */
690 			slicelen = 0;
691 	}
692 
693 	if ((stripdev = calloc(1, strlen(prefixt) + 1)) == NULL) {
694 		logmsg(MSG_INFO, "Unable to allocate memory\n");
695 		(void) printf("NOT_MAPPED\n");
696 		free(prefixt);
697 		return;
698 	}
699 
700 	if ((strstr(prefixt, "/scsi_vhci") == NULL) &&
701 	    (strstr(prefixt, "/pci") == NULL) &&
702 	    (strstr(prefixt, "/sbus") == NULL)) {
703 		prefixp = strrchr(prefixt, '/');
704 		(void) strlcpy(stripdev,
705 		    (prefixp == NULL) ? prefixt : prefixp + 1,
706 		    (prefixp == NULL) ?
707 		    strlen(prefixt) + 1: strlen(prefixp) + 1);
708 		if (prefixp != NULL)
709 			prefixt[strlen(argdev) - strlen(prefixp) + 1] = '\0';
710 	} else {
711 		if ((physpath != BOOT) &&
712 		    (physpath != BOOT_PATH)) {
713 			logmsg(MSG_INFO, "Invalid device path provided\n");
714 			(void) printf("NOT_MAPPED\n");
715 			free(stripdev);
716 			free(prefixt);
717 			return;
718 		}
719 		(void) strlcpy(stripdev, argdev, strlen(argdev) + 1);
720 	}
721 
722 	logmsg(MSG_INFO,
723 	    "stripdev (%s), prefixt(%s), prefixp(%s), slice(%s)\n",
724 	    (stripdev == NULL) ? "null" : stripdev,
725 	    (prefixt == NULL) ? "null" : prefixt,
726 	    (prefixp == NULL) ? "null" : prefixp,
727 	    (slice == NULL) ? "null" : slice);
728 
729 	if (slicelen > 0)
730 		stripdev[strlen(stripdev) - slicelen] = '\0';
731 
732 	/* search for the shortened version */
733 	rv = nvlist_lookup_string(mapnvl, stripdev, &thisdevid);
734 	if (rv) {
735 		if ((physpath != BOOT) &&
736 		    (physpath != BOOT_PATH)) {
737 			logmsg(MSG_INFO,
738 			    "searched mapnvl for '%s', got %s (%d)\n",
739 			    stripdev, strerror(rv), rv);
740 			(void) printf("NOT_MAPPED\n");
741 			free(stripdev);
742 			free(prefixt);
743 			return;
744 		}
745 	}
746 
747 	logmsg(MSG_INFO, "device %s has devid %s\n", stripdev, thisdevid);
748 
749 	if (nvlist_lookup_nvlist(mapnvl, thisdevid, &thisdev) != 0) {
750 		logmsg(MSG_INFO, "device (%s) in mapnvl but "
751 		    "not mapped!\n", thisdevid);
752 		(void) printf("NOT_MAPPED\n");
753 		free(stripdev);
754 		free(prefixt);
755 		return;
756 	}
757 
758 	/* quick exit */
759 	if (!mpxenabled && (strstr(argdev, "/pci") != NULL ||
760 	    strstr(argdev, "/sbus") != NULL)) {
761 		(void) printf("%s\n", argdev);
762 		free(stripdev);
763 		free(prefixt);
764 		return;
765 	}
766 
767 	(void) nvlist_lookup_boolean_value(thisdev, NVL_MPXEN, &mpxenp);
768 
769 	if (physpath == BOOT) {
770 		(void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath);
771 		if ((strstr(argdev, "/scsi_vhci") != NULL) &&
772 		    (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) {
773 			/* Need to translate vhci to phci */
774 			vhci_to_phci(stripdev, slice, DISPLAY_ONE_PATH);
775 		} else {
776 			(void) printf("%s%s\n", mpxpath,
777 			    ((slicelen > 0) && slice != NULL) ? slice : "");
778 		}
779 	} else if (physpath == BOOT_PATH) {
780 		(void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath);
781 		if ((strstr(argdev, "/scsi_vhci") != NULL) &&
782 		    (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) {
783 			/* Need to translate vhci to phci */
784 			vhci_to_phci(stripdev, slice, DISPLAY_ALL_PATH);
785 		} else {
786 			(void) printf("%s%s\n", mpxpath,
787 			    ((slicelen > 0) && slice != NULL) ? slice : "");
788 		}
789 	} else {
790 		(void) nvlist_lookup_string(thisdev,
791 		    ((readonlyroot) ? NVL_PHYSPATH :
792 		    ((mpxenp == B_TRUE) ? NVL_MPXPATH : NVL_PATH)),
793 		    &mpxpath);
794 		logmsg(MSG_INFO, "mpxpath = %s\n",
795 		    (mpxpath == NULL) ? "null" : mpxpath);
796 		if (readonlyroot ||
797 		    (strstr(mpxpath, "/scsi_vhci") != NULL) ||
798 		    (strstr(mpxpath, "/pci") != NULL) ||
799 		    (strstr(mpxpath, "/sbus") != NULL)) {
800 			/*
801 			 * If we see a physical path here it means that
802 			 * devlinks aren't fully initialised yet, so we
803 			 * are still in maintenance/single-user mode.
804 			 */
805 			(void) printf("/devices%s:%c\n", mpxpath,
806 			    slice[1] + '1');
807 		} else {
808 			(void) printf("%s%s%s\n",
809 			    (prefixt[0] == '/') ? prefixt : "",
810 			    mpxpath,
811 			    ((slicelen > 0) && slice != NULL) ? slice : "");
812 		}
813 	}
814 	free(prefixt);
815 	free(stripdev);
816 }
817 
818 /*
819  * Validate the in-kernel and on-disk forms of our devid cache,
820  * returns  -1 for unfixable error and 0 for success.
821  */
822 static int
823 validate_devnvl()
824 {
825 	di_node_t	curnode;
826 	int		rv1 = -1;
827 	int		rv2 = -1;
828 
829 	/*
830 	 * Method: we walk through the kernel's concept of the device tree
831 	 * looking for "ssd" then "sd" nodes.
832 	 * We check to see whether the device's devid is already in our nvlist
833 	 * (on disk) nvlist cache file. If it is, we check that it's components
834 	 * match what we've got already and fill any missing fields.
835 	 * If the devid isn't in our on-disk nvlist already then we add it
836 	 * and populate the property nvpairs.
837 	 *
838 	 * At the end of this function we should have this program's concept
839 	 * of the devid-keyed nvlist matching what is in the ondisk form which
840 	 * is ready to be written out.
841 	 * If we can't do this, then we return -1.
842 	 */
843 	curnode = di_drv_first_node("ssd", devinfo_root);
844 	if (curnode != DI_NODE_NIL)
845 		rv1 = mpxio_nvl_boilerplate(curnode);
846 
847 	curnode = di_drv_first_node("sd", devinfo_root);
848 	if (curnode != DI_NODE_NIL)
849 		rv2 = mpxio_nvl_boilerplate(curnode);
850 
851 	if (rv1 + rv2 == -2)
852 		return (-1);
853 
854 	return (0);
855 }
856 
857 /*
858  * According to devfs path name, it will print device node name.
859  */
860 static void
861 print_node_name(char *drv_name, char *strdevfspath)
862 {
863 	di_node_t	curnode;
864 	char *devfspath = NULL;
865 	char *node_name = NULL;
866 
867 	curnode = di_drv_first_node(drv_name, devinfo_root);
868 	for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
869 		devfspath = di_devfs_path(curnode);
870 		logmsg(MSG_INFO, "find: devfspath %s\n", devfspath);
871 
872 		if (devfspath == NULL)
873 			continue;
874 
875 		if ((strlen(strdevfspath) == strlen(devfspath)) &&
876 		    (strncmp(strdevfspath, devfspath,
877 		    strlen(devfspath)) == 0)) {
878 			node_name = find_link(curnode);
879 			if (node_name == NULL) {
880 				(void) printf("NOT MAPPED\n");
881 			} else {
882 				(void) printf("%s\n", node_name);
883 			}
884 			return;
885 		}
886 	}
887 }
888 
889 /*
890  * report device node name, search "ssd" and "sd" nodes,
891  * print the device node name which device path is same as
892  * parameter.
893  */
894 static void
895 report_dev_node_name(char *strdevfspath)
896 {
897 	logmsg(MSG_INFO, "strdevfspath: %s\n", strdevfspath);
898 	print_node_name("ssd", strdevfspath);
899 	print_node_name("sd", strdevfspath);
900 }
901 
902 static int
903 mpxio_nvl_boilerplate(di_node_t curnode)
904 {
905 	int		rv;
906 	char		*strdevid;
907 	ddi_devid_t	curdevid;
908 	nvlist_t	*newnvl;
909 
910 	for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
911 		errno = 0;
912 
913 		curdevid = NULL;
914 		get_devid(curnode, &curdevid);
915 		if (curdevid == NULL)
916 			/*
917 			 * There's no devid registered for this device
918 			 * so it's not cool enough to play with us
919 			 */
920 			continue;
921 
922 		strdevid = devid_str_encode(curdevid, NULL);
923 		/* does this exist in the on-disk cache? */
924 		rv = nvlist_lookup_nvlist(mapnvl, strdevid, &newnvl);
925 		if (rv == ENOENT) {
926 			logmsg(MSG_INFO, "nvlist for %s not found\n", strdevid);
927 			/* no, so alloc a new nvl to store it */
928 			if (nvlist_alloc(&newnvl, NV_UNIQUE_NAME, 0) != 0) {
929 				logmsg(MSG_ERROR,
930 				    gettext("Unable to allocate space for "
931 				    "a devid property list: %s\n"),
932 				    strerror(errno));
933 				return (-1);
934 			}
935 		} else {
936 			if ((rv != ENOTSUP) && (rv != EINVAL))
937 				logmsg(MSG_INFO,
938 				    "%s exists in ondisknvl, verifying\n",
939 				    strdevid);
940 		}
941 
942 		if (popcheck_devnvl(curnode, newnvl, strdevid) != 0) {
943 			logmsg(MSG_ERROR,
944 			    gettext("Unable to populate devid nvpair "
945 			    "for device with devid %s\n"),
946 			    strdevid);
947 			devid_str_free(strdevid);
948 			nvlist_free(newnvl);
949 			return (-1);
950 		}
951 
952 		/* Now add newnvl into our cache. */
953 		errno = 0;
954 		rv = nvlist_add_nvlist(mapnvl, strdevid, newnvl);
955 		if (rv) {
956 			logmsg(MSG_ERROR,
957 			    gettext("Unable to add device (devid %s) "
958 			    "to in-kernel nvl: %s (%d)\n"),
959 			    strdevid, strerror(rv), rv);
960 			devid_str_free(strdevid);
961 			nvlist_free(newnvl);
962 			return (-1);
963 		}
964 		logmsg(MSG_INFO,
965 		    gettext("added device (devid %s) to mapnvl\n\n"),
966 		    strdevid);
967 		devid_str_free(strdevid);
968 	}
969 	return (0);
970 }
971 
972 /*
973  * Operates on a single di_node_t, collecting all the device properties
974  * that we need. devnvl is allocated by the caller, and we add our nvpairs
975  * to it if they don't already exist.
976  *
977  * We are _only_ interested in devices which have a devid. We pull in
978  * devices even when they're excluded via stmsboot -D (driver), because
979  * we don't want to miss out on any devid data that might be handy later.
980  */
981 static int
982 popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, char *strdevid)
983 {
984 	char *path = NULL;
985 	char *curpath = NULL;
986 	char *devfspath = NULL;
987 	char *prop = NULL;
988 	int scsivhciparent = 0;
989 	int rv = 0;
990 	boolean_t mpxenp = B_FALSE;
991 
992 	errno = 0;
993 	devfspath = di_devfs_path(thisnode);
994 	if (devfspath == NULL) {
995 		logmsg(MSG_ERROR,
996 		    gettext("Unable to determine devfs path for node: %s\n"),
997 		    strerror(errno));
998 		return (-1);
999 	}
1000 
1001 	/* Add a convenient devfspath to devid inverse map */
1002 	if (nvlist_add_string(mapnvl, devfspath, strdevid) != 0) {
1003 		logmsg(MSG_ERROR,
1004 		    gettext("Unable to add device path %s with devid "
1005 		    "%s to mapnvl\n"), devfspath, strdevid);
1006 		return (-1);
1007 	}
1008 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, di_parent_node(thisnode),
1009 	    "mpxio-disable", &prop) >= 0) {
1010 		if (strncmp(prop, "yes", 3) == 0) {
1011 			if (!mpxprop)
1012 				mpxprop++;
1013 		}
1014 	}
1015 
1016 	if (strncmp(di_driver_name(di_parent_node(thisnode)),
1017 	    "scsi_vhci", 9) == 0) {
1018 		scsivhciparent = 1;
1019 		if (!mpxenabled)
1020 			mpxenabled++;
1021 
1022 		rv = nvlist_lookup_boolean_value(devnvl, NVL_MPXEN, &mpxenp);
1023 		if (rv || (mpxenp == B_FALSE)) {
1024 			rv = nvlist_add_boolean_value(devnvl,
1025 			    NVL_MPXEN, B_TRUE);
1026 			if (rv) {
1027 				logmsg(MSG_ERROR,
1028 				    gettext("Unable to add property %s "
1029 				    "(set to B_TRUE) for device %s: "
1030 				    "%s (%d)\n"),
1031 				    NVL_MPXEN, devfspath,
1032 				    strerror(rv), rv);
1033 				return (-1);
1034 			}
1035 			logmsg(MSG_INFO, "NVL_MPXEN :: (B_FALSE->B_TRUE)\n");
1036 		}
1037 	} else {
1038 		/* turn _off_ the flag if it was enabled */
1039 		rv = nvlist_add_boolean_value(devnvl, NVL_MPXEN, B_FALSE);
1040 		if (rv) {
1041 			logmsg(MSG_ERROR,
1042 			    gettext("Unable to add property %s "
1043 			    "(set to B_FALSE) for device %s: %s (%d)\n"),
1044 			    NVL_MPXEN, devfspath,
1045 			    strerror(rv), rv);
1046 			return (-1);
1047 		}
1048 		logmsg(MSG_INFO, "NVL_MPXEN :: (B_TRUE-> B_FALSE)\n");
1049 	}
1050 
1051 	rv = nvlist_add_string(devnvl, NVL_PHYSPATH, devfspath);
1052 	if (rv) {
1053 		logmsg(MSG_ERROR,
1054 		    gettext("Unable to add physical device path (%s) "
1055 		    "property to nvl\n"));
1056 		return (-1);
1057 	}
1058 
1059 	if ((curpath = calloc(1, MAXPATHLEN)) == NULL) {
1060 		logmsg(MSG_ERROR,
1061 		    gettext("Unable to allocate space for current path\n"));
1062 		return (-1);
1063 	}
1064 	curpath = find_link(thisnode);
1065 	if (curpath == NULL) {
1066 		if (readonlyroot) {
1067 			return (0);
1068 		}
1069 		logmsg(MSG_ERROR,
1070 		    gettext("Unable to determine device path for node %s\n"),
1071 		    devfspath);
1072 		return (-1);
1073 	}
1074 
1075 	rv = nvlist_lookup_string(devnvl, NVL_MPXPATH, &path);
1076 
1077 	if (scsivhciparent) {
1078 		(void) nvlist_add_string(devnvl, NVL_MPXPATH, curpath);
1079 	} else {
1080 		(void) nvlist_add_string(devnvl, NVL_PATH, curpath);
1081 		path = curpath;
1082 	}
1083 
1084 	/*
1085 	 * This next block provides the path to devid inverse mapping
1086 	 * that other functions require
1087 	 */
1088 	if (path != NULL) {
1089 		if (nvlist_add_string(mapnvl, path, strdevid) != 0) {
1090 			logmsg(MSG_ERROR,
1091 			    gettext("Unable to add device %s with devid "
1092 			    "%s to mapnvl\n"), path, strdevid);
1093 			return (-1);
1094 		}
1095 		logmsg(MSG_INFO, "popcheck_devnvl: added path %s :: %s\n",
1096 		    path, strdevid);
1097 	}
1098 
1099 	if (nvlist_add_string(mapnvl, curpath, strdevid) != 0) {
1100 			logmsg(MSG_ERROR,
1101 			    gettext("Unable to add device %s with devid "
1102 			    "%s to mapnvl: %s\n"),
1103 			    curpath, strdevid, strerror(errno));
1104 			return (-1);
1105 	}
1106 	logmsg(MSG_INFO, "popcheck_devnvl: added curpath %s :: %s\n",
1107 	    curpath, strdevid);
1108 
1109 	return (0);
1110 }
1111 
1112 static void
1113 print_mpx_capable(di_node_t curnode)
1114 {
1115 	char *prop;
1116 	char *path;
1117 	char *aliases = NULL;
1118 
1119 	if (cap_N_option) {
1120 		aliases = calloc(1, MAXPATHLEN + 1);
1121 		if (aliases == NULL) {
1122 			logmsg(MSG_ERROR,
1123 			    gettext("Unable to allocate memory for a device "
1124 			    "alias list\n"));
1125 			return;
1126 		}
1127 	}
1128 
1129 	for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
1130 		if (di_prop_lookup_strings(DDI_DEV_T_ANY, curnode,
1131 		    "initiator-port", &prop) >= 0) {
1132 			if ((path = di_devfs_path(curnode)) == NULL) {
1133 				logmsg(MSG_INFO,
1134 				    "Unable to find devfs path for device "
1135 				    "%s: %s\n", &curnode, strerror(errno));
1136 				continue;
1137 			}
1138 			if (cap_N_option) {
1139 				char *nodename = di_node_name(curnode);
1140 				/* nodename is never going to be null */
1141 				if (strstr(aliases, nodename) == NULL)
1142 					/* haven't seen this nodename before */
1143 					(void) snprintf(aliases,
1144 					    MAXPATHLEN + 1, "%s|%s",
1145 					    ((aliases != NULL) ? aliases : ""),
1146 					    nodename);
1147 			} else
1148 				(void) printf("%s\n", path);
1149 		}
1150 	}
1151 	if (cap_N_option)
1152 		(void) printf("%s\n", aliases);
1153 }
1154 
1155 static int
1156 link_cb(di_devlink_t devlink, void *arg)
1157 {
1158 	const char *result;
1159 
1160 	result = di_devlink_path(devlink);
1161 	if (result == NULL) {
1162 		arg = (void *)"(null)";
1163 	} else {
1164 		(void) strlcpy(arg, result, strlen(result));
1165 	}
1166 	logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n",
1167 	    ((result != NULL) ? result : "(null)"));
1168 	return (DI_WALK_CONTINUE);
1169 }
1170 
1171 static char *
1172 find_link(di_node_t cnode)
1173 {
1174 	di_minor_t devminor = DI_MINOR_NIL;
1175 	di_devlink_handle_t	hdl;
1176 	char *devfspath = NULL;
1177 	char *minorpath = NULL;
1178 	char *linkname = NULL;
1179 	char *cbresult = NULL;
1180 
1181 	devfspath = di_devfs_path(cnode);
1182 	if (cnode == DI_NODE_NIL) {
1183 		logmsg(MSG_ERROR,
1184 		    gettext("find_ctrl must be called with non-null "
1185 		    "di_node_t\n"));
1186 		return (NULL);
1187 	}
1188 	logmsg(MSG_INFO, "find_link: devfspath %s\n", devfspath);
1189 
1190 	if (((cbresult = calloc(1, MAXPATHLEN)) == NULL) ||
1191 	    ((minorpath = calloc(1, MAXPATHLEN)) == NULL) ||
1192 	    ((linkname = calloc(1, MAXPATHLEN)) == NULL)) {
1193 		logmsg(MSG_ERROR, "unable to allocate space for dev link\n");
1194 		return (NULL);
1195 	}
1196 
1197 	devminor = di_minor_next(cnode, devminor);
1198 	hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK);
1199 	if (hdl == NULL) {
1200 		logmsg((readonlyroot ? MSG_INFO : MSG_ERROR),
1201 		    gettext("unable to take devlink snapshot: %s\n"),
1202 		    strerror(errno));
1203 		return (NULL);
1204 	}
1205 
1206 	linkname = "^dsk/";
1207 	(void) snprintf(minorpath, MAXPATHLEN, "%s:c", devfspath);
1208 
1209 	errno = 0;
1210 	if (di_devlink_walk(hdl, linkname, minorpath, DI_PRIMARY_LINK,
1211 	    (void *)cbresult, link_cb) < 0) {
1212 		logmsg(MSG_ERROR,
1213 		    gettext("Unable to walk devlink snapshot for %s: %s\n"),
1214 		    minorpath, strerror(errno));
1215 		return (NULL);
1216 	}
1217 
1218 	if (di_devlink_fini(&hdl) < 0) {
1219 		logmsg(MSG_ERROR,
1220 		    gettext("Unable to close devlink snapshot: %s\n"),
1221 		    strerror(errno));
1222 	}
1223 	if (strstr(cbresult, "dsk/") == NULL)
1224 		return (devfspath);
1225 
1226 	bzero(minorpath, MAXPATHLEN);
1227 	/* strip off the trailing "s2" */
1228 	bcopy(cbresult, minorpath, strlen(cbresult) - 1);
1229 	/* Now strip off the /dev/dsk/ prefix for output flexibility */
1230 	linkname = strrchr(minorpath, '/');
1231 	return (++linkname);
1232 }
1233 
1234 /*
1235  * handle case where device has been probed but its target driver is not
1236  * attached so enumeration has not quite finished. Opening the /devices
1237  * pathname will force the kernel to finish the enumeration process and
1238  * let us get the data we need.
1239  */
1240 static void
1241 get_devid(di_node_t node, ddi_devid_t *thisdevid)
1242 {
1243 	int fd;
1244 	char realpath[MAXPATHLEN];
1245 	char *openpath = di_devfs_path(node);
1246 
1247 	errno = 0;
1248 	bzero(realpath, MAXPATHLEN);
1249 	if (strstr(openpath, "/devices") == NULL) {
1250 		(void) snprintf(realpath, MAXPATHLEN,
1251 		    "/devices%s:c,raw", openpath);
1252 		fd = open(realpath, O_RDONLY|O_NDELAY);
1253 	} else {
1254 		fd = open(openpath, O_RDONLY|O_NDELAY);
1255 	}
1256 
1257 	if (fd < 0) {
1258 		logmsg(MSG_INFO, "Unable to open path %s: %s\n",
1259 		    openpath, strerror(errno));
1260 		return;
1261 	}
1262 
1263 	if (devid_get(fd, thisdevid) != 0) {
1264 		logmsg(MSG_INFO,
1265 		    "'%s' node (%s) without a devid registered\n",
1266 		    di_driver_name(node), di_devfs_path(node));
1267 	}
1268 	(void) close(fd);
1269 }
1270 
1271 static int
1272 print_bootpath()
1273 {
1274 	char *bootprop = NULL;
1275 
1276 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root,
1277 	    "bootpath", &bootprop) >= 0) {
1278 		(void) printf("%s\n", bootprop);
1279 		return (0);
1280 	} else if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root,
1281 	    "boot-path", &bootprop) >= 0) {
1282 		(void) printf("%s\n", bootprop);
1283 		return (0);
1284 	} else {
1285 		(void) printf("ERROR: no bootpath/boot-path property found\n");
1286 		return (ENOENT);
1287 	}
1288 }
1289 
1290 static void
1291 get_phci_driver_name(char *phci_path, char **driver_name)
1292 {
1293 	di_node_t phci_node = DI_NODE_NIL;
1294 	char *tmp = NULL;
1295 
1296 	phci_node = di_init(phci_path, DINFOCPYONE);
1297 	if (phci_node == DI_NODE_NIL) {
1298 		logmsg(MSG_ERROR,
1299 		    gettext("Unable to take phci snapshot "
1300 		    "(%s: %d)\n"), strerror(errno), errno);
1301 		return;
1302 	}
1303 	tmp = di_driver_name(phci_node);
1304 	if (tmp != NULL) {
1305 		(void) strncpy(*driver_name, tmp, 10);
1306 	}
1307 	di_fini(phci_node);
1308 }
1309 
1310 /*
1311  * We only call this routine if we have a scsi_vhci node and must
1312  * determine the actual physical path of its first online client
1313  * path.
1314  */
1315 static void
1316 vhci_to_phci(char *devpath, char *slice, int d_flag)
1317 {
1318 	sv_iocdata_t	ioc;
1319 	sv_path_info_t	*pi;
1320 	int		vhci_fd;
1321 	int		rv;
1322 	uint_t		npaths = 0;
1323 	char nodename[MAXPATHLEN];
1324 	char *phci_driver = NULL;
1325 
1326 	vhci_fd = open(VHCI_CTL_NODE, O_RDWR);
1327 	if (vhci_fd < 0)
1328 		goto failure;
1329 
1330 	bzero(&ioc, sizeof (sv_iocdata_t));
1331 	ioc.client = devpath;
1332 	ioc.ret_elem = &npaths;
1333 	rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc);
1334 	if (rv || npaths == 0) {
1335 		logmsg(MSG_INFO,
1336 		    "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() failed, "
1337 		    "%s (%d)\n", strerror(rv), rv);
1338 		goto failure;
1339 	}
1340 
1341 	bzero(&ioc, sizeof (sv_iocdata_t));
1342 	ioc.client = devpath;
1343 	ioc.buf_elem = npaths;
1344 	ioc.ret_elem = &npaths;
1345 	if ((ioc.ret_buf = calloc(npaths, sizeof (sv_path_info_t)))
1346 	    == NULL)
1347 		goto failure;
1348 	rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc);
1349 	if (rv || npaths == 0) {
1350 		logmsg(MSG_INFO,
1351 		    "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() (#2) "
1352 		    "failed, %s (%d)\n", strerror(rv), rv);
1353 		free(ioc.ret_buf);
1354 		goto failure;
1355 	}
1356 
1357 	if (ioc.buf_elem < npaths)
1358 		npaths = ioc.buf_elem;
1359 
1360 	phci_driver = malloc(10);
1361 	if (phci_driver == NULL) {
1362 		logmsg(MSG_INFO,
1363 		    "vhci_to_phci: Memory allocation failed\n");
1364 		free(ioc.ret_buf);
1365 		goto failure;
1366 	}
1367 
1368 	pi = (sv_path_info_t *)ioc.ret_buf;
1369 	while (npaths--) {
1370 		bzero(nodename, MAXPATHLEN);
1371 		bzero(phci_driver, 10);
1372 
1373 		get_phci_driver_name(pi->device.ret_phci,
1374 		    &phci_driver);
1375 		logmsg(MSG_INFO, "phci driver name: %s\n", phci_driver);
1376 		/*
1377 		 * A hack, but nicer than a platform-specific ifdef
1378 		 * fp on SPARC using "ssd" as nodename
1379 		 * mpt use "sd" when mpxio disabled, use "disk" when
1380 		 * mpxio is enabled
1381 		 * for alll other cases, "disk" should be used as the
1382 		 * nodename
1383 		 */
1384 		if (strstr(devpath, "ssd") != NULL) {
1385 			(void) snprintf(nodename, 5, "ssd");
1386 		} else if (strncmp(phci_driver, "mpt", 10) == 0) {
1387 			(void) snprintf(nodename, 5, "sd");
1388 		} else {
1389 			(void) snprintf(nodename, 5, "disk");
1390 		}
1391 		if ((d_flag == DISPLAY_ONE_PATH) &&
1392 		    (pi->ret_state == MDI_PATHINFO_STATE_ONLINE)) {
1393 			(void) printf("%s/%s@%s", pi->device.ret_phci,
1394 			    nodename, pi->ret_addr);
1395 			if ((slice != NULL) && (strlen(slice) <= 3)) {
1396 				(void) printf("%s\n", slice);
1397 			} else {
1398 				(void) printf("\n");
1399 			}
1400 			break;
1401 		} else if (d_flag == DISPLAY_ALL_PATH) {
1402 			(void) printf("%s/%s@%s", pi->device.ret_phci,
1403 			    nodename, pi->ret_addr);
1404 			if ((slice != NULL) && (strlen(slice) <= 3)) {
1405 				(void) printf("%s\n", slice);
1406 			} else {
1407 				(void) printf("\n");
1408 			}
1409 		}
1410 		pi++;
1411 	}
1412 	free(ioc.ret_buf);
1413 	free(phci_driver);
1414 	return;
1415 
1416 failure:
1417 	(void) printf("NOT_MAPPED\n");
1418 }
1419 
1420 /*
1421  * Write /etc/vfstab to /etc/vfstab.new, with any remapped device
1422  * names substituted.
1423  *
1424  * Returns:
1425  *	0	successful operation
1426  *	-1	failed
1427  */
1428 static int
1429 update_vfstab()
1430 {
1431 	FILE *fdin, *fdout;
1432 	char *buf, *tmpbuf;
1433 	char fname[MAXPATHLEN];
1434 	int rv = -1, rval = -1;
1435 	char cdev[MAXPATHLEN];
1436 	char bdev[MAXPATHLEN];
1437 	char mntpt[MAXPATHLEN];
1438 	char fstype[512];
1439 	char fsckpass[512];
1440 	char mntboot[512];
1441 	char mntopt[MAXPATHLEN];
1442 	char fmt[80];
1443 	char *prefixt = NULL;
1444 	char *curdev = NULL;
1445 	char *thisdevid = NULL;
1446 	char *slice = NULL;
1447 	nvlist_t *thisdev;
1448 	boolean_t devmpx = B_FALSE;
1449 
1450 	buf = calloc(1, MAXPATHLEN);
1451 	tmpbuf = calloc(1, MAXPATHLEN);
1452 	if (buf == NULL || tmpbuf == NULL)
1453 		return (-1);
1454 
1455 	(void) snprintf(fname, MAXPATHLEN, "/etc/mpxio/vfstab.new");
1456 
1457 	fdin = fopen("/etc/vfstab", "r");
1458 	fdout = fopen(fname, "w+");
1459 	if (fdin == NULL || fdout == NULL) {
1460 		logmsg(MSG_INFO, "Unable to open vfstab or create a backup "
1461 		    "vfstab %s\n");
1462 		return (-1);
1463 	}
1464 
1465 	(void) snprintf(fmt, sizeof (fmt),
1466 	    "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1,
1467 	    sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1,
1468 	    sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1);
1469 
1470 	while (fgets(buf, MAXPATHLEN, fdin) != NULL) {
1471 		if (strlen(buf) == (MAXPATHLEN - 1) &&
1472 		    buf[MAXPATHLEN-2] != '\n') {
1473 			logmsg(MSG_ERROR,
1474 			    gettext("/etc/vfstab line length too long, "
1475 			    "exceeded %2$d: \"%3$s\"\n"),
1476 			    MAXPATHLEN - 2, buf);
1477 			goto out;
1478 		}
1479 
1480 		prefixt = NULL;
1481 		curdev = NULL;
1482 		slice = NULL;
1483 		thisdevid = NULL;
1484 		thisdev = NULL;
1485 
1486 		/* LINTED - variable format specifier */
1487 		rv = sscanf(buf, fmt, bdev, cdev, mntpt, fstype, fsckpass,
1488 		    mntboot, mntopt);
1489 
1490 		/*
1491 		 * Walk through the lines in the input file (/etc/vfstab),
1492 		 * skipping anything which is _not_ a COGD (common or garden
1493 		 * disk), ie all the /devices, /system, /dev/md, /dev/vx and
1494 		 * /dev/zvol and so forth.
1495 		 */
1496 		if ((rv == 7) && (bdev[0] == '/') &&
1497 		    (strstr(bdev, "/dev/dsk"))) {
1498 			slice = strrchr(bdev, 's');
1499 			/* take a copy, strip off /dev/dsk/ */
1500 			prefixt = strrchr(bdev, 'c');
1501 			prefixt[strlen(bdev) - 9 - strlen(slice)] = '\0';
1502 			slice++; /* advance past the s */
1503 			rval = nvlist_lookup_string(mapnvl, prefixt,
1504 			    &thisdevid);
1505 			if (rval) {
1506 				/* Whoa, where did this device go?! */
1507 				logmsg(MSG_INFO,
1508 				    "error looking up device %s\n", prefixt);
1509 				/* Comment-out this line in the new version */
1510 				(void) snprintf(tmpbuf, MAXPATHLEN,
1511 				    "# DEVICE NOT FOUND %s", buf);
1512 				(void) fprintf(fdout, "%s", tmpbuf);
1513 				continue;
1514 			} else {
1515 				/* The device exists in our mapnvl */
1516 				(void) nvlist_lookup_nvlist(mapnvl, thisdevid,
1517 				    &thisdev);
1518 				(void) nvlist_lookup_boolean_value(thisdev,
1519 				    NVL_MPXEN, &devmpx);
1520 				(void) nvlist_lookup_string(thisdev,
1521 				    ((devmpx == B_TRUE)
1522 				    ? NVL_MPXPATH : NVL_PATH),
1523 				    &curdev);
1524 			}
1525 		}
1526 
1527 		if ((prefixt != NULL) && (curdev != NULL) &&
1528 		    (rv = (strncmp(prefixt, curdev, strlen(prefixt)) != 0))) {
1529 			/* Mapping change for this device */
1530 			if (strcmp(fstype, "swap") == 0) {
1531 				(void) snprintf(tmpbuf, MAXPATHLEN,
1532 				    "/dev/dsk/%ss%s\t-\t-\tswap\t"
1533 				    "%s\t%s\t%s\n",
1534 				    curdev, slice, fsckpass, mntboot, mntopt);
1535 			} else {
1536 				(void) snprintf(tmpbuf, MAXPATHLEN,
1537 				    "/dev/dsk/%ss%s\t/dev/rdsk/%ss%s\t"
1538 				    "%s\t%s\t%s\t%s\t%s\n",
1539 				    curdev, slice, curdev, slice,
1540 				    mntpt, fstype, fsckpass, mntboot, mntopt);
1541 			}
1542 			errno = 0;
1543 			(void) fprintf(fdout, "%s", tmpbuf);
1544 		} else {
1545 			(void) fprintf(fdout, "%s", buf);
1546 		}
1547 
1548 		errno = 0;
1549 		if (fflush(fdout) != 0) {
1550 			logmsg(MSG_ERROR,
1551 			    gettext("fprintf failed to write to %s: %s (%d)\n"),
1552 			    fname, strerror(errno), errno);
1553 			goto out;
1554 		}
1555 	}
1556 out:
1557 	(void) fclose(fdin);
1558 	(void) fclose(fdout);
1559 	free(buf);
1560 	free(tmpbuf);
1561 	return (errno);
1562 }
1563