xref: /titanic_52/usr/src/cmd/stmsboot/stmsboot_util.c (revision 940d71d237794874e18a0eb72f6564821a823517)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <stropts.h>
38 #include <strings.h>
39 #include <dirent.h>
40 #include <sys/param.h>
41 #include <sys/scsi/adapters/scsi_vhci.h>
42 #include <libdevinfo.h>
43 #include <libgen.h>
44 #include <dlfcn.h>
45 #include <link.h>
46 #include <locale.h>
47 #include <libintl.h>
48 #include <sys/syscall.h>
49 #include <sys/mnttab.h>
50 #include <sys/vfstab.h>
51 #include <sys/mount.h>
52 #include <devid.h>
53 #include <sys/libdevid.h>
54 
55 #define	VHCI_CTL_NODE	"/devices/scsi_vhci:devctl"
56 #define	SLASH_DEVICES	"/devices/"
57 
58 #ifdef	sparc
59 #define	DISK_NODE_NAME	"ssd"
60 #define	DISK_DRV_NAME	"ssd"
61 #else	/* sparc */
62 #define	DISK_NODE_NAME	"disk"
63 #define	DISK_DRV_NAME	"sd"
64 #endif
65 
66 #define	DISK_AT_G	"disk@g"
67 #define	SLASH_FP_AT	"/fp@"
68 #define	SLASH_SCSI_VHCI	"/scsi_vhci"
69 #define	DEV_DSK		"/dev/dsk/"
70 #define	DEV_RDSK	"/dev/rdsk/"
71 #define	SYS_FILENAME_LEN	256
72 
73 /*
74  * Save directory is the directory in which system files are saved.
75  * Save directory must be under the root filesystem, as this program is
76  * typically run before any other filesystems are mounted.
77  */
78 #define	SAVE_DIR	"/etc/mpxio"
79 
80 /* fcp driver publishes this property */
81 #define	NODE_WWN_PROP	"node-wwn"
82 
83 /*
84  * For SAS, we look for "sas-$drivername", eg sas-mpt, but
85  * we strncat the driver name later once we've parsed the
86  * args passed in from the shell.
87  */
88 #define	SASPROP	 "sas-"
89 
90 
91 typedef enum {
92 	CLIENT_TYPE_UNKNOWN,
93 	CLIENT_TYPE_PHCI,
94 	CLIENT_TYPE_VHCI
95 } client_type_t;
96 
97 struct devlink_cbarg {
98 	char *devlink;
99 	size_t len;
100 };
101 
102 static di_node_t devinfo_root = DI_NODE_NIL;
103 static di_devlink_handle_t devlink_hdl = NULL;
104 static int vhci_fd = -1;
105 static int patch_vfstab, cap_m_option, debug;
106 static int list_option, list_guid_mappings, list_controllernum = -1;
107 static char *mapdev = "";
108 static char *map_vhciname = "";
109 static char *stmsboot = "stmsboot";
110 
111 char *drvname = (char *)NULL; /* "fp" or "mpt" or ... */
112 /* "node-wwn" if drvname=fp, or "sas-$drivername" otherwise */
113 char *drvprop = (char *)NULL;
114 static int parent = 0; /* for "-n" usage */
115 
116 static int make_temp(char *, char *, char *, size_t);
117 static void commit_change(char *, char *, char *, int);
118 static int map_devname(char *, char *, size_t, int);
119 static int update_vfstab(char *, char *);
120 static int list_mappings(int, int);
121 static int canopen(char *);
122 static client_type_t client_by_props(char *path);
123 static void list_nodes(char *drivername);
124 static int canread(char *, char *);
125 
126 static void logerr(char *, ...);
127 static void logdmsg(char *, ...);
128 static void *s_malloc(const size_t);
129 static char *s_strdup(const char *);
130 static void s_strlcpy(char *, const char *, size_t);
131 static int map_openable_vhciname(char *, char *, size_t);
132 /*
133  * Using an exit function not marked __NORETURN causes a warning with gcc.
134  * To suppress the warning, use __NORETURN attribute.
135  */
136 static void clean_exit(int)__NORETURN;
137 
138 /*
139  * Print usage and exit.
140  */
141 static void
142 usage(char *argv0)
143 {
144 	char *progname;
145 
146 	progname = strrchr(argv0, '/');
147 	if (progname != NULL)
148 		progname++;
149 	else
150 		progname = argv0;
151 
152 	/*
153 	 * -u	update /etc/vfstab
154 	 * -m devname
155 	 *	if devname is phci based name and not open-able, map it to
156 	 *	vhci based /devices name.
157 	 *	if devname is vhci based name and not open-able, map it to
158 	 *	phci based /devices name.
159 	 * -M devname
160 	 *	same as -m except that /dev link is printed instead of
161 	 *	/devices name.
162 	 * -l controller
163 	 *	list non-STMS to STMS device name mappings for the specific
164 	 *	controller
165 	 * -L	list non-STMS to STMS device name mappings for all controllers
166 	 * -p devname
167 	 *	if devname is vhci based name and open-able, get the first
168 	 *	onlined phci based name without /devices prefix.
169 	 *	Used in stmsboot to update the phci based bootpath.
170 	 * -D drvname
171 	 *	if supplied, indicates that we're going to operate on
172 	 *	devices attached to this driver
173 	 * -n
174 	 *	if supplied, returns name of the node containing "fp" or
175 	 *	"sas-$driver", appends "sd@" or "ssd@" or "disk@". Can only
176 	 *	be used if -D drv is specified as well
177 	 */
178 	(void) fprintf(stderr, gettext("usage: %s -u | -m devname | "
179 	    "-M devname | -l controller | -L | \n"
180 	    "\t\t-p devname | -D { fp | mpt } | -n\n"), progname);
181 	exit(2);
182 }
183 
184 /*
185  * Parse command line arguments.
186  */
187 static void
188 parse_args(int argc, char *argv[])
189 {
190 	char opt;
191 	int n = 0;
192 
193 	if (argc == 1) {
194 		usage(argv[0]);
195 		/*NOTREACHED*/
196 	}
197 
198 	while ((opt = getopt(argc, argv, "udm:M:Ll:gp:D:n")) != EOF) {
199 		switch (opt) {
200 		case 'u':
201 			patch_vfstab = 1;
202 			n++;
203 			break;
204 
205 		case 'd':
206 			debug = 1;
207 			break;
208 
209 		case 'm':
210 			mapdev = s_strdup(optarg);
211 			n++;
212 			break;
213 
214 		case 'M':
215 			mapdev = s_strdup(optarg);
216 			cap_m_option = 1;
217 			n++;
218 			break;
219 
220 		case 'L':
221 			list_option = 1;
222 			n++;
223 			break;
224 
225 		case 'l':
226 			list_option = 1;
227 			list_controllernum = (int)atol(optarg);
228 			if (list_controllernum < 0) {
229 				logerr(gettext("controller number %d is "
230 				    "invalid\n"), list_controllernum);
231 				clean_exit(1);
232 			}
233 			n++;
234 			break;
235 
236 		case 'g':
237 			/*
238 			 * private option to display non-STMS device name
239 			 * to GUID mappings.
240 			 */
241 			list_guid_mappings = 1;
242 			break;
243 
244 		case 'p':
245 			/*
246 			 * map openable vhci based name to phci base name
247 			 */
248 			map_vhciname = s_strdup(optarg);
249 			n++;
250 			break;
251 
252 		case 'D':
253 			/*
254 			 * Grab the driver name we need to look for. Each
255 			 * time we add support for a new SAS or FC driver
256 			 * to this utility, make sure that its driver name
257 			 * is checked here.
258 			 */
259 			drvname = s_malloc(sizeof (optarg) + 1);
260 			drvname = s_strdup(optarg);
261 			if (strcmp(drvname, "fp") == 0) {
262 				drvprop = s_malloc(sizeof (NODE_WWN_PROP));
263 				(void) snprintf(drvprop, sizeof (NODE_WWN_PROP),
264 				    NODE_WWN_PROP);
265 			} else if (strcmp(drvname, "mpt") == 0) {
266 				drvprop = s_malloc(sizeof (SASPROP) +
267 				    sizeof (drvname) + 1);
268 				(void) snprintf(drvprop, sizeof (SASPROP) +
269 				    sizeof (drvname), "%s%s",
270 				    SASPROP, drvname);
271 			} else {
272 				logerr(gettext("Driver %s is not supported\n"),
273 				    drvname);
274 				clean_exit(1);
275 			}
276 
277 			break;
278 
279 		case 'n':
280 			++parent;
281 			n++;
282 			break;
283 
284 		default:
285 			usage(argv[0]);
286 			/*NOTREACHED*/
287 		}
288 	}
289 
290 	if (n != 1) {
291 		usage(argv[0]);
292 		/*NOTREACHED*/
293 	}
294 }
295 
296 int
297 main(int argc, char *argv[])
298 {
299 	char save_vfstab[SYS_FILENAME_LEN], tmp_vfstab[SYS_FILENAME_LEN];
300 	int vfstab_updated;
301 
302 	(void) setlocale(LC_ALL, "");
303 	(void) textdomain(TEXT_DOMAIN);
304 
305 	if (getuid() != 0) {
306 		logerr(gettext("must be super-user to run this program\n"));
307 		clean_exit(1);
308 	}
309 
310 	parse_args(argc, argv);
311 	(void) umask(022);
312 
313 	/*
314 	 * NOTE: The mpxio boot-up script executes this program with the
315 	 * mapping (-m) option before the /usr is even mounted and when the
316 	 * root filesystem is still mounted read-only.
317 	 */
318 	if (*mapdev != '\0') {
319 		char newname[MAXPATHLEN];
320 
321 		if (map_devname(mapdev, newname, sizeof (newname),
322 		    cap_m_option) == 0) {
323 			(void) printf("%s\n", newname);
324 			clean_exit(0);
325 		}
326 		clean_exit(1);
327 	}
328 	if (*map_vhciname != '\0') {
329 		char newname[MAXPATHLEN];
330 
331 		if (map_openable_vhciname(map_vhciname, newname,
332 		    sizeof (newname)) == 0) {
333 			(void) printf("%s\n", newname);
334 			clean_exit(0);
335 		}
336 		clean_exit(1);
337 	}
338 
339 	if (list_option || list_guid_mappings) {
340 		if (list_mappings(list_controllernum, list_guid_mappings) == 0)
341 			clean_exit(0);
342 		clean_exit(1);
343 	}
344 
345 	if (parent > 0) {
346 		if (strcmp(drvname, "") == 0) {
347 			usage(argv[0]);
348 			clean_exit(1);
349 		} else {
350 			list_nodes(drvname);
351 			clean_exit(0);
352 		}
353 	}
354 
355 	/* create a directory where a copy of the system files are saved */
356 	if (patch_vfstab) {
357 		if (mkdirp(SAVE_DIR, 0755) != 0 && errno != EEXIST) {
358 			logerr(gettext("mkdirp: failed to create %1$s: %2$s\n"),
359 			    SAVE_DIR, strerror(errno));
360 			clean_exit(1);
361 		}
362 
363 		if (make_temp(VFSTAB, save_vfstab, tmp_vfstab,
364 		    SYS_FILENAME_LEN) != 0)
365 			clean_exit(1);
366 
367 		/* build new vfstab without modifying the existing one */
368 		if ((vfstab_updated = update_vfstab(VFSTAB, tmp_vfstab))
369 		    == -1) {
370 			logerr(gettext("failed to update %s\n"), VFSTAB);
371 			clean_exit(1);
372 		}
373 
374 		commit_change(VFSTAB, save_vfstab, tmp_vfstab, vfstab_updated);
375 	}
376 
377 	clean_exit(0);
378 	/*NOTREACHED*/
379 }
380 
381 /*
382  * Make saved and temporary filenames in SAVE_DIR.
383  *
384  * ex: if the filename is /etc/vfstab then the save_filename and tmp_filename
385  * would be SAVE_DIR/vfstab and SAVE_DIR/vfstab.tmp respectively.
386  *
387  * Returns 0 on success, -1 on failure.
388  */
389 static int
390 make_temp(char *filename, char *save_filename, char *tmp_filename, size_t len)
391 {
392 	char *ptr;
393 
394 	if ((ptr = strrchr(filename, '/')) == NULL) {
395 		logdmsg("invalid file %s\n", filename);
396 		return (-1);
397 	}
398 	(void) snprintf(save_filename, len, "%s%s", SAVE_DIR, ptr);
399 	(void) snprintf(tmp_filename, len, "%s%s.tmp", SAVE_DIR, ptr);
400 	logdmsg("make_temp: %s: save = %s, temp = %s\n", filename,
401 	    save_filename, tmp_filename);
402 	return (0);
403 }
404 
405 /*
406  * Commit the changes made to the system file
407  */
408 static void
409 commit_change(char *filename, char *save_filename, char *tmp_filename,
410     int updated)
411 {
412 	int x;
413 
414 	if (updated) {
415 		/* save the original */
416 		if ((x = rename(filename, save_filename)) != 0) {
417 			logerr(gettext("rename %1$s to %2$s failed: %3$s\n"),
418 			    filename, save_filename, strerror(errno));
419 		}
420 
421 		/* now rename the new file to the actual file */
422 		if (rename(tmp_filename, filename) != 0) {
423 			logerr(gettext("rename %1$s to %2$s failed: %3$s\n"),
424 			    tmp_filename, filename, strerror(errno));
425 
426 			/* restore the original */
427 			if (x == 0 && rename(save_filename, filename) != 0) {
428 				logerr(
429 				    gettext("rename %1$s to %2$s failed: %3$s\n"
430 				    "%4$s is a copy of the original %5$s file"
431 				    "\n"),
432 				    save_filename, filename, strerror(errno),
433 				    save_filename, filename);
434 			}
435 		} else
436 			(void) printf(gettext("%1$s: %2$s has been updated.\n"),
437 			    stmsboot, filename);
438 	} else {
439 		/* remove the temp file */
440 		(void) unlink(tmp_filename);
441 		(void) printf(gettext("%1$s: %2$s was not modified as no "
442 		    "changes were needed.\n"), stmsboot, filename);
443 	}
444 }
445 
446 /*
447  * Get the GUID of the device.
448  *
449  * physpath	/devices name without the /devices prefix and minor name
450  *		component.
451  * guid		caller supplied buffer where the GUID will be placed on return
452  * guid_len	length of the caller supplied guid buffer.
453  * no_delay_flag if set open the device with O_NDELAY
454  * node		di_node corresponding to physpath if already available,
455  *		otherwise pass DI_NODE_NIL.
456  *
457  * Returns 0 on success, -1 on failure.
458  */
459 static int
460 get_guid(char *physpath, char *guid, int guid_len, int no_delay_flag,
461 	di_node_t node)
462 {
463 	int		fd;
464 	ddi_devid_t	devid;
465 	int		rv	= -1;
466 	char		*i_guid	= NULL;
467 	char		physpath_raw[MAXPATHLEN];
468 	uchar_t		*wwnp;
469 	int		i, n, snapshot_taken = 0;
470 
471 	logdmsg("get_guid: physpath = %s\n", physpath);
472 
473 #ifdef sparc
474 	(void) snprintf(physpath_raw, MAXPATHLEN,
475 	    "/devices%s:a,raw", physpath);
476 #else
477 	(void) snprintf(physpath_raw, MAXPATHLEN,
478 	    "/devices%s:c,raw", physpath);
479 #endif
480 
481 	*guid = '\0';
482 
483 	if (no_delay_flag)
484 		no_delay_flag = O_NDELAY;
485 
486 	/*
487 	 * Open the raw device
488 	 * Without the O_DELAY flag, the open will fail on standby paths of
489 	 * T3 if its mp_support mode is "mpxio".
490 	 */
491 	if ((fd = open(physpath_raw, O_RDONLY | no_delay_flag)) == -1) {
492 		logdmsg("get_guid: failed to open %s: %s\n", physpath_raw,
493 		    strerror(errno));
494 		return (-1);
495 	}
496 
497 	if (devid_get(fd, &devid) == 0) {
498 		i_guid = devid_to_guid(devid);
499 		devid_free(devid);
500 
501 		if (i_guid != NULL) {
502 			s_strlcpy(guid, i_guid, guid_len);
503 			devid_free_guid(i_guid);
504 			rv = 0;
505 			goto out;
506 		} else {
507 			logdmsg("get_guid: devid_to_guid() failed\n");
508 			logdmsg("Unable to get a GUID for device "
509 			    "%s\n", physpath_raw);
510 		}
511 
512 	} else
513 		logdmsg("get_guid: devid_get() failed: %s\n", strerror(errno));
514 
515 	/*
516 	 * Unless we're looking at an fp-attached device, we now
517 	 * fallback to node name as the guid as this is what the
518 	 * fcp driver does. A sas-attached device will have the
519 	 * client-guid property set.
520 	 */
521 	if (node == DI_NODE_NIL) {
522 		if ((node = di_init(physpath, DINFOCPYALL | DINFOFORCE))
523 		    == DI_NODE_NIL) {
524 			logdmsg("get_guid: di_init on %s failed: %s\n",
525 			    physpath, strerror(errno));
526 			goto out;
527 		}
528 		snapshot_taken = 1;
529 	}
530 
531 	/* non-fp fallout */
532 	if (strstr(physpath, "fp") == (char *)NULL) {
533 		if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
534 		    "client-guid", &guid) < 0) {
535 			logdmsg("get_guid: non-fp-attached device, "
536 			    "bailing out\n");
537 			goto out;
538 		}
539 	}
540 
541 	if ((n = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, NODE_WWN_PROP,
542 	    &wwnp)) == -1) {
543 		logdmsg("get_guid: di_prop_lookup_bytes() failed to lookup "
544 		    "%s: %s\n", NODE_WWN_PROP, strerror(errno));
545 		goto out;
546 	}
547 
548 	if (guid_len >= ((n * 2) + 1)) {
549 		for (i = 0; i < n; i++) {
550 			(void) sprintf(guid + (i * 2), "%02x", (uint_t)(*wwnp));
551 			wwnp++;
552 		}
553 		rv = 0;
554 	} else
555 		logerr(gettext("insufficient buffer size: need %1$d "
556 		    "bytes, passed %2$d bytes\n"), (n * 2) + 1, guid_len);
557 
558 out:
559 	if (snapshot_taken)
560 		di_fini(node);
561 
562 	(void) close(fd);
563 	logdmsg("get_guid: GUID = %s\n", guid);
564 	return (rv);
565 }
566 
567 /*
568  * Given client_name return whether it is a phci or vhci based name.
569  * client_name is /devices name of a client without the /devices prefix.
570  *
571  * client_name				Return value
572  * on sparc:
573  * .../fp@xxx/ssd@yyy			CLIENT_TYPE_PHCI (fc)
574  * .../LSILogic,sas@xxx/sd@yyy		CLIENT_TYPE_PHCI (sas)
575  * .../scsi_vhci/ssd@yyy		CLIENT_TYPE_VHCI (fc)
576  * .../scsi_vhci/disk@yyy		CLIENT_TYPE_VHCI (sas)
577  * other				CLIENT_TYPE_UNKNOWN
578  * on x86:
579  * .../fp@xxx/disk@yyy			CLIENT_TYPE_PHCI (fc)
580  * .../pci1000,????@xxx/sd@yyy		CLIENT_TYPE_PHCI (sas)
581  * .../scsi_vhci/disk@yyy		CLIENT_TYPE_VHCI
582  * other				CLIENT_TYPE_UNKNOWN
583  */
584 static client_type_t
585 client_name_type(char *client_name)
586 {
587 	client_type_t client_type = CLIENT_TYPE_UNKNOWN;
588 	char *p1;
589 	char *client_path;
590 
591 	client_path = s_strdup(client_name);
592 	logdmsg("client_name_type: client is %s\n", client_path);
593 
594 	if (*client_name != '/')
595 		return (CLIENT_TYPE_UNKNOWN);
596 
597 	if ((p1 = strrchr(client_name, '/')) == NULL ||
598 	    ((strncmp(p1, "/ssd@", sizeof ("/ssd@") - 1) != 0) &&
599 	    (strncmp(p1, "/sd@", sizeof ("/sd@") - 1) != 0) &&
600 	    (strncmp(p1, "/disk@", sizeof ("/disk@") - 1) != 0))) {
601 		logdmsg("client_name_type: p1 = %s\n", p1);
602 		return (CLIENT_TYPE_UNKNOWN);
603 	}
604 
605 	*p1 = '\0';
606 
607 	/*
608 	 * Courtesy of the if (..) block above, we know that any
609 	 * device path we have now is either PHCI or VHCI
610 	 */
611 	client_type = client_by_props(client_path);
612 
613 	logdmsg("client_name_type: client_type = %d\n", client_type);
614 
615 	*p1 = '/';
616 	return (client_type);
617 }
618 
619 /*
620  * client_by_props() is called to determine what the client type
621  * is, based on properties in the device tree:
622  *
623  * drivername	property	type
624  * -------------------------------------
625  *  fp		node-wwn	CLIENT_TYPE_PHCI
626  *  mpt		sas-mpt		CLIENT_TYPE_PHCI
627  *  mpt		client-guid	CLIENT_TYPE_PHCI (corner case)
628  *
629  * Normally, the "client-guid" property only shows up for a node
630  * if we've enumerated that node under scsi_vhci. During testing
631  * of this function, one particular corner case was found which
632  * requires an exception handler.
633  */
634 
635 static client_type_t
636 client_by_props(char *path) {
637 
638 	di_node_t clientnode = DI_NODE_NIL;
639 	di_node_t parentnode = DI_NODE_NIL;
640 	unsigned int rval = CLIENT_TYPE_UNKNOWN;
641 	uchar_t *byteprop[32];
642 	char *charprop = NULL;
643 	char *physpath;
644 	char *parentpath;
645 
646 	physpath = s_malloc(MAXPATHLEN);
647 	bzero(physpath, MAXPATHLEN);
648 
649 	physpath = s_strdup(path);
650 
651 	logdmsg("client_by_props: physpath = (%s)\n", physpath);
652 
653 	/* easy short-circuits */
654 	if (strstr(physpath, "scsi_vhci") != (char *)NULL) {
655 		logdmsg("client_by_props: found "
656 		    "'scsi_vhci' on path (%s)\n", physpath);
657 		rval = CLIENT_TYPE_VHCI;
658 		goto out;
659 	} else if ((strstr(physpath, "ide") != (char *)NULL) ||
660 	    (strstr(physpath, "storage") != (char *)NULL)) {
661 		logdmsg("client_by_props: ignoring this device\n");
662 		goto out;
663 	}
664 
665 	parentpath = s_malloc(MAXPATHLEN);
666 	bzero(parentpath, MAXPATHLEN);
667 
668 	(void) strncpy(parentpath, physpath, strlen(physpath) -
669 	    strlen(strrchr(physpath, '/')));
670 
671 	if ((parentnode = di_init(parentpath, DINFOCPYALL |
672 	    DINFOFORCE)) == DI_NODE_NIL) {
673 		logdmsg("client_by_props: unable to di_init(%s)\n",
674 		    parentpath);
675 		goto out;
676 	}
677 
678 	if (strstr(physpath, "fp") != (char *)NULL) {
679 		if (drvprop == (char *)NULL) {
680 			drvprop = s_malloc(strlen(NODE_WWN_PROP) + 1);
681 		}
682 		logdmsg("NODE_WWN_PROP\n");
683 		(void) snprintf(drvprop, strlen(NODE_WWN_PROP) + 1,
684 		    NODE_WWN_PROP);
685 	} else {
686 		if (drvname == (char *)NULL) {
687 			drvname = di_driver_name(parentnode);
688 			logdmsg("client_by_props: drvname = %s\n", drvname);
689 		}
690 
691 		if (drvprop == (char *)NULL) {
692 			drvprop = s_malloc(sizeof (SASPROP) +
693 			    sizeof (drvname) + 1);
694 		}
695 		(void) snprintf(drvprop, sizeof (SASPROP) +
696 		    sizeof (drvname), "%s%s", SASPROP, drvname);
697 
698 		logdmsg("parentpath: %s\nphyspath: %s\n"
699 		    "length %d, strrchr: %d\n",
700 		    parentpath, physpath, strlen(physpath),
701 		    strlen(strrchr(physpath, '/')));
702 	}
703 
704 	logdmsg("client_by_props: searching for property '%s'\n", drvprop);
705 
706 	if ((clientnode = di_init(physpath, DINFOCPYALL | DINFOFORCE)) ==
707 	    DI_NODE_NIL) {
708 		logdmsg("client_by_props: unable to di_init(%s)\n",
709 		    physpath);
710 
711 		/*
712 		 * On x86/x64 systems, we won't be able to di_init() the
713 		 * node we want in the device tree, however the parent
714 		 * node will still have 'mpxio-disable' set, so we can
715 		 * check for that property and make our decision on type
716 		 */
717 
718 		if (di_prop_lookup_strings(DDI_DEV_T_ANY, parentnode,
719 		    "mpxio-disable", &charprop) > -1) {
720 			rval = CLIENT_TYPE_PHCI;
721 			di_fini(parentnode);
722 			logdmsg("client_by_props: device %s is PHCI\n",
723 			    physpath);
724 		}
725 		goto out;
726 	}
727 
728 	if (di_prop_lookup_bytes(DDI_DEV_T_ANY,
729 	    clientnode, drvprop, byteprop) > -1) {
730 		logdmsg("client_by_props: found prop %s on "
731 		    "path %s\n", drvprop, physpath);
732 		rval = CLIENT_TYPE_PHCI;
733 	} else if (di_prop_lookup_strings(DDI_DEV_T_ANY,
734 	    clientnode, "client-guid", &charprop) > -1) {
735 			/*
736 			 * A corner case was seen during testing where
737 			 * scsi_vhci was loaded, but not all applicable
738 			 * devices were enumerated under it. That left
739 			 * the phci mapping along with the "client-guid"
740 			 * property.
741 			 */
742 			logdmsg("client_by_props: weird... \n");
743 			rval = CLIENT_TYPE_PHCI;
744 	} else {
745 		logdmsg("client_by_props: unable to find "
746 		    "property 'client-guid', 'mpxio-disable' "
747 		    "or '%s' anywhere on path (%s)\n",
748 		    drvprop, physpath);
749 		logdmsg("client_by_props: this node is unknown\n");
750 	}
751 
752 	di_fini(parentnode);
753 	di_fini(clientnode);
754 out:
755 	free(physpath);
756 	return (rval);
757 }
758 
759 
760 /*
761  * Given a phci or vhci devname which is either a /dev link or /devices name
762  * get the corresponding physical node path (without the /devices prefix)
763  * and minor name.
764  *
765  * Returns 0 on success, -1 on failure.
766  */
767 static int
768 get_physname_minor(char *devname, char *physname, int physname_len,
769     char *minorname, int minorname_len)
770 {
771 	int linksize;
772 	char buf[MAXPATHLEN];
773 	char *p, *m;
774 
775 	if (strncmp(devname, DEV_DSK, sizeof (DEV_DSK) - 1) == 0 ||
776 	    strncmp(devname, DEV_RDSK, sizeof (DEV_RDSK) - 1) == 0) {
777 		if ((linksize = readlink(devname, buf, MAXPATHLEN))
778 		    > 0 && linksize <= (MAXPATHLEN - 1)) {
779 			buf[linksize] = '\0';
780 		} else
781 			return (-1);
782 	} else
783 		s_strlcpy(buf, devname, MAXPATHLEN);
784 
785 	if ((p = strstr(buf, SLASH_DEVICES)) == NULL)
786 		return (-1);
787 
788 	/* point to '/' after /devices */
789 	p += sizeof (SLASH_DEVICES) - 2;
790 
791 	if ((m = strrchr(p, ':')) == NULL) {
792 		logdmsg("get_physname_minor: no minor name component in %s\n",
793 		    buf);
794 		return (-1);
795 	}
796 
797 	*m = '\0';
798 	m++;
799 
800 	if (client_name_type(p) == CLIENT_TYPE_UNKNOWN)
801 		return (-1);
802 
803 	s_strlcpy(physname, p, physname_len);
804 	s_strlcpy(minorname, m, minorname_len);
805 	logdmsg("get_physname_minor: %s: physname = %s, minor = %s\n",
806 	    devname, physname, minorname);
807 	return (0);
808 }
809 
810 
811 /*
812  * Map phci based client name to vhci based client name.
813  *
814  * phci_name
815  *	phci based client /devices name without the /devices prefix and
816  *	minor name component.
817  *	ex:
818  *
819  *	(FC)
820  *	for sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
821  *	for x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0
822  *
823  *	(SAS)
824  *	for sparc: /pci@0,2/LSILogic,sas@1/disk@6,0
825  *	for x86: /pci1000,3060@3/sd@0,0
826  *
827  * vhci_name
828  *	Caller supplied buffer where vhci /devices name will be placed on
829  *	return (without the /devices prefix and minor name component).
830  *	ex:
831  *
832  *	(FC)
833  *	for sparc: /scsi_vhci/ssd@g2000002037cd9f72
834  *	for x86: /scsi_vhci/disk@g2000002037cd9f72
835  *
836  *	(SAS)
837  *	both: /scsi_vhci/disk@g600a0b8000254d3e00000284453ed8ac
838  *
839  * vhci_name_len
840  *	Length of the caller supplied vhci_name buffer.
841  *
842  * Returns 0 on success, -1 on failure.
843  */
844 static int
845 phci_to_vhci(char *phci_name, char *vhci_name, size_t vhci_name_len)
846 {
847 	sv_iocdata_t ioc;
848 	char *slash, *at;
849 	char vhci_name_buf[MAXPATHLEN];
850 	char phci_name_buf[MAXPATHLEN];
851 	char addr_buf[MAXNAMELEN];
852 
853 	logdmsg("phci_to_vhci: client = %s\n", phci_name);
854 
855 	s_strlcpy(phci_name_buf, phci_name, MAXPATHLEN);
856 
857 	if (client_name_type(phci_name_buf) != CLIENT_TYPE_PHCI ||
858 	    (slash = strrchr(phci_name_buf, '/')) == NULL ||
859 	    ((strncmp(slash, "/ssd@", sizeof ("/ssd@") - 1) != 0) &&
860 	    (strncmp(slash, "/sd@", sizeof ("/sd@") - 1) != 0) &&
861 	    (strncmp(slash, "/disk@", sizeof ("/disk@") - 1) != 0))) {
862 		logdmsg("phci_to_vhci: %s is not of CLIENT_TYPE_PHCI\n",
863 		    phci_name);
864 		return (-1);
865 	}
866 
867 	if (vhci_fd < 0) {
868 		if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0)
869 			return (-1);
870 	}
871 
872 	*slash = '\0';
873 
874 	at = strchr(slash + 1, '@');
875 	s_strlcpy(addr_buf, at + 1, MAXNAMELEN);
876 
877 	bzero(&ioc, sizeof (sv_iocdata_t));
878 	ioc.client = vhci_name_buf;
879 	ioc.phci = phci_name_buf;
880 	ioc.addr = addr_buf;
881 
882 	if (ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_NAME, &ioc) != 0) {
883 		logdmsg("SCSI_VHCI_GET_CLIENT_NAME on %s "
884 		    "failed: %s\n", phci_name, strerror(errno));
885 		return (-1);
886 	}
887 
888 	s_strlcpy(vhci_name, vhci_name_buf, vhci_name_len);
889 	logdmsg("phci_to_vhci: %s maps to %s\n", phci_name, vhci_name);
890 	return (0);
891 }
892 
893 /*
894  * Map vhci based client name to phci based client name.
895  * If the client has multiple paths, only one of the paths with which client
896  * can be accessed is returned. This function does not use SCSI_VHCI ioctls
897  * as it is called on mpxio disabled paths.
898  *
899  * vhci_name
900  *	vhci based client /devices name without the /devices prefix and
901  *	minor name component.
902  *	ex:
903  *	sparc: /scsi_vhci/ssd@g2000002037cd9f72
904  *	x86: /scsi_vhci/disk@g2000002037cd9f72
905  *
906  * phci_name
907  *	Caller supplied buffer where phci /devices name will be placed on
908  *	return (without the /devices prefix and minor name component).
909  *	ex:
910  *	sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
911  *	x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0
912  *
913  * phci_name_len
914  *	Length of the caller supplied phci_name buffer.
915  *
916  * minor
917  *	The slice of the disk of interest.
918  *
919  * Returns 0 on success, -1 on failure.
920  */
921 static int
922 vhci_to_phci(char *vhci_name, char *phci_name, size_t phci_name_len,
923     char *minor)
924 {
925 	di_node_t node = DI_NODE_NIL;
926 	char *vhci_guid, *devfspath;
927 	char phci_guid[MAXPATHLEN];
928 	char root_guid[MAXPATHLEN];
929 	char root_phys[MAXPATHLEN];
930 	char root_minor[MAXPATHLEN];
931 	char root_path[MAXPATHLEN];
932 	char *node_name;
933 	FILE *mntfp;
934 	struct mnttab mntpref, rootmnt;
935 
936 	logdmsg("vhci_to_phci: client = %s\n", vhci_name);
937 
938 	bzero(&mntpref, sizeof (mntpref));
939 	mntpref.mnt_mountp = "/";
940 
941 	if (!(mntfp = fopen(MNTTAB, "r"))) {
942 		logdmsg("vhci_to_phci: can't open %s\n", MNTTAB);
943 		return (-1);
944 	}
945 
946 	if (getmntany(mntfp, &rootmnt, &mntpref)) {
947 		logdmsg("vhci_to_phci: can't find / in %s\n", MNTTAB);
948 		return (-1);
949 	}
950 
951 	(void) fclose(mntfp);
952 
953 	if (client_name_type(vhci_name) != CLIENT_TYPE_VHCI) {
954 		logdmsg("vhci_to_phci: %s is not of CLIENT_TYPE_VHCI\n",
955 		    vhci_name);
956 		return (-1);
957 	}
958 
959 
960 	if ((vhci_guid = strrchr(vhci_name, '@')) == NULL ||
961 	    *(++vhci_guid) != 'g') {
962 		logerr(gettext("couldn't get guid from %s\n"), vhci_name);
963 		return (-1);
964 	}
965 
966 	/* point to guid */
967 	++vhci_guid;
968 
969 	/*
970 	 * Get devinfo snapshot and walk all ssd nodes whose parent is fp.
971 	 * For each node get the guid and match it with vhci_guid.
972 	 */
973 	if (devinfo_root == DI_NODE_NIL) {
974 		logdmsg("vhci_to_phci: taking devinfo snapshot\n");
975 		if ((devinfo_root = di_init("/", DINFOCPYALL | DINFOFORCE))
976 		    == DI_NODE_NIL) {
977 			logerr(gettext("di_init failed: %s\n"),
978 			    strerror(errno));
979 			return (-1);
980 		}
981 		logdmsg("vhci_to_phci: done taking devinfo snapshot\n");
982 	}
983 
984 	if (strncmp(rootmnt.mnt_special, SLASH_DEVICES,
985 	    sizeof (SLASH_DEVICES)-1))
986 		(void) snprintf(root_path, sizeof (root_path), "/devices%s",
987 		    rootmnt.mnt_special);
988 	else
989 		(void) strcpy(root_path, rootmnt.mnt_special);
990 
991 	/*
992 	 * remove the /devices and minor components to call get_guid()
993 	 * if we can't get the guid, drop through to the regular processing.
994 	 */
995 	if ((get_physname_minor(root_path, root_phys, sizeof (root_phys),
996 	    root_minor, sizeof (root_minor)) ||
997 	    (get_guid(root_phys, root_guid, sizeof (root_guid), 0,
998 	    node) != 0))) {
999 		logdmsg("vhci_to_phci: can't get_guid for / (%s)\n",
1000 		    rootmnt.mnt_special);
1001 		(void) strcpy(root_guid, "");
1002 	}
1003 
1004 	/*
1005 	 * We check the guid of the root device against the vhci guid so we
1006 	 * can return a preferred path.
1007 	 */
1008 	if ((strcmp(root_guid, vhci_guid) == 0) &&
1009 	    (canread(root_phys, minor))) {
1010 		s_strlcpy(phci_name, root_phys, phci_name_len);
1011 		logdmsg("vhci_to_phci: %s maps to %s preferred path\n",
1012 		    vhci_name, phci_name);
1013 		return (0);
1014 	}
1015 
1016 	/*
1017 	 * When we finally get a unified "sd" driver for all
1018 	 * architectures that Solaris runs on, we can remove this
1019 	 * first loop around for "ssd"
1020 	 */
1021 	for (node = di_drv_first_node("ssd", devinfo_root);
1022 	    node != DI_NODE_NIL; node = di_drv_next_node(node)) {
1023 
1024 		if ((node_name = di_node_name(node)) == NULL)
1025 			continue;
1026 
1027 		if ((strcmp(node_name, "disk") != 0) &&
1028 		    (strcmp(node_name, "sd") != 0) &&
1029 		    (strcmp(node_name, "ssd") != 0))
1030 			continue;
1031 
1032 		if (di_parent_node(node) == DI_NODE_NIL)
1033 			continue;
1034 
1035 		if ((devfspath = di_devfs_path(node)) == NULL)
1036 			continue;
1037 
1038 		/*
1039 		 * Don't set no_delay_flag to have get_guid() fail on
1040 		 * standby paths of T3. So we'll find the preferred paths.
1041 		 */
1042 		if (get_guid(devfspath, phci_guid,
1043 		    sizeof (phci_guid), 0, node) != 0)
1044 			continue;
1045 
1046 		/*
1047 		 * If the GUID's match, and we can read data from the path of
1048 		 * interest, we conclude we have the correct path to use.
1049 		 */
1050 		if ((strcmp(phci_guid, vhci_guid) == 0) &&
1051 		    (canread(devfspath, minor)))  {
1052 			s_strlcpy(phci_name, devfspath, phci_name_len);
1053 			di_devfs_path_free(devfspath);
1054 			logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name,
1055 			    phci_name);
1056 			return (0);
1057 		}
1058 
1059 		di_devfs_path_free(devfspath);
1060 	}
1061 
1062 	for (node = di_drv_first_node("sd", devinfo_root);
1063 	    node != DI_NODE_NIL; node = di_drv_next_node(node)) {
1064 
1065 		if ((node_name = di_node_name(node)) == NULL)
1066 			continue;
1067 
1068 		if ((strcmp(node_name, "disk") != 0) &&
1069 		    (strcmp(node_name, "sd") != 0) &&
1070 		    (strcmp(node_name, "ssd") != 0))
1071 			continue;
1072 
1073 		if (di_parent_node(node) == DI_NODE_NIL)
1074 			continue;
1075 
1076 		if ((devfspath = di_devfs_path(node)) == NULL)
1077 			continue;
1078 
1079 		/*
1080 		 * Don't set no_delay_flag to have get_guid() fail on
1081 		 * standby paths of T3. So we'll find the preferred paths.
1082 		 */
1083 		if (get_guid(devfspath, phci_guid,
1084 		    sizeof (phci_guid), 0, node) != 0)
1085 			continue;
1086 
1087 		/*
1088 		 * If the GUID's match, and we can read data from the path of
1089 		 * interest, we conclude we have the correct path to use.
1090 		 */
1091 		if ((strcmp(phci_guid, vhci_guid) == 0) &&
1092 		    (canread(devfspath, minor))) {
1093 			s_strlcpy(phci_name, devfspath, phci_name_len);
1094 			di_devfs_path_free(devfspath);
1095 			logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name,
1096 			    phci_name);
1097 			return (0);
1098 		}
1099 
1100 		di_devfs_path_free(devfspath);
1101 	}
1102 
1103 	logdmsg("vhci_to_phci: couldn't get phci name for %s\n", vhci_name);
1104 	return (-1);
1105 }
1106 
1107 /*
1108  * Map vhci based client name to phci based client name.
1109  * If the client has multiple paths, only one of the paths with which client
1110  * can be accessed is returned.
1111  * This function uses SCSI_VHCI ioctls to get the phci paths
1112  *
1113  * vhci_name
1114  *	vhci based client /devices name without the /devices prefix and
1115  *	minor name component.
1116  *	ex:
1117  *	sparc: /scsi_vhci/ssd@g2000002037cd9f72
1118  *	x86: /scsi_vhci/disk@g2000002037cd9f72
1119  *
1120  * phci_name
1121  *	Caller supplied buffer where phci /devices name will be placed on
1122  *	return (without the /devices prefix and minor name component).
1123  *	ex:
1124  *	sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
1125  *	x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0
1126  *
1127  * phci_name_len
1128  *	Length of the caller supplied phci_name buffer.
1129  *
1130  * Returns 0 on success, -1 on failure.
1131  */
1132 
1133 static int
1134 vhci_to_phci_by_ioctl(char *vhci_name, char *phci_name, size_t phci_name_len)
1135 {
1136 	sv_iocdata_t	ioc;
1137 	uint_t npaths;
1138 	char *node_name, *at;
1139 	char vhci_name_buf[MAXPATHLEN];
1140 	int  ret;
1141 	sv_path_info_t *pi;
1142 
1143 	logdmsg("vhci_to_phci_by_ioctl: client = %s\n", vhci_name);
1144 
1145 	if (vhci_fd < 0) {
1146 		if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0)
1147 			return (-1);
1148 	}
1149 
1150 	(void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN);
1151 
1152 	/* first get the number paths */
1153 	bzero(&ioc, sizeof (sv_iocdata_t));
1154 	ioc.client = vhci_name_buf;
1155 	ioc.ret_elem = &npaths;
1156 	if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO,
1157 	    &ioc)) != 0 || npaths == 0) {
1158 		logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s "
1159 		    "failed: %s\n", vhci_name,
1160 		    ret?strerror(errno):"got 0 paths");
1161 		return (-1);
1162 	}
1163 
1164 	/* now allocate memory for the path information and get all paths */
1165 	bzero(&ioc, sizeof (sv_iocdata_t));
1166 	ioc.client = vhci_name_buf;
1167 	ioc.buf_elem = npaths;
1168 	ioc.ret_elem = &npaths;
1169 	if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths,
1170 	    sizeof (sv_path_info_t))) == NULL)
1171 		return (-1);
1172 	if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO,
1173 	    &ioc)) != 0 || npaths == 0) {
1174 		logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s "
1175 		    "failed: %s\n", vhci_name,
1176 		    ret?strerror(errno):"got 0 paths");
1177 		goto out;
1178 	}
1179 
1180 	if (ioc.buf_elem < npaths)
1181 		npaths = ioc.buf_elem;
1182 	if ((node_name = strrchr(vhci_name_buf, '/')) == NULL ||
1183 	    (at = strchr(node_name, '@')) == NULL)
1184 		goto out;
1185 
1186 	node_name++;
1187 	*at = '\0';
1188 
1189 	logdmsg("vhci_to_phci_by_ioctl: node_name is %s\n", node_name);
1190 #ifndef sparc
1191 	/*
1192 	 * We need to use a libdevinfo call to get this info
1193 	 * in an architecturally-neutral fashion. Phase-II for sure!
1194 	 */
1195 	node_name = "sd";
1196 #endif
1197 
1198 	/*
1199 	 * return the first online paths as non-online paths may
1200 	 * not be accessible in the target environment.
1201 	 */
1202 	pi = (sv_path_info_t *)ioc.ret_buf;
1203 	while (npaths--) {
1204 		if (MDI_PATHINFO_STATE_ONLINE == pi->ret_state) {
1205 			(void) snprintf(phci_name, phci_name_len, "%s/%s@%s",
1206 			    pi->device.ret_phci, node_name,
1207 			    pi->ret_addr);
1208 			logdmsg("vhci_to_phci_by_ioctl: %s maps to %s\n",
1209 			    vhci_name, phci_name);
1210 			free(ioc.ret_buf);
1211 			return (0);
1212 		}
1213 		pi++;
1214 	}
1215 
1216 out:
1217 	logdmsg("vhci_to_phci_by_ioctl: couldn't get phci name for %s\n",
1218 	    vhci_name);
1219 	free(ioc.ret_buf);
1220 	return (-1);
1221 
1222 }
1223 
1224 /*
1225  * Map physname from phci name space to vhci name space or vice-versa
1226  *
1227  * physname
1228  *	phci or vhci based client /devices name without the /devices prefix and
1229  *	minor name component.
1230  *
1231  * new_physname
1232  *	Caller supplied buffer where the mapped physical name is stored on
1233  *	return (without the /devices prefix and minor name component).
1234  *
1235  * len
1236  *	Length of the caller supplied new_physname buffer.
1237  *
1238  * minor
1239  *	The slice of the disk of interest.
1240  *
1241  * Returns 0 on success, -1 on failure.
1242  */
1243 static int
1244 map_physname(char *physname, char *new_physname, size_t len, char *minor)
1245 {
1246 	int type;
1247 	int rv;
1248 
1249 	type = client_name_type(physname);
1250 	logdmsg("map_physname: type (%d) physname = %s\n",
1251 	    type, physname);
1252 
1253 	if (type == CLIENT_TYPE_VHCI)
1254 		rv = vhci_to_phci(physname, new_physname, len, minor);
1255 	else if (type == CLIENT_TYPE_PHCI)
1256 		rv = phci_to_vhci(physname, new_physname, len);
1257 	else
1258 		rv = -1;
1259 
1260 	logdmsg("map_physname: returning %d\n", rv);
1261 	return (rv);
1262 }
1263 
1264 static int
1265 devlink_callback(di_devlink_t devlink, void *argptr)
1266 {
1267 	const char *link;
1268 	struct devlink_cbarg *argp = argptr;
1269 
1270 	if ((link = di_devlink_path(devlink)) != NULL) {
1271 		s_strlcpy(argp->devlink, link, argp->len);
1272 		return (DI_WALK_TERMINATE);
1273 	}
1274 
1275 	return (DI_WALK_CONTINUE);
1276 }
1277 
1278 /*
1279  * Lookup the /dev link corresponding to physname and minorname.
1280  *
1281  * physname	client /devices path without the /devices prefix and minor
1282  *		name component.
1283  * minorname	client minor name.
1284  * devlink	caller supplied buffer where the /dev link is placed on return.
1285  * len		caller supplied devlink buffer length
1286  *
1287  * Returns 0 on success, -1 on failure.
1288  */
1289 static int
1290 lookup_devlink(char *physname, char *minorname, char *devlink, size_t len)
1291 {
1292 	char buf[MAXPATHLEN];
1293 	struct devlink_cbarg arg;
1294 
1295 	if (devlink_hdl == NULL) {
1296 		logdmsg("lookup_devlink: taking devlink snapshot\n");
1297 		if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
1298 			logerr(gettext("di_devlink_init failed: %s\n"),
1299 			    strerror(errno));
1300 			clean_exit(1);
1301 		}
1302 	}
1303 
1304 	*devlink = '\0';
1305 	(void) snprintf(buf, MAXPATHLEN, "%s:%s", physname, minorname);
1306 	arg.devlink = devlink;
1307 	arg.len = len;
1308 	if (di_devlink_walk(devlink_hdl, NULL, buf, DI_PRIMARY_LINK, &arg,
1309 	    devlink_callback) != 0) {
1310 		logdmsg("lookup_devlink: di_devlink_walk on %s failed: %s\n",
1311 		    buf, strerror(errno));
1312 		return (-1);
1313 	}
1314 
1315 	if (*devlink == '\0') {
1316 		logdmsg("lookup_devlink: failed to lookup devlink for %s\n",
1317 		    buf);
1318 		return (-1);
1319 	}
1320 
1321 	logdmsg("lookup_devlink: /dev link for %s:%s = %s\n", physname,
1322 	    minorname, devlink);
1323 	return (0);
1324 }
1325 
1326 /*
1327  * open infile for reading and return its file pointer in *fp_in.
1328  * open outfile for writing and return its file pointer in *fp_out.
1329  *
1330  * Returns 0 on success, -1 on failure.
1331  */
1332 static int
1333 open_in_out_files(char *infile, char *outfile, FILE **fp_in, FILE **fp_out)
1334 {
1335 	FILE *fin = NULL;
1336 	FILE *fout = NULL;
1337 	struct stat sbuf;
1338 
1339 	if ((fin = fopen(infile, "r")) == NULL) {
1340 		logerr(gettext("failed to fopen %1$s: %2$s\n"),
1341 		    infile, strerror(errno));
1342 		goto out;
1343 	}
1344 
1345 	if (fstat(fileno(fin), &sbuf) != 0) {
1346 		logerr(gettext("fstat failed on %1$s: %2$s\n"),
1347 		    infile, strerror(errno));
1348 		goto out;
1349 	}
1350 
1351 	if ((fout = fopen(outfile, "w")) == NULL) {
1352 		logerr(gettext("failed to fopen %1$s: %2$s\n"),
1353 		    outfile, strerror(errno));
1354 		goto out;
1355 	}
1356 
1357 	if (fchmod(fileno(fout), (sbuf.st_mode & 0777)) != 0) {
1358 		logerr(gettext("failed to fchmod %1$s to 0%2$o: %3$s\n"),
1359 		    outfile, sbuf.st_mode & 0777, strerror(errno));
1360 		goto out;
1361 	}
1362 
1363 	if (fchown(fileno(fout), sbuf.st_uid, sbuf.st_gid) != 0) {
1364 		logerr(gettext("failed to fchown %1$s to uid %2$d and "
1365 		    "gid %3$d: %4$s\n"),
1366 		    outfile, sbuf.st_uid, sbuf.st_gid, strerror(errno));
1367 		goto out;
1368 	}
1369 
1370 	*fp_in = fin;
1371 	*fp_out = fout;
1372 	return (0);
1373 
1374 out:
1375 	if (fin != NULL)
1376 		(void) fclose(fin);
1377 	if (fout != NULL)
1378 		(void) fclose(fout);
1379 	return (-1);
1380 }
1381 
1382 /*
1383  * If the devname is a phci based name and not open-able, map it to vhci
1384  * based name. If the devname is a vhci based name and not open-able, map it
1385  * to phci based name.
1386  *
1387  * devname	either a /dev link or /devices name to client device
1388  * new_devname	caller supplied buffer where the mapped device name is
1389  *		placed on return.
1390  * len		caller supplied new_devname buffer length
1391  * devlink_flag	pass 1 if requesting the /dev link to the mapped device.
1392  *		pass 0 if requesting the /devices name of the mapped device.
1393  *
1394  * Returns 0 on success, -1 on failure.
1395  */
1396 static int
1397 map_devname(char *devname, char *new_devname, size_t len, int devlink_flag)
1398 {
1399 	char physname[MAXPATHLEN];
1400 	char minor[MAXNAMELEN];
1401 	char new_physname[MAXPATHLEN];
1402 
1403 	logdmsg("map_devname: checking devname %s\n", devname);
1404 	if ((get_physname_minor(devname, physname, sizeof (physname),
1405 	    minor, sizeof (minor)) == 0) &&
1406 	    (canopen(devname) == 0) &&
1407 	    (map_physname(physname, new_physname,
1408 	    sizeof (new_physname), minor) == 0)) {
1409 
1410 		logdmsg("map_devname: now looking up devlink\n");
1411 
1412 		if (devlink_flag) {
1413 			if (lookup_devlink(new_physname, minor, new_devname,
1414 			    len) == 0)
1415 				return (0);
1416 		} else {
1417 			(void) snprintf(new_devname, len, "/devices%s:%s",
1418 			    new_physname, minor);
1419 			return (0);
1420 		}
1421 	}
1422 
1423 	logdmsg("map_devname: failed to find mapping for %s\n", devname);
1424 	return (-1);
1425 }
1426 
1427 /*
1428  * If the devname is a vhci based name and open-able, map it to phci
1429  * based name.
1430  *
1431  * devname	either a /dev link or /devices name to client device
1432  * new_devname	caller supplied buffer where the mapped device name without
1433  *		/devices prefix is placed on return.
1434  * len		caller supplied new_devname buffer length
1435  */
1436 static int
1437 map_openable_vhciname(char *devname, char *new_devname, size_t len)
1438 {
1439 	char physname[MAXPATHLEN];
1440 	char minor[MAXNAMELEN];
1441 	char new_physname[MAXPATHLEN];
1442 
1443 	if (get_physname_minor(devname, physname, sizeof (physname),
1444 	    minor, sizeof (minor)) == 0 &&
1445 	    canopen(devname) == 1 &&
1446 	    client_name_type(physname) == CLIENT_TYPE_VHCI &&
1447 	    vhci_to_phci_by_ioctl(physname, new_physname,
1448 		sizeof (new_physname)) == 0) {
1449 		(void) snprintf(new_devname, len, "%s:%s",
1450 		    new_physname, minor);
1451 		return (0);
1452 	}
1453 
1454 	return (-1);
1455 }
1456 /*
1457  * Make a new /etc/vfstab:
1458  * Read vfstab_in, convert the device name entries to appropriate vhci or phci
1459  * based names, and write to vfstab_out. Only device names whose physical
1460  * paths are either phci or vhci based names and not open-able are considered
1461  * for conversion. Open-able device name entries are not converted as it
1462  * means that the device is already accessible; hence no need to convert.
1463  *
1464  * Returns:
1465  * 	0	successful but vfstab_out contents are the same as vfstab_in
1466  *	1	successful and vfstab_out changed from vfstab_in
1467  *	-1	failed
1468  */
1469 static int
1470 update_vfstab(char *vfstab_in, char *vfstab_out)
1471 {
1472 	FILE *fp_in, *fp_out;
1473 	char *buf, *tmpbuf;
1474 	char *vfs_cache[2];
1475 	int idx = 0, count = 0;
1476 	int rv = -1;
1477 	int vfstab_updated = 0;
1478 	int i;
1479 	char cdev[MAXPATHLEN];
1480 	char bdev[MAXPATHLEN];
1481 	char mntpt[MAXPATHLEN];
1482 	char fstype[512];
1483 	char fsckpass[512];
1484 	char mntboot[512];
1485 	char mntopt[MAX_MNTOPT_STR];
1486 	char phys_bdev[MAXPATHLEN], phys_cdev[MAXPATHLEN];
1487 	char bdev_minor[MAXNAMELEN], cdev_minor[MAXNAMELEN];
1488 	char new_physname[MAXPATHLEN];
1489 	char new_bdevlink[MAXPATHLEN], new_cdevlink[MAXPATHLEN];
1490 	char fmt[80];
1491 
1492 	if (open_in_out_files(vfstab_in, vfstab_out, &fp_in, &fp_out) != 0)
1493 		return (-1);
1494 
1495 	/*
1496 	 * Read one line at time from vfstab_in. If no conversion is needed
1497 	 * for the line simply write the line to vfstab_out. If conversion is
1498 	 * needed, first write the existing line as a comment to vfstab_out
1499 	 * and then write the converted line.
1500 	 *
1501 	 * To avoid commented entries piling up in vfstab in case if the
1502 	 * user runs stmsboot multiple times to switch on and off from mpxio,
1503 	 * add the commented line only if not already there. To do this
1504 	 * cache the last two vfstab lines processed and add the commented
1505 	 * entry only if it is not found in the cache. We only need to cache
1506 	 * the last two lines because a device can have at most two names -
1507 	 * one mpxio and one non-mpxio name. Therefore for any device name
1508 	 * entry we at most add two comments - one with mpxio name and one
1509 	 * with non-mpxio name - no matter how many times stmsboot is run.
1510 	 */
1511 	buf = (char *)s_malloc(VFS_LINE_MAX);
1512 	tmpbuf = (char *)s_malloc(VFS_LINE_MAX);
1513 	vfs_cache[0] = (char *)s_malloc(VFS_LINE_MAX);
1514 	vfs_cache[1] = (char *)s_malloc(VFS_LINE_MAX);
1515 
1516 	(void) snprintf(fmt, sizeof (fmt),
1517 	    "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1,
1518 	    sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1,
1519 	    sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1);
1520 
1521 	while (fgets(buf, VFS_LINE_MAX, fp_in) != NULL) {
1522 		if (strlen(buf) == (VFS_LINE_MAX - 1) &&
1523 		    buf[VFS_LINE_MAX-2] != '\n') {
1524 			logerr(gettext("%1$s line size too long, "
1525 			    "exceeded %2$d: \"%3$s\"\n"),
1526 			    VFSTAB, VFS_LINE_MAX - 2, buf);
1527 			goto out;
1528 		}
1529 
1530 		/* LINTED - format specifier */
1531 		if ((sscanf(buf, fmt, bdev, cdev, mntpt,
1532 		    fstype, fsckpass, mntboot, mntopt) != 7) ||
1533 		    (bdev[0] == '#') ||
1534 		    (get_physname_minor(bdev, phys_bdev, sizeof (phys_bdev),
1535 		    bdev_minor, sizeof (bdev_minor)) != 0) ||
1536 
1537 		    (strcmp(fstype, "swap") != 0 &&
1538 		    ((get_physname_minor(cdev, phys_cdev, sizeof (phys_cdev),
1539 		    cdev_minor, sizeof (cdev_minor)) != 0) ||
1540 		    (strcmp(phys_bdev, phys_cdev) != 0))) ||
1541 
1542 		    canopen(bdev) ||
1543 		    (map_physname(phys_bdev, new_physname,
1544 		    sizeof (new_physname), bdev_minor) != 0) ||
1545 		    (lookup_devlink(new_physname, bdev_minor, new_bdevlink,
1546 		    sizeof (new_bdevlink)) != 0) ||
1547 
1548 		    (strcmp(fstype, "swap") != 0 &&
1549 		    (lookup_devlink(new_physname, cdev_minor, new_cdevlink,
1550 		    sizeof (new_cdevlink)) != 0))) {
1551 
1552 			/* cache the last two entries */
1553 			(void) strlcpy(vfs_cache[idx], buf, VFS_LINE_MAX);
1554 			idx = (idx == 0) ? 1 : 0;
1555 			if (count < 2)
1556 				count++;
1557 
1558 			if (fputs(buf, fp_out) == EOF) {
1559 				logerr(gettext("fputs \"%1$s\" to %2$s "
1560 				    "failed: %3$s\n"),
1561 				    buf, vfstab_out, strerror(errno));
1562 				goto out;
1563 			}
1564 
1565 		} else {
1566 			/*
1567 			 * comment the entry in vfstab only if it is not
1568 			 * already in the cache.
1569 			 */
1570 			if (client_name_type(phys_bdev) == CLIENT_TYPE_VHCI)
1571 				(void) snprintf(tmpbuf, VFS_LINE_MAX,
1572 				    "# mpxio: %s", buf);
1573 			else
1574 				(void) snprintf(tmpbuf, VFS_LINE_MAX,
1575 				    "# non-mpxio: %s", buf);
1576 
1577 			for (i = 0; i < count; i++) {
1578 				if (strcmp(vfs_cache[i], tmpbuf) == 0)
1579 					break;
1580 			}
1581 
1582 			if (i == count) {
1583 				if (fputs(tmpbuf, fp_out) == EOF) {
1584 					logerr(gettext("fputs \"%1$s\" to %2$s "
1585 					    "failed: %3$s\n"), tmpbuf,
1586 					    vfstab_out, strerror(errno));
1587 					goto out;
1588 				}
1589 			}
1590 
1591 			count = 0;
1592 			idx = 0;
1593 
1594 			if (fprintf(fp_out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
1595 			    new_bdevlink,
1596 			    (strcmp(fstype, "swap") != 0) ? new_cdevlink : cdev,
1597 			    mntpt, fstype, fsckpass, mntboot, mntopt) < 0) {
1598 				logerr(gettext("fprintf failed to write to "
1599 				    "%1$s: %2$s\n"),
1600 				    vfstab_out, strerror(errno));
1601 				goto out;
1602 			}
1603 			vfstab_updated = 1;
1604 		}
1605 	}
1606 
1607 	rv = vfstab_updated;
1608 out:
1609 	(void) fclose(fp_in);
1610 	(void) fclose(fp_out);
1611 	free(buf);
1612 	free(tmpbuf);
1613 	free(vfs_cache[0]);
1614 	free(vfs_cache[1]);
1615 	return (rv);
1616 }
1617 
1618 /*
1619  * if guidmap is 0, list non-STMS to STMS device name mappings for the
1620  * specified controller.
1621  * if guidmap is 1, list non-STMS to GUID mappings for the specified controller.
1622  * If controller is -1 list mappings for all controllers.
1623  *
1624  * Returns 0 on success, -1 on failure.
1625  */
1626 static int
1627 list_mappings(int controller, int guidmap)
1628 {
1629 	int cnum, len, mapped;
1630 	int header = 1;
1631 	char *p1, *p2;
1632 	DIR *dirp;
1633 	struct dirent *direntry;
1634 	char devname[MAXPATHLEN];
1635 	char physname[MAXPATHLEN];
1636 	char new_devname[MAXPATHLEN];
1637 	char new_physname[MAXPATHLEN];
1638 	char guid[MAXPATHLEN];
1639 	char minor[MAXNAMELEN];
1640 
1641 	if ((dirp = opendir("/dev/rdsk")) == NULL)
1642 		return (-1);
1643 
1644 	while ((direntry = readdir(dirp)) != NULL) {
1645 		if (strcmp(direntry->d_name, ".") == 0 ||
1646 		    strcmp(direntry->d_name, "..") == 0 ||
1647 		    (len = strlen(direntry->d_name)) < 2 ||
1648 		    strcmp(direntry->d_name + len - 2, "s0") != 0 ||
1649 		    sscanf(direntry->d_name, "c%dt", &cnum) != 1 ||
1650 		    (controller != -1 && controller != cnum))
1651 			continue;
1652 
1653 		(void) snprintf(devname, MAXPATHLEN, "/dev/rdsk/%s",
1654 		    direntry->d_name);
1655 
1656 		if (get_physname_minor(devname, physname, sizeof (physname),
1657 		    minor, sizeof (minor)) != 0 ||
1658 		    client_name_type(physname) != CLIENT_TYPE_PHCI) {
1659 			logdmsg("list_mappings: continuing\n");
1660 			continue;
1661 		}
1662 
1663 		/*
1664 		 * First try phci_to_vhci() mapping. It will work if the
1665 		 * device is under MPxIO control. If the device is not under
1666 		 * MPxIO, phci_to_vhci() will fail in which case try to lookup
1667 		 * if an old mapping exists using guid lookup.
1668 		 */
1669 		mapped = 1;
1670 		if (phci_to_vhci(physname, new_physname,
1671 		    sizeof (new_physname)) != 0) {
1672 			if (get_guid(physname, guid, sizeof (guid), 1,
1673 			    DI_NODE_NIL) == 0)
1674 				(void) snprintf(new_physname, MAXPATHLEN,
1675 				    "/scsi_vhci/%s%s", DISK_AT_G, guid);
1676 			else
1677 				mapped = 0;
1678 		}
1679 
1680 		if (mapped == 0)
1681 			continue;
1682 
1683 		/* strip the slice number part */
1684 		devname[strlen(devname) - 2] = '\0';
1685 
1686 		if (guidmap == 0) {
1687 			if (lookup_devlink(new_physname, minor,
1688 			    new_devname, sizeof (new_devname)) != 0)
1689 				continue;
1690 
1691 			/* strip the slice number part */
1692 			new_devname[strlen(new_devname) - 2] = '\0';
1693 
1694 			if (header) {
1695 				(void) printf(
1696 				    gettext("non-STMS device name\t\t\t"
1697 				    "STMS device name\n"
1698 				    "------------------------------------------"
1699 				    "------------------------\n"));
1700 				header = 0;
1701 			}
1702 			(void) printf("%s\t\t%s\n", devname, new_devname);
1703 		} else {
1704 			/* extract guid part */
1705 			/* we should be using a getguid() call instead */
1706 			if ((p1 = strstr(new_physname, "@"))
1707 			    == NULL) {
1708 				logdmsg("invalid vhci: %s\n", new_physname);
1709 				continue;
1710 			}
1711 
1712 			logdmsg("\tp1 = %s\n", p1);
1713 
1714 			p1 += 2; /* "@" + [nwg] */
1715 			if ((p2 = strrchr(p1, ':')) != NULL)
1716 				*p2 = '\0';
1717 
1718 			if (header) {
1719 				(void) printf(
1720 				    gettext("non-STMS device name\t\t\tGUID\n"
1721 				    "------------------------------------------"
1722 				    "------------------------\n"));
1723 				header = 0;
1724 			}
1725 			(void) printf("%s\t\t%s\n", devname, p1);
1726 		}
1727 	}
1728 
1729 	(void) closedir(dirp);
1730 	return (0);
1731 }
1732 
1733 /*
1734  * Check if the file can be opened.
1735  *
1736  * Return 1 if the file can be opened, 0 otherwise.
1737  */
1738 static int
1739 canopen(char *filename)
1740 {
1741 	int fd;
1742 
1743 	if ((fd = open(filename, O_RDONLY)) == -1)
1744 		return (0);
1745 
1746 	logdmsg("canopen: was able to open %s\n", filename);
1747 	(void) close(fd);
1748 	return (1);
1749 }
1750 
1751 
1752 /*
1753  * This function traverses the device tree looking for nodes
1754  * which have "drivername" as a property. We return a list of
1755  * these nodes, without duplicate entries.
1756  * Since there can be many different pci/pcie devices that all
1757  * share the same driver but which have different pci vid/did
1758  * combinations, we have to be smart about returning only those
1759  * pci vid/dids which have the "sas-*" property unless the
1760  * drivername is "fp", in which case we're searching for "node-wwn"
1761  */
1762 static void
1763 list_nodes(char *drivername)
1764 {
1765 	di_node_t devroot = DI_NODE_NIL;
1766 	di_node_t thisnode = DI_NODE_NIL;
1767 	char *aliaslist;
1768 	char *iitype = NULL; /* the "initiator-interconnect-type" property */
1769 	int *intprop = NULL;
1770 	int i = 1; /* fencepost */
1771 	int irval = 0;
1772 	int crval = 0;
1773 
1774 	/*
1775 	 * Since the "fp" driver enumerates with its own name,
1776 	 * we can special-case its handling.
1777 	 */
1778 	if (strcmp(drvname, "fp") == 0) {
1779 		(void) fprintf(stdout, "fp\n");
1780 	} else {
1781 
1782 		if ((devroot = di_init("/", DINFOCPYALL | DINFOFORCE))
1783 		    == DI_NODE_NIL) {
1784 			logerr(gettext("list_nodes: di_init failed: "
1785 			"%s\n"), strerror(errno));
1786 		}
1787 
1788 		if ((thisnode = di_drv_first_node(drivername, devroot))
1789 		    != NULL) {
1790 			logdmsg("list_nodes: searching for property "
1791 			    "%s\n", drvprop);
1792 
1793 			aliaslist = s_malloc(1024 * sizeof (char));
1794 			bzero(aliaslist, 1024);
1795 			while (thisnode != DI_NODE_NIL) {
1796 				logdmsg("devfs-name %s driver-name %s "
1797 				    "node-name %s\n",
1798 				    di_devfs_path(thisnode),
1799 				    di_driver_name(thisnode),
1800 				    di_node_name(thisnode));
1801 
1802 			/* We check the child node for drvprop */
1803 			irval = di_prop_lookup_ints(DDI_DEV_T_ANY,
1804 			    di_child_node(thisnode), drvprop, &intprop);
1805 			/* and this node for the correct initiator type */
1806 			crval = di_prop_lookup_strings(DDI_DEV_T_ANY,
1807 			    thisnode, "initiator-interconnect-type", &iitype);
1808 
1809 			/*
1810 			 * examine the return codes from di_prop_lookup*()
1811 			 * functions to guard against library errors
1812 			 */
1813 			if ((irval > -1) || ((crval > -1) &&
1814 			    (strncmp(iitype, "SATA", 4) == 0))) {
1815 
1816 				if (strstr(aliaslist,
1817 				    di_node_name(thisnode)) == (char *)NULL) {
1818 					char *nname;
1819 
1820 					nname = di_node_name(thisnode);
1821 
1822 					if (i) {
1823 					(void) snprintf(aliaslist,
1824 					    strlen(nname) + 1, "%s", nname);
1825 						--i;
1826 					} else {
1827 					if (strstr(aliaslist,
1828 					    di_node_name(thisnode)) ==
1829 					    (char *)NULL) {
1830 						/* add 2 for the n-1 + "|" */
1831 						(void) snprintf(aliaslist,
1832 						    strlen(nname) + 2 +
1833 						    strlen(aliaslist),
1834 						    "%s|%s", aliaslist,
1835 						    nname);
1836 						}
1837 					}
1838 				}
1839 			} else {
1840 				logdmsg("unable to lookup property %s "
1841 				    "for node %s. Error %d: %s\n",
1842 				    drvprop, di_devfs_path(thisnode),
1843 				    errno, strerror(errno));
1844 			}
1845 			thisnode = di_drv_next_node(thisnode);
1846 		}
1847 		(void) fprintf(stdout, "%s\n", aliaslist);
1848 		}
1849 
1850 		di_fini(devroot);
1851 	}
1852 }
1853 
1854 static void
1855 logerr(char *msg, ...)
1856 {
1857 	va_list ap;
1858 
1859 	(void) fprintf(stderr, "%s: ", stmsboot);
1860 	va_start(ap, msg);
1861 	/* LINTED - format specifier */
1862 	(void) vfprintf(stderr, msg, ap);
1863 	va_end(ap);
1864 }
1865 
1866 /* log debug message */
1867 static void
1868 logdmsg(char *msg, ...)
1869 {
1870 	va_list ap;
1871 
1872 	if (debug) {
1873 		va_start(ap, msg);
1874 		/* LINTED - format specifier */
1875 		(void) vprintf(msg, ap);
1876 		va_end(ap);
1877 	}
1878 }
1879 
1880 static void *
1881 s_malloc(const size_t size)
1882 {
1883 	void *rp;
1884 
1885 	if ((rp = malloc(size)) == NULL) {
1886 		logerr(gettext("malloc failed to allocate %d bytes\n"), size);
1887 		clean_exit(1);
1888 	}
1889 	return (rp);
1890 }
1891 
1892 static char *
1893 s_strdup(const char *ptr)
1894 {
1895 	void *rp;
1896 
1897 	if ((rp = strdup(ptr)) == NULL) {
1898 		logerr(gettext("strdup failed to dup %s\n"), ptr);
1899 		clean_exit(1);
1900 	}
1901 	return (rp);
1902 }
1903 
1904 static void
1905 s_strlcpy(char *dst, const char *src, size_t dstsize)
1906 {
1907 	int n;
1908 
1909 	if ((n = strlcpy(dst, src, dstsize)) >= dstsize) {
1910 		logerr(gettext("strlcpy: destination buffer size is %1$d "
1911 		    "bytes, need to at least %2$d bytes\n"), dstsize, n + 1);
1912 		clean_exit(1);
1913 	}
1914 }
1915 
1916 static void
1917 clean_exit(int status)
1918 {
1919 	if (devinfo_root != DI_NODE_NIL)
1920 		di_fini(devinfo_root);
1921 
1922 	if (devlink_hdl != NULL)
1923 		(void) di_devlink_fini(&devlink_hdl);
1924 
1925 	if (vhci_fd != -1)
1926 		(void) close(vhci_fd);
1927 
1928 	exit(status);
1929 }
1930 
1931 /*
1932  * Attempt to read some data from the specified slice from the device.
1933  */
1934 static int
1935 canread(char *physname, char *minor)
1936 {
1937 	char    devname[MAXPATHLEN];
1938 	int	fd, rv = 0;
1939 	char    tbuf[512];
1940 
1941 	(void) snprintf(devname, MAXPATHLEN, "/devices%s:%s", physname, minor);
1942 	if ((fd = open(devname, O_RDONLY)) == -1) {
1943 		logdmsg("canread: failed to open %s: %s\n", devname,
1944 		    strerror(errno));
1945 		return (rv);
1946 	}
1947 
1948 	if (read(fd, tbuf, sizeof (tbuf)) < 0)
1949 		logdmsg("canread: failed to read %s: %s\n", devname,
1950 		    strerror(errno));
1951 	else
1952 		rv = 1;
1953 
1954 	(void) close(fd);
1955 	return (rv);
1956 }
1957