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