Home | History | Annotate | Download | only in libdevinfo
      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