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