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