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