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