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