xref: /titanic_50/usr/src/cmd/stmsboot/stmsboot_util.c (revision f482c776bc557f0256e776932c7842b9db390de1)
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 #define	SLASH_DISK_AT	"/ssd@"
61 #else	/* sparc */
62 #define	DISK_NODE_NAME	"disk"
63 #define	DISK_DRV_NAME	"sd"
64 #define	SLASH_DISK_AT	"/disk@"
65 #endif
66 
67 #define	DISK_AT_G	"disk@g"
68 #define	SLASH_FP_AT	"/fp@"
69 #define	SLASH_SCSI_VHCI	"/scsi_vhci"
70 #define	SLASH_SD_AT	"/sd@"
71 #define	DEV_DSK		"/dev/dsk/"
72 #define	DEV_RDSK	"/dev/rdsk/"
73 #define	SYS_FILENAME_LEN	256
74 
75 /*
76  * Save directory is the directory in which system files are saved.
77  * Save directory must be under the root filesystem, as this program is
78  * typically run before any other filesystems are mounted.
79  */
80 #define	SAVE_DIR	"/etc/mpxio"
81 
82 /* fcp driver publishes this property */
83 #define	NODE_WWN_PROP	"node-wwn"
84 
85 /*
86  * For SAS, we look for "sas-$drivername", eg sas-mpt, but
87  * we strncat the driver name later once we've parsed the
88  * args passed in from the shell.
89  */
90 #define	SASPROP	 "sas-"
91 
92 
93 typedef enum {
94 	CLIENT_TYPE_UNKNOWN,
95 	CLIENT_TYPE_PHCI,
96 	CLIENT_TYPE_VHCI
97 } client_type_t;
98 
99 struct devlink_cbarg {
100 	char *devlink;
101 	size_t len;
102 };
103 
104 static di_node_t devinfo_root = DI_NODE_NIL;
105 static di_devlink_handle_t devlink_hdl = NULL;
106 static int vhci_fd = -1;
107 static int patch_vfstab, cap_m_option, debug;
108 static int list_option, list_guid_mappings, list_controllernum = -1;
109 static char *mapdev = "";
110 static char *map_vhciname = "";
111 static char *stmsboot = "stmsboot";
112 
113 char *drvname = (char *)NULL; /* "fp" or "mpt" or ... */
114 /* "node-wwn" if drvname=fp, or "sas-$drivername" otherwise */
115 char *drvprop = (char *)NULL;
116 static int parent = 0; /* for "-n" usage */
117 
118 static int make_temp(char *, char *, char *, size_t);
119 static void commit_change(char *, char *, char *, int);
120 static int map_devname(char *, char *, size_t, int);
121 static int update_vfstab(char *, char *);
122 static int list_mappings(int, int);
123 static int canopen(char *);
124 static client_type_t client_by_props(char *path);
125 static void list_nodes(char *drivername);
126 
127 static void logerr(char *, ...);
128 static void logdmsg(char *, ...);
129 static void *s_malloc(const size_t);
130 static char *s_strdup(const char *);
131 static void s_strlcpy(char *, const char *, size_t);
132 static int map_openable_vhciname(char *, char *, size_t);
133 /*
134  * Using an exit function not marked __NORETURN causes a warning with gcc.
135  * To suppress the warning, use __NORETURN attribute.
136  */
137 static void clean_exit(int)__NORETURN;
138 
139 /*
140  * Print usage and exit.
141  */
142 static void
143 usage(char *argv0)
144 {
145 	char *progname;
146 
147 	progname = strrchr(argv0, '/');
148 	if (progname != NULL)
149 		progname++;
150 	else
151 		progname = argv0;
152 
153 	/*
154 	 * -u	update /etc/vfstab
155 	 * -m devname
156 	 *	if devname is phci based name and not open-able, map it to
157 	 *	vhci based /devices name.
158 	 *	if devname is vhci based name and not open-able, map it to
159 	 *	phci based /devices name.
160 	 * -M devname
161 	 *	same as -m except that /dev link is printed instead of
162 	 *	/devices name.
163 	 * -l controller
164 	 *	list non-STMS to STMS device name mappings for the specific
165 	 *	controller
166 	 * -L	list non-STMS to STMS device name mappings for all controllers
167 	 * -p devname
168 	 *	if devname is vhci based name and open-able, get the first
169 	 *	onlined phci based name without /devices prefix.
170 	 *	Used in stmsboot to update the phci based bootpath.
171 	 * -D drvname
172 	 *	if supplied, indicates that we're going to operate on
173 	 *	devices attached to this driver
174 	 * -n
175 	 *	if supplied, returns name of the node containing "fp" or
176 	 *	"sas-$driver", appends "sd@" or "ssd@" or "disk@". Can only
177 	 *	be used if -D drv is specified as well
178 	 */
179 	(void) fprintf(stderr, gettext("usage: %s -u | -m devname | "
180 	    "-M devname | -l controller | -L | \n"
181 	    "\t\t-p devname | -D { fp | mpt } | -n\n"), progname);
182 	exit(2);
183 }
184 
185 /*
186  * Parse command line arguments.
187  */
188 static void
189 parse_args(int argc, char *argv[])
190 {
191 	char opt;
192 	int n = 0;
193 
194 	if (argc == 1) {
195 		usage(argv[0]);
196 		/*NOTREACHED*/
197 	}
198 
199 	while ((opt = getopt(argc, argv, "udm:M:Ll:gp:D:n")) != EOF) {
200 		switch (opt) {
201 		case 'u':
202 			patch_vfstab = 1;
203 			n++;
204 			break;
205 
206 		case 'd':
207 			debug = 1;
208 			break;
209 
210 		case 'm':
211 			mapdev = s_strdup(optarg);
212 			n++;
213 			break;
214 
215 		case 'M':
216 			mapdev = s_strdup(optarg);
217 			cap_m_option = 1;
218 			n++;
219 			break;
220 
221 		case 'L':
222 			list_option = 1;
223 			n++;
224 			break;
225 
226 		case 'l':
227 			list_option = 1;
228 			list_controllernum = (int)atol(optarg);
229 			if (list_controllernum < 0) {
230 				logerr(gettext("controller number %d is "
231 				    "invalid\n"), list_controllernum);
232 				clean_exit(1);
233 			}
234 			n++;
235 			break;
236 
237 		case 'g':
238 			/*
239 			 * private option to display non-STMS device name
240 			 * to GUID mappings.
241 			 */
242 			list_guid_mappings = 1;
243 			break;
244 
245 		case 'p':
246 			/*
247 			 * map openable vhci based name to phci base name
248 			 */
249 			map_vhciname = s_strdup(optarg);
250 			n++;
251 			break;
252 
253 		case 'D':
254 			/*
255 			 * Grab the driver name we need to look for. Each
256 			 * time we add support for a new SAS or FC driver
257 			 * to this utility, make sure that its driver name
258 			 * is checked here.
259 			 */
260 			drvname = s_malloc(sizeof (optarg) + 1);
261 			drvname = s_strdup(optarg);
262 			if (strcmp(drvname, "fp") == 0) {
263 				drvprop = s_malloc(sizeof (NODE_WWN_PROP));
264 				(void) snprintf(drvprop, sizeof (NODE_WWN_PROP),
265 				    NODE_WWN_PROP);
266 			} else if (strcmp(drvname, "mpt") == 0) {
267 				drvprop = s_malloc(sizeof (SASPROP) +
268 				    sizeof (drvname) + 1);
269 				(void) snprintf(drvprop, sizeof (SASPROP) +
270 				    sizeof (drvname), "%s%s",
271 				    SASPROP, drvname);
272 			} else {
273 				logerr(gettext("Driver %s is not supported\n"),
274 				    drvname);
275 				clean_exit(1);
276 			}
277 
278 			break;
279 
280 		case 'n':
281 			++parent;
282 			n++;
283 			break;
284 
285 		default:
286 			usage(argv[0]);
287 			/*NOTREACHED*/
288 		}
289 	}
290 
291 	if (n != 1) {
292 		usage(argv[0]);
293 		/*NOTREACHED*/
294 	}
295 }
296 
297 int
298 main(int argc, char *argv[])
299 {
300 	char save_vfstab[SYS_FILENAME_LEN], tmp_vfstab[SYS_FILENAME_LEN];
301 	int vfstab_updated;
302 
303 	(void) setlocale(LC_ALL, "");
304 	(void) textdomain(TEXT_DOMAIN);
305 
306 	if (getuid() != 0) {
307 		logerr(gettext("must be super-user to run this program\n"));
308 		clean_exit(1);
309 	}
310 
311 	parse_args(argc, argv);
312 	(void) umask(022);
313 
314 	/*
315 	 * NOTE: The mpxio boot-up script executes this program with the
316 	 * mapping (-m) option before the /usr is even mounted and when the
317 	 * root filesystem is still mounted read-only.
318 	 */
319 	if (*mapdev != '\0') {
320 		char newname[MAXPATHLEN];
321 
322 		if (map_devname(mapdev, newname, sizeof (newname),
323 		    cap_m_option) == 0) {
324 			(void) printf("%s\n", newname);
325 			clean_exit(0);
326 		}
327 		clean_exit(1);
328 	}
329 	if (*map_vhciname != '\0') {
330 		char newname[MAXPATHLEN];
331 
332 		if (map_openable_vhciname(map_vhciname, newname,
333 		    sizeof (newname)) == 0) {
334 			(void) printf("%s\n", newname);
335 			clean_exit(0);
336 		}
337 		clean_exit(1);
338 	}
339 
340 	if (list_option || list_guid_mappings) {
341 		if (list_mappings(list_controllernum, list_guid_mappings) == 0)
342 			clean_exit(0);
343 		clean_exit(1);
344 	}
345 
346 	if (parent > 0) {
347 		if (strcmp(drvname, "") == 0) {
348 			usage(argv[0]);
349 			clean_exit(1);
350 		} else {
351 			list_nodes(drvname);
352 			clean_exit(0);
353 		}
354 	}
355 
356 	/* create a directory where a copy of the system files are saved */
357 	if (patch_vfstab) {
358 		if (mkdirp(SAVE_DIR, 0755) != 0 && errno != EEXIST) {
359 			logerr(gettext("mkdirp: failed to create %1$s: %2$s\n"),
360 			    SAVE_DIR, strerror(errno));
361 			clean_exit(1);
362 		}
363 
364 		if (make_temp(VFSTAB, save_vfstab, tmp_vfstab,
365 		    SYS_FILENAME_LEN) != 0)
366 			clean_exit(1);
367 
368 		/* build new vfstab without modifying the existing one */
369 		if ((vfstab_updated = update_vfstab(VFSTAB, tmp_vfstab))
370 		    == -1) {
371 			logerr(gettext("failed to update %s\n"), VFSTAB);
372 			clean_exit(1);
373 		}
374 
375 		commit_change(VFSTAB, save_vfstab, tmp_vfstab, vfstab_updated);
376 	}
377 
378 	clean_exit(0);
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  * Map phci based client name to vhci based client name.
762  *
763  * phci_name
764  *	phci based client /devices name without the /devices prefix and
765  *	minor name component.
766  *	ex:
767  *
768  *	(FC)
769  *	for sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
770  *	for x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0
771  *
772  *	(SAS)
773  *	for sparc: /pci@0,2/LSILogic,sas@1/disk@6,0
774  *	for x86: /pci1000,3060@3/sd@0,0
775  *
776  * vhci_name
777  *	Caller supplied buffer where vhci /devices name will be placed on
778  *	return (without the /devices prefix and minor name component).
779  *	ex:
780  *
781  *	(FC)
782  *	for sparc: /scsi_vhci/ssd@g2000002037cd9f72
783  *	for x86: /scsi_vhci/disk@g2000002037cd9f72
784  *
785  *	(SAS)
786  *	both: /scsi_vhci/disk@g600a0b8000254d3e00000284453ed8ac
787  *
788  * vhci_name_len
789  *	Length of the caller supplied vhci_name buffer.
790  *
791  * Returns 0 on success, -1 on failure.
792  */
793 static int
794 phci_to_vhci(char *phci_name, char *vhci_name, size_t vhci_name_len)
795 {
796 	sv_iocdata_t ioc;
797 	char *slash, *at;
798 	char vhci_name_buf[MAXPATHLEN];
799 	char phci_name_buf[MAXPATHLEN];
800 	char addr_buf[MAXNAMELEN];
801 
802 	logdmsg("phci_to_vhci: client = %s\n", phci_name);
803 
804 	s_strlcpy(phci_name_buf, phci_name, MAXPATHLEN);
805 
806 	if (client_name_type(phci_name_buf) != CLIENT_TYPE_PHCI ||
807 	    (slash = strrchr(phci_name_buf, '/')) == NULL ||
808 	    ((strncmp(slash, "/ssd@", sizeof ("/ssd@") - 1) != 0) &&
809 	    (strncmp(slash, "/sd@", sizeof ("/sd@") - 1) != 0) &&
810 	    (strncmp(slash, "/disk@", sizeof ("/disk@") - 1) != 0))) {
811 		logdmsg("phci_to_vhci: %s is not of CLIENT_TYPE_PHCI\n",
812 		    phci_name);
813 		return (-1);
814 	}
815 
816 	if (vhci_fd < 0) {
817 		if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0)
818 			return (-1);
819 	}
820 
821 	*slash = '\0';
822 
823 	at = strchr(slash + 1, '@');
824 	s_strlcpy(addr_buf, at + 1, MAXNAMELEN);
825 
826 	bzero(&ioc, sizeof (sv_iocdata_t));
827 	ioc.client = vhci_name_buf;
828 	ioc.phci = phci_name_buf;
829 	ioc.addr = addr_buf;
830 
831 	if (ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_NAME, &ioc) != 0) {
832 		logdmsg("SCSI_VHCI_GET_CLIENT_NAME on %s "
833 		    "failed: %s\n", phci_name, strerror(errno));
834 		return (-1);
835 	}
836 
837 	s_strlcpy(vhci_name, vhci_name_buf, vhci_name_len);
838 	logdmsg("phci_to_vhci: %s maps to %s\n", phci_name, vhci_name);
839 	return (0);
840 }
841 
842 /*
843  * Map vhci based client name to phci based client name.
844  * If the client has multiple paths, only one of the paths with which client
845  * can be accessed is returned. This function does not use SCSI_VHCI ioctls
846  * as it is called on mpxio disabled paths.
847  *
848  * vhci_name
849  *	vhci based client /devices name without the /devices prefix and
850  *	minor name component.
851  *	ex:
852  *	sparc: /scsi_vhci/ssd@g2000002037cd9f72
853  *	x86: /scsi_vhci/disk@g2000002037cd9f72
854  *
855  * phci_name
856  *	Caller supplied buffer where phci /devices name will be placed on
857  *	return (without the /devices prefix and minor name component).
858  *	ex:
859  *	sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
860  *	x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0
861  *
862  * phci_name_len
863  *	Length of the caller supplied phci_name buffer.
864  *
865  * Returns 0 on success, -1 on failure.
866  */
867 static int
868 vhci_to_phci(char *vhci_name, char *phci_name, size_t phci_name_len)
869 {
870 	di_node_t node = DI_NODE_NIL;
871 	char *vhci_guid, *devfspath;
872 	char phci_guid[MAXPATHLEN];
873 	char *node_name;
874 
875 	logdmsg("vhci_to_phci: client = %s\n", vhci_name);
876 
877 	if (client_name_type(vhci_name) != CLIENT_TYPE_VHCI) {
878 		logdmsg("vhci_to_phci: %s is not of CLIENT_TYPE_VHCI\n",
879 		    vhci_name);
880 		return (-1);
881 	}
882 
883 
884 	if ((vhci_guid = strrchr(vhci_name, '@')) == NULL ||
885 	    *(++vhci_guid) != 'g') {
886 		logerr(gettext("couldn't get guid from %s\n"), vhci_name);
887 		return (-1);
888 	}
889 
890 	/* point to guid */
891 	++vhci_guid;
892 
893 	/*
894 	 * Get devinfo snapshot and walk all ssd nodes whose parent is fp.
895 	 * For each node get the guid and match it with vhci_guid.
896 	 */
897 	if (devinfo_root == DI_NODE_NIL) {
898 		logdmsg("vhci_to_phci: taking devinfo snapshot\n");
899 		if ((devinfo_root = di_init("/", DINFOCPYALL | DINFOFORCE))
900 		    == DI_NODE_NIL) {
901 			logerr(gettext("di_init failed: %s\n"),
902 			    strerror(errno));
903 			return (-1);
904 		}
905 		logdmsg("vhci_to_phci: done taking devinfo snapshot\n");
906 	}
907 
908 
909 	/*
910 	 * When we finally get a unified "sd" driver for all
911 	 * architectures that Solaris runs on, we can remove this
912 	 * first loop around for "ssd"
913 	 */
914 	for (node = di_drv_first_node("ssd", devinfo_root);
915 	    node != DI_NODE_NIL; node = di_drv_next_node(node)) {
916 
917 		if ((node_name = di_node_name(node)) == NULL)
918 			continue;
919 
920 		if ((strcmp(node_name, "disk") != 0) &&
921 		    (strcmp(node_name, "sd") != 0) &&
922 		    (strcmp(node_name, "ssd") != 0))
923 			continue;
924 
925 		if (di_parent_node(node) == DI_NODE_NIL)
926 			continue;
927 
928 		if ((devfspath = di_devfs_path(node)) == NULL)
929 			continue;
930 
931 		/*
932 		 * Don't set no_delay_flag to have get_guid() fail on
933 		 * standby paths of T3. So we'll find the preferred paths.
934 		 */
935 		if (get_guid(devfspath, phci_guid,
936 		    sizeof (phci_guid), 0, node) != 0)
937 			continue;
938 
939 		if (strcmp(phci_guid, vhci_guid) == 0) {
940 			s_strlcpy(phci_name, devfspath, phci_name_len);
941 			di_devfs_path_free(devfspath);
942 			logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name,
943 			    phci_name);
944 			return (0);
945 		}
946 
947 		di_devfs_path_free(devfspath);
948 	}
949 
950 	for (node = di_drv_first_node("sd", devinfo_root);
951 	    node != DI_NODE_NIL; node = di_drv_next_node(node)) {
952 
953 		if ((node_name = di_node_name(node)) == NULL)
954 			continue;
955 
956 		if ((strcmp(node_name, "disk") != 0) &&
957 		    (strcmp(node_name, "sd") != 0) &&
958 		    (strcmp(node_name, "ssd") != 0))
959 			continue;
960 
961 		if (di_parent_node(node) == DI_NODE_NIL)
962 			continue;
963 
964 		if ((devfspath = di_devfs_path(node)) == NULL)
965 			continue;
966 
967 		/*
968 		 * Don't set no_delay_flag to have get_guid() fail on
969 		 * standby paths of T3. So we'll find the preferred paths.
970 		 */
971 		if (get_guid(devfspath, phci_guid,
972 		    sizeof (phci_guid), 0, node) != 0)
973 			continue;
974 
975 		if (strcmp(phci_guid, vhci_guid) == 0) {
976 			s_strlcpy(phci_name, devfspath, phci_name_len);
977 			di_devfs_path_free(devfspath);
978 			logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name,
979 			    phci_name);
980 			return (0);
981 		}
982 
983 		di_devfs_path_free(devfspath);
984 	}
985 
986 	logdmsg("vhci_to_phci: couldn't get phci name for %s\n", vhci_name);
987 	return (-1);
988 }
989 
990 /*
991  * Map vhci based client name to phci based client name.
992  * If the client has multiple paths, only one of the paths with which client
993  * can be accessed is returned.
994  * This function uses SCSI_VHCI ioctls to get the phci paths
995  *
996  * vhci_name
997  *	vhci based client /devices name without the /devices prefix and
998  *	minor name component.
999  *	ex:
1000  *	sparc: /scsi_vhci/ssd@g2000002037cd9f72
1001  *	x86: /scsi_vhci/disk@g2000002037cd9f72
1002  *
1003  * phci_name
1004  *	Caller supplied buffer where phci /devices name will be placed on
1005  *	return (without the /devices prefix and minor name component).
1006  *	ex:
1007  *	sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
1008  *	x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0
1009  *
1010  * phci_name_len
1011  *	Length of the caller supplied phci_name buffer.
1012  *
1013  * Returns 0 on success, -1 on failure.
1014  */
1015 
1016 static int
1017 vhci_to_phci_by_ioctl(char *vhci_name, char *phci_name, size_t phci_name_len)
1018 {
1019 	sv_iocdata_t	ioc;
1020 	uint_t npaths;
1021 	char *node_name, *at;
1022 	char vhci_name_buf[MAXPATHLEN];
1023 	int  ret;
1024 	sv_path_info_t *pi;
1025 
1026 	logdmsg("vhci_to_phci_by_ioctl: client = %s\n", vhci_name);
1027 
1028 	if (vhci_fd < 0) {
1029 		if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0)
1030 			return (-1);
1031 	}
1032 
1033 	(void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN);
1034 
1035 	/* first get the number paths */
1036 	bzero(&ioc, sizeof (sv_iocdata_t));
1037 	ioc.client = vhci_name_buf;
1038 	ioc.ret_elem = &npaths;
1039 	if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO,
1040 	    &ioc)) != 0 || npaths == 0) {
1041 		logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s "
1042 		    "failed: %s\n", vhci_name,
1043 		    ret?strerror(errno):"got 0 paths");
1044 		return (-1);
1045 	}
1046 
1047 	/* now allocate memory for the path information and get all paths */
1048 	bzero(&ioc, sizeof (sv_iocdata_t));
1049 	ioc.client = vhci_name_buf;
1050 	ioc.buf_elem = npaths;
1051 	ioc.ret_elem = &npaths;
1052 	if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths,
1053 	    sizeof (sv_path_info_t))) == NULL)
1054 		return (-1);
1055 	if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO,
1056 	    &ioc)) != 0 || npaths == 0) {
1057 		logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s "
1058 		    "failed: %s\n", vhci_name,
1059 		    ret?strerror(errno):"got 0 paths");
1060 		goto out;
1061 	}
1062 
1063 	if (ioc.buf_elem < npaths)
1064 		npaths = ioc.buf_elem;
1065 	if ((node_name = strrchr(vhci_name_buf, '/')) == NULL ||
1066 	    (at = strchr(node_name, '@')) == NULL)
1067 		goto out;
1068 
1069 	node_name++;
1070 	*at = '\0';
1071 
1072 	logdmsg("vhci_to_phci_by_ioctl: node_name is %s\n", node_name);
1073 #ifndef sparc
1074 	/*
1075 	 * We need to use a libdevinfo call to get this info
1076 	 * in an architecturally-neutral fashion. Phase-II for sure!
1077 	 */
1078 	node_name = "sd";
1079 #endif
1080 
1081 	/*
1082 	 * return the first online paths as non-online paths may
1083 	 * not be accessible in the target environment.
1084 	 */
1085 	pi = (sv_path_info_t *)ioc.ret_buf;
1086 	while (npaths--) {
1087 		if (MDI_PATHINFO_STATE_ONLINE == pi->ret_state) {
1088 			(void) snprintf(phci_name, phci_name_len, "%s/%s@%s",
1089 			    pi->device.ret_phci, node_name,
1090 			    pi->ret_addr);
1091 			logdmsg("vhci_to_phci_by_ioctl: %s maps to %s\n",
1092 			    vhci_name, phci_name);
1093 			free(ioc.ret_buf);
1094 			return (0);
1095 		}
1096 		pi++;
1097 	}
1098 
1099 out:
1100 	logdmsg("vhci_to_phci_by_ioctl: couldn't get phci name for %s\n",
1101 	    vhci_name);
1102 	free(ioc.ret_buf);
1103 	return (-1);
1104 
1105 }
1106 
1107 /*
1108  * Map physname from phci name space to vhci name space or vice-versa
1109  *
1110  * physname
1111  *	phci or vhci based client /devices name without the /devices prefix and
1112  *	minor name component.
1113  *
1114  * new_physname
1115  *	Caller supplied buffer where the mapped physical name is stored on
1116  *	return (without the /devices prefix and minor name component).
1117  *
1118  * len
1119  *	Length of the caller supplied new_physname buffer.
1120  *
1121  * Returns 0 on success, -1 on failure.
1122  */
1123 static int
1124 map_physname(char *physname, char *new_physname, size_t len)
1125 {
1126 	int type;
1127 	int rv;
1128 
1129 	type = client_name_type(physname);
1130 	logdmsg("map_physname: type (%d) physname = %s\n",
1131 	    type, physname);
1132 
1133 	if (type == CLIENT_TYPE_VHCI)
1134 		rv = vhci_to_phci(physname, new_physname, len);
1135 	else if (type == CLIENT_TYPE_PHCI)
1136 		rv = phci_to_vhci(physname, new_physname, len);
1137 	else
1138 		rv = -1;
1139 
1140 	logdmsg("map_physname: returning %d\n", rv);
1141 	return (rv);
1142 }
1143 
1144 /*
1145  * Given a phci or vhci devname which is either a /dev link or /devices name
1146  * get the corresponding physical node path (without the /devices prefix)
1147  * and minor name.
1148  *
1149  * Returns 0 on success, -1 on failure.
1150  */
1151 static int
1152 get_physname_minor(char *devname, char *physname, int physname_len,
1153     char *minorname, int minorname_len)
1154 {
1155 	int linksize;
1156 	char buf[MAXPATHLEN];
1157 	char *p, *m;
1158 
1159 	if (strncmp(devname, DEV_DSK, sizeof (DEV_DSK) - 1) == 0 ||
1160 	    strncmp(devname, DEV_RDSK, sizeof (DEV_RDSK) - 1) == 0) {
1161 		if ((linksize = readlink(devname, buf, MAXPATHLEN))
1162 		    > 0 && linksize <= (MAXPATHLEN - 1)) {
1163 			buf[linksize] = '\0';
1164 		} else
1165 			return (-1);
1166 	} else
1167 		s_strlcpy(buf, devname, MAXPATHLEN);
1168 
1169 	if ((p = strstr(buf, SLASH_DEVICES)) == NULL)
1170 		return (-1);
1171 
1172 	/* point to '/' after /devices */
1173 	p += sizeof (SLASH_DEVICES) - 2;
1174 
1175 	if ((m = strrchr(p, ':')) == NULL) {
1176 		logdmsg("get_physname_minor: no minor name component in %s\n",
1177 		    buf);
1178 		return (-1);
1179 	}
1180 
1181 	*m = '\0';
1182 	m++;
1183 
1184 	if (client_name_type(p) == CLIENT_TYPE_UNKNOWN)
1185 		return (-1);
1186 
1187 	s_strlcpy(physname, p, physname_len);
1188 	s_strlcpy(minorname, m, minorname_len);
1189 	logdmsg("get_physname_minor: %s: physname = %s, minor = %s\n",
1190 	    devname, physname, minorname);
1191 	return (0);
1192 }
1193 
1194 static int
1195 devlink_callback(di_devlink_t devlink, void *argptr)
1196 {
1197 	const char *link;
1198 	struct devlink_cbarg *argp = argptr;
1199 
1200 	if ((link = di_devlink_path(devlink)) != NULL) {
1201 		s_strlcpy(argp->devlink, link, argp->len);
1202 		return (DI_WALK_TERMINATE);
1203 	}
1204 
1205 	return (DI_WALK_CONTINUE);
1206 }
1207 
1208 /*
1209  * Lookup the /dev link corresponding to physname and minorname.
1210  *
1211  * physname	client /devices path without the /devices prefix and minor
1212  *		name component.
1213  * minorname	client minor name.
1214  * devlink	caller supplied buffer where the /dev link is placed on return.
1215  * len		caller supplied devlink buffer length
1216  *
1217  * Returns 0 on success, -1 on failure.
1218  */
1219 static int
1220 lookup_devlink(char *physname, char *minorname, char *devlink, size_t len)
1221 {
1222 	char buf[MAXPATHLEN];
1223 	struct devlink_cbarg arg;
1224 
1225 	if (devlink_hdl == NULL) {
1226 		logdmsg("lookup_devlink: taking devlink snapshot\n");
1227 		if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
1228 			logerr(gettext("di_devlink_init failed: %s\n"),
1229 			    strerror(errno));
1230 			clean_exit(1);
1231 		}
1232 	}
1233 
1234 	*devlink = '\0';
1235 	(void) snprintf(buf, MAXPATHLEN, "%s:%s", physname, minorname);
1236 	arg.devlink = devlink;
1237 	arg.len = len;
1238 	if (di_devlink_walk(devlink_hdl, NULL, buf, DI_PRIMARY_LINK, &arg,
1239 	    devlink_callback) != 0) {
1240 		logdmsg("lookup_devlink: di_devlink_walk on %s failed: %s\n",
1241 		    buf, strerror(errno));
1242 		return (-1);
1243 	}
1244 
1245 	if (*devlink == '\0') {
1246 		logdmsg("lookup_devlink: failed to lookup devlink for %s\n",
1247 		    buf);
1248 		return (-1);
1249 	}
1250 
1251 	logdmsg("lookup_devlink: /dev link for %s:%s = %s\n", physname,
1252 	    minorname, devlink);
1253 	return (0);
1254 }
1255 
1256 /*
1257  * open infile for reading and return its file pointer in *fp_in.
1258  * open outfile for writing and return its file pointer in *fp_out.
1259  *
1260  * Returns 0 on success, -1 on failure.
1261  */
1262 static int
1263 open_in_out_files(char *infile, char *outfile, FILE **fp_in, FILE **fp_out)
1264 {
1265 	FILE *fin = NULL;
1266 	FILE *fout = NULL;
1267 	struct stat sbuf;
1268 
1269 	if ((fin = fopen(infile, "r")) == NULL) {
1270 		logerr(gettext("failed to fopen %1$s: %2$s\n"),
1271 		    infile, strerror(errno));
1272 		goto out;
1273 	}
1274 
1275 	if (fstat(fileno(fin), &sbuf) != 0) {
1276 		logerr(gettext("fstat failed on %1$s: %2$s\n"),
1277 		    infile, strerror(errno));
1278 		goto out;
1279 	}
1280 
1281 	if ((fout = fopen(outfile, "w")) == NULL) {
1282 		logerr(gettext("failed to fopen %1$s: %2$s\n"),
1283 		    outfile, strerror(errno));
1284 		goto out;
1285 	}
1286 
1287 	if (fchmod(fileno(fout), (sbuf.st_mode & 0777)) != 0) {
1288 		logerr(gettext("failed to fchmod %1$s to 0%2$o: %3$s\n"),
1289 		    outfile, sbuf.st_mode & 0777, strerror(errno));
1290 		goto out;
1291 	}
1292 
1293 	if (fchown(fileno(fout), sbuf.st_uid, sbuf.st_gid) != 0) {
1294 		logerr(gettext("failed to fchown %1$s to uid %2$d and "
1295 		    "gid %3$d: %4$s\n"),
1296 		    outfile, sbuf.st_uid, sbuf.st_gid, strerror(errno));
1297 		goto out;
1298 	}
1299 
1300 	*fp_in = fin;
1301 	*fp_out = fout;
1302 	return (0);
1303 
1304 out:
1305 	if (fin != NULL)
1306 		(void) fclose(fin);
1307 	if (fout != NULL)
1308 		(void) fclose(fout);
1309 	return (-1);
1310 }
1311 
1312 /*
1313  * If the devname is a phci based name and not open-able, map it to vhci
1314  * based name. If the devname is a vhci based name and not open-able, map it
1315  * to phci based name.
1316  *
1317  * devname	either a /dev link or /devices name to client device
1318  * new_devname	caller supplied buffer where the mapped device name is
1319  *		placed on return.
1320  * len		caller supplied new_devname buffer length
1321  * devlink_flag	pass 1 if requesting the /dev link to the mapped device.
1322  *		pass 0 if requesting the /devices name of the mapped device.
1323  *
1324  * Returns 0 on success, -1 on failure.
1325  */
1326 static int
1327 map_devname(char *devname, char *new_devname, size_t len, int devlink_flag)
1328 {
1329 	char physname[MAXPATHLEN];
1330 	char minor[MAXNAMELEN];
1331 	char new_physname[MAXPATHLEN];
1332 
1333 	logdmsg("map_devname: checking devname %s\n", devname);
1334 	if ((get_physname_minor(devname, physname, sizeof (physname),
1335 	    minor, sizeof (minor)) == 0) &&
1336 	    (canopen(devname) == 0) &&
1337 	    (map_physname(physname, new_physname,
1338 	    sizeof (new_physname)) == 0)) {
1339 
1340 		logdmsg("map_devname: now looking up devlink\n");
1341 
1342 		if (devlink_flag) {
1343 			if (lookup_devlink(new_physname, minor, new_devname,
1344 			    len) == 0)
1345 				return (0);
1346 		} else {
1347 			(void) snprintf(new_devname, len, "/devices%s:%s",
1348 			    new_physname, minor);
1349 			return (0);
1350 		}
1351 	}
1352 
1353 	logdmsg("map_devname: failed to find mapping for %s\n", devname);
1354 	return (-1);
1355 }
1356 
1357 /*
1358  * If the devname is a vhci based name and open-able, map it to phci
1359  * based name.
1360  *
1361  * devname	either a /dev link or /devices name to client device
1362  * new_devname	caller supplied buffer where the mapped device name without
1363  *		/devices prefix is placed on return.
1364  * len		caller supplied new_devname buffer length
1365  */
1366 static int
1367 map_openable_vhciname(char *devname, char *new_devname, size_t len)
1368 {
1369 	char physname[MAXPATHLEN];
1370 	char minor[MAXNAMELEN];
1371 	char new_physname[MAXPATHLEN];
1372 
1373 	if (get_physname_minor(devname, physname, sizeof (physname),
1374 	    minor, sizeof (minor)) == 0 &&
1375 	    canopen(devname) == 1 &&
1376 	    client_name_type(physname) == CLIENT_TYPE_VHCI &&
1377 	    vhci_to_phci_by_ioctl(physname, new_physname,
1378 		sizeof (new_physname)) == 0) {
1379 		(void) snprintf(new_devname, len, "%s:%s",
1380 		    new_physname, minor);
1381 		return (0);
1382 	}
1383 
1384 	return (-1);
1385 }
1386 /*
1387  * Make a new /etc/vfstab:
1388  * Read vfstab_in, convert the device name entries to appropriate vhci or phci
1389  * based names, and write to vfstab_out. Only device names whose physical
1390  * paths are either phci or vhci based names and not open-able are considered
1391  * for conversion. Open-able device name entries are not converted as it
1392  * means that the device is already accessible; hence no need to convert.
1393  *
1394  * Returns:
1395  * 	0	successful but vfstab_out contents are the same as vfstab_in
1396  *	1	successful and vfstab_out changed from vfstab_in
1397  *	-1	failed
1398  */
1399 static int
1400 update_vfstab(char *vfstab_in, char *vfstab_out)
1401 {
1402 	FILE *fp_in, *fp_out;
1403 	char *buf, *tmpbuf;
1404 	char *vfs_cache[2];
1405 	int idx = 0, count = 0;
1406 	int rv = -1;
1407 	int vfstab_updated = 0;
1408 	int i;
1409 	char cdev[MAXPATHLEN];
1410 	char bdev[MAXPATHLEN];
1411 	char mntpt[MAXPATHLEN];
1412 	char fstype[512];
1413 	char fsckpass[512];
1414 	char mntboot[512];
1415 	char mntopt[MAX_MNTOPT_STR];
1416 	char phys_bdev[MAXPATHLEN], phys_cdev[MAXPATHLEN];
1417 	char bdev_minor[MAXNAMELEN], cdev_minor[MAXNAMELEN];
1418 	char new_physname[MAXPATHLEN];
1419 	char new_bdevlink[MAXPATHLEN], new_cdevlink[MAXPATHLEN];
1420 	char fmt[80];
1421 
1422 	if (open_in_out_files(vfstab_in, vfstab_out, &fp_in, &fp_out) != 0)
1423 		return (-1);
1424 
1425 	/*
1426 	 * Read one line at time from vfstab_in. If no conversion is needed
1427 	 * for the line simply write the line to vfstab_out. If conversion is
1428 	 * needed, first write the existing line as a comment to vfstab_out
1429 	 * and then write the converted line.
1430 	 *
1431 	 * To avoid commented entries piling up in vfstab in case if the
1432 	 * user runs stmsboot multiple times to switch on and off from mpxio,
1433 	 * add the commented line only if not already there. To do this
1434 	 * cache the last two vfstab lines processed and add the commented
1435 	 * entry only if it is not found in the cache. We only need to cache
1436 	 * the last two lines because a device can have at most two names -
1437 	 * one mpxio and one non-mpxio name. Therefore for any device name
1438 	 * entry we at most add two comments - one with mpxio name and one
1439 	 * with non-mpxio name - no matter how many times stmsboot is run.
1440 	 */
1441 	buf = (char *)s_malloc(VFS_LINE_MAX);
1442 	tmpbuf = (char *)s_malloc(VFS_LINE_MAX);
1443 	vfs_cache[0] = (char *)s_malloc(VFS_LINE_MAX);
1444 	vfs_cache[1] = (char *)s_malloc(VFS_LINE_MAX);
1445 
1446 	(void) snprintf(fmt, sizeof (fmt),
1447 	    "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1,
1448 	    sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1,
1449 	    sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1);
1450 
1451 	while (fgets(buf, VFS_LINE_MAX, fp_in) != NULL) {
1452 		if (strlen(buf) == (VFS_LINE_MAX - 1) &&
1453 		    buf[VFS_LINE_MAX-2] != '\n') {
1454 			logerr(gettext("%1$s line size too long, "
1455 			    "exceeded %2$d: \"%3$s\"\n"),
1456 			    VFSTAB, VFS_LINE_MAX - 2, buf);
1457 			goto out;
1458 		}
1459 
1460 		/* LINTED - format specifier */
1461 		if ((sscanf(buf, fmt, bdev, cdev, mntpt,
1462 		    fstype, fsckpass, mntboot, mntopt) != 7) ||
1463 		    (bdev[0] == '#') ||
1464 		    (get_physname_minor(bdev, phys_bdev, sizeof (phys_bdev),
1465 		    bdev_minor, sizeof (bdev_minor)) != 0) ||
1466 
1467 		    (strcmp(fstype, "swap") != 0 &&
1468 		    ((get_physname_minor(cdev, phys_cdev, sizeof (phys_cdev),
1469 		    cdev_minor, sizeof (cdev_minor)) != 0) ||
1470 		    (strcmp(phys_bdev, phys_cdev) != 0))) ||
1471 
1472 		    canopen(bdev) ||
1473 		    (map_physname(phys_bdev, new_physname,
1474 		    sizeof (new_physname)) != 0) ||
1475 		    (lookup_devlink(new_physname, bdev_minor, new_bdevlink,
1476 		    sizeof (new_bdevlink)) != 0) ||
1477 
1478 		    (strcmp(fstype, "swap") != 0 &&
1479 		    (lookup_devlink(new_physname, cdev_minor, new_cdevlink,
1480 		    sizeof (new_cdevlink)) != 0))) {
1481 
1482 			/* cache the last two entries */
1483 			(void) strlcpy(vfs_cache[idx], buf, VFS_LINE_MAX);
1484 			idx = (idx == 0) ? 1 : 0;
1485 			if (count < 2)
1486 				count++;
1487 
1488 			if (fputs(buf, fp_out) == EOF) {
1489 				logerr(gettext("fputs \"%1$s\" to %2$s "
1490 				    "failed: %3$s\n"),
1491 				    buf, vfstab_out, strerror(errno));
1492 				goto out;
1493 			}
1494 
1495 		} else {
1496 			/*
1497 			 * comment the entry in vfstab only if it is not
1498 			 * already in the cache.
1499 			 */
1500 			if (client_name_type(phys_bdev) == CLIENT_TYPE_VHCI)
1501 				(void) snprintf(tmpbuf, VFS_LINE_MAX,
1502 				    "# mpxio: %s", buf);
1503 			else
1504 				(void) snprintf(tmpbuf, VFS_LINE_MAX,
1505 				    "# non-mpxio: %s", buf);
1506 
1507 			for (i = 0; i < count; i++) {
1508 				if (strcmp(vfs_cache[i], tmpbuf) == 0)
1509 					break;
1510 			}
1511 
1512 			if (i == count) {
1513 				if (fputs(tmpbuf, fp_out) == EOF) {
1514 					logerr(gettext("fputs \"%1$s\" to %2$s "
1515 					    "failed: %3$s\n"), tmpbuf,
1516 					    vfstab_out, strerror(errno));
1517 					goto out;
1518 				}
1519 			}
1520 
1521 			count = 0;
1522 			idx = 0;
1523 
1524 			if (fprintf(fp_out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
1525 			    new_bdevlink,
1526 			    (strcmp(fstype, "swap") != 0) ? new_cdevlink : cdev,
1527 			    mntpt, fstype, fsckpass, mntboot, mntopt) < 0) {
1528 				logerr(gettext("fprintf failed to write to "
1529 				    "%1$s: %2$s\n"),
1530 				    vfstab_out, strerror(errno));
1531 				goto out;
1532 			}
1533 			vfstab_updated = 1;
1534 		}
1535 	}
1536 
1537 	rv = vfstab_updated;
1538 out:
1539 	(void) fclose(fp_in);
1540 	(void) fclose(fp_out);
1541 	free(buf);
1542 	free(tmpbuf);
1543 	free(vfs_cache[0]);
1544 	free(vfs_cache[1]);
1545 	return (rv);
1546 }
1547 
1548 /*
1549  * if guidmap is 0, list non-STMS to STMS device name mappings for the
1550  * specified controller.
1551  * if guidmap is 1, list non-STMS to GUID mappings for the specified controller.
1552  * If controller is -1 list mappings for all controllers.
1553  *
1554  * Returns 0 on success, -1 on failure.
1555  */
1556 static int
1557 list_mappings(int controller, int guidmap)
1558 {
1559 	int cnum, len, mapped;
1560 	int header = 1;
1561 	char *p1, *p2;
1562 	DIR *dirp;
1563 	struct dirent *direntry;
1564 	char devname[MAXPATHLEN];
1565 	char physname[MAXPATHLEN];
1566 	char new_devname[MAXPATHLEN];
1567 	char new_physname[MAXPATHLEN];
1568 	char guid[MAXPATHLEN];
1569 	char minor[MAXNAMELEN];
1570 
1571 	if ((dirp = opendir("/dev/rdsk")) == NULL)
1572 		return (-1);
1573 
1574 	while ((direntry = readdir(dirp)) != NULL) {
1575 		if (strcmp(direntry->d_name, ".") == 0 ||
1576 		    strcmp(direntry->d_name, "..") == 0 ||
1577 		    (len = strlen(direntry->d_name)) < 2 ||
1578 		    strcmp(direntry->d_name + len - 2, "s0") != 0 ||
1579 		    sscanf(direntry->d_name, "c%dt", &cnum) != 1 ||
1580 		    (controller != -1 && controller != cnum))
1581 			continue;
1582 
1583 		(void) snprintf(devname, MAXPATHLEN, "/dev/rdsk/%s",
1584 		    direntry->d_name);
1585 
1586 		if (get_physname_minor(devname, physname, sizeof (physname),
1587 		    minor, sizeof (minor)) != 0 ||
1588 		    client_name_type(physname) != CLIENT_TYPE_PHCI) {
1589 			logdmsg("list_mappings: continuing\n");
1590 			continue;
1591 		}
1592 
1593 		/*
1594 		 * First try phci_to_vhci() mapping. It will work if the
1595 		 * device is under MPxIO control. If the device is not under
1596 		 * MPxIO, phci_to_vhci() will fail in which case try to lookup
1597 		 * if an old mapping exists using guid lookup.
1598 		 */
1599 		mapped = 1;
1600 		if (phci_to_vhci(physname, new_physname,
1601 		    sizeof (new_physname)) != 0) {
1602 			if (get_guid(physname, guid, sizeof (guid), 1,
1603 			    DI_NODE_NIL) == 0)
1604 				(void) snprintf(new_physname, MAXPATHLEN,
1605 				    "/scsi_vhci/%s%s", DISK_AT_G, guid);
1606 			else
1607 				mapped = 0;
1608 		}
1609 
1610 		if (mapped == 0)
1611 			continue;
1612 
1613 		/* strip the slice number part */
1614 		devname[strlen(devname) - 2] = '\0';
1615 
1616 		if (guidmap == 0) {
1617 			if (lookup_devlink(new_physname, minor,
1618 			    new_devname, sizeof (new_devname)) != 0)
1619 				continue;
1620 
1621 			/* strip the slice number part */
1622 			new_devname[strlen(new_devname) - 2] = '\0';
1623 
1624 			if (header) {
1625 				(void) printf(
1626 				    gettext("non-STMS device name\t\t\t"
1627 				    "STMS device name\n"
1628 				    "------------------------------------------"
1629 				    "------------------------\n"));
1630 				header = 0;
1631 			}
1632 			(void) printf("%s\t\t%s\n", devname, new_devname);
1633 		} else {
1634 			/* extract guid part */
1635 			/* we should be using a getguid() call instead */
1636 			if ((p1 = strstr(new_physname, "@"))
1637 			    == NULL) {
1638 				logdmsg("invalid vhci: %s\n", new_physname);
1639 				continue;
1640 			}
1641 
1642 			logdmsg("\tp1 = %s\n", p1);
1643 
1644 			p1 += 2; /* "@" + [nwg] */
1645 			if ((p2 = strrchr(p1, ':')) != NULL)
1646 				*p2 = '\0';
1647 
1648 			if (header) {
1649 				(void) printf(
1650 				    gettext("non-STMS device name\t\t\tGUID\n"
1651 				    "------------------------------------------"
1652 				    "------------------------\n"));
1653 				header = 0;
1654 			}
1655 			(void) printf("%s\t\t%s\n", devname, p1);
1656 		}
1657 	}
1658 
1659 	(void) closedir(dirp);
1660 	return (0);
1661 }
1662 
1663 /*
1664  * Check if the file can be opened.
1665  *
1666  * Return 1 if the file can be opened, 0 otherwise.
1667  */
1668 static int
1669 canopen(char *filename)
1670 {
1671 	int fd;
1672 
1673 	if ((fd = open(filename, O_RDONLY)) == -1)
1674 		return (0);
1675 
1676 	logdmsg("canopen: was able to open %s\n", filename);
1677 	(void) close(fd);
1678 	return (1);
1679 }
1680 
1681 
1682 /*
1683  * This function traverses the device tree looking for nodes
1684  * which have "drivername" as a property. We return a list of
1685  * these nodes, with SLASH_DISK_AT appended.
1686  * Since there can be many different pci/pcie devices that all
1687  * share the same driver but which have different pci vid/did
1688  * combinations, we have to be smart about returning only those
1689  * pci vid/dids which have the "sas-*" property unless the
1690  * drivername is "fp", in which case we're searching for "node-wwn"
1691  */
1692 static void
1693 list_nodes(char *drivername)
1694 {
1695 	char *aliaslist;
1696 	char *mpxprop = NULL;
1697 	di_node_t devroot = DI_NODE_NIL;
1698 	di_node_t thisnode = DI_NODE_NIL;
1699 	int *intprop = NULL;
1700 	int i = 1; /* fencepost */
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 			if ((di_prop_lookup_ints(DDI_DEV_T_ANY,
1732 			    di_child_node(thisnode), drvprop,
1733 			    &intprop) > -1) ||
1734 			    (((di_prop_lookup_strings(DDI_DEV_T_ANY,
1735 			    thisnode, "mpxio-disable", &mpxprop) > -1) &&
1736 			    strcmp(di_driver_name(thisnode),
1737 			    drivername)) == 0)) {
1738 
1739 				if (strstr(aliaslist,
1740 				    di_node_name(thisnode))
1741 				    == (char *)NULL) {
1742 					char *nname;
1743 
1744 					nname = di_node_name(thisnode);
1745 
1746 					if (i) {
1747 					(void) snprintf(aliaslist,
1748 					    strlen(nname), "%s", nname);
1749 						--i;
1750 					} else {
1751 					if (strstr(aliaslist,
1752 					    di_node_name(thisnode)) ==
1753 					    (char *)NULL) {
1754 						(void) snprintf(aliaslist,
1755 						    strlen(nname) +
1756 						    strlen(aliaslist),
1757 						    "%s|%s", aliaslist,
1758 						    nname);
1759 						}
1760 					}
1761 				}
1762 			} else {
1763 				logdmsg("unable to lookup property %s "
1764 				    "for node %s. Error %d: %s\n",
1765 				    drvprop, di_devfs_path(thisnode),
1766 				    errno, strerror(errno));
1767 			}
1768 			thisnode = di_drv_next_node(thisnode);
1769 		}
1770 		(void) fprintf(stdout, "%s\n", aliaslist);
1771 		}
1772 
1773 		di_fini(devroot);
1774 	}
1775 }
1776 
1777 static void
1778 logerr(char *msg, ...)
1779 {
1780 	va_list ap;
1781 
1782 	(void) fprintf(stderr, "%s: ", stmsboot);
1783 	va_start(ap, msg);
1784 	/* LINTED - format specifier */
1785 	(void) vfprintf(stderr, msg, ap);
1786 	va_end(ap);
1787 }
1788 
1789 /* log debug message */
1790 static void
1791 logdmsg(char *msg, ...)
1792 {
1793 	va_list ap;
1794 
1795 	if (debug) {
1796 		va_start(ap, msg);
1797 		/* LINTED - format specifier */
1798 		(void) vprintf(msg, ap);
1799 		va_end(ap);
1800 	}
1801 }
1802 
1803 static void *
1804 s_malloc(const size_t size)
1805 {
1806 	void *rp;
1807 
1808 	if ((rp = malloc(size)) == NULL) {
1809 		logerr(gettext("malloc failed to allocate %d bytes\n"), size);
1810 		clean_exit(1);
1811 	}
1812 	return (rp);
1813 }
1814 
1815 static char *
1816 s_strdup(const char *ptr)
1817 {
1818 	void *rp;
1819 
1820 	if ((rp = strdup(ptr)) == NULL) {
1821 		logerr(gettext("strdup failed to dup %s\n"), ptr);
1822 		clean_exit(1);
1823 	}
1824 	return (rp);
1825 }
1826 
1827 static void
1828 s_strlcpy(char *dst, const char *src, size_t dstsize)
1829 {
1830 	int n;
1831 
1832 	if ((n = strlcpy(dst, src, dstsize)) >= dstsize) {
1833 		logerr(gettext("strlcpy: destination buffer size is %1$d "
1834 		    "bytes, need to at least %2$d bytes\n"), dstsize, n + 1);
1835 		clean_exit(1);
1836 	}
1837 }
1838 
1839 static void
1840 clean_exit(int status)
1841 {
1842 	if (devinfo_root != DI_NODE_NIL)
1843 		di_fini(devinfo_root);
1844 
1845 	if (devlink_hdl != NULL)
1846 		(void) di_devlink_fini(&devlink_hdl);
1847 
1848 	if (vhci_fd != -1)
1849 		(void) close(vhci_fd);
1850 
1851 	exit(status);
1852 }
1853