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