xref: /titanic_50/usr/src/lib/libdevinfo/devfsmap.c (revision c1c6f601cc48d067049ea58a07349897a8e225f2)
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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #ifdef	lint
29 #define	_REENTRANT	/* for localtime_r */
30 #endif
31 
32 #include <stdio.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <strings.h>
37 #include <stdarg.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #include <stropts.h>
43 #include <time.h>
44 #include <sys/param.h>
45 #include <sys/vfstab.h>
46 #include <dirent.h>
47 #ifdef __sparc
48 #include <sys/scsi/adapters/scsi_vhci.h>
49 #include <sys/sunmdi.h>
50 #endif /* __sparc */
51 #include "libdevinfo.h"
52 #include "device_info.h"
53 
54 #define	isnewline(ch)	((ch) == '\n' || (ch) == '\r' || (ch) == '\f')
55 #define	isnamechar(ch)  (isalpha(ch) || isdigit(ch) || (ch) == '_' ||\
56 	(ch) == '-')
57 #define	MAX_TOKEN_SIZE	1024
58 #define	BUFSIZE		1024
59 #define	STRVAL(s)	((s) ? (s) : "NULL")
60 
61 #define	SCSI_VHCI_CONF		"/kernel/drv/scsi_vhci.conf"
62 #define	QLC_CONF		"/kernel/drv/qlc.conf"
63 #define	FP_CONF			"/kernel/drv/fp.conf"
64 #define	DRIVER_CLASSES		"/etc/driver_classes"
65 #define	FP_AT			"fp@"
66 #define	VHCI_CTL_NODE		"/devices/scsi_vhci:devctl"
67 #define	SLASH_DEVICES		"/devices"
68 #define	SLASH_DEVICES_SLASH	"/devices/"
69 #define	SLASH_FP_AT		"/fp@"
70 #define	SLASH_SCSI_VHCI		"/scsi_vhci"
71 #define	META_DEV		"/dev/md/dsk/"
72 #define	SLASH_DEV_SLASH		"/dev/"
73 
74 /*
75  * Macros to produce a quoted string containing the value of a
76  * preprocessor macro. For example, if SIZE is defined to be 256,
77  * VAL2STR(SIZE) is "256". This is used to construct format
78  * strings for scanf-family functions below.
79  */
80 #define	QUOTE(x)	#x
81 #define	VAL2STR(x)	QUOTE(x)
82 
83 #ifdef __sparc
84 
85 typedef enum {
86 	CLIENT_TYPE_UNKNOWN,
87 	CLIENT_TYPE_PHCI,
88 	CLIENT_TYPE_VHCI
89 } client_type_t;
90 
91 typedef enum {
92 	T_EQUALS,
93 	T_AMPERSAND,
94 	T_BIT_OR,
95 	T_STAR,
96 	T_POUND,
97 	T_COLON,
98 	T_SEMICOLON,
99 	T_COMMA,
100 	T_SLASH,
101 	T_WHITE_SPACE,
102 	T_NEWLINE,
103 	T_EOF,
104 	T_STRING,
105 	T_HEXVAL,
106 	T_DECVAL,
107 	T_NAME
108 } token_t;
109 
110 typedef enum {
111 	begin, parent, drvname, drvclass, prop,
112 	parent_equals, name_equals, drvclass_equals,
113 	parent_equals_string, name_equals_string,
114 	drvclass_equals_string,
115 	prop_equals, prop_equals_string, prop_equals_integer,
116 	prop_equals_string_comma, prop_equals_integer_comma
117 } conf_state_t;
118 
119 /* structure to hold entries with mpxio-disable property in driver.conf file */
120 struct conf_entry {
121 	char *name;
122 	char *parent;
123 	char *class;
124 	char *unit_address;
125 	int port;
126 	int mpxio_disable;
127 	struct conf_entry *next;
128 };
129 
130 struct conf_file {
131 	char *filename;
132 	FILE *fp;
133 	int linenum;
134 };
135 
136 static char *tok_err = "Unexpected token '%s'\n";
137 
138 #endif /* __sparc */
139 
140 /* #define	DEBUG */
141 
142 #ifdef DEBUG
143 
144 int devfsmap_debug = 0;
145 /* /var/run is not mounted at install time. Therefore use /tmp */
146 char *devfsmap_logfile = "/tmp/devfsmap.log";
147 static FILE *logfp;
148 #define	logdmsg(args)	log_debug_msg args
149 static void vlog_debug_msg(char *, va_list);
150 static void log_debug_msg(char *, ...);
151 #ifdef __sparc
152 static void log_confent_list(char *, struct conf_entry *, int);
153 static void log_pathlist(char **);
154 #endif /* __sparc */
155 
156 #else /* DEBUG */
157 #define	logdmsg(args)	/* nothing */
158 #endif /* DEBUG */
159 
160 #ifdef __sparc
161 
162 /*
163  * Leave NEWLINE as the next character.
164  */
165 static void
166 find_eol(FILE *fp)
167 {
168 	int ch;
169 
170 	while ((ch = getc(fp)) != EOF) {
171 		if (isnewline(ch)) {
172 			(void) ungetc(ch, fp);
173 			break;
174 		}
175 	}
176 }
177 
178 /* ignore parsing errors */
179 /*ARGSUSED*/
180 static void
181 file_err(struct conf_file *filep, char *fmt, ...)
182 {
183 #ifdef DEBUG
184 	va_list ap;
185 
186 	va_start(ap, fmt);
187 	log_debug_msg("WARNING: %s line # %d: ",
188 	    filep->filename, filep->linenum);
189 	vlog_debug_msg(fmt, ap);
190 	va_end(ap);
191 #endif /* DEBUG */
192 }
193 
194 /* return the next token from the given driver.conf file, or -1 on error */
195 static token_t
196 lex(struct conf_file *filep, char *val, size_t size)
197 {
198 	char	*cp;
199 	int	ch, oval, badquote;
200 	size_t	remain;
201 	token_t token;
202 	FILE	*fp = filep->fp;
203 
204 	if (size < 2)
205 		return (-1);
206 
207 	cp = val;
208 	while ((ch = getc(fp)) == ' ' || ch == '\t')
209 		;
210 
211 	remain = size - 1;
212 	*cp++ = (char)ch;
213 	switch (ch) {
214 	case '=':
215 		token = T_EQUALS;
216 		break;
217 	case '&':
218 		token = T_AMPERSAND;
219 		break;
220 	case '|':
221 		token = T_BIT_OR;
222 		break;
223 	case '*':
224 		token = T_STAR;
225 		break;
226 	case '#':
227 		token = T_POUND;
228 		break;
229 	case ':':
230 		token = T_COLON;
231 		break;
232 	case ';':
233 		token = T_SEMICOLON;
234 		break;
235 	case ',':
236 		token = T_COMMA;
237 		break;
238 	case '/':
239 		token = T_SLASH;
240 		break;
241 	case ' ':
242 	case '\t':
243 	case '\f':
244 		while ((ch = getc(fp)) == ' ' ||
245 		    ch == '\t' || ch == '\f') {
246 			if (--remain == 0) {
247 				*cp = '\0';
248 				return (-1);
249 			}
250 			*cp++ = (char)ch;
251 		}
252 		(void) ungetc(ch, fp);
253 		token = T_WHITE_SPACE;
254 		break;
255 	case '\n':
256 	case '\r':
257 		token = T_NEWLINE;
258 		break;
259 	case '"':
260 		remain++;
261 		cp--;
262 		badquote = 0;
263 		while (!badquote && (ch  = getc(fp)) != '"') {
264 			switch (ch) {
265 			case '\n':
266 			case EOF:
267 				file_err(filep, "Missing \"\n");
268 				remain = size - 1;
269 				cp = val;
270 				*cp++ = '\n';
271 				badquote = 1;
272 				/* since we consumed the newline/EOF */
273 				(void) ungetc(ch, fp);
274 				break;
275 
276 			case '\\':
277 				if (--remain == 0) {
278 					*cp = '\0';
279 					return (-1);
280 				}
281 				ch = (char)getc(fp);
282 				if (!isdigit(ch)) {
283 					/* escape the character */
284 					*cp++ = (char)ch;
285 					break;
286 				}
287 				oval = 0;
288 				while (ch >= '0' && ch <= '7') {
289 					ch -= '0';
290 					oval = (oval << 3) + ch;
291 					ch = (char)getc(fp);
292 				}
293 				(void) ungetc(ch, fp);
294 				/* check for character overflow? */
295 				if (oval > 127) {
296 					file_err(filep,
297 					    "Character "
298 					    "overflow detected.\n");
299 				}
300 				*cp++ = (char)oval;
301 				break;
302 			default:
303 				if (--remain == 0) {
304 					*cp = '\0';
305 					return (-1);
306 				}
307 				*cp++ = (char)ch;
308 				break;
309 			}
310 		}
311 		token = T_STRING;
312 		break;
313 
314 	case EOF:
315 		token = T_EOF;
316 		break;
317 
318 	default:
319 		/*
320 		 * detect a lone '-' (including at the end of a line), and
321 		 * identify it as a 'name'
322 		 */
323 		if (ch == '-') {
324 			if (--remain == 0) {
325 				*cp = '\0';
326 				return (-1);
327 			}
328 			*cp++ = (char)(ch = getc(fp));
329 			if (ch == ' ' || ch == '\t' || ch == '\n') {
330 				(void) ungetc(ch, fp);
331 				remain++;
332 				cp--;
333 				token = T_NAME;
334 				break;
335 			}
336 		} else if (ch == '~' || ch == '-') {
337 			if (--remain == 0) {
338 				*cp = '\0';
339 				return (-1);
340 			}
341 			*cp++ = (char)(ch = getc(fp));
342 		}
343 
344 
345 		if (isdigit(ch)) {
346 			if (ch == '0') {
347 				if ((ch = getc(fp)) == 'x') {
348 					if (--remain == 0) {
349 						*cp = '\0';
350 						return (-1);
351 					}
352 					*cp++ = (char)ch;
353 					ch = getc(fp);
354 					while (isxdigit(ch)) {
355 						if (--remain == 0) {
356 							*cp = '\0';
357 							return (-1);
358 						}
359 						*cp++ = (char)ch;
360 						ch = getc(fp);
361 					}
362 					(void) ungetc(ch, fp);
363 					token = T_HEXVAL;
364 				} else {
365 					goto digit;
366 				}
367 			} else {
368 				ch = getc(fp);
369 digit:
370 				while (isdigit(ch)) {
371 					if (--remain == 0) {
372 						*cp = '\0';
373 						return (-1);
374 					}
375 					*cp++ = (char)ch;
376 					ch = getc(fp);
377 				}
378 				(void) ungetc(ch, fp);
379 				token = T_DECVAL;
380 			}
381 		} else if (isalpha(ch) || ch == '\\') {
382 			if (ch != '\\') {
383 				ch = getc(fp);
384 			} else {
385 				/*
386 				 * if the character was a backslash,
387 				 * back up so we can overwrite it with
388 				 * the next (i.e. escaped) character.
389 				 */
390 				remain++;
391 				cp--;
392 			}
393 			while (isnamechar(ch) || ch == '\\') {
394 				if (ch == '\\')
395 					ch = getc(fp);
396 				if (--remain == 0) {
397 					*cp = '\0';
398 					return (-1);
399 				}
400 				*cp++ = (char)ch;
401 				ch = getc(fp);
402 			}
403 			(void) ungetc(ch, fp);
404 			token = T_NAME;
405 		} else {
406 			return (-1);
407 		}
408 		break;
409 	}
410 
411 	*cp = '\0';
412 
413 	return (token);
414 }
415 
416 static void
417 free_confent(struct conf_entry *confent)
418 {
419 	if (confent->name)
420 		free(confent->name);
421 	if (confent->parent)
422 		free(confent->parent);
423 	if (confent->class)
424 		free(confent->class);
425 	if (confent->unit_address)
426 		free(confent->unit_address);
427 	free(confent);
428 }
429 
430 static void
431 free_confent_list(struct conf_entry *confent_list)
432 {
433 	struct conf_entry *confent, *next;
434 
435 	for (confent = confent_list; confent != NULL; confent = next) {
436 		next = confent->next;
437 		free_confent(confent);
438 	}
439 }
440 
441 /*
442  * Parse the next entry from the driver.conf file and return in the form of
443  * a pointer to the conf_entry.
444  */
445 static struct conf_entry *
446 parse_conf_entry(struct conf_file *filep, char *tokbuf, size_t linesize)
447 {
448 	char *prop_name, *string;
449 	token_t token;
450 	struct conf_entry *confent;
451 	conf_state_t state;
452 	int failed = 1;
453 
454 	if ((confent = calloc(1, sizeof (*confent))) == NULL)
455 		return (NULL);
456 
457 	confent->port = -1;
458 	confent->mpxio_disable = -1;
459 
460 	state = begin;
461 	token = T_NAME;
462 	prop_name = NULL;
463 	string = NULL;
464 	do {
465 		switch (token) {
466 		case T_NAME:
467 			switch (state) {
468 			case prop_equals_string:
469 			case prop_equals_integer:
470 			case begin:
471 				state = prop;
472 				if ((prop_name = strdup(tokbuf)) == NULL)
473 					goto bad;
474 				break;
475 			default:
476 				file_err(filep, tok_err, tokbuf);
477 			}
478 			break;
479 		case T_EQUALS:
480 			switch (state) {
481 			case prop:
482 				state = prop_equals;
483 				break;
484 			default:
485 				file_err(filep, tok_err, tokbuf);
486 			}
487 			break;
488 		case T_STRING:
489 			switch (state) {
490 			case prop_equals:
491 				if ((string = strdup(tokbuf)) == NULL)
492 					goto bad;
493 
494 				state = begin;
495 				if (strcmp(prop_name, "PARENT") == 0 ||
496 				    strcmp(prop_name, "parent") == 0) {
497 					if (confent->parent) {
498 						file_err(filep,
499 				"'parent' property already specified\n");
500 						goto bad;
501 					}
502 					confent->parent = string;
503 				} else if (strcmp(prop_name, "NAME") == 0 ||
504 				    strcmp(prop_name, "name") == 0) {
505 					if (confent->name) {
506 						file_err(filep,
507 				"'name' property already specified\n");
508 						goto bad;
509 					}
510 					confent->name = string;
511 				} else if (strcmp(prop_name, "CLASS") == 0 ||
512 				    strcmp(prop_name, "class") == 0) {
513 					if (confent->class) {
514 						file_err(filep,
515 				"'class' property already specified\n");
516 						goto bad;
517 					}
518 					confent->class = string;
519 				} else if (strcmp(prop_name, "unit-address")
520 				    == 0) {
521 					if (confent->unit_address) {
522 						file_err(filep,
523 				"'unit-address' property already specified\n");
524 						goto bad;
525 					}
526 					confent->unit_address = string;
527 				} else if (strcmp(prop_name, "mpxio-disable")
528 				    == 0) {
529 					if (confent->mpxio_disable != -1) {
530 						file_err(filep,
531 				"'mpxio-disable' property already specified\n");
532 						goto bad;
533 					}
534 					if (strcmp(string, "yes") == 0)
535 						confent->mpxio_disable = 1;
536 					else if (strcmp(string, "no") == 0)
537 						confent->mpxio_disable = 0;
538 					else {
539 						file_err(filep,
540 				"'mpxio-disable' property setting is invalid. "
541 				"The value must be either \"yes\" or \"no\"\n");
542 						goto bad;
543 					}
544 					free(string);
545 				} else {
546 					free(string);
547 					state = prop_equals_string;
548 				}
549 				string = NULL;
550 				free(prop_name);
551 				prop_name = NULL;
552 				break;
553 
554 			case prop_equals_string_comma:
555 				state = prop_equals_string;
556 				break;
557 			default:
558 				file_err(filep, tok_err, tokbuf);
559 			}
560 			break;
561 		case T_HEXVAL:
562 		case T_DECVAL:
563 			switch (state) {
564 			case prop_equals:
565 				if (strcmp(prop_name, "port") == 0) {
566 					if (confent->port != -1) {
567 						file_err(filep,
568 					"'port' property already specified\n");
569 						goto bad;
570 					}
571 					confent->port =
572 					    (int)strtol(tokbuf, NULL, 0);
573 					state = begin;
574 				} else
575 					state = prop_equals_integer;
576 				free(prop_name);
577 				prop_name = NULL;
578 				break;
579 
580 			case prop_equals_integer_comma:
581 				state = prop_equals_integer;
582 				break;
583 			default:
584 				file_err(filep, tok_err, tokbuf);
585 			}
586 			break;
587 		case T_COMMA:
588 			switch (state) {
589 			case prop_equals_string:
590 				state = prop_equals_string_comma;
591 				break;
592 			case prop_equals_integer:
593 				state = prop_equals_integer_comma;
594 				break;
595 			default:
596 				file_err(filep, tok_err, tokbuf);
597 			}
598 			break;
599 		case T_NEWLINE:
600 			filep->linenum++;
601 			break;
602 		case T_POUND:
603 			find_eol(filep->fp);
604 			break;
605 		case T_EOF:
606 			file_err(filep, "Unexpected EOF\n");
607 			goto bad;
608 		default:
609 			file_err(filep, tok_err, tokbuf);
610 			goto bad;
611 		}
612 	} while ((token = lex(filep, tokbuf, linesize)) != T_SEMICOLON);
613 
614 	failed = 0;
615 
616 bad:
617 	if (prop_name)
618 		free(prop_name);
619 	if (string)
620 		free(string);
621 	if (failed == 1) {
622 		free_confent(confent);
623 		return (NULL);
624 	}
625 	return (confent);
626 }
627 
628 /*
629  * Parse all entries with mpxio-disable property in the given driver.conf
630  * file.
631  *
632  * fname		driver.conf file name
633  * confent_list		on return *confent_list will contain the list of
634  *			driver.conf file entries with mpxio-disable property.
635  * mpxio_disable	on return *mpxio_disable is set to the setting of the
636  * 			driver global mpxio-dissable property as follows.
637  *			0  if driver mpxio-disable="no"
638  *			1  if driver mpxio-disable="yes"
639  *			-1 if driver mpxio-disable property isn't specified.
640  */
641 static void
642 parse_conf_file(char *fname, struct conf_entry **confent_list,
643     int *mpxio_disable)
644 {
645 	struct conf_entry *confent, *tail = NULL;
646 	token_t token;
647 	struct conf_file file;
648 	char tokval[MAX_TOKEN_SIZE];
649 
650 	*confent_list = NULL;
651 	*mpxio_disable = -1;
652 	if ((file.fp = fopen(fname, "r")) == NULL)
653 		return;
654 
655 	file.filename = fname;
656 	file.linenum = 1;
657 
658 	while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) {
659 		switch (token) {
660 		case T_POUND:
661 			/*
662 			 * Skip comments.
663 			 */
664 			find_eol(file.fp);
665 			break;
666 		case T_NAME:
667 			if ((confent = parse_conf_entry(&file, tokval,
668 			    MAX_TOKEN_SIZE)) == NULL)
669 				break;
670 			/*
671 			 * No name indicates global property.
672 			 * Make sure parent and class not NULL.
673 			 */
674 			if (confent->name == NULL) {
675 				if (confent->parent ||
676 				    confent->class) {
677 					file_err(&file,
678 					    "missing name attribute\n");
679 				} else if (confent->mpxio_disable != -1) {
680 					if (*mpxio_disable == -1)
681 						*mpxio_disable =
682 						    confent->mpxio_disable;
683 					else
684 						file_err(&file,
685 				"'mpxio-disable' property already specified\n");
686 				}
687 				free_confent(confent);
688 				break;
689 			}
690 
691 			/*
692 			 * This is a node spec, either parent or class
693 			 * must be specified.
694 			 */
695 			if (confent->parent == NULL && confent->class == NULL) {
696 				file_err(&file,
697 				    "missing parent or class attribute\n");
698 				free_confent(confent);
699 				break;
700 			}
701 
702 			/* only need entries with mpxio_disable property */
703 			if (confent->mpxio_disable == -1) {
704 				free_confent(confent);
705 				break;
706 			}
707 
708 			if (tail)
709 				tail->next = confent;
710 			else
711 				*confent_list = confent;
712 			tail = confent;
713 			break;
714 
715 		case T_NEWLINE:
716 			file.linenum++;
717 			break;
718 		default:
719 			break;
720 		}
721 	}
722 
723 	(void) fclose(file.fp);
724 }
725 
726 /*
727  * Return the driver class of the given driver_name.
728  * The memory for the driver class is allocated by this function and the
729  * caller must free it.
730  */
731 static char *
732 get_driver_class(char *rootdir, char *driver_name)
733 {
734 	FILE *fp;
735 	char buf[BUFSIZE];
736 	char driver[BUFSIZE];
737 	char class_name[BUFSIZE];
738 
739 	logdmsg(("get_driver_class: rootdir = %s, driver name = %s\n",
740 	    rootdir, driver_name));
741 
742 	(void) snprintf(buf, sizeof (buf), "%s%s", rootdir, DRIVER_CLASSES);
743 
744 	if ((fp = fopen(buf, "r")) == NULL) {
745 		logdmsg(("get_driver_class: failed to open %s: %s\n",
746 		    buf, strerror(errno)));
747 		return (NULL);
748 	}
749 
750 	while (fgets(buf, sizeof (buf), fp) != NULL) {
751 		/* LINTED - unbounded string specifier */
752 		if ((sscanf(buf, "%s %s", driver, class_name) == 2) &&
753 		    driver[0] != '#' && strcmp(driver, driver_name) == 0) {
754 			logdmsg(("get_driver_class: driver class = %s\n",
755 			    class_name));
756 			(void) fclose(fp);
757 			return (strdup(class_name));
758 		}
759 	}
760 
761 	(void) fclose(fp);
762 	return (NULL);
763 }
764 
765 static int
766 lookup_in_confent_list(struct conf_entry *confent_list,
767     int match_class, char *parent, char *unit_addr, int port)
768 {
769 	struct conf_entry *confent;
770 	char *par;
771 
772 	logdmsg(("lookup_in_confent_list: %s = \"%s\", unit_addr = \"%s\", "
773 	    "port = %d\n", (match_class) ? "class" : "parent", parent,
774 	    STRVAL(unit_addr), port));
775 
776 	for (confent = confent_list; confent != NULL; confent = confent->next) {
777 		par = (match_class) ? confent->class : confent->parent;
778 		if (unit_addr) {
779 			if (confent->unit_address != NULL &&
780 			    strcmp(confent->unit_address, unit_addr) == 0 &&
781 			    par != NULL && strcmp(par, parent) == 0)
782 				return (confent->mpxio_disable);
783 		} else {
784 			if (confent->port == port &&
785 			    par != NULL && strcmp(par, parent) == 0)
786 				return (confent->mpxio_disable);
787 		}
788 	}
789 	return (-1);
790 }
791 
792 /*
793  * lookup mpxio-disabled property setting for the given path in the given
794  * driver.conf file. Match the entries from most specific to least specific.
795  *
796  * conf_file	the path name of either fp.conf, qlc.conf or scsi_vhci.conf
797  * path		/devices node path without the /devices prefix.
798  *		If the conf_file is fp.conf, path must be a fp node path
799  *		if the conf_file is qlc.conf, path must be a qlc node path.
800  *		if the conf_file is scsi_vhci.conf, path must be NULL.
801  *		ex:	/pci@8,600000/SUNW,qlc@4/fp@0,0
802  *			/pci@8,600000/SUNW,qlc@4
803  *
804  * returns:
805  *	0	if mpxio-disable="no"
806  *	1	if mpxio-disable="yes"
807  *	-1	if mpxio-disable property isn't specified.
808  */
809 static int
810 lookup_in_conf_file(char *rootdir, char *conf_file, char *path)
811 {
812 	struct conf_entry *confent_list = NULL;
813 	int mpxio_disable;
814 	di_node_t par_node = DI_NODE_NIL;
815 	char *node_name = NULL, *node_addr = NULL;
816 	char *unit_addr = NULL;
817 	int port = -1;
818 	char *par_node_name = NULL, *par_node_addr = NULL;
819 	char *par_binding_name = NULL, *par_driver_name = NULL;
820 	char *par_driver_class = NULL, *par_node_name_addr;
821 	int rv = -1;
822 	char buf[MAXPATHLEN];
823 
824 	logdmsg(("lookup_in_conf_file: rootdir = \"%s\", conf_file = \"%s\", "
825 	    "path = \"%s\"\n", rootdir, conf_file, STRVAL(path)));
826 
827 	(void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, conf_file);
828 	parse_conf_file(buf, &confent_list, &mpxio_disable);
829 #ifdef DEBUG
830 	log_confent_list(buf, confent_list, mpxio_disable);
831 #endif
832 
833 	/* if path is NULL, return driver global mpxio-disable setting */
834 	if (path == NULL) {
835 		rv = mpxio_disable;
836 		goto done;
837 	}
838 
839 	if ((node_name = strrchr(path, '/')) == NULL)
840 		goto done;
841 
842 	*node_name = '\0';
843 	node_name++;
844 
845 	if ((node_addr = strchr(node_name, '@')) == NULL)
846 		goto done;
847 
848 	*node_addr = '\0';
849 	node_addr++;
850 
851 	if (strcmp(node_name, "fp") == 0) {
852 		/* get port number; encoded in the node addr as a hex number */
853 		port = (int)strtol(node_addr, NULL, 16);
854 	} else
855 		unit_addr = node_addr;
856 
857 	/*
858 	 * Match from most specific to least specific;
859 	 * first, start the lookup based on full path.
860 	 */
861 	if ((rv = lookup_in_confent_list(confent_list, 0, path,
862 	    unit_addr, port)) != -1)
863 		goto done;
864 
865 	/* lookup nodename@address */
866 	if ((par_node_name_addr = strrchr(path, '/')) != NULL) {
867 		par_node_name_addr++;
868 		if ((rv = lookup_in_confent_list(confent_list, 0,
869 		    par_node_name_addr, unit_addr, port)) != -1)
870 			goto done;
871 	}
872 
873 	/* di_init() doesn't work when 0 is passed in flags */
874 	par_node = di_init(path, DINFOMINOR);
875 	if (par_node != DI_NODE_NIL) {
876 		par_node_name = di_node_name(par_node);
877 		par_node_addr = di_bus_addr(par_node);
878 		par_binding_name = di_binding_name(par_node);
879 		par_driver_name = di_driver_name(par_node);
880 	}
881 
882 	logdmsg(("par_node_name = %s\n", STRVAL(par_node_name)));
883 	logdmsg(("par_node_addr = %s\n", STRVAL(par_node_addr)));
884 	logdmsg(("par_binding_name = %s\n", STRVAL(par_binding_name)));
885 	logdmsg(("par_driver_name = %s\n", STRVAL(par_driver_name)));
886 
887 	/* lookup bindingname@address */
888 	if (par_binding_name != NULL && par_binding_name != par_node_name &&
889 	    par_node_addr != NULL) {
890 		(void) snprintf(buf, sizeof (buf), "%s@%s", par_binding_name,
891 		    par_node_addr);
892 		if ((rv = lookup_in_confent_list(confent_list, 0,
893 		    buf, unit_addr, port)) != -1)
894 			goto done;
895 	}
896 
897 	/* lookup binding name */
898 	if (par_binding_name != NULL) {
899 		if ((rv = lookup_in_confent_list(confent_list, 0,
900 		    par_binding_name, unit_addr, port)) != -1)
901 			goto done;
902 	}
903 
904 	if (par_driver_name != NULL) {
905 		/* lookup driver name */
906 		if ((rv = lookup_in_confent_list(confent_list, 0,
907 		    par_driver_name, unit_addr, port)) != -1)
908 			goto done;
909 
910 		/* finally, lookup class name */
911 		par_driver_class = get_driver_class(rootdir, par_driver_name);
912 		if (par_driver_class != NULL) {
913 			if ((rv = lookup_in_confent_list(confent_list, 1,
914 			    par_driver_class, unit_addr, port)) != -1)
915 				goto done;
916 		}
917 	}
918 
919 	/*
920 	 * no match so far;
921 	 * use the driver global mpxio-disable setting if exists.
922 	 */
923 	rv = mpxio_disable;
924 
925 done:
926 	if (node_name != NULL)
927 		*(node_name - 1) = '/';
928 	if (node_addr != NULL)
929 		*(node_addr - 1) = '@';
930 	if (par_driver_class != NULL)
931 		free(par_driver_class);
932 	if (confent_list != NULL)
933 		free_confent_list(confent_list);
934 	if (par_node != DI_NODE_NIL)
935 		di_fini(par_node);
936 
937 	return (rv);
938 }
939 
940 /*
941  * Given client_name return whether it is a phci or vhci based name.
942  * client_name is /devices name of a client without the /devices prefix.
943  *
944  * client_name			Return value
945  * .../fp@xxx/ssd@yyy		CLIENT_TYPE_PHCI
946  * .../scsi_vhci/ssd@yyy	CLIENT_TYPE_VHCI
947  * other			CLIENT_TYPE_UNKNOWN
948  */
949 static client_type_t
950 client_name_type(char *client_name)
951 {
952 	client_type_t client_type;
953 	char *p1, *p2;
954 
955 	logdmsg(("client_name_type: client_name = %s\n", client_name));
956 
957 	if (strncmp(client_name, SLASH_SCSI_VHCI,
958 	    sizeof (SLASH_SCSI_VHCI) - 1) == 0)
959 		return (CLIENT_TYPE_VHCI);
960 
961 	if (*client_name != '/')
962 		return (CLIENT_TYPE_UNKNOWN);
963 
964 	if ((p1 = strrchr(client_name, '/')) == NULL)
965 		return (CLIENT_TYPE_UNKNOWN);
966 
967 	*p1 = '\0';
968 
969 	if ((p2 = strrchr(client_name, '/')) != NULL &&
970 	    strncmp(p2, SLASH_FP_AT, sizeof (SLASH_FP_AT) - 1) == 0)
971 		client_type = CLIENT_TYPE_PHCI;
972 	else
973 		client_type = CLIENT_TYPE_UNKNOWN;
974 
975 	*p1 = '/';
976 	return (client_type);
977 }
978 
979 /*
980  * Compare controller name portion of dev1 and dev2.
981  *
982  * rootdir	root directory of the target environment
983  * dev1		can be either a /dev link or /devices name in the target
984  *		environemnt
985  * dev2		/devices name of a device without the /devices prefix
986  *
987  * Returns:
988  *	0	if controller names match
989  *	1	if controller names don't match
990  *	-1	an error occurred.
991  */
992 static int
993 compare_controller(char *rootdir, char *dev1, char *dev2)
994 {
995 	int linksize;
996 	char *p1, *p;
997 	char physdev1[MAXPATHLEN];
998 	char buf[MAXPATHLEN];
999 
1000 	logdmsg(("compare_controller: rootdir = %s, dev1 = %s, dev2 = %s\n",
1001 	    rootdir, dev1, dev2));
1002 
1003 	if (strncmp(dev1, SLASH_DEV_SLASH, sizeof (SLASH_DEV_SLASH) - 1)
1004 	    == 0) {
1005 		(void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, dev1);
1006 		if ((linksize = readlink(buf, physdev1, MAXPATHLEN)) > 0 &&
1007 		    linksize < (MAXPATHLEN - 1)) {
1008 			physdev1[linksize] = '\0';
1009 			logdmsg(("compare_controller: physdev1 = %s\n",
1010 			    physdev1));
1011 		} else
1012 			return (-1);
1013 	} else
1014 		(void) strlcpy(physdev1, dev1, MAXPATHLEN);
1015 
1016 	if ((p1 = strstr(physdev1, SLASH_DEVICES)) == NULL)
1017 		return (-1);
1018 
1019 	p1 += sizeof (SLASH_DEVICES) - 1;
1020 	/* strip the device portion */
1021 	if ((p = strrchr(p1, '/')) == NULL)
1022 		return (-1);
1023 	*p = '\0';
1024 
1025 	if ((p = strrchr(dev2, '/')) == NULL)
1026 		return (-1);
1027 	*p = '\0';
1028 
1029 	logdmsg(("compare_controller: path1 = %s, path2 = %s\n",
1030 	    p1, dev2));
1031 	if (strcmp(p1, dev2) == 0) {
1032 		*p = '/';
1033 		return (0);
1034 	} else {
1035 		*p = '/';
1036 		return (1);
1037 	}
1038 }
1039 
1040 /*
1041  * Check if the specified device path is on the root controller.
1042  *
1043  * rootdir	root directory of the target environment
1044  * path		/devices name of a device without the /devices prefix
1045  *
1046  * Returns
1047  *	1	if the path is on the root controller
1048  *	0	if the path is not on the root controller
1049  *	-1	if an error occurs
1050  */
1051 static int
1052 is_root_controller(char *rootdir, char *path)
1053 {
1054 	FILE *fp;
1055 	char *tmpfile;
1056 	int rv = -1;
1057 	struct vfstab vfsent;
1058 	char buf[MAXPATHLEN];
1059 	char ctd[MAXNAMELEN + 1];
1060 
1061 	logdmsg(("is_root_controller: rootdir = %s, path = %s\n", rootdir,
1062 	    path));
1063 
1064 	(void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, VFSTAB);
1065 
1066 	if ((fp = fopen(buf, "r")) == NULL) {
1067 		logdmsg(("is_root_controller: failed to open %s: %s\n",
1068 		    buf, strerror(errno)));
1069 		return (-1);
1070 	}
1071 
1072 	if (getvfsfile(fp, &vfsent, "/") != 0) {
1073 		logdmsg(("is_root_controller: getvfsfile: failed to read "
1074 		    "vfstab entry for mount point \"/\": %s\n",
1075 		    strerror(errno)));
1076 		(void) fclose(fp);
1077 		return (-1);
1078 	}
1079 	(void) fclose(fp);
1080 
1081 	/* check if the root is an svm metadisk */
1082 	if (strncmp(vfsent.vfs_special, META_DEV, sizeof (META_DEV) - 1) != 0) {
1083 		if (compare_controller(rootdir, vfsent.vfs_special, path) == 0)
1084 			return (1);
1085 		else
1086 			return (0);
1087 	}
1088 
1089 	/* Don't use /var/run as it is not mounted in miniroot */
1090 	if ((tmpfile = tempnam("/tmp", "diirc")) == NULL) {
1091 		logdmsg(("is_root_controller: tempnam: failed: %s\n",
1092 		    strerror(errno)));
1093 		return (-1);
1094 	}
1095 
1096 	/* get metadisk components using metastat command */
1097 	(void) snprintf(buf, MAXPATHLEN,
1098 	    "/usr/sbin/metastat -p %s 2>/dev/null | "
1099 	    "/usr/bin/grep ' 1 1 ' | "
1100 	    "/usr/bin/sed -e 's/^.* 1 1 //' | "
1101 	    "/usr/bin/cut -f1 -d ' ' > %s",
1102 	    vfsent.vfs_special + sizeof (META_DEV) - 1, tmpfile);
1103 
1104 	logdmsg(("is_root_controller: command = %s\n", buf));
1105 	fp = NULL;
1106 	if (system(buf) == 0 && (fp = fopen(tmpfile, "r")) != NULL) {
1107 		while (fscanf(fp, "%" VAL2STR(MAXNAMELEN) "s", ctd) == 1) {
1108 			(void) snprintf(buf, MAXPATHLEN, "/dev/dsk/%s", ctd);
1109 			if (compare_controller(rootdir, buf, path) == 0) {
1110 				rv = 1;
1111 				goto out;
1112 			}
1113 		}
1114 		rv = 0;
1115 	}
1116 
1117 out:
1118 	if (fp)
1119 		(void) fclose(fp);
1120 	(void) unlink(tmpfile);
1121 	free(tmpfile);
1122 	return (rv);
1123 }
1124 
1125 static int
1126 file_exists(char *rootdir, char *path)
1127 {
1128 	struct stat stbuf;
1129 	char fullpath[MAXPATHLEN];
1130 	int x;
1131 
1132 	(void) snprintf(fullpath, MAXPATHLEN, "%s%s", rootdir, path);
1133 
1134 	x = stat(fullpath, &stbuf);
1135 	logdmsg(("file_exists: %s: %s\n", fullpath, (x == 0) ? "yes" : "no"));
1136 	if (x == 0)
1137 		return (1);
1138 	else
1139 		return (0);
1140 }
1141 
1142 /*
1143  * Check if mpxio is enabled or disabled on the specified device path.
1144  * Looks through the .conf files to determine the mpxio setting.
1145  *
1146  * rootdir	root directory of the target environment
1147  * path		/devices name of a device without the /devices prefix and
1148  *		minor name component.
1149  *
1150  * Returns
1151  *	1	if mpxio is disabled
1152  *	0	if mpxio is enabled
1153  *	-1	if an error occurs
1154  */
1155 static int
1156 is_mpxio_disabled(char *rootdir, char *path)
1157 {
1158 	int mpxio_disable;
1159 	char *p;
1160 	int check_root_controller;
1161 
1162 	logdmsg(("is_mpxio_disabled: rootdir = %s, path = %s\n",
1163 	    rootdir, path));
1164 
1165 	if (file_exists(rootdir, SCSI_VHCI_CONF) == 0) {
1166 		/*
1167 		 * scsi_vhci.conf doesn't exist:
1168 		 *  if upgrading from a pre solaris 9 release. or
1169 		 *  if this function is called during fresh or flash install
1170 		 *  prior to installing scsi_vhci.conf file.
1171 		 */
1172 		if (file_exists(rootdir, "/kernel/drv"))
1173 			/* upgrading from pre solaris 9 */
1174 			return (1);
1175 		else
1176 			/* fresh or flash install */
1177 			return (0);
1178 	}
1179 
1180 	mpxio_disable = lookup_in_conf_file(rootdir, SCSI_VHCI_CONF, NULL);
1181 
1182 	/*
1183 	 * scsi_vhci.conf contains mpxio-disable property only in s9 and
1184 	 * s8+sfkpatch. This property is no longer present from s10 onwards.
1185 	 */
1186 	if (mpxio_disable == 1) {
1187 		/* upgrading from s8 or s9 with mpxio globally disabled */
1188 		return (1);
1189 	} else if (mpxio_disable == 0) {
1190 		/* upgrading from s8 or s9 with mpxio globally enabled */
1191 		check_root_controller = 1;
1192 	} else {
1193 		/*
1194 		 * We are looking at the s10 version of the file. This is
1195 		 * the case if this function is called after installing the
1196 		 * new scsi_vhci.conf file.
1197 		 */
1198 		check_root_controller = 0;
1199 	}
1200 
1201 	if ((mpxio_disable = lookup_in_conf_file(rootdir, FP_CONF, path))
1202 	    != -1)
1203 		return (mpxio_disable);
1204 
1205 	if ((p = strrchr(path, '/')) == NULL)
1206 		return (-1);
1207 
1208 	*p = '\0';
1209 	if ((mpxio_disable = lookup_in_conf_file(rootdir, QLC_CONF, path))
1210 	    != -1) {
1211 		*p = '/';
1212 		return (mpxio_disable);
1213 	}
1214 	*p = '/';
1215 
1216 	/*
1217 	 * mpxio-disable setting is not found in the .conf files.
1218 	 * The default is to enable mpxio, except if the path is on the root
1219 	 * controller.
1220 	 *
1221 	 * In s8 and s9 mpxio is not supported on the root controller.
1222 	 * NWS supplies a patch to enable root controller support in s8 and s9.
1223 	 * If the system had the patch installed, the fp.conf file would have
1224 	 * explicit "mpxio-disable=no" for the root controller. So we would
1225 	 * have found the mpxio-disable setting when we looked up this property
1226 	 * in the fp.conf file.
1227 	 */
1228 	if (check_root_controller) {
1229 		mpxio_disable = is_root_controller(rootdir, path);
1230 		logdmsg(("is_mpxio_disabled: is_root_controller returned %d\n",
1231 		    mpxio_disable));
1232 	} else
1233 		mpxio_disable = 0;
1234 
1235 	return (mpxio_disable);
1236 }
1237 
1238 static int
1239 vhci_ctl(sv_iocdata_t *iocp, int cmd)
1240 {
1241 	int fd, rv;
1242 
1243 	if ((fd = open(VHCI_CTL_NODE, O_RDWR)) < 0)
1244 		return (-1);
1245 	rv = ioctl(fd, cmd, iocp);
1246 	(void) close(fd);
1247 	return (rv);
1248 }
1249 
1250 /*
1251  * Convert a phci client name to vhci client name.
1252  *
1253  * phci_name	phci client /devices name without the /devices prefix and
1254  *		minor name component.
1255  *		ex: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
1256  *
1257  * Returns 	on success, vhci client name is returned. The memory for
1258  *		the vhci name is allocated by this function and the caller
1259  * 		must free it.
1260  *		on failure, NULL is returned.
1261  */
1262 static char *
1263 phci_to_vhci(char *phci_name)
1264 {
1265 	sv_iocdata_t ioc;
1266 	char *slash, *addr, *retp;
1267 	char vhci_name_buf[MAXPATHLEN];
1268 	char phci_name_buf[MAXPATHLEN];
1269 	char addr_buf[MAXNAMELEN];
1270 
1271 	logdmsg(("phci_to_vhci: pchi_name =  %s\n", phci_name));
1272 	(void) strlcpy(phci_name_buf, phci_name, MAXPATHLEN);
1273 
1274 	if ((slash = strrchr(phci_name_buf, '/')) == NULL ||
1275 	    (addr = strchr(slash, '@')) == NULL)
1276 		return (NULL);
1277 
1278 	*slash = '\0';
1279 	addr++;
1280 	(void) strlcpy(addr_buf, addr, MAXNAMELEN);
1281 
1282 	bzero(&ioc, sizeof (sv_iocdata_t));
1283 	ioc.client = vhci_name_buf;
1284 	ioc.phci = phci_name_buf;
1285 	ioc.addr = addr_buf;
1286 	if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_NAME) != 0) {
1287 		logdmsg(("phci_to_vhci: vhci_ctl failed: %s\n",
1288 		    strerror(errno)));
1289 		return (NULL);
1290 	}
1291 
1292 	retp = strdup(vhci_name_buf);
1293 	logdmsg(("phci_to_vhci: vhci name = %s\n", STRVAL(retp)));
1294 	return (retp);
1295 }
1296 
1297 static int
1298 add_to_phci_list(char **phci_list, sv_path_info_t *pi, int npaths, int state,
1299     char *node_name)
1300 {
1301 	int rv = 0;
1302 	char name[MAXPATHLEN];
1303 
1304 	while (npaths--) {
1305 		if (state == pi->ret_state) {
1306 			(void) snprintf(name, MAXPATHLEN, "%s/%s@%s",
1307 			    pi->device.ret_phci, node_name, pi->ret_addr);
1308 			if ((*phci_list = strdup(name)) == NULL)
1309 				return (-1);
1310 			phci_list++;
1311 			rv++;
1312 		}
1313 		pi++;
1314 	}
1315 
1316 	return (rv);
1317 }
1318 
1319 static void
1320 free_pathlist(char **pathlist)
1321 {
1322 	char **p;
1323 
1324 	if (pathlist != NULL) {
1325 		for (p = pathlist; *p != NULL; p++)
1326 			free(*p);
1327 		free(pathlist);
1328 	}
1329 }
1330 
1331 
1332 /*
1333  * Convert a vhci client name to phci client names.
1334  *
1335  * vhci_name	vhci client /devices name without the /devices prefix and
1336  *		minor name component.
1337  * num_paths	On return, *num_paths is set to the number paths in the
1338  *		returned path list.
1339  *
1340  * Returns 	NULL terminated path list containing phci client paths is
1341  *		returned on success. The memory for the path list is
1342  *		allocated by this function and the caller must free it by
1343  *		calling free_pathlist().
1344  *		NULL is returned on failure.
1345  */
1346 static char **
1347 vhci_to_phci(char *vhci_name, int *num_paths)
1348 {
1349 	sv_iocdata_t ioc;
1350 	uint_t npaths;
1351 	int n;
1352 	char **phci_list = NULL;
1353 	char *node_name, *at;
1354 	char vhci_name_buf[MAXPATHLEN];
1355 
1356 	logdmsg(("vhci_to_phci: vchi_name =  %s\n", vhci_name));
1357 
1358 	*num_paths = 0;
1359 	(void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN);
1360 
1361 	/* first get the number paths */
1362 	bzero(&ioc, sizeof (sv_iocdata_t));
1363 	ioc.client = vhci_name_buf;
1364 	ioc.ret_elem = &npaths;
1365 	if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 ||
1366 	    npaths == 0) {
1367 		logdmsg(("vhci_to_phci: vhci_ctl failed to get npaths: %s\n",
1368 		    strerror(errno)));
1369 		return (NULL);
1370 	}
1371 
1372 	/* now allocate memory for the path information and get all paths */
1373 	bzero(&ioc, sizeof (sv_iocdata_t));
1374 	ioc.client = vhci_name_buf;
1375 	ioc.buf_elem = npaths;
1376 	ioc.ret_elem = &npaths;
1377 	if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths,
1378 	    sizeof (sv_path_info_t))) == NULL)
1379 		return (NULL);
1380 	if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 ||
1381 	    npaths == 0) {
1382 		logdmsg(("vhci_to_phci: vhci_ctl failed: %s\n",
1383 		    strerror(errno)));
1384 		goto out;
1385 	}
1386 
1387 	if (ioc.buf_elem < npaths)
1388 		npaths = ioc.buf_elem;
1389 
1390 	if ((node_name = strrchr(vhci_name_buf, '/')) == NULL ||
1391 	    (at = strchr(node_name, '@')) == NULL)
1392 		goto out;
1393 
1394 	node_name++;
1395 	*at = '\0';
1396 
1397 	/* allocate one more (than npaths) for the terminating NULL pointer */
1398 	if ((phci_list = calloc(npaths + 1, sizeof (char *))) == NULL)
1399 		goto out;
1400 
1401 	/*
1402 	 * add only online paths as non-online paths may not be accessible
1403 	 * in the target environment.
1404 	 */
1405 	if ((n = add_to_phci_list(phci_list, ioc.ret_buf, npaths,
1406 	    MDI_PATHINFO_STATE_ONLINE, node_name)) <= 0)
1407 		goto out;
1408 
1409 	free(ioc.ret_buf);
1410 	*num_paths = n;
1411 
1412 #ifdef DEBUG
1413 	logdmsg(("vhci_to_phci: phci list:\n"));
1414 	log_pathlist(phci_list);
1415 #endif
1416 	return (phci_list);
1417 
1418 out:
1419 	free(ioc.ret_buf);
1420 	if (phci_list)
1421 		free_pathlist(phci_list);
1422 	return (NULL);
1423 }
1424 
1425 /*
1426  * build list of paths accessible from the target environment
1427  */
1428 static int
1429 build_pathlist(char *rootdir, char *vhcipath, char **pathlist, int npaths)
1430 {
1431 	int mpxio_disabled;
1432 	int i, j;
1433 	char *vpath = NULL;
1434 
1435 	for (i = 0; i < npaths; i++) {
1436 		mpxio_disabled = is_mpxio_disabled(rootdir, pathlist[i]);
1437 		logdmsg(("build_pathlist: mpxio_disabled = %d "
1438 		    "on path %s\n", mpxio_disabled, pathlist[i]));
1439 		if (mpxio_disabled == -1)
1440 			return (-1);
1441 		if (mpxio_disabled == 0) {
1442 			/*
1443 			 * mpxio is enabled on this phci path.
1444 			 * So use vhci path instead of phci path.
1445 			 */
1446 			if (vpath == NULL) {
1447 				if ((vpath = strdup(vhcipath)) == NULL)
1448 					return (-1);
1449 				free(pathlist[i]);
1450 				/* keep vhci path at beginning of the list */
1451 				for (j = i; j > 0; j--)
1452 					pathlist[j] = pathlist[j - 1];
1453 				pathlist[0] = vpath;
1454 			} else {
1455 				free(pathlist[i]);
1456 				npaths--;
1457 				for (j = i; j < npaths; j++)
1458 					pathlist[j] = pathlist[j + 1];
1459 				pathlist[npaths] = NULL;
1460 				/* compensate for i++ in the for loop */
1461 				i--;
1462 			}
1463 		}
1464 	}
1465 
1466 #ifdef DEBUG
1467 	logdmsg(("build_pathlist: returning npaths = %d, pathlist:\n", npaths));
1468 	log_pathlist(pathlist);
1469 #endif
1470 	return (npaths);
1471 }
1472 
1473 /*
1474  * Check if the specified device is refenced in the vfstab file.
1475  * Return 1 if referenced, 0 if not.
1476  *
1477  * rootdir	root directory of the target environment
1478  * nodepath	/devices path of a device in the target environment without
1479  *		the /devices prefix and minor component.
1480  */
1481 static int
1482 is_dev_in_vfstab(char *rootdir, char *nodepath)
1483 {
1484 	FILE *fp;
1485 	int linksize;
1486 	struct vfstab vfsent;
1487 	char *abspath, *minor;
1488 	char physpath[MAXPATHLEN];
1489 	char buf[MAXPATHLEN];
1490 
1491 	logdmsg(("is_dev_in_vfstab: rootdir = %s, nodepath = %s\n",
1492 	    rootdir, nodepath));
1493 
1494 	(void) snprintf(buf, sizeof (buf), "%s%s", rootdir, VFSTAB);
1495 
1496 	if ((fp = fopen(buf, "r")) == NULL)
1497 		return (0);
1498 
1499 	/*
1500 	 * read device specials from vfstab and compare names at physical
1501 	 * node path level.
1502 	 */
1503 	while (getvfsent(fp, &vfsent) == 0) {
1504 		if (strncmp(vfsent.vfs_special, SLASH_DEV_SLASH,
1505 		    sizeof (SLASH_DEV_SLASH) - 1) == 0) {
1506 			(void) snprintf(buf, MAXPATHLEN, "%s%s",
1507 			    rootdir, vfsent.vfs_special);
1508 			if ((linksize = readlink(buf, physpath,
1509 			    MAXPATHLEN)) > 0 && linksize < (MAXPATHLEN - 1)) {
1510 				physpath[linksize] = '\0';
1511 				if ((abspath = strstr(physpath,
1512 				    SLASH_DEVICES_SLASH)) == NULL)
1513 					continue;
1514 			} else
1515 				continue;
1516 		} else if (strncmp(vfsent.vfs_special, SLASH_DEVICES_SLASH,
1517 		    sizeof (SLASH_DEVICES_SLASH) - 1) == 0) {
1518 			(void) strlcpy(physpath, vfsent.vfs_special,
1519 			    MAXPATHLEN);
1520 			abspath = physpath;
1521 		} else
1522 			continue;
1523 
1524 		/* point to / after /devices */
1525 		abspath += sizeof (SLASH_DEVICES_SLASH) - 2;
1526 		/* strip minor component */
1527 		if ((minor = strrchr(abspath, ':')) != NULL)
1528 			*minor = '\0';
1529 
1530 		if (strcmp(nodepath, abspath) == 0) {
1531 			(void) fclose(fp);
1532 			logdmsg(("is_dev_in_vfstab: returning 1\n"));
1533 			return (1);
1534 		}
1535 	}
1536 
1537 	(void) fclose(fp);
1538 	return (0);
1539 }
1540 
1541 #endif /* __sparc */
1542 
1543 static int
1544 devlink_callback(di_devlink_t devlink, void *argp)
1545 {
1546 	const char *link;
1547 
1548 	if ((link = di_devlink_path(devlink)) != NULL)
1549 		(void) strlcpy((char *)argp, link, MAXPATHLEN);
1550 
1551 	return (DI_WALK_CONTINUE);
1552 }
1553 
1554 /*
1555  * Get the /dev name in the install environment corresponding to physpath.
1556  *
1557  * physpath	/devices path in the install environment without the /devices
1558  * 		prefix.
1559  * buf		caller supplied buffer where the /dev name is placed on return
1560  * bufsz	length of the buffer
1561  *
1562  * Returns	strlen of the /dev name on success, -1 on failure.
1563  */
1564 static int
1565 get_install_devlink(char *physpath, char *buf, size_t bufsz)
1566 {
1567 	di_devlink_handle_t devlink_hdl;
1568 	char devname[MAXPATHLEN];
1569 
1570 	logdmsg(("get_install_devlink: physpath = %s\n", physpath));
1571 
1572 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
1573 		logdmsg(("get_install_devlink: di_devlink_init() failed: %s\n",
1574 		    strerror(errno)));
1575 		return (-1);
1576 	}
1577 
1578 	devname[0] = '\0';
1579 	if (di_devlink_walk(devlink_hdl, NULL, physpath, DI_PRIMARY_LINK,
1580 	    devname, devlink_callback) != 0 || devname[0] == '\0') {
1581 		logdmsg(("get_install_devlink: di_devlink_walk failed: %s\n",
1582 		    strerror(errno)));
1583 		(void) di_devlink_fini(&devlink_hdl);
1584 		return (-1);
1585 	}
1586 
1587 	(void) di_devlink_fini(&devlink_hdl);
1588 
1589 	logdmsg(("get_install_devlink: devlink = %s\n", devname));
1590 	return (strlcpy(buf, devname, bufsz));
1591 }
1592 
1593 /*
1594  * Get the /dev name in the target environment corresponding to physpath.
1595  *
1596  * rootdir	root directory of the target environment
1597  * physpath	/devices path in the target environment without the /devices
1598  * 		prefix.
1599  * buf		caller supplied buffer where the /dev name is placed on return
1600  * bufsz	length of the buffer
1601  *
1602  * Returns	strlen of the /dev name on success, -1 on failure.
1603  */
1604 static int
1605 get_target_devlink(char *rootdir, char *physpath, char *buf, size_t bufsz)
1606 {
1607 	char *p;
1608 	int linksize;
1609 	DIR *dirp;
1610 	struct dirent *direntry;
1611 	char dirpath[MAXPATHLEN];
1612 	char devname[MAXPATHLEN];
1613 	char physdev[MAXPATHLEN];
1614 
1615 	logdmsg(("get_target_devlink: rootdir = %s, physpath = %s\n",
1616 	    rootdir, physpath));
1617 
1618 	if ((p = strrchr(physpath, '/')) == NULL)
1619 		return (-1);
1620 
1621 	if (strstr(p, ",raw") != NULL) {
1622 		(void) snprintf(dirpath, MAXPATHLEN, "%s/dev/rdsk", rootdir);
1623 	} else {
1624 		(void) snprintf(dirpath, MAXPATHLEN, "%s/dev/dsk", rootdir);
1625 	}
1626 
1627 	if ((dirp = opendir(dirpath)) == NULL)
1628 		return (-1);
1629 
1630 	while ((direntry = readdir(dirp)) != NULL) {
1631 		if (strcmp(direntry->d_name, ".") == 0 ||
1632 		    strcmp(direntry->d_name, "..") == 0)
1633 			continue;
1634 
1635 		(void) snprintf(devname, MAXPATHLEN, "%s/%s",
1636 		    dirpath, direntry->d_name);
1637 
1638 		if ((linksize = readlink(devname, physdev, MAXPATHLEN)) > 0 &&
1639 		    linksize < (MAXPATHLEN - 1)) {
1640 			physdev[linksize] = '\0';
1641 			if ((p = strstr(physdev, SLASH_DEVICES_SLASH)) !=
1642 			    NULL && strcmp(p + sizeof (SLASH_DEVICES) - 1,
1643 			    physpath) == 0) {
1644 				(void) closedir(dirp);
1645 				logdmsg(("get_target_devlink: devlink = %s\n",
1646 				    devname + strlen(rootdir)));
1647 				return (strlcpy(buf, devname + strlen(rootdir),
1648 				    bufsz));
1649 			}
1650 		}
1651 	}
1652 
1653 	(void) closedir(dirp);
1654 	return (-1);
1655 }
1656 
1657 /*
1658  * Convert device name to physpath.
1659  *
1660  * rootdir	root directory
1661  * devname	a /dev name or /devices name under rootdir
1662  * physpath	caller supplied buffer where the /devices path will be placed
1663  *		on return (without the /devices prefix).
1664  * physpathlen	length of the physpath buffer
1665  *
1666  * Returns 0 on success, -1 on failure.
1667  */
1668 static int
1669 devname2physpath(char *rootdir, char *devname, char *physpath, int physpathlen)
1670 {
1671 	int linksize;
1672 	char *p;
1673 	char devlink[MAXPATHLEN];
1674 	char tmpphyspath[MAXPATHLEN];
1675 
1676 	logdmsg(("devname2physpath: rootdir = %s, devname = %s\n",
1677 	    rootdir, devname));
1678 
1679 	if (strncmp(devname, SLASH_DEVICES_SLASH,
1680 	    sizeof (SLASH_DEVICES_SLASH) - 1) != 0) {
1681 		if (*rootdir == '\0')
1682 			linksize = readlink(devname, tmpphyspath, MAXPATHLEN);
1683 		else {
1684 			(void) snprintf(devlink, MAXPATHLEN, "%s%s",
1685 			    rootdir, devname);
1686 			linksize = readlink(devlink, tmpphyspath, MAXPATHLEN);
1687 		}
1688 		if (linksize > 0 && linksize < (MAXPATHLEN - 1)) {
1689 			tmpphyspath[linksize] = '\0';
1690 			if ((p = strstr(tmpphyspath, SLASH_DEVICES_SLASH))
1691 			    == NULL)
1692 				return (-1);
1693 		} else
1694 			return (-1);
1695 	} else
1696 		p = devname;
1697 
1698 	(void) strlcpy(physpath, p + sizeof (SLASH_DEVICES) - 1, physpathlen);
1699 	logdmsg(("devname2physpath: physpath = %s\n", physpath));
1700 	return (0);
1701 }
1702 
1703 /*
1704  * Map a device name (devname) from the target environment to the
1705  * install environment.
1706  *
1707  * rootdir	root directory of the target environment
1708  * devname	/dev or /devices name under the target environment
1709  * buf		caller supplied buffer where the mapped /dev name is placed
1710  *		on return
1711  * bufsz	length of the buffer
1712  *
1713  * Returns	strlen of the mapped /dev name on success, -1 on failure.
1714  */
1715 int
1716 devfs_target2install(const char *rootdir, const char *devname, char *buf,
1717     size_t bufsz)
1718 {
1719 	char physpath[MAXPATHLEN];
1720 
1721 	logdmsg(("devfs_target2install: rootdir = %s, devname = %s\n",
1722 	    STRVAL(rootdir), STRVAL(devname)));
1723 
1724 	if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0)
1725 		return (-1);
1726 
1727 	if (strcmp(rootdir, "/") == 0)
1728 		rootdir = "";
1729 
1730 	if (devname2physpath((char *)rootdir, (char *)devname, physpath,
1731 	    MAXPATHLEN) != 0)
1732 		return (-1);
1733 
1734 #ifdef __sparc
1735 	if (client_name_type(physpath) == CLIENT_TYPE_PHCI) {
1736 		char *mapped_node_path, *minor;
1737 		char minorbuf[MAXNAMELEN];
1738 
1739 		/* strip minor component if present */
1740 		if ((minor = strrchr(physpath, ':')) != NULL) {
1741 			*minor = '\0';
1742 			minor++;
1743 			(void) strlcpy(minorbuf, minor, MAXNAMELEN);
1744 		}
1745 		if ((mapped_node_path = phci_to_vhci(physpath)) != NULL) {
1746 			if (minor)
1747 				(void) snprintf(physpath, MAXPATHLEN,
1748 				    "%s:%s", mapped_node_path, minorbuf);
1749 			else
1750 				(void) strlcpy(physpath, mapped_node_path,
1751 				    MAXPATHLEN);
1752 			free(mapped_node_path);
1753 			logdmsg(("devfs_target2install: mapped physpath: %s\n",
1754 			    physpath));
1755 
1756 		} else if (minor)
1757 			*(minor - 1) = ':';
1758 	}
1759 #endif /* __sparc */
1760 
1761 	return (get_install_devlink(physpath, buf, bufsz));
1762 }
1763 
1764 /*
1765  * Map a device name (devname) from the install environment to the target
1766  * environment.
1767  *
1768  * rootdir	root directory of the target environment
1769  * devname	/dev or /devices name under the install environment
1770  * buf		caller supplied buffer where the mapped /dev name is placed
1771  *		on return
1772  * bufsz	length of the buffer
1773  *
1774  * Returns	strlen of the mapped /dev name on success, -1 on failure.
1775  */
1776 int
1777 devfs_install2target(const char *rootdir, const char *devname, char *buf,
1778     size_t bufsz)
1779 {
1780 	char physpath[MAXPATHLEN];
1781 
1782 	logdmsg(("devfs_install2target: rootdir = %s, devname = %s\n",
1783 	    STRVAL(rootdir), STRVAL(devname)));
1784 
1785 	if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0)
1786 		return (-1);
1787 
1788 	if (strcmp(rootdir, "/") == 0)
1789 		rootdir = "";
1790 
1791 	if (devname2physpath("", (char *)devname, physpath, MAXPATHLEN) != 0)
1792 		return (-1);
1793 
1794 #ifdef __sparc
1795 	if (client_name_type(physpath) == CLIENT_TYPE_VHCI) {
1796 		char **pathlist;
1797 		int npaths, i, j;
1798 		char *minor;
1799 		char minorbuf[MAXNAMELEN];
1800 
1801 		/* strip minor component if present */
1802 		if ((minor = strrchr(physpath, ':')) != NULL) {
1803 			*minor = '\0';
1804 			minor++;
1805 			(void) strlcpy(minorbuf, minor, MAXNAMELEN);
1806 		}
1807 
1808 		if ((pathlist = vhci_to_phci(physpath, &npaths)) == NULL)
1809 			return (-1);
1810 
1811 		if ((npaths = build_pathlist((char *)rootdir, physpath,
1812 		    pathlist, npaths)) <= 0) {
1813 			free_pathlist(pathlist);
1814 			return (-1);
1815 		}
1816 
1817 		/*
1818 		 * in case of more than one path, try to use the path
1819 		 * referenced in the vfstab file, otherwise use the first path.
1820 		 */
1821 		j = 0;
1822 		if (npaths > 1) {
1823 			for (i = 0; i < npaths; i++) {
1824 				if (is_dev_in_vfstab((char *)rootdir,
1825 				    pathlist[i])) {
1826 					j = i;
1827 					break;
1828 				}
1829 			}
1830 		}
1831 
1832 		if (minor)
1833 			(void) snprintf(physpath, MAXPATHLEN,
1834 			    "%s:%s", pathlist[j], minorbuf);
1835 		else
1836 			(void) strlcpy(physpath, pathlist[j], MAXPATHLEN);
1837 		free_pathlist(pathlist);
1838 	}
1839 #endif /* __sparc */
1840 
1841 	return (get_target_devlink((char *)rootdir, physpath, buf, bufsz));
1842 }
1843 
1844 #ifdef DEBUG
1845 
1846 static void
1847 vlog_debug_msg(char *fmt, va_list ap)
1848 {
1849 	time_t clock;
1850 	struct tm t;
1851 
1852 	if (!devfsmap_debug)
1853 		return;
1854 
1855 	if (logfp == NULL) {
1856 		if (*devfsmap_logfile != '\0') {
1857 			logfp = fopen(devfsmap_logfile, "a");
1858 			if (logfp)
1859 				(void) fprintf(logfp, "\nNew Log:\n");
1860 		}
1861 
1862 		if (logfp == NULL)
1863 			logfp = stdout;
1864 	}
1865 
1866 	clock = time(NULL);
1867 	(void) localtime_r(&clock, &t);
1868 	(void) fprintf(logfp, "%02d:%02d:%02d ", t.tm_hour, t.tm_min,
1869 	    t.tm_sec);
1870 	(void) vfprintf(logfp, fmt, ap);
1871 	(void) fflush(logfp);
1872 }
1873 
1874 static void
1875 log_debug_msg(char *fmt, ...)
1876 {
1877 	va_list ap;
1878 
1879 	va_start(ap, fmt);
1880 	vlog_debug_msg(fmt, ap);
1881 	va_end(ap);
1882 }
1883 
1884 #ifdef __sparc
1885 
1886 static char *
1887 mpxio_disable_string(int mpxio_disable)
1888 {
1889 	if (mpxio_disable == 0)
1890 		return ("no");
1891 	else if (mpxio_disable == 1)
1892 		return ("yes");
1893 	else
1894 		return ("not specified");
1895 }
1896 
1897 static void
1898 log_confent_list(char *filename, struct conf_entry *confent_list,
1899     int global_mpxio_disable)
1900 {
1901 	struct conf_entry *confent;
1902 
1903 	log_debug_msg("log_confent_list: filename = %s:\n", filename);
1904 	if (global_mpxio_disable != -1)
1905 		log_debug_msg("\tdriver global mpxio_disable = \"%s\"\n\n",
1906 		    mpxio_disable_string(global_mpxio_disable));
1907 
1908 	for (confent = confent_list; confent != NULL; confent = confent->next) {
1909 		if (confent->name)
1910 			log_debug_msg("\tname = %s\n", confent->name);
1911 		if (confent->parent)
1912 			log_debug_msg("\tparent = %s\n", confent->parent);
1913 		if (confent->class)
1914 			log_debug_msg("\tclass = %s\n", confent->class);
1915 		if (confent->unit_address)
1916 			log_debug_msg("\tunit_address = %s\n",
1917 			    confent->unit_address);
1918 		if (confent->port != -1)
1919 			log_debug_msg("\tport = %d\n", confent->port);
1920 		log_debug_msg("\tmpxio_disable = \"%s\"\n\n",
1921 			    mpxio_disable_string(confent->mpxio_disable));
1922 	}
1923 }
1924 
1925 static void
1926 log_pathlist(char **pathlist)
1927 {
1928 	char **p;
1929 
1930 	for (p = pathlist; *p != NULL; p++)
1931 		log_debug_msg("\t%s\n", *p);
1932 }
1933 
1934 #endif /* __sparc */
1935 
1936 #endif /* DEBUG */
1937