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