xref: /titanic_44/usr/src/cmd/stmsboot/stmsboot_util.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 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 #define	SLASH_SSD_AT	"/ssd@"
57 #define	SLASH_FP_AT	"/fp@"
58 #define	SLASH_SCSI_VHCI	"/scsi_vhci"
59 #define	DEV_DSK		"/dev/dsk/"
60 #define	DEV_RDSK	"/dev/rdsk/"
61 #define	SYS_FILENAME_LEN	256
62 
63 /*
64  * Save directory is the directory in which system files are saved.
65  * Save directory must be under the root filesystem, as this program is
66  * typically run before any other filesystems are mounted.
67  */
68 #define	SAVE_DIR	"/etc/mpxio"
69 
70 /* fcp driver publishes this property */
71 #define	NODE_WWN_PROP	"node-wwn"
72 
73 typedef enum {
74 	CLIENT_TYPE_UNKNOWN,
75 	CLIENT_TYPE_PHCI,
76 	CLIENT_TYPE_VHCI
77 } client_type_t;
78 
79 struct devlink_cbarg {
80 	char *devlink;
81 	size_t len;
82 };
83 
84 static di_node_t devinfo_root = DI_NODE_NIL;
85 static di_devlink_handle_t devlink_hdl = NULL;
86 static int vhci_fd = -1;
87 static int patch_vfstab, cap_m_option, debug;
88 static int list_option, list_guid_mappings, list_controllernum = -1;
89 static char *mapdev = "";
90 static char *stmsboot = "stmsboot";
91 
92 static int make_temp(char *, char *, char *, size_t);
93 static void commit_change(char *, char *, char *, int);
94 static int map_devname(char *, char *, size_t, int);
95 static int update_vfstab(char *, char *);
96 static int list_mappings(int, int);
97 static int canopen(char *);
98 static void logerr(char *, ...);
99 static void logdmsg(char *, ...);
100 static void *s_malloc(const size_t);
101 static char *s_strdup(const char *);
102 static void s_strlcpy(char *, const char *, size_t);
103 static void clean_exit(int);
104 
105 /*
106  * Print usage and exit.
107  */
108 static void
109 usage(char *argv0)
110 {
111 	char *progname;
112 
113 	progname = strrchr(argv0, '/');
114 	if (progname != NULL)
115 		progname++;
116 	else
117 		progname = argv0;
118 
119 	/*
120 	 * -u	update /etc/vfstab
121 	 * -m devname
122 	 *	if devname is phci based name and not open-able, map it to
123 	 *	vhci based /devices name.
124 	 *	if devname is vhci based name and not open-able, map it to
125 	 *	phci based /devices name.
126 	 * -M devname
127 	 *	same as -m except that /dev link is printed instead of
128 	 *	/devices name.
129 	 * -l controller
130 	 *	list non-STMS to STMS device name mappings for the specific
131 	 *	controller
132 	 * -L	list non-STMS to STMS device name mappings for all controllers
133 	 */
134 	(void) fprintf(stderr, gettext("usage: %s -u | -m devname | "
135 	    "-M devname | -l controller | -L\n"), progname);
136 	exit(2);
137 }
138 
139 /*
140  * Parse command line arguments.
141  */
142 static void
143 parse_args(int argc, char *argv[])
144 {
145 	char opt;
146 	int n = 0;
147 
148 	if (argc == 1) {
149 		usage(argv[0]);
150 		/*NOTREACHED*/
151 	}
152 
153 	while ((opt = getopt(argc, argv, "udm:M:Ll:g")) != EOF) {
154 		switch (opt) {
155 		case 'u':
156 			patch_vfstab = 1;
157 			n++;
158 			break;
159 
160 		case 'd':
161 			debug = 1;
162 			break;
163 
164 		case 'm':
165 			mapdev = s_strdup(optarg);
166 			n++;
167 			break;
168 
169 		case 'M':
170 			mapdev = s_strdup(optarg);
171 			cap_m_option = 1;
172 			n++;
173 			break;
174 
175 		case 'L':
176 			list_option = 1;
177 			n++;
178 			break;
179 
180 		case 'l':
181 			list_option = 1;
182 			list_controllernum = (int)atol(optarg);
183 			if (list_controllernum < 0) {
184 				logerr(gettext("controller number %d is "
185 				    "invalid\n"), list_controllernum);
186 				clean_exit(1);
187 			}
188 			n++;
189 			break;
190 
191 		case 'g':
192 			/*
193 			 * private option to display non-STMS device name
194 			 * to GUID mappings.
195 			 */
196 			list_guid_mappings = 1;
197 			n++;
198 			break;
199 
200 		default:
201 			usage(argv[0]);
202 			/*NOTREACHED*/
203 		}
204 	}
205 
206 	if (n != 1)
207 		usage(argv[0]);
208 		/*NOTREACHED*/
209 }
210 
211 void
212 main(int argc, char *argv[])
213 {
214 	char save_vfstab[SYS_FILENAME_LEN], tmp_vfstab[SYS_FILENAME_LEN];
215 	int vfstab_updated;
216 
217 	(void) setlocale(LC_ALL, "");
218 	(void) textdomain(TEXT_DOMAIN);
219 
220 	if (getuid() != 0) {
221 		logerr(gettext("must be super-user to run this program\n"));
222 		clean_exit(1);
223 	}
224 
225 	parse_args(argc, argv);
226 	(void) umask(022);
227 
228 	/*
229 	 * NOTE: The mpxio boot-up script executes this program with the
230 	 * mapping (-m) option before the /usr is even mounted and when the
231 	 * root filesystem is still mounted read-only.
232 	 */
233 	if (*mapdev != '\0') {
234 		char newname[MAXPATHLEN];
235 
236 		if (map_devname(mapdev, newname, sizeof (newname),
237 		    cap_m_option) == 0) {
238 			(void) printf("%s\n", newname);
239 			clean_exit(0);
240 		}
241 		clean_exit(1);
242 	}
243 
244 	if (list_option || list_guid_mappings) {
245 		if (list_mappings(list_controllernum, list_guid_mappings) == 0)
246 			clean_exit(0);
247 		clean_exit(1);
248 	}
249 
250 	/* create a directory where a copy of the system files are saved */
251 	if (patch_vfstab) {
252 		if (mkdirp(SAVE_DIR, 0755) != 0 && errno != EEXIST) {
253 			logerr(gettext("mkdirp: failed to create %1$s: %2$s\n"),
254 			    SAVE_DIR, strerror(errno));
255 			clean_exit(1);
256 		}
257 
258 		if (make_temp(VFSTAB, save_vfstab, tmp_vfstab,
259 		    SYS_FILENAME_LEN) != 0)
260 			clean_exit(1);
261 
262 		/* build new vfstab without modifying the existing one */
263 		if ((vfstab_updated = update_vfstab(VFSTAB, tmp_vfstab))
264 		    == -1) {
265 			logerr(gettext("failed to update %s\n"), VFSTAB);
266 			clean_exit(1);
267 		}
268 
269 		commit_change(VFSTAB, save_vfstab, tmp_vfstab, vfstab_updated);
270 	}
271 
272 	clean_exit(0);
273 }
274 
275 /*
276  * Make saved and temporary filenames in SAVE_DIR.
277  *
278  * ex: if the filename is /etc/vfstab then the save_filename and tmp_filename
279  * would be SAVE_DIR/vfstab and SAVE_DIR/vfstab.tmp respectively.
280  *
281  * Returns 0 on success, -1 on failure.
282  */
283 static int
284 make_temp(char *filename, char *save_filename, char *tmp_filename, size_t len)
285 {
286 	char *ptr;
287 
288 	if ((ptr = strrchr(filename, '/')) == NULL) {
289 		logdmsg("invalid file %s\n", filename);
290 		return (-1);
291 	}
292 	(void) snprintf(save_filename, len, "%s%s", SAVE_DIR, ptr);
293 	(void) snprintf(tmp_filename, len, "%s%s.tmp", SAVE_DIR, ptr);
294 	logdmsg("make_temp: %s: save = %s, temp = %s\n", filename,
295 	    save_filename, tmp_filename);
296 	return (0);
297 }
298 
299 /*
300  * Commit the changes made to the system file
301  */
302 static void
303 commit_change(char *filename, char *save_filename, char *tmp_filename,
304     int updated)
305 {
306 	int x;
307 
308 	if (updated) {
309 		/* save the original */
310 		if ((x = rename(filename, save_filename)) != 0) {
311 			logerr(gettext("rename %1$s to %2$s failed: %3$s\n"),
312 			    filename, save_filename, strerror(errno));
313 		}
314 
315 		/* now rename the new file to the actual file */
316 		if (rename(tmp_filename, filename) != 0) {
317 			logerr(gettext("rename %1$s to %2$s failed: %3$s\n"),
318 			    tmp_filename, filename, strerror(errno));
319 
320 			/* restore the original */
321 			if (x == 0 && rename(save_filename, filename) != 0) {
322 				logerr(
323 				    gettext("rename %1$s to %2$s failed: %3$s\n"
324 				    "%4$s is a copy of the original %5$s file"
325 				    "\n"),
326 				    save_filename, filename, strerror(errno),
327 				    save_filename, filename);
328 			}
329 		} else
330 			(void) printf(gettext("%1$s: %2$s has been updated.\n"),
331 			    stmsboot, filename);
332 	} else {
333 		/* remove the temp file */
334 		(void) unlink(tmp_filename);
335 		(void) printf(gettext("%1$s: %2$s was not modified as no "
336 		    "changes were needed.\n"), stmsboot, filename);
337 	}
338 }
339 
340 /*
341  * Get the GUID of the device.
342  *
343  * physpath	/devices name without the /devices prefix and minor name
344  *		component.
345  * guid		caller supplied buffer where the GUID will be placed on return
346  * guid_len	length of the caller supplied guid buffer.
347  * no_dealy_flag if set open the device with O_NDELAY
348  * node		di_node corresponding to physpath if already available,
349  *		otherwise pass DI_NODE_NIL.
350  *
351  * Returns 0 on success, -1 on failure.
352  */
353 static int
354 get_guid(char *physpath, char *guid, int guid_len, int no_delay_flag,
355 	di_node_t node)
356 {
357 	int		fd;
358 	ddi_devid_t	devid;
359 	int		rv	= -1;
360 	char		*i_guid	= NULL;
361 	char		physpath_raw[MAXPATHLEN];
362 	uchar_t		*wwnp;
363 	int		i, n, snapshot_taken = 0;
364 
365 	logdmsg("get_guid: physpath = %s\n", physpath);
366 
367 	(void) snprintf(physpath_raw, MAXPATHLEN,
368 	    "/devices%s:a,raw", physpath);
369 
370 	*guid = '\0';
371 
372 	if (no_delay_flag)
373 		no_delay_flag = O_NDELAY;
374 
375 	/*
376 	 * Open the raw device
377 	 * Without the O_DELAY flag, the open will fail on standby paths of
378 	 * T3 if its mp_support mode is "mpxio".
379 	 */
380 	if ((fd = open(physpath_raw, O_RDONLY | no_delay_flag)) == -1) {
381 		logdmsg("get_guid: failed to open %s: %s\n", physpath_raw,
382 		    strerror(errno));
383 		return (-1);
384 	}
385 
386 	if (devid_get(fd, &devid) == 0) {
387 		i_guid = devid_to_guid(devid);
388 		devid_free(devid);
389 
390 		if (i_guid != NULL) {
391 			s_strlcpy(guid, i_guid, guid_len);
392 			devid_free_guid(i_guid);
393 			rv = 0;
394 			goto out;
395 		} else
396 			logdmsg("get_guid: devid_to_guid() failed\n");
397 	} else
398 		logdmsg("get_guid: devid_get() failed: %s\n", strerror(errno));
399 
400 	/* fallback to node name as the guid as this is what fcp driver does */
401 	if (node == DI_NODE_NIL) {
402 		if ((node = di_init(physpath, DINFOCPYALL | DINFOFORCE))
403 		    == DI_NODE_NIL) {
404 			logdmsg("get_guid: di_init on %s failed: %s\n",
405 			    physpath, strerror(errno));
406 			goto out;
407 		}
408 		snapshot_taken = 1;
409 	}
410 
411 	if ((n = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, NODE_WWN_PROP,
412 	    &wwnp)) == -1) {
413 		logdmsg("get_guid: di_prop_lookup_bytes() failed to lookup "
414 		    "%s: %s\n", NODE_WWN_PROP, strerror(errno));
415 		goto out;
416 	}
417 
418 	if (guid_len >= ((n * 2) + 1)) {
419 		for (i = 0; i < n; i++) {
420 			(void) sprintf(guid + (i * 2), "%02x", (uint_t)(*wwnp));
421 			wwnp++;
422 		}
423 		rv = 0;
424 	} else
425 		logerr(gettext("insufficient buffer size: need %1$d "
426 		    "bytes, passed %2$d bytes\n"), (n * 2) + 1, guid_len);
427 
428 out:
429 	if (snapshot_taken)
430 		di_fini(node);
431 
432 	(void) close(fd);
433 	logdmsg("get_guid: GUID = %s\n", guid);
434 	return (rv);
435 }
436 
437 /*
438  * Given client_name return whether it is a phci or vhci based name.
439  * client_name is /devices name of a client without the /devices prefix.
440  *
441  * client_name			Return value
442  * .../fp@xxx/ssd@yyy		CLIENT_TYPE_PHCI
443  * .../scsi_vhci/ssd@yyy	CLIENT_TYPE_VHCI
444  * other			CLIENT_TYPE_UNKNOWN
445  */
446 static client_type_t
447 client_name_type(char *client_name)
448 {
449 	client_type_t client_type = CLIENT_TYPE_UNKNOWN;
450 	char *p1, *p2;
451 
452 	if (*client_name != '/')
453 		return (CLIENT_TYPE_UNKNOWN);
454 
455 	/* we only care for ssd devices */
456 	if ((p1 = strrchr(client_name, '/')) == NULL ||
457 	    strncmp(p1, SLASH_SSD_AT, sizeof (SLASH_SSD_AT) - 1) != 0)
458 		return (CLIENT_TYPE_UNKNOWN);
459 
460 	*p1 = '\0';
461 
462 	if ((p2 = strrchr(client_name, '/')) != NULL) {
463 		if (strncmp(p2, SLASH_FP_AT, sizeof (SLASH_FP_AT) - 1) == 0)
464 			client_type = CLIENT_TYPE_PHCI;
465 		else if (strncmp(p2, SLASH_SCSI_VHCI,
466 		    sizeof (SLASH_SCSI_VHCI) - 1) == 0)
467 			client_type = CLIENT_TYPE_VHCI;
468 	}
469 
470 	*p1 = '/';
471 	return (client_type);
472 }
473 
474 /*
475  * Map phci based client name to vhci based client name.
476  *
477  * phci_name
478  *	phci based client /devices name without the /devices prefix and
479  *	minor name component.
480  *	ex: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
481  *
482  * vhci_name
483  *	Caller supplied buffer where vhci /devices name will be placed on
484  *	return (without the /devices prefix and minor name component).
485  *	ex: /scsi_vhci/ssd@g2000002037cd9f72
486  *
487  * vhci_name_len
488  *	Length of the caller supplied vhci_name buffer.
489  *
490  * Returns 0 on success, -1 on failure.
491  */
492 static int
493 phci_to_vhci(char *phci_name, char *vhci_name, size_t vhci_name_len)
494 {
495 	sv_iocdata_t ioc;
496 	char *slash;
497 	char vhci_name_buf[MAXPATHLEN];
498 	char phci_name_buf[MAXPATHLEN];
499 	char addr_buf[MAXNAMELEN];
500 
501 	logdmsg("phci_to_vhci: client = %s\n", phci_name);
502 
503 	s_strlcpy(phci_name_buf, phci_name, MAXPATHLEN);
504 
505 	if (client_name_type(phci_name_buf) != CLIENT_TYPE_PHCI ||
506 	    (slash = strrchr(phci_name_buf, '/')) == NULL ||
507 	    strncmp(slash, SLASH_SSD_AT, sizeof (SLASH_SSD_AT) - 1) != 0) {
508 		logdmsg("phci_to_vhci: %s is not of CLIENT_TYPE_PHCI\n",
509 		    phci_name);
510 		return (-1);
511 	}
512 
513 	if (vhci_fd < 0) {
514 		if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0)
515 			return (-1);
516 	}
517 
518 	*slash = '\0';
519 	s_strlcpy(addr_buf, slash + sizeof (SLASH_SSD_AT) - 1, MAXNAMELEN);
520 
521 	bzero(&ioc, sizeof (sv_iocdata_t));
522 	ioc.client = vhci_name_buf;
523 	ioc.phci = phci_name_buf;
524 	ioc.addr = addr_buf;
525 
526 	if (ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_NAME, &ioc) != 0) {
527 		logdmsg("SCSI_VHCI_GET_CLIENT_NAME on %s "
528 		    "failed: %s\n", phci_name, strerror(errno));
529 		return (-1);
530 	}
531 
532 	s_strlcpy(vhci_name, vhci_name_buf, vhci_name_len);
533 	logdmsg("phci_to_vhci: %s maps to %s\n", phci_name, vhci_name);
534 	return (0);
535 }
536 
537 /*
538  * Map vhci based client name to phci based client name.
539  * If the client has multiple paths, only one of the paths with which client
540  * can be accessed is returned. This function does not use SCSI_VHCI ioctls
541  * as it is called on mpxio disabled paths.
542  *
543  * vhci_name
544  *	vhci based client /devices name without the /devices prefix and
545  *	minor name component.
546  *	ex: /scsi_vhci/ssd@g2000002037cd9f72
547  *
548  * phci_name
549  *	Caller supplied buffer where phci /devices name will be placed on
550  *	return (without the /devices prefix and minor name component).
551  *	ex: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
552  *
553  * phci_name_len
554  *	Length of the caller supplied phci_name buffer.
555  *
556  * Returns 0 on success, -1 on failure.
557  */
558 static int
559 vhci_to_phci(char *vhci_name, char *phci_name, size_t phci_name_len)
560 {
561 	di_node_t node, parent;
562 	char *vhci_guid, *devfspath;
563 	char phci_guid[MAXPATHLEN];
564 	char *parent_name, *node_name;
565 
566 	logdmsg("vhci_to_phci: client = %s\n", vhci_name);
567 
568 	if (client_name_type(vhci_name) != CLIENT_TYPE_VHCI) {
569 		logdmsg("vhci_to_phci: %s is not of CLIENT_TYPE_VHCI\n",
570 		    vhci_name);
571 		return (-1);
572 	}
573 
574 	if ((vhci_guid = strrchr(vhci_name, '@')) == NULL ||
575 	    *(++vhci_guid) != 'g') {
576 		logerr(gettext("couldn't get guid from %s\n"), vhci_name);
577 		return (-1);
578 	}
579 
580 	/* point to guid */
581 	++vhci_guid;
582 
583 	/*
584 	 * Get devinfo snapshot and walk all ssd nodes whose parent is fp.
585 	 * For each node get the guid and match it with vhci_guid.
586 	 */
587 	if (devinfo_root == DI_NODE_NIL) {
588 		logdmsg("vhci_to_phci: taking devinfo snapshot\n");
589 		if ((devinfo_root = di_init("/", DINFOCPYALL | DINFOFORCE))
590 		    == DI_NODE_NIL) {
591 			logerr(gettext("di_init failed: %s\n"),
592 			    strerror(errno));
593 			return (-1);
594 		}
595 		logdmsg("vhci_to_phci: done taking devinfo snapshot\n");
596 	}
597 
598 	for (node = di_drv_first_node("ssd", devinfo_root);
599 	    node != DI_NODE_NIL; node = di_drv_next_node(node)) {
600 		if ((node_name = di_node_name(node)) == NULL ||
601 		    strcmp(node_name, "ssd") != 0 ||
602 		    (parent = di_parent_node(node)) == DI_NODE_NIL ||
603 		    (parent_name = di_node_name(parent)) == NULL ||
604 		    strcmp(parent_name, "fp") != 0 ||
605 		    (devfspath = di_devfs_path(node)) == NULL)
606 			continue;
607 
608 		/*
609 		 * Don't set no_delay_flag to have get_guid() fail on
610 		 * standby paths of T3. So we'll find the preferred paths.
611 		 */
612 		if (get_guid(devfspath, phci_guid,
613 		    sizeof (phci_guid), 0, node) == 0 &&
614 		    strcmp(phci_guid, vhci_guid) == 0) {
615 			s_strlcpy(phci_name, devfspath, phci_name_len);
616 			di_devfs_path_free(devfspath);
617 			logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name,
618 			    phci_name);
619 			return (0);
620 		}
621 
622 		di_devfs_path_free(devfspath);
623 	}
624 
625 	logdmsg("vhci_to_phci: couldn't get phci name for %s\n", vhci_name);
626 	return (-1);
627 }
628 
629 /*
630  * Map physname from phci name space to vhci name space or vice-versa
631  *
632  * physname
633  *	phci or vhci based client /devices name without the /devices prefix and
634  *	minor name component.
635  *
636  * new_physname
637  *	Caller supplied buffer where the mapped physical name is stored on
638  *	return (without the /devices prefix and minor name component).
639  *
640  * len
641  *	Length of the caller supplied new_physname buffer.
642  *
643  * Returns 0 on success, -1 on failure.
644  */
645 static int
646 map_physname(char *physname, char *new_physname, size_t len)
647 {
648 	int type;
649 	int rv;
650 
651 	if ((type = client_name_type(physname)) == CLIENT_TYPE_VHCI)
652 		rv = vhci_to_phci(physname, new_physname, len);
653 	else if (type == CLIENT_TYPE_PHCI)
654 		rv = phci_to_vhci(physname, new_physname, len);
655 	else
656 		rv = -1;
657 
658 	return (rv);
659 }
660 
661 /*
662  * Given a phci or vhci devname which is either a /dev link or /devices name
663  * get the corresponding physical node path (without the /devices prefix)
664  * and minor name.
665  *
666  * Returns 0 on success, -1 on failure.
667  */
668 static int
669 get_physname_minor(char *devname, char *physname, int physname_len,
670     char *minorname, int minorname_len)
671 {
672 	int linksize;
673 	char buf[MAXPATHLEN];
674 	char *p, *m;
675 
676 	if (strncmp(devname, DEV_DSK, sizeof (DEV_DSK) - 1) == 0 ||
677 	    strncmp(devname, DEV_RDSK, sizeof (DEV_RDSK) - 1) == 0) {
678 		if ((linksize = readlink(devname, buf, MAXPATHLEN))
679 		    > 0 && linksize <= (MAXPATHLEN - 1)) {
680 			buf[linksize] = '\0';
681 		} else
682 			return (-1);
683 	} else
684 		s_strlcpy(buf, devname, MAXPATHLEN);
685 
686 	if ((p = strstr(buf, SLASH_DEVICES)) == NULL)
687 		return (-1);
688 
689 	/* point to '/' after /devices */
690 	p += sizeof (SLASH_DEVICES) - 2;
691 
692 	if ((m = strrchr(p, ':')) == NULL) {
693 		logdmsg("get_physname_minor: no minor name component in %s\n",
694 		    buf);
695 		return (-1);
696 	}
697 
698 	*m = '\0';
699 	m++;
700 
701 	if (client_name_type(p) == CLIENT_TYPE_UNKNOWN)
702 		return (-1);
703 
704 	s_strlcpy(physname, p, physname_len);
705 	s_strlcpy(minorname, m, minorname_len);
706 	logdmsg("get_physname_minor: %s: physname = %s, minor = %s\n",
707 	    devname, physname, minorname);
708 	return (0);
709 }
710 
711 static int
712 devlink_callback(di_devlink_t devlink, void *argptr)
713 {
714 	const char *link;
715 	struct devlink_cbarg *argp = argptr;
716 
717 	if ((link = di_devlink_path(devlink)) != NULL) {
718 		s_strlcpy(argp->devlink, link, argp->len);
719 		return (DI_WALK_TERMINATE);
720 	}
721 
722 	return (DI_WALK_CONTINUE);
723 }
724 
725 /*
726  * Lookup the /dev link corresponding to physname and minorname.
727  *
728  * physname	client /devices path without the /devices prefix and minor
729  *		name component.
730  * minorname	client minor name.
731  * devlink	caller supplied buffer where the /dev link is placed on return.
732  * len		caller supplied devlink buffer length
733  *
734  * Returns 0 on success, -1 on failure.
735  */
736 static int
737 lookup_devlink(char *physname, char *minorname, char *devlink, size_t len)
738 {
739 	char buf[MAXPATHLEN];
740 	struct devlink_cbarg arg;
741 
742 	if (devlink_hdl == NULL) {
743 		logdmsg("lookup_devlink: taking devlink snapshot\n");
744 		if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
745 			logerr(gettext("di_devlink_init failed: %s\n"),
746 			    strerror(errno));
747 			clean_exit(1);
748 		}
749 	}
750 
751 	*devlink = '\0';
752 	(void) snprintf(buf, MAXPATHLEN, "%s:%s", physname, minorname);
753 	arg.devlink = devlink;
754 	arg.len = len;
755 	if (di_devlink_walk(devlink_hdl, NULL, buf, DI_PRIMARY_LINK, &arg,
756 	    devlink_callback) != 0) {
757 		logdmsg("lookup_devlink: di_devlink_walk on %s failed: %s\n",
758 		    buf, strerror(errno));
759 		return (-1);
760 	}
761 
762 	if (*devlink == '\0') {
763 		logdmsg("lookup_devlink: failed to lookup devlink for %s\n",
764 		    buf);
765 		return (-1);
766 	}
767 
768 	logdmsg("lookup_devlink: /dev link for %s:%s = %s\n", physname,
769 	    minorname, devlink);
770 	return (0);
771 }
772 
773 /*
774  * open infile for reading and return its file pointer in *fp_in.
775  * open outfile for writing and return its file pointer in *fp_out.
776  *
777  * Returns 0 on success, -1 on failure.
778  */
779 static int
780 open_in_out_files(char *infile, char *outfile, FILE **fp_in, FILE **fp_out)
781 {
782 	FILE *fin = NULL;
783 	FILE *fout = NULL;
784 	struct stat sbuf;
785 
786 	if ((fin = fopen(infile, "r")) == NULL) {
787 		logerr(gettext("failed to fopen %1$s: %2$s\n"),
788 		    infile, strerror(errno));
789 		goto out;
790 	}
791 
792 	if (fstat(fileno(fin), &sbuf) != 0) {
793 		logerr(gettext("fstat failed on %1$s: %2$s\n"),
794 		    infile, strerror(errno));
795 		goto out;
796 	}
797 
798 	if ((fout = fopen(outfile, "w")) == NULL) {
799 		logerr(gettext("failed to fopen %1$s: %2$s\n"),
800 		    outfile, strerror(errno));
801 		goto out;
802 	}
803 
804 	if (fchmod(fileno(fout), (sbuf.st_mode & 0777)) != 0) {
805 		logerr(gettext("failed to fchmod %1$s to 0%2$o: %3$s\n"),
806 		    outfile, sbuf.st_mode & 0777, strerror(errno));
807 		goto out;
808 	}
809 
810 	if (fchown(fileno(fout), sbuf.st_uid, sbuf.st_gid) != 0) {
811 		logerr(gettext("failed to fchown %1$s to uid %2$d and "
812 		    "gid %3$d: %4$s\n"),
813 		    outfile, sbuf.st_uid, sbuf.st_gid, strerror(errno));
814 		goto out;
815 	}
816 
817 	*fp_in = fin;
818 	*fp_out = fout;
819 	return (0);
820 
821 out:
822 	if (fin != NULL)
823 		(void) fclose(fin);
824 	if (fout != NULL)
825 		(void) fclose(fout);
826 	return (-1);
827 }
828 
829 /*
830  * If the devname is a phci based name and not open-able, map it to vhci
831  * based name. If the devname is a vhci based name and not open-able, map it
832  * to phci based name.
833  *
834  * devname	either a /dev link or /devices name to client device
835  * new_devname	caller supplied buffer where the mapped device name is
836  *		placed on return.
837  * len		caller supplied new_devname buffer length
838  * devlink_flag	pass 1 if requesting the /dev link to the mapped device.
839  *		pass 0 if requesting the /devices name of the mapped device.
840  *
841  * Returns 0 on success, -1 on failure.
842  */
843 static int
844 map_devname(char *devname, char *new_devname, size_t len, int devlink_flag)
845 {
846 	char physname[MAXPATHLEN];
847 	char minor[MAXNAMELEN];
848 	char new_physname[MAXNAMELEN];
849 
850 	if (get_physname_minor(devname, physname, sizeof (physname),
851 	    minor, sizeof (minor)) == 0 &&
852 	    canopen(devname) == 0 &&
853 	    map_physname(physname, new_physname, sizeof (new_physname)) == 0) {
854 
855 		if (devlink_flag) {
856 			if (lookup_devlink(new_physname, minor, new_devname,
857 			    len) == 0)
858 				return (0);
859 		} else {
860 			(void) snprintf(new_devname, len, "/devices%s:%s",
861 			    new_physname, minor);
862 			return (0);
863 		}
864 	}
865 
866 	return (-1);
867 }
868 
869 /*
870  * Make a new /etc/vfstab:
871  * Read vfstab_in, convert the device name entries to appropriate vhci or phci
872  * based names, and write to vfstab_out. Only device names whose physical
873  * paths are either phci or vhci based names and not open-able are considered
874  * for conversion. Open-able device name entries are not converted as it
875  * means that the device is already accessible; hence no need to convert.
876  *
877  * Returns:
878  * 	0	successful but vfstab_out contents are the same as vfstab_in
879  *	1	successful and vfstab_out changed from vfstab_in
880  *	-1	failed
881  */
882 static int
883 update_vfstab(char *vfstab_in, char *vfstab_out)
884 {
885 	FILE *fp_in, *fp_out;
886 	char *buf, *tmpbuf;
887 	char *vfs_cache[2];
888 	int idx = 0, count = 0;
889 	int rv = -1;
890 	int vfstab_updated = 0;
891 	int i;
892 	char cdev[MAXPATHLEN];
893 	char bdev[MAXPATHLEN];
894 	char mntpt[MAXPATHLEN];
895 	char fstype[512];
896 	char fsckpass[512];
897 	char mntboot[512];
898 	char mntopt[MAX_MNTOPT_STR];
899 	char phys_bdev[MAXPATHLEN], phys_cdev[MAXPATHLEN];
900 	char bdev_minor[MAXNAMELEN], cdev_minor[MAXNAMELEN];
901 	char new_physname[MAXPATHLEN];
902 	char new_bdevlink[MAXPATHLEN], new_cdevlink[MAXPATHLEN];
903 	char fmt[80];
904 
905 	if (open_in_out_files(vfstab_in, vfstab_out, &fp_in, &fp_out) != 0)
906 		return (-1);
907 
908 	/*
909 	 * Read one line at time from vfstab_in. If no conversion is needed
910 	 * for the line simply write the line to vfstab_out. If conversion is
911 	 * needed, first write the existing line as a comment to vfstab_out
912 	 * and then write the converted line.
913 	 *
914 	 * To avoid commented entries piling up in vfstab in case if the
915 	 * user runs stmsboot multiple times to switch on and off from mpxio,
916 	 * add the commented line only if not already there. To do this
917 	 * cache the last two vfstab lines processed and add the commented
918 	 * entry only if it is not found in the cache. We only need to cache
919 	 * the last two lines because a device can have at most two names -
920 	 * one mpxio and one non-mpxio name. Therefore for any device name
921 	 * entry we at most add two comments - one with mpxio name and one
922 	 * with non-mpxio name - no matter how many times stmsboot is run.
923 	 */
924 	buf = (char *)s_malloc(VFS_LINE_MAX);
925 	tmpbuf = (char *)s_malloc(VFS_LINE_MAX);
926 	vfs_cache[0] = (char *)s_malloc(VFS_LINE_MAX);
927 	vfs_cache[1] = (char *)s_malloc(VFS_LINE_MAX);
928 
929 	(void) snprintf(fmt, sizeof (fmt),
930 	    "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1,
931 	    sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1,
932 	    sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1);
933 
934 	while (fgets(buf, VFS_LINE_MAX, fp_in) != NULL) {
935 		if (strlen(buf) == (VFS_LINE_MAX - 1) &&
936 		    buf[VFS_LINE_MAX-2] != '\n') {
937 			logerr(gettext("%1$s line size too long, "
938 			    "exceeded %2$d: \"%3$s\"\n"),
939 			    VFSTAB, VFS_LINE_MAX - 2, buf);
940 			goto out;
941 		}
942 
943 		/* LINTED - format specifier */
944 		if ((sscanf(buf, fmt, bdev, cdev, mntpt,
945 		    fstype, fsckpass, mntboot, mntopt) != 7) ||
946 		    (bdev[0] == '#') ||
947 		    (get_physname_minor(bdev, phys_bdev, sizeof (phys_bdev),
948 		    bdev_minor, sizeof (bdev_minor)) != 0) ||
949 
950 		    (strcmp(fstype, "swap") != 0 &&
951 		    ((get_physname_minor(cdev, phys_cdev, sizeof (phys_cdev),
952 		    cdev_minor, sizeof (cdev_minor)) != 0) ||
953 		    (strcmp(phys_bdev, phys_cdev) != 0))) ||
954 
955 		    canopen(bdev) ||
956 		    (map_physname(phys_bdev, new_physname,
957 		    sizeof (new_physname)) != 0) ||
958 		    (lookup_devlink(new_physname, bdev_minor, new_bdevlink,
959 		    sizeof (new_bdevlink)) != 0) ||
960 
961 		    (strcmp(fstype, "swap") != 0 &&
962 		    (lookup_devlink(new_physname, cdev_minor, new_cdevlink,
963 		    sizeof (new_cdevlink)) != 0))) {
964 
965 			/* cache the last two entries */
966 			(void) strlcpy(vfs_cache[idx], buf, VFS_LINE_MAX);
967 			idx = (idx == 0) ? 1 : 0;
968 			if (count < 2)
969 				count++;
970 
971 			if (fputs(buf, fp_out) == EOF) {
972 				logerr(gettext("fputs \"%1$s\" to %2$s "
973 				    "failed: %3$s\n"),
974 				    buf, vfstab_out, strerror(errno));
975 				goto out;
976 			}
977 
978 		} else {
979 			/*
980 			 * comment the entry in vfstab only if it is not
981 			 * already in the cache.
982 			 */
983 			if (client_name_type(phys_bdev) == CLIENT_TYPE_VHCI)
984 				(void) snprintf(tmpbuf, VFS_LINE_MAX,
985 				    "# mpxio: %s", buf);
986 			else
987 				(void) snprintf(tmpbuf, VFS_LINE_MAX,
988 				    "# non-mpxio: %s", buf);
989 
990 			for (i = 0; i < count; i++) {
991 				if (strcmp(vfs_cache[i], tmpbuf) == 0)
992 					break;
993 			}
994 
995 			if (i == count) {
996 				if (fputs(tmpbuf, fp_out) == EOF) {
997 					logerr(gettext("fputs \"%1$s\" to %2$s "
998 					    "failed: %3$s\n"), tmpbuf,
999 					    vfstab_out, strerror(errno));
1000 					goto out;
1001 				}
1002 			}
1003 
1004 			count = 0;
1005 			idx = 0;
1006 
1007 			if (fprintf(fp_out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
1008 			    new_bdevlink,
1009 			    (strcmp(fstype, "swap") != 0) ? new_cdevlink : cdev,
1010 			    mntpt, fstype, fsckpass, mntboot, mntopt) < 0) {
1011 				logerr(gettext("fprintf failed to write to "
1012 				    "%1$s: %2$s\n"),
1013 				    vfstab_out, strerror(errno));
1014 				goto out;
1015 			}
1016 			vfstab_updated = 1;
1017 		}
1018 	}
1019 
1020 	rv = vfstab_updated;
1021 out:
1022 	(void) fclose(fp_in);
1023 	(void) fclose(fp_out);
1024 	free(buf);
1025 	free(tmpbuf);
1026 	free(vfs_cache[0]);
1027 	free(vfs_cache[1]);
1028 	return (rv);
1029 }
1030 
1031 /*
1032  * if guidmap is 0, list non-STMS to STMS device name mappings for the
1033  * specified controller.
1034  * if guidmap is 1, list non-STMS to GUID mappings for the specified controller.
1035  * If controller is -1 list mappings for all controllers.
1036  *
1037  * Returns 0 on success, -1 on failure.
1038  */
1039 static int
1040 list_mappings(int controller, int guidmap)
1041 {
1042 	int cnum, len, mapped;
1043 	int header = 1;
1044 	char *p1, *p2;
1045 	DIR *dirp;
1046 	struct dirent *direntry;
1047 	char devname[MAXPATHLEN];
1048 	char physname[MAXPATHLEN];
1049 	char new_devname[MAXPATHLEN];
1050 	char new_physname[MAXPATHLEN];
1051 	char guid[MAXPATHLEN];
1052 	char minor[MAXNAMELEN];
1053 
1054 	if ((dirp = opendir("/dev/rdsk")) == NULL)
1055 		return (-1);
1056 
1057 	while ((direntry = readdir(dirp)) != NULL) {
1058 		if (strcmp(direntry->d_name, ".") == 0 ||
1059 		    strcmp(direntry->d_name, "..") == 0 ||
1060 		    (len = strlen(direntry->d_name)) < 2 ||
1061 		    strcmp(direntry->d_name + len - 2, "s0") != 0 ||
1062 		    sscanf(direntry->d_name, "c%dt", &cnum) != 1 ||
1063 		    (controller != -1 && controller != cnum))
1064 			continue;
1065 
1066 		(void) snprintf(devname, MAXPATHLEN, "/dev/rdsk/%s",
1067 		    direntry->d_name);
1068 
1069 		if (get_physname_minor(devname, physname, sizeof (physname),
1070 		    minor, sizeof (minor)) != 0 ||
1071 		    client_name_type(physname) != CLIENT_TYPE_PHCI)
1072 			continue;
1073 
1074 		/*
1075 		 * First try phci_to_vhci() mapping. It will work if the
1076 		 * device is under MPxIO control. If the device is not under
1077 		 * MPxIO, phci_to_vhci() will fail in which case try to lookup
1078 		 * if an old mapping exists using guid lookup.
1079 		 */
1080 		mapped = 1;
1081 		if (phci_to_vhci(physname, new_physname,
1082 		    sizeof (new_physname)) != 0) {
1083 			if (get_guid(physname, guid, sizeof (guid), 1,
1084 			    DI_NODE_NIL) == 0)
1085 				(void) snprintf(new_physname, MAXPATHLEN,
1086 				    "/scsi_vhci/ssd@g%s", guid);
1087 			else
1088 				mapped = 0;
1089 		}
1090 
1091 		if (mapped == 0)
1092 			continue;
1093 
1094 		/* strip the slice number part */
1095 		devname[strlen(devname) - 2] = '\0';
1096 
1097 		if (guidmap == 0) {
1098 			if (lookup_devlink(new_physname, minor,
1099 			    new_devname, sizeof (new_devname)) != 0)
1100 				continue;
1101 
1102 			/* strip the slice number part */
1103 			new_devname[strlen(new_devname) - 2] = '\0';
1104 
1105 			if (header) {
1106 				(void) printf(
1107 				    gettext("non-STMS device name\t\t\t"
1108 				    "STMS device name\n"
1109 				    "------------------------------------------"
1110 				    "------------------------\n"));
1111 				header = 0;
1112 			}
1113 			(void) printf("%s\t\t%s\n", devname, new_devname);
1114 		} else {
1115 			/* extract guid part */
1116 			if ((p1 = strstr(new_physname, "ssd@g")) == NULL) {
1117 				logdmsg("invalid vhci: %s\n", new_physname);
1118 				continue;
1119 			}
1120 			p1 += sizeof ("ssd@g") - 1;
1121 			if ((p2 = strrchr(p1, ':')) != NULL)
1122 				*p2 = '\0';
1123 
1124 			if (header) {
1125 				(void) printf(
1126 				    gettext("non-STMS device name\t\t\tGUID\n"
1127 				    "------------------------------------------"
1128 				    "------------------------\n"));
1129 				header = 0;
1130 			}
1131 			(void) printf("%s\t\t%s\n", devname, p1);
1132 		}
1133 	}
1134 
1135 	(void) closedir(dirp);
1136 	return (0);
1137 }
1138 
1139 /*
1140  * Check if the file can be opened.
1141  *
1142  * Return 1 if the file can be opened, 0 otherwise.
1143  */
1144 static int
1145 canopen(char *filename)
1146 {
1147 	int fd;
1148 
1149 	if ((fd = open(filename, O_RDONLY)) == -1)
1150 		return (0);
1151 
1152 	(void) close(fd);
1153 	return (1);
1154 }
1155 
1156 static void
1157 logerr(char *msg, ...)
1158 {
1159 	va_list ap;
1160 
1161 	(void) fprintf(stderr, "%s: ", stmsboot);
1162 	va_start(ap, msg);
1163 	/* LINTED - format specifier */
1164 	(void) vfprintf(stderr, msg, ap);
1165 	va_end(ap);
1166 }
1167 
1168 /* log debug message */
1169 static void
1170 logdmsg(char *msg, ...)
1171 {
1172 	va_list ap;
1173 
1174 	if (debug) {
1175 		va_start(ap, msg);
1176 		/* LINTED - format specifier */
1177 		(void) vprintf(msg, ap);
1178 		va_end(ap);
1179 	}
1180 }
1181 
1182 static void *
1183 s_malloc(const size_t size)
1184 {
1185 	void *rp;
1186 
1187 	if ((rp = malloc(size)) == NULL) {
1188 		logerr(gettext("malloc failed to allocate %d bytes\n"), size);
1189 		clean_exit(1);
1190 	}
1191 	return (rp);
1192 }
1193 
1194 static char *
1195 s_strdup(const char *ptr)
1196 {
1197 	void *rp;
1198 
1199 	if ((rp = strdup(ptr)) == NULL) {
1200 		logerr(gettext("strdup failed to dup %s\n"), ptr);
1201 		clean_exit(1);
1202 	}
1203 	return (rp);
1204 }
1205 
1206 static void
1207 s_strlcpy(char *dst, const char *src, size_t dstsize)
1208 {
1209 	int n;
1210 
1211 	if ((n = strlcpy(dst, src, dstsize)) >= dstsize) {
1212 		logerr(gettext("strlcpy: destination buffer size is %1$d "
1213 		    "bytes, need to at least %2$d bytes\n"), dstsize, n + 1);
1214 		clean_exit(1);
1215 	}
1216 }
1217 
1218 static void
1219 clean_exit(int status)
1220 {
1221 	if (devinfo_root != DI_NODE_NIL)
1222 		di_fini(devinfo_root);
1223 
1224 	if (devlink_hdl != NULL)
1225 		(void) di_devlink_fini(&devlink_hdl);
1226 
1227 	if (vhci_fd != -1)
1228 		(void) close(vhci_fd);
1229 
1230 	exit(status);
1231 }
1232