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