xref: /onnv-gate/usr/src/cmd/modload/add_drv.c (revision 10842:e5e88c478ff5)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54145Scth  * Common Development and Distribution License (the "License").
64145Scth  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
228565SJerry.Gilliam@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <stdio.h>
270Sstevel@tonic-gate #include <stdlib.h>
280Sstevel@tonic-gate #include <libelf.h>
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <sys/stat.h>
310Sstevel@tonic-gate #include <sys/buf.h>
320Sstevel@tonic-gate #include <wait.h>
330Sstevel@tonic-gate #include <unistd.h>
340Sstevel@tonic-gate #include <libintl.h>
350Sstevel@tonic-gate #include <sys/modctl.h>
360Sstevel@tonic-gate #include <sys/systeminfo.h>
370Sstevel@tonic-gate #include <string.h>
380Sstevel@tonic-gate #include <limits.h>
390Sstevel@tonic-gate #include <locale.h>
400Sstevel@tonic-gate #include <ftw.h>
410Sstevel@tonic-gate #include <sys/sunddi.h>
420Sstevel@tonic-gate #include <libdevinfo.h>
430Sstevel@tonic-gate #include <sys/sysmacros.h>
440Sstevel@tonic-gate #include <fcntl.h>
4510585SDhanaraj.M@Sun.COM #include <zone.h>
460Sstevel@tonic-gate #include "addrem.h"
470Sstevel@tonic-gate #include "errmsg.h"
480Sstevel@tonic-gate #include "plcysubr.h"
490Sstevel@tonic-gate 
500Sstevel@tonic-gate /*
510Sstevel@tonic-gate  * globals needed for libdevinfo - there is no way to pass
520Sstevel@tonic-gate  * private data to the find routine.
530Sstevel@tonic-gate  */
540Sstevel@tonic-gate struct dev_list {
550Sstevel@tonic-gate 	int clone;
560Sstevel@tonic-gate 	char *dev_name;
570Sstevel@tonic-gate 	char *driver_name;
580Sstevel@tonic-gate 	struct dev_list *next;
590Sstevel@tonic-gate };
600Sstevel@tonic-gate 
610Sstevel@tonic-gate static char *kelf_desc = NULL;
620Sstevel@tonic-gate static int kelf_type = ELFCLASSNONE;
630Sstevel@tonic-gate 
640Sstevel@tonic-gate static char *new_drv;
650Sstevel@tonic-gate static struct dev_list *conflict_lst = NULL;
660Sstevel@tonic-gate 
670Sstevel@tonic-gate static int module_not_found(char *, char *, int, char **, int *);
680Sstevel@tonic-gate static void usage();
690Sstevel@tonic-gate static int update_minor_perm(char *, char *);
709268SJerry.Gilliam@Sun.COM static int devfs_update_minor_perm(char *, char *);
710Sstevel@tonic-gate static int update_driver_classes(char *, char *);
720Sstevel@tonic-gate static int drv_name_conflict(di_node_t);
730Sstevel@tonic-gate static int devfs_node(di_node_t node, void *arg);
740Sstevel@tonic-gate static int drv_name_match(char *, int, char *, char *);
750Sstevel@tonic-gate static void print_drv_conflict_info(int);
760Sstevel@tonic-gate static void check_dev_dir(int);
770Sstevel@tonic-gate static int dev_node(const char *, const struct stat *, int, struct FTW *);
780Sstevel@tonic-gate static void free_conflict_list(struct dev_list *);
790Sstevel@tonic-gate static int clone(di_node_t node);
800Sstevel@tonic-gate static int elf_type(char *, char **, int *);
810Sstevel@tonic-gate static int correct_location(char *, char **, int *);
820Sstevel@tonic-gate static int isaspec_drvmod_discovery();
830Sstevel@tonic-gate static void remove_slashes(char *);
840Sstevel@tonic-gate static int update_extra_privs(char *, char *privlist);
850Sstevel@tonic-gate static int ignore_root_basedir();
860Sstevel@tonic-gate 
870Sstevel@tonic-gate int
main(int argc,char * argv[])880Sstevel@tonic-gate main(int argc, char *argv[])
890Sstevel@tonic-gate {
900Sstevel@tonic-gate 	int opt;
910Sstevel@tonic-gate 	major_t major_num;
920Sstevel@tonic-gate 	char driver_name[FILENAME_MAX + 1];
930Sstevel@tonic-gate 	int driver_name_size = sizeof (driver_name);
940Sstevel@tonic-gate 	char path_driver_name[MAXPATHLEN];
950Sstevel@tonic-gate 	int path_driver_name_size = sizeof (path_driver_name);
960Sstevel@tonic-gate 	char *perms = NULL;
970Sstevel@tonic-gate 	char *aliases = NULL;
980Sstevel@tonic-gate 	char *classes = NULL;
990Sstevel@tonic-gate 	char *policy = NULL;
1000Sstevel@tonic-gate 	char *priv = NULL;
1010Sstevel@tonic-gate 	int noload_flag = 0;
1020Sstevel@tonic-gate 	int verbose_flag = 0;
1030Sstevel@tonic-gate 	int force_flag = 0;
104*10842SJerry.Gilliam@Sun.COM 	int update_only = 0;
1050Sstevel@tonic-gate 	int i_flag = 0;
1060Sstevel@tonic-gate 	int c_flag = 0;
1070Sstevel@tonic-gate 	int m_flag = 0;
1080Sstevel@tonic-gate 	int cleanup_flag = 0;
1090Sstevel@tonic-gate 	int server = 0;
1100Sstevel@tonic-gate 	char *basedir = NULL;
1110Sstevel@tonic-gate 	int is_unique;
1120Sstevel@tonic-gate 	char *slash;
1130Sstevel@tonic-gate 	int conflict;
1140Sstevel@tonic-gate 	di_node_t root_node;	/* for device tree snapshot */
1150Sstevel@tonic-gate 	char *drvelf_desc = NULL;
1160Sstevel@tonic-gate 	int drvelf_type = ELFCLASSNONE;
117*10842SJerry.Gilliam@Sun.COM 	int config_flags;
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate 	moddir = NULL;
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1220Sstevel@tonic-gate #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
1230Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
1240Sstevel@tonic-gate #endif
1250Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	/*  must be run by root */
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	if (geteuid() != 0) {
1300Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NOT_ROOT));
1310Sstevel@tonic-gate 		exit(1);
1320Sstevel@tonic-gate 	}
1330Sstevel@tonic-gate 
134*10842SJerry.Gilliam@Sun.COM 	while ((opt = getopt(argc, argv, "vfm:ni:b:c:p:P:u")) != EOF) {
1350Sstevel@tonic-gate 		switch (opt) {
1360Sstevel@tonic-gate 		case 'm' :
1370Sstevel@tonic-gate 			m_flag = 1;
1380Sstevel@tonic-gate 			perms = optarg;
1390Sstevel@tonic-gate 			break;
1400Sstevel@tonic-gate 		case 'f':
1410Sstevel@tonic-gate 			force_flag++;
1420Sstevel@tonic-gate 			break;
1430Sstevel@tonic-gate 		case 'v':
1440Sstevel@tonic-gate 			verbose_flag++;
1450Sstevel@tonic-gate 			break;
1460Sstevel@tonic-gate 		case 'n':
1470Sstevel@tonic-gate 			noload_flag++;
1480Sstevel@tonic-gate 			break;
1490Sstevel@tonic-gate 		case 'i' :
1500Sstevel@tonic-gate 			i_flag = 1;
1510Sstevel@tonic-gate 			aliases = optarg;
1520Sstevel@tonic-gate 			if (check_space_within_quote(aliases) == ERROR) {
1530Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_NO_SPACE),
1548565SJerry.Gilliam@Sun.COM 				    aliases);
1550Sstevel@tonic-gate 				exit(1);
1560Sstevel@tonic-gate 			}
1570Sstevel@tonic-gate 			break;
1580Sstevel@tonic-gate 		case 'b' :
1590Sstevel@tonic-gate 			server = 1;
1600Sstevel@tonic-gate 			basedir = optarg;
1610Sstevel@tonic-gate 			if (strcmp(basedir, "/") == 0 &&
1620Sstevel@tonic-gate 			    ignore_root_basedir()) {
1630Sstevel@tonic-gate 				server = 0;
1640Sstevel@tonic-gate 				basedir = NULL;
1650Sstevel@tonic-gate 			}
1660Sstevel@tonic-gate 			break;
1670Sstevel@tonic-gate 		case 'c':
1680Sstevel@tonic-gate 			c_flag = 1;
1690Sstevel@tonic-gate 			classes = optarg;
1700Sstevel@tonic-gate 			break;
1710Sstevel@tonic-gate 		case 'p':
1720Sstevel@tonic-gate 			policy = optarg;
1730Sstevel@tonic-gate 			break;
1740Sstevel@tonic-gate 		case 'P':
1750Sstevel@tonic-gate 			priv = optarg;
1760Sstevel@tonic-gate 			break;
177*10842SJerry.Gilliam@Sun.COM 		case 'u':
178*10842SJerry.Gilliam@Sun.COM 			/*
179*10842SJerry.Gilliam@Sun.COM 			 * Update binding files and kernel but
180*10842SJerry.Gilliam@Sun.COM 			 * do not load or configure devices.
181*10842SJerry.Gilliam@Sun.COM 			 */
182*10842SJerry.Gilliam@Sun.COM 			update_only = 1;
183*10842SJerry.Gilliam@Sun.COM 			break;
1840Sstevel@tonic-gate 		case '?' :
1850Sstevel@tonic-gate 		default:
1860Sstevel@tonic-gate 			usage();
1870Sstevel@tonic-gate 			exit(1);
1880Sstevel@tonic-gate 		}
1890Sstevel@tonic-gate 	}
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	if (argv[optind] != NULL) {
1930Sstevel@tonic-gate 		if (strlcpy(driver_name, argv[optind], driver_name_size) >=
1940Sstevel@tonic-gate 		    driver_name_size) {
1950Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DRVNAME_TOO_LONG),
1960Sstevel@tonic-gate 			    driver_name_size, argv[optind]);
1970Sstevel@tonic-gate 			exit(1);
1980Sstevel@tonic-gate 		}
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 		/*
2010Sstevel@tonic-gate 		 * check for extra args
2020Sstevel@tonic-gate 		 */
2030Sstevel@tonic-gate 		if ((optind + 1) != argc) {
2040Sstevel@tonic-gate 			usage();
2050Sstevel@tonic-gate 			exit(1);
2060Sstevel@tonic-gate 		}
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	} else {
2090Sstevel@tonic-gate 		usage();
2100Sstevel@tonic-gate 		exit(1);
2110Sstevel@tonic-gate 	}
2120Sstevel@tonic-gate 
21310585SDhanaraj.M@Sun.COM 	if (getzoneid() != GLOBAL_ZONEID) {
21410585SDhanaraj.M@Sun.COM 		(void) fprintf(stderr, gettext(ERR_NOT_GLOBAL_ZONE));
21510585SDhanaraj.M@Sun.COM 		exit(1);
21610585SDhanaraj.M@Sun.COM 	}
21710585SDhanaraj.M@Sun.COM 
2180Sstevel@tonic-gate 	/*
2190Sstevel@tonic-gate 	 * Fail if add_drv was invoked with a pathname prepended to the
2200Sstevel@tonic-gate 	 * driver_name argument.
2210Sstevel@tonic-gate 	 *
2220Sstevel@tonic-gate 	 * Check driver_name for any '/'s. If found, we assume that caller
2230Sstevel@tonic-gate 	 * is trying to specify a pathname.
2240Sstevel@tonic-gate 	 */
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	slash = strchr(driver_name, '/');
2270Sstevel@tonic-gate 	if (slash) {
2280Sstevel@tonic-gate 		remove_slashes(driver_name);
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 		/* extract module name out of path */
2310Sstevel@tonic-gate 		slash = strrchr(driver_name, '/');
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 		if (slash != NULL) {
2340Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_PATH_SPEC),
2350Sstevel@tonic-gate 			    driver_name);
2360Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_INSTALL_FAIL),
2370Sstevel@tonic-gate 			    ++slash);
2380Sstevel@tonic-gate 			exit(1);
2390Sstevel@tonic-gate 		}
2400Sstevel@tonic-gate 	}
2410Sstevel@tonic-gate 	new_drv = driver_name;
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	/* set up add_drv filenames */
2440Sstevel@tonic-gate 	if ((build_filenames(basedir)) == ERROR) {
2450Sstevel@tonic-gate 		exit(1);
2460Sstevel@tonic-gate 	}
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 	/* must be only running version of add_drv/rem_drv */
2490Sstevel@tonic-gate 	enter_lock();
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	if ((check_perms_aliases(m_flag, i_flag)) == ERROR)
2520Sstevel@tonic-gate 		err_exit();
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 	if ((check_name_to_major(R_OK | W_OK)) == ERROR)
2550Sstevel@tonic-gate 		err_exit();
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	/*
2580Sstevel@tonic-gate 	 * check validity of options
2590Sstevel@tonic-gate 	 */
2600Sstevel@tonic-gate 	if (m_flag) {
2610Sstevel@tonic-gate 		if ((check_perm_opts(perms)) == ERROR) {
2620Sstevel@tonic-gate 			usage();
2630Sstevel@tonic-gate 			err_exit();
2640Sstevel@tonic-gate 		}
2650Sstevel@tonic-gate 	}
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	if (i_flag) {
2680Sstevel@tonic-gate 		if (aliases != NULL)
2690Sstevel@tonic-gate 			if ((aliases_unique(aliases)) == ERROR)
2700Sstevel@tonic-gate 				err_exit();
2710Sstevel@tonic-gate 	}
2720Sstevel@tonic-gate 
273*10842SJerry.Gilliam@Sun.COM 	/* -u and -n/-b are mutually exclusive */
274*10842SJerry.Gilliam@Sun.COM 	if (update_only && (noload_flag || server)) {
275*10842SJerry.Gilliam@Sun.COM 		usage();
276*10842SJerry.Gilliam@Sun.COM 		err_exit();
277*10842SJerry.Gilliam@Sun.COM 	}
278*10842SJerry.Gilliam@Sun.COM 
2798565SJerry.Gilliam@Sun.COM 	/* update kernel unless -b or -n */
2808565SJerry.Gilliam@Sun.COM 	if (noload_flag == 0 && server == 0 &&
2818565SJerry.Gilliam@Sun.COM 	    priv != NULL && check_priv_entry(priv, 1) != 0)
2820Sstevel@tonic-gate 		err_exit();
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	if (policy != NULL &&
2850Sstevel@tonic-gate 	    (policy = check_plcy_entry(policy, driver_name, B_FALSE)) == NULL) {
2860Sstevel@tonic-gate 		err_exit();
2870Sstevel@tonic-gate 	}
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	if ((unique_driver_name(driver_name, name_to_major,
2900Sstevel@tonic-gate 	    &is_unique)) == ERROR)
2910Sstevel@tonic-gate 		err_exit();
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	if (is_unique == NOT_UNIQUE) {
2940Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NOT_UNIQUE), driver_name);
2950Sstevel@tonic-gate 		err_exit();
2960Sstevel@tonic-gate 	}
2970Sstevel@tonic-gate 
2988565SJerry.Gilliam@Sun.COM 	if (noload_flag == 0 && server == 0) {
2990Sstevel@tonic-gate 		if (elf_type("/dev/ksyms", &kelf_desc, &kelf_type) == ERROR) {
3000Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_KERNEL_ISA));
3010Sstevel@tonic-gate 			err_exit();
3020Sstevel@tonic-gate 		}
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 		if (module_not_found(driver_name, path_driver_name,
3050Sstevel@tonic-gate 		    path_driver_name_size, &drvelf_desc, &drvelf_type) ==
3060Sstevel@tonic-gate 		    ERROR) {
3070Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NOMOD), driver_name);
3080Sstevel@tonic-gate 			err_exit();
3090Sstevel@tonic-gate 		}
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 		/*
3120Sstevel@tonic-gate 		 * If the driver location is incorrect but the kernel and driver
3130Sstevel@tonic-gate 		 * are of the same ISA, suggest a fix.  If the driver location
3140Sstevel@tonic-gate 		 * is incorrect and the ISA's mismatch, notify the user that
3150Sstevel@tonic-gate 		 * this driver can not be loaded on this kernel.  In both cases,
3160Sstevel@tonic-gate 		 * do not attempt to load the driver module.
3170Sstevel@tonic-gate 		 */
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 		if (correct_location(path_driver_name, &drvelf_desc,
3200Sstevel@tonic-gate 		    (&drvelf_type)) == ERROR) {
3210Sstevel@tonic-gate 			noload_flag = 1;
3220Sstevel@tonic-gate 			if (kelf_type == drvelf_type) {
3230Sstevel@tonic-gate 				(void) fprintf(stderr,
3240Sstevel@tonic-gate 				    gettext(ERR_SOL_LOCATION), driver_name,
3250Sstevel@tonic-gate 				    driver_name);
3260Sstevel@tonic-gate 			} else {
3270Sstevel@tonic-gate 				(void) fprintf(stderr,
3280Sstevel@tonic-gate 				    gettext(ERR_NOT_LOADABLE),
3290Sstevel@tonic-gate 				    drvelf_desc, driver_name, kelf_desc);
3300Sstevel@tonic-gate 			}
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 		/*
3330Sstevel@tonic-gate 		 * The driver location is correct.  Verify that the kernel ISA
3340Sstevel@tonic-gate 		 * and driver ISA match.  If they do not match, produce an error
3350Sstevel@tonic-gate 		 * message and do not attempt to load the module.
3360Sstevel@tonic-gate 		 */
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 		} else if (kelf_type != drvelf_type) {
3390Sstevel@tonic-gate 			noload_flag = 1;
3400Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_ISA_MISMATCH),
3410Sstevel@tonic-gate 			    kelf_desc, driver_name, drvelf_desc);
3420Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NOT_LOADABLE),
3430Sstevel@tonic-gate 			    drvelf_desc, driver_name, kelf_desc);
3440Sstevel@tonic-gate 		}
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 		/*
3480Sstevel@tonic-gate 		 * Check for a more specific driver conflict - see
3490Sstevel@tonic-gate 		 * PSARC/1995/239
3500Sstevel@tonic-gate 		 * Note that drv_name_conflict() can return -1 for error
3510Sstevel@tonic-gate 		 * or 1 for a conflict.  Since the default is to fail unless
3520Sstevel@tonic-gate 		 * the -f flag is specified, we don't bother to differentiate.
3530Sstevel@tonic-gate 		 */
3540Sstevel@tonic-gate 		if ((root_node = di_init("/", DINFOSUBTREE | DINFOMINOR))
3550Sstevel@tonic-gate 		    == DI_NODE_NIL) {
3560Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEVTREE));
3570Sstevel@tonic-gate 			conflict = -1;
3580Sstevel@tonic-gate 		} else {
3590Sstevel@tonic-gate 			conflict = drv_name_conflict(root_node);
3600Sstevel@tonic-gate 			di_fini(root_node);
3610Sstevel@tonic-gate 		}
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 		if (conflict) {
3640Sstevel@tonic-gate 			/*
3650Sstevel@tonic-gate 			 * if the force flag is not set, we fail here
3660Sstevel@tonic-gate 			 */
3670Sstevel@tonic-gate 			if (!force_flag) {
3680Sstevel@tonic-gate 				(void) fprintf(stderr,
3690Sstevel@tonic-gate 				    gettext(ERR_INSTALL_FAIL), driver_name);
3700Sstevel@tonic-gate 				(void) fprintf(stderr, "Device managed by "
3710Sstevel@tonic-gate 				    "another driver.\n");
3720Sstevel@tonic-gate 				if (verbose_flag)
3730Sstevel@tonic-gate 					print_drv_conflict_info(force_flag);
3740Sstevel@tonic-gate 				err_exit();
3750Sstevel@tonic-gate 			}
3760Sstevel@tonic-gate 			/*
3770Sstevel@tonic-gate 			 * The force flag was specified so we print warnings
3780Sstevel@tonic-gate 			 * and install the driver anyways
3790Sstevel@tonic-gate 			 */
3800Sstevel@tonic-gate 			if (verbose_flag)
3810Sstevel@tonic-gate 				print_drv_conflict_info(force_flag);
3820Sstevel@tonic-gate 			free_conflict_list(conflict_lst);
3830Sstevel@tonic-gate 		}
3840Sstevel@tonic-gate 	}
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 	if ((update_name_to_major(driver_name, &major_num, server)) == ERROR) {
3870Sstevel@tonic-gate 		err_exit();
3880Sstevel@tonic-gate 	}
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	cleanup_flag |= CLEAN_NAM_MAJ;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	if (m_flag) {
3940Sstevel@tonic-gate 		cleanup_flag |= CLEAN_MINOR_PERM;
3950Sstevel@tonic-gate 		if (update_minor_perm(driver_name, perms) == ERROR) {
3960Sstevel@tonic-gate 			remove_entry(cleanup_flag, driver_name);
3970Sstevel@tonic-gate 			err_exit();
3980Sstevel@tonic-gate 		}
3990Sstevel@tonic-gate 	}
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 	if (i_flag) {
4020Sstevel@tonic-gate 		cleanup_flag |= CLEAN_DRV_ALIAS;
4030Sstevel@tonic-gate 		if (update_driver_aliases(driver_name, aliases) == ERROR) {
4040Sstevel@tonic-gate 			remove_entry(cleanup_flag, driver_name);
4050Sstevel@tonic-gate 			err_exit();
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 		}
4080Sstevel@tonic-gate 	}
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	if (c_flag) {
4110Sstevel@tonic-gate 		cleanup_flag |= CLEAN_DRV_CLASSES;
4120Sstevel@tonic-gate 		if (update_driver_classes(driver_name, classes) == ERROR) {
4130Sstevel@tonic-gate 			remove_entry(cleanup_flag, driver_name);
4140Sstevel@tonic-gate 			err_exit();
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 		}
4170Sstevel@tonic-gate 	}
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	if (priv != NULL) {
4200Sstevel@tonic-gate 		cleanup_flag |= CLEAN_DRV_PRIV;
4210Sstevel@tonic-gate 		if (update_extra_privs(driver_name, priv) == ERROR) {
4220Sstevel@tonic-gate 			remove_entry(cleanup_flag, driver_name);
4230Sstevel@tonic-gate 			err_exit();
4240Sstevel@tonic-gate 		}
4250Sstevel@tonic-gate 	}
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	if (policy != NULL) {
4280Sstevel@tonic-gate 		cleanup_flag |= CLEAN_DEV_POLICY;
4290Sstevel@tonic-gate 		if (update_device_policy(device_policy, policy, B_FALSE)
4308565SJerry.Gilliam@Sun.COM 		    == ERROR) {
4310Sstevel@tonic-gate 			remove_entry(cleanup_flag, driver_name);
4320Sstevel@tonic-gate 			err_exit();
4330Sstevel@tonic-gate 		}
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 
4368565SJerry.Gilliam@Sun.COM 	if (noload_flag || server) {
4370Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(BOOT_CLIENT));
4380Sstevel@tonic-gate 	} else {
4390Sstevel@tonic-gate 		/*
4400Sstevel@tonic-gate 		 * paranoia - if we crash whilst configuring the driver
4410Sstevel@tonic-gate 		 * this might avert possible file corruption.
4420Sstevel@tonic-gate 		 */
4430Sstevel@tonic-gate 		sync();
4440Sstevel@tonic-gate 
445*10842SJerry.Gilliam@Sun.COM 		config_flags = 0;
446*10842SJerry.Gilliam@Sun.COM 		if (verbose_flag)
447*10842SJerry.Gilliam@Sun.COM 			config_flags |= CONFIG_DRV_VERBOSE;
448*10842SJerry.Gilliam@Sun.COM 		if (update_only)
449*10842SJerry.Gilliam@Sun.COM 			config_flags |= CONFIG_DRV_UPDATE_ONLY;
450*10842SJerry.Gilliam@Sun.COM 
4510Sstevel@tonic-gate 		if (config_driver(driver_name, major_num, aliases, classes,
452*10842SJerry.Gilliam@Sun.COM 		    cleanup_flag, config_flags) == ERROR) {
4530Sstevel@tonic-gate 			err_exit();
4540Sstevel@tonic-gate 		}
4550Sstevel@tonic-gate 		if (m_flag) {
4560Sstevel@tonic-gate 			if (devfs_update_minor_perm(basedir,
4579268SJerry.Gilliam@Sun.COM 			    driver_name) == ERROR) {
4580Sstevel@tonic-gate 				err_exit();
4590Sstevel@tonic-gate 			}
4600Sstevel@tonic-gate 		}
461*10842SJerry.Gilliam@Sun.COM 		if (update_only) {
462*10842SJerry.Gilliam@Sun.COM 			(void) fprintf(stderr, gettext(INFO_UPDATE_ONLY),
463*10842SJerry.Gilliam@Sun.COM 			    driver_name);
464*10842SJerry.Gilliam@Sun.COM 		} else if (noload_flag) {
4650Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CONFIG_NOLOAD),
4660Sstevel@tonic-gate 			    driver_name);
467*10842SJerry.Gilliam@Sun.COM 		} else {
468*10842SJerry.Gilliam@Sun.COM 			load_driver(driver_name, verbose_flag);
469*10842SJerry.Gilliam@Sun.COM 		}
4700Sstevel@tonic-gate 	}
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 	if (create_reconfig(basedir) == ERROR)
4730Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CREATE_RECONFIG));
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	cleanup_moddir();
4760Sstevel@tonic-gate 	exit_unlock();
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 	if (verbose_flag) {
4790Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(DRIVER_INSTALLED), driver_name);
4800Sstevel@tonic-gate 	}
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	return (NOERR);
4830Sstevel@tonic-gate }
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate /*
4860Sstevel@tonic-gate  *	Searches for the driver module along the module path (returned
4870Sstevel@tonic-gate  *	from modctl) and returns a string (in drv_path) representing the path
4880Sstevel@tonic-gate  *	where drv_name was found.  ERROR is returned if function is unable
4890Sstevel@tonic-gate  *	to locate drv_name.
4900Sstevel@tonic-gate  */
4910Sstevel@tonic-gate int
module_not_found(char * drv_name,char * drv_path,int drv_path_size,char ** drvelf_desc,int * drvelf_type_ptr)4920Sstevel@tonic-gate module_not_found(char *drv_name, char *drv_path, int drv_path_size,
4930Sstevel@tonic-gate     char **drvelf_desc, int *drvelf_type_ptr)
4940Sstevel@tonic-gate {
4950Sstevel@tonic-gate 	struct stat buf;
4960Sstevel@tonic-gate 	char data [MAXMODPATHS];
4970Sstevel@tonic-gate 	char pathsave [MAXMODPATHS];
4980Sstevel@tonic-gate 	char *next = data;
4990Sstevel@tonic-gate 	struct drvmod_dir *curdir = NULL;
5000Sstevel@tonic-gate 	char foundpath[MAXPATHLEN];
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	if (modctl(MODGETPATH, NULL, data) != 0) {
5030Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_MODPATH));
5040Sstevel@tonic-gate 		return (ERROR);
5050Sstevel@tonic-gate 	}
5060Sstevel@tonic-gate 	(void) strcpy(pathsave, data);
5070Sstevel@tonic-gate 	next = strtok(data, MOD_SEP);
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	if (isaspec_drvmod_discovery() == ERROR)
5100Sstevel@tonic-gate 		err_exit();
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	curdir = moddir;
5130Sstevel@tonic-gate 	while (curdir != NULL) {
5140Sstevel@tonic-gate 		while (next != NULL) {
5150Sstevel@tonic-gate 			(void) snprintf(foundpath, sizeof (foundpath),
5160Sstevel@tonic-gate 			    "%s/drv/%s/%s", next, curdir->direc, drv_name);
5170Sstevel@tonic-gate 			if ((stat(foundpath, &buf) == 0) &&
5180Sstevel@tonic-gate 			    ((buf.st_mode & S_IFMT) == S_IFREG)) {
5190Sstevel@tonic-gate 				if (elf_type(foundpath, drvelf_desc,
5200Sstevel@tonic-gate 				    drvelf_type_ptr) == ERROR) {
5210Sstevel@tonic-gate 					(void) fprintf(stderr,
5220Sstevel@tonic-gate 					    gettext(ERR_INSTALL_FAIL),
5230Sstevel@tonic-gate 					    drv_name);
5240Sstevel@tonic-gate 					err_exit();
5250Sstevel@tonic-gate 				}
5260Sstevel@tonic-gate 				remove_slashes(foundpath);
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 				if (strlcpy(drv_path, foundpath, drv_path_size)
5290Sstevel@tonic-gate 				    >= drv_path_size) {
5300Sstevel@tonic-gate 					return (ERROR);
5310Sstevel@tonic-gate 				}
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 				return (NOERR);
5340Sstevel@tonic-gate 			}
5350Sstevel@tonic-gate 			next = strtok((char *)NULL, MOD_SEP);
5360Sstevel@tonic-gate 		}
5370Sstevel@tonic-gate 		(void) strcpy(data, pathsave);
5380Sstevel@tonic-gate 		next = strtok(data, MOD_SEP);
5390Sstevel@tonic-gate 		curdir = curdir->next;
5400Sstevel@tonic-gate 	}
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 	return (ERROR);
5430Sstevel@tonic-gate }
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate static void
usage()5460Sstevel@tonic-gate usage()
5470Sstevel@tonic-gate {
5480Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(USAGE));
5490Sstevel@tonic-gate }
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate static int
update_driver_classes(char * driver_name,char * classes)5520Sstevel@tonic-gate update_driver_classes(
5530Sstevel@tonic-gate 	char *driver_name,
5540Sstevel@tonic-gate 	char *classes)
5550Sstevel@tonic-gate {
5560Sstevel@tonic-gate 	/* make call to update the classes file */
5570Sstevel@tonic-gate 	return (append_to_file(driver_name, classes, driver_classes,
5584145Scth 	    ' ', "\t", 0));
5590Sstevel@tonic-gate }
5600Sstevel@tonic-gate 
5610Sstevel@tonic-gate static int
update_minor_perm(char * driver_name,char * perm_list)5620Sstevel@tonic-gate update_minor_perm(
5630Sstevel@tonic-gate 	char *driver_name,
5640Sstevel@tonic-gate 	char *perm_list)
5650Sstevel@tonic-gate {
56610599SJerry.Gilliam@Sun.COM 	return (append_to_minor_perm(driver_name, perm_list, minor_perm));
5670Sstevel@tonic-gate }
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate /*
5710Sstevel@tonic-gate  * Complete the minor perm update by communicating the minor perm
5720Sstevel@tonic-gate  * data to the kernel.  This information is used by devfs to ensure
5730Sstevel@tonic-gate  * that devices always have the correct permissions when attached.
5740Sstevel@tonic-gate  * The minor perm file must be updated and the driver configured
5750Sstevel@tonic-gate  * in the system for this step to complete correctly.
5760Sstevel@tonic-gate  */
5770Sstevel@tonic-gate static int
devfs_update_minor_perm(char * basedir,char * driver_name)5780Sstevel@tonic-gate devfs_update_minor_perm(
5790Sstevel@tonic-gate 	char *basedir,
5809268SJerry.Gilliam@Sun.COM 	char *driver_name)
5810Sstevel@tonic-gate {
5820Sstevel@tonic-gate 	int rval = 0;
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	if (basedir == NULL || (strcmp(basedir, "/") == 0)) {
5850Sstevel@tonic-gate 		if (devfs_add_minor_perm(driver_name,
5860Sstevel@tonic-gate 		    log_minorperm_error) != 0) {
5870Sstevel@tonic-gate 			(void) fprintf(stderr,
5880Sstevel@tonic-gate 			    gettext(ERR_UPDATE_PERM), driver_name);
5890Sstevel@tonic-gate 		}
5900Sstevel@tonic-gate 	}
5910Sstevel@tonic-gate 	return (rval);
5920Sstevel@tonic-gate }
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate static int
update_extra_privs(char * driver_name,char * privlist)5950Sstevel@tonic-gate update_extra_privs(
5960Sstevel@tonic-gate 	char *driver_name,
5970Sstevel@tonic-gate 	char *privlist)
5980Sstevel@tonic-gate {
5994145Scth 	return (append_to_file(driver_name, privlist, extra_privs,
6004145Scth 	    ',', ":", 0));
6010Sstevel@tonic-gate }
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate /*
6040Sstevel@tonic-gate  * Check to see if the driver we are adding is a more specific
6050Sstevel@tonic-gate  * driver for a device already attached to a less specific driver.
6060Sstevel@tonic-gate  * In other words, see if this driver comes earlier on the compatible
6070Sstevel@tonic-gate  * list of a device already attached to another driver.
6080Sstevel@tonic-gate  * If so, the new node will not be created (since the device is
6090Sstevel@tonic-gate  * already attached) but when the system reboots, it will attach to
6100Sstevel@tonic-gate  * the new driver but not have a node - we need to warn the user
6110Sstevel@tonic-gate  * if this is the case.
6120Sstevel@tonic-gate  */
6130Sstevel@tonic-gate static int
drv_name_conflict(di_node_t root_node)6140Sstevel@tonic-gate drv_name_conflict(di_node_t root_node)
6150Sstevel@tonic-gate {
6160Sstevel@tonic-gate 	/*
6170Sstevel@tonic-gate 	 * walk the device tree checking each node
6180Sstevel@tonic-gate 	 */
6190Sstevel@tonic-gate 	if (di_walk_node(root_node, DI_WALK_SIBFIRST, NULL, devfs_node) == -1) {
6200Sstevel@tonic-gate 		free_conflict_list(conflict_lst);
6210Sstevel@tonic-gate 		conflict_lst = (struct dev_list *)NULL;
6220Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_DEVTREE));
6230Sstevel@tonic-gate 		return (-1);
6240Sstevel@tonic-gate 	}
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	if (conflict_lst == NULL)
6270Sstevel@tonic-gate 		/* no conflicts found */
6280Sstevel@tonic-gate 		return (0);
6290Sstevel@tonic-gate 	else
6300Sstevel@tonic-gate 		/* conflicts! */
6310Sstevel@tonic-gate 		return (1);
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate /*
6350Sstevel@tonic-gate  * called via di_walk_node().
6360Sstevel@tonic-gate  * called for each node in the device tree.  We skip nodes that:
6370Sstevel@tonic-gate  *	1. are not hw nodes (since they cannot have generic names)
6380Sstevel@tonic-gate  *	2. that do not have a compatible property
6390Sstevel@tonic-gate  *	3. whose node name = binding name.
6400Sstevel@tonic-gate  *	4. nexus nodes - the name of a generic nexus node would
6410Sstevel@tonic-gate  *	not be affected by a driver change.
6420Sstevel@tonic-gate  * Otherwise, we parse the compatible property, if we find a
6430Sstevel@tonic-gate  * match with the new driver before we find a match with the
6440Sstevel@tonic-gate  * current driver, then we have a conflict and we save the
6450Sstevel@tonic-gate  * node away.
6460Sstevel@tonic-gate  */
6470Sstevel@tonic-gate /*ARGSUSED*/
6480Sstevel@tonic-gate static int
devfs_node(di_node_t node,void * arg)6490Sstevel@tonic-gate devfs_node(di_node_t node, void *arg)
6500Sstevel@tonic-gate {
6510Sstevel@tonic-gate 	char *binding_name, *node_name, *compat_names, *devfsnm;
6520Sstevel@tonic-gate 	struct dev_list *new_entry;
6530Sstevel@tonic-gate 	char strbuf[MAXPATHLEN];
6540Sstevel@tonic-gate 	int n_names;
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate 	/*
6570Sstevel@tonic-gate 	 * if there is no compatible property, we don't
6580Sstevel@tonic-gate 	 * have to worry about any conflicts.
6590Sstevel@tonic-gate 	 */
6600Sstevel@tonic-gate 	if ((n_names = di_compatible_names(node, &compat_names)) <= 0)
6610Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	/*
6640Sstevel@tonic-gate 	 * if the binding name and the node name match, then
6650Sstevel@tonic-gate 	 * either no driver existed that could be bound to this node,
6660Sstevel@tonic-gate 	 * or the driver name is the same as the node name.
6670Sstevel@tonic-gate 	 */
6680Sstevel@tonic-gate 	binding_name = di_binding_name(node);
6690Sstevel@tonic-gate 	node_name = di_node_name(node);
6700Sstevel@tonic-gate 	if ((binding_name == NULL) || (strcmp(node_name, binding_name) == 0))
6710Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	/*
6740Sstevel@tonic-gate 	 * we can skip nexus drivers since they do not
6750Sstevel@tonic-gate 	 * have major/minor number info encoded in their
6760Sstevel@tonic-gate 	 * /devices name and therefore won't change.
6770Sstevel@tonic-gate 	 */
6780Sstevel@tonic-gate 	if (di_driver_ops(node) & DI_BUS_OPS)
6790Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	/*
6820Sstevel@tonic-gate 	 * check for conflicts
6830Sstevel@tonic-gate 	 * If we do find that the new driver is a more specific driver
6840Sstevel@tonic-gate 	 * than the driver already attached to the device, we'll save
6850Sstevel@tonic-gate 	 * away the node name for processing later.
6860Sstevel@tonic-gate 	 */
6870Sstevel@tonic-gate 	if (drv_name_match(compat_names, n_names, binding_name, new_drv)) {
6880Sstevel@tonic-gate 		devfsnm = di_devfs_path(node);
6899268SJerry.Gilliam@Sun.COM 		(void) snprintf(strbuf, sizeof (strbuf),
6909268SJerry.Gilliam@Sun.COM 		    "%s%s", DEVFS_ROOT, devfsnm);
6910Sstevel@tonic-gate 		di_devfs_path_free(devfsnm);
6920Sstevel@tonic-gate 		new_entry = (struct dev_list *)calloc(1,
6930Sstevel@tonic-gate 		    sizeof (struct dev_list));
6940Sstevel@tonic-gate 		if (new_entry == (struct dev_list *)NULL) {
6950Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
6960Sstevel@tonic-gate 			err_exit();
6970Sstevel@tonic-gate 		}
6980Sstevel@tonic-gate 		/* save the /devices name */
6990Sstevel@tonic-gate 		if ((new_entry->dev_name = strdup(strbuf)) == NULL) {
7000Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
7010Sstevel@tonic-gate 			free(new_entry);
7020Sstevel@tonic-gate 			err_exit();
7030Sstevel@tonic-gate 		}
7040Sstevel@tonic-gate 		/* save the driver name */
7050Sstevel@tonic-gate 		if ((new_entry->driver_name = strdup(di_driver_name(node)))
7060Sstevel@tonic-gate 		    == NULL) {
7070Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
7080Sstevel@tonic-gate 			free(new_entry->dev_name);
7090Sstevel@tonic-gate 			free(new_entry);
7100Sstevel@tonic-gate 			err_exit();
7110Sstevel@tonic-gate 		}
7120Sstevel@tonic-gate 		/* check to see if this is a clone device */
7130Sstevel@tonic-gate 		if (clone(node))
7140Sstevel@tonic-gate 			new_entry->clone = 1;
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate 		/* add it to the list */
7170Sstevel@tonic-gate 		new_entry->next = conflict_lst;
7180Sstevel@tonic-gate 		conflict_lst = new_entry;
7190Sstevel@tonic-gate 	}
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 	return (DI_WALK_CONTINUE);
7220Sstevel@tonic-gate }
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate static int
clone(di_node_t node)7250Sstevel@tonic-gate clone(di_node_t node)
7260Sstevel@tonic-gate {
7270Sstevel@tonic-gate 	di_minor_t minor = DI_MINOR_NIL;
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
7300Sstevel@tonic-gate 		if (di_minor_type(minor) == DDM_ALIAS)
7310Sstevel@tonic-gate 			return (1);
7320Sstevel@tonic-gate 	}
7330Sstevel@tonic-gate 	return (0);
7340Sstevel@tonic-gate }
7350Sstevel@tonic-gate /*
7360Sstevel@tonic-gate  * check to see if the new_name shows up on the compat list before
7370Sstevel@tonic-gate  * the cur_name (driver currently attached to the device).
7380Sstevel@tonic-gate  */
7390Sstevel@tonic-gate static int
drv_name_match(char * compat_names,int n_names,char * cur_name,char * new_name)7400Sstevel@tonic-gate drv_name_match(char *compat_names, int n_names, char *cur_name, char *new_name)
7410Sstevel@tonic-gate {
7420Sstevel@tonic-gate 	int i, ret = 0;
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 	if (strcmp(cur_name, new_name) == 0)
7450Sstevel@tonic-gate 		return (0);
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	/* parse the coompatible list */
7480Sstevel@tonic-gate 	for (i = 0; i < n_names; i++) {
7490Sstevel@tonic-gate 		if (strcmp(compat_names, new_name) == 0) {
7500Sstevel@tonic-gate 			ret = 1;
7510Sstevel@tonic-gate 			break;
7520Sstevel@tonic-gate 		}
7530Sstevel@tonic-gate 		if (strcmp(compat_names, cur_name) == 0) {
7540Sstevel@tonic-gate 			break;
7550Sstevel@tonic-gate 		}
7560Sstevel@tonic-gate 		compat_names += strlen(compat_names) + 1;
7570Sstevel@tonic-gate 	}
7580Sstevel@tonic-gate 	return (ret);
7590Sstevel@tonic-gate }
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate /*
7620Sstevel@tonic-gate  * A more specific driver is being added for a device already attached
7630Sstevel@tonic-gate  * to a less specific driver.  Print out a general warning and if
7640Sstevel@tonic-gate  * the force flag was passed in, give the user a hint as to what
7650Sstevel@tonic-gate  * nodes may be affected in /devices and /dev
7660Sstevel@tonic-gate  */
7670Sstevel@tonic-gate static void
print_drv_conflict_info(int force)7680Sstevel@tonic-gate print_drv_conflict_info(int force)
7690Sstevel@tonic-gate {
7700Sstevel@tonic-gate 	struct dev_list *ptr;
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 	if (conflict_lst == NULL)
7730Sstevel@tonic-gate 		return;
7740Sstevel@tonic-gate 	if (force) {
7750Sstevel@tonic-gate 		(void) fprintf(stderr,
7760Sstevel@tonic-gate 		    "\nA reconfiguration boot must be performed to "
7770Sstevel@tonic-gate 		    "complete the\n");
7780Sstevel@tonic-gate 		(void) fprintf(stderr, "installation of this driver.\n");
7790Sstevel@tonic-gate 	}
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	if (force) {
7820Sstevel@tonic-gate 		(void) fprintf(stderr,
7830Sstevel@tonic-gate 		    "\nThe following entries in /devices will be "
7840Sstevel@tonic-gate 		    "affected:\n\n");
7850Sstevel@tonic-gate 	} else {
7860Sstevel@tonic-gate 		(void) fprintf(stderr,
7870Sstevel@tonic-gate 		    "\nDriver installation failed because the following\n");
7880Sstevel@tonic-gate 		(void) fprintf(stderr,
7890Sstevel@tonic-gate 		    "entries in /devices would be affected:\n\n");
7900Sstevel@tonic-gate 	}
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	ptr = conflict_lst;
7930Sstevel@tonic-gate 	while (ptr != NULL) {
7940Sstevel@tonic-gate 		(void) fprintf(stderr, "\t%s", ptr->dev_name);
7950Sstevel@tonic-gate 		if (ptr->clone)
7960Sstevel@tonic-gate 			(void) fprintf(stderr, " (clone device)\n");
7970Sstevel@tonic-gate 		else
7980Sstevel@tonic-gate 			(void) fprintf(stderr, "[:*]\n");
7990Sstevel@tonic-gate 		(void) fprintf(stderr, "\t(Device currently managed by driver "
8000Sstevel@tonic-gate 		    "\"%s\")\n\n", ptr->driver_name);
8010Sstevel@tonic-gate 		ptr = ptr->next;
8020Sstevel@tonic-gate 	}
8030Sstevel@tonic-gate 	check_dev_dir(force);
8040Sstevel@tonic-gate }
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate /*
8070Sstevel@tonic-gate  * use nftw to walk through /dev looking for links that match
8080Sstevel@tonic-gate  * an entry in the conflict list.
8090Sstevel@tonic-gate  */
8100Sstevel@tonic-gate static void
check_dev_dir(int force)8110Sstevel@tonic-gate check_dev_dir(int force)
8120Sstevel@tonic-gate {
8130Sstevel@tonic-gate 	int  walk_flags = FTW_PHYS | FTW_MOUNT;
8140Sstevel@tonic-gate 	int ft_depth = 15;
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 	if (force) {
8170Sstevel@tonic-gate 		(void) fprintf(stderr, "\nThe following entries in /dev will "
8180Sstevel@tonic-gate 		    "be affected:\n\n");
8190Sstevel@tonic-gate 	} else {
8200Sstevel@tonic-gate 		(void) fprintf(stderr, "\nThe following entries in /dev would "
8210Sstevel@tonic-gate 		    "be affected:\n\n");
8220Sstevel@tonic-gate 	}
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	(void) nftw("/dev", dev_node, ft_depth, walk_flags);
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 	(void) fprintf(stderr, "\n");
8270Sstevel@tonic-gate }
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate /*
8300Sstevel@tonic-gate  * checks a /dev link to see if it matches any of the conlficting
8310Sstevel@tonic-gate  * /devices nodes in conflict_lst.
8320Sstevel@tonic-gate  */
8330Sstevel@tonic-gate /*ARGSUSED1*/
8340Sstevel@tonic-gate static int
dev_node(const char * node,const struct stat * node_stat,int flags,struct FTW * ftw_info)8350Sstevel@tonic-gate dev_node(const char *node, const struct stat *node_stat, int flags,
8360Sstevel@tonic-gate 	struct FTW *ftw_info)
8370Sstevel@tonic-gate {
8380Sstevel@tonic-gate 	char linkbuf[MAXPATHLEN];
8390Sstevel@tonic-gate 	struct dev_list *ptr;
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	if (readlink(node, linkbuf, MAXPATHLEN) == -1)
8420Sstevel@tonic-gate 		return (0);
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate 	ptr = conflict_lst;
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 	while (ptr != NULL) {
8470Sstevel@tonic-gate 		if (strstr(linkbuf, ptr->dev_name) != NULL)
8480Sstevel@tonic-gate 			(void) fprintf(stderr, "\t%s\n", node);
8490Sstevel@tonic-gate 		ptr = ptr->next;
8500Sstevel@tonic-gate 	}
8510Sstevel@tonic-gate 	return (0);
8520Sstevel@tonic-gate }
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate static void
free_conflict_list(struct dev_list * list)8560Sstevel@tonic-gate free_conflict_list(struct dev_list *list)
8570Sstevel@tonic-gate {
8580Sstevel@tonic-gate 	struct dev_list *save;
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 	/* free up any dev_list structs we allocated. */
8610Sstevel@tonic-gate 	while (list != NULL) {
8620Sstevel@tonic-gate 		save = list;
8630Sstevel@tonic-gate 		list = list->next;
8640Sstevel@tonic-gate 		free(save->dev_name);
8650Sstevel@tonic-gate 		free(save);
8660Sstevel@tonic-gate 	}
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate int
elf_type(char * file,char ** elfdesc,int * elf_type_ptr)8700Sstevel@tonic-gate elf_type(char *file, char **elfdesc, int *elf_type_ptr)
8710Sstevel@tonic-gate {
8720Sstevel@tonic-gate 	int fd;
8730Sstevel@tonic-gate 	Elf *elf;
8740Sstevel@tonic-gate 	char *ident;
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	if ((fd = open(file, O_RDONLY)) < 0) {
8770Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANNOT_OPEN), file,
8780Sstevel@tonic-gate 		    strerror(errno));
8790Sstevel@tonic-gate 		return (ERROR);
8800Sstevel@tonic-gate 	}
8810Sstevel@tonic-gate 	if (elf_version(EV_CURRENT) == EV_NONE) {
8820Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_ELF_VERSION),
8830Sstevel@tonic-gate 		    elf_errmsg(-1));
8840Sstevel@tonic-gate 		(void) close(fd);
8850Sstevel@tonic-gate 		return (ERROR);
8860Sstevel@tonic-gate 	}
8870Sstevel@tonic-gate 	elf = elf_begin(fd, ELF_C_READ, NULL);
8880Sstevel@tonic-gate 	if (elf_kind(elf) != ELF_K_ELF) {
8890Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_ELF_KIND), file);
8900Sstevel@tonic-gate 		(void) elf_end(elf);
8910Sstevel@tonic-gate 		(void) close(fd);
8920Sstevel@tonic-gate 		return (ERROR);
8930Sstevel@tonic-gate 	}
8940Sstevel@tonic-gate 	ident = elf_getident(elf, 0);
8950Sstevel@tonic-gate 	if (ident[EI_CLASS] == ELFCLASS32) {
8960Sstevel@tonic-gate 		*elfdesc = "32";
8970Sstevel@tonic-gate 		*elf_type_ptr = ELFCLASS32;
8980Sstevel@tonic-gate 	} else if (ident[EI_CLASS] == ELFCLASS64) {
8990Sstevel@tonic-gate 		*elfdesc = "64";
9000Sstevel@tonic-gate 		*elf_type_ptr = ELFCLASS64;
9010Sstevel@tonic-gate 	} else {
9020Sstevel@tonic-gate 		*elfdesc = "none";
9030Sstevel@tonic-gate 		*elf_type_ptr = ELFCLASSNONE;
9040Sstevel@tonic-gate 	}
9050Sstevel@tonic-gate 	(void) elf_end(elf);
9060Sstevel@tonic-gate 	(void) close(fd);
9070Sstevel@tonic-gate 	return (NOERR);
9080Sstevel@tonic-gate }
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate int
correct_location(char * drv_path,char ** drvelf_desc,int * drvelf_type_ptr)9110Sstevel@tonic-gate correct_location(char *drv_path, char **drvelf_desc, int *drvelf_type_ptr)
9120Sstevel@tonic-gate {
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 	char copy_drv_path[MAXPATHLEN];
9150Sstevel@tonic-gate 	char *token = copy_drv_path;
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	(void) strcpy(copy_drv_path, drv_path);
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	if (elf_type(drv_path, drvelf_desc, drvelf_type_ptr) == ERROR) {
9200Sstevel@tonic-gate 		err_exit();
9210Sstevel@tonic-gate 	}
9220Sstevel@tonic-gate 	token = strtok(copy_drv_path, DIR_SEP);
9230Sstevel@tonic-gate 	while (token != NULL) {
9240Sstevel@tonic-gate 		if (strcmp("drv", token) == 0) {
9250Sstevel@tonic-gate 			token = strtok((char *)NULL, DIR_SEP);
9260Sstevel@tonic-gate 			if (strcmp(DRVDIR64, token) == 0) {
9270Sstevel@tonic-gate 				if (*drvelf_type_ptr == ELFCLASS64)
9280Sstevel@tonic-gate 					return (NOERR);
9290Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_LOCATION),
9300Sstevel@tonic-gate 				    *drvelf_desc, drv_path);
9310Sstevel@tonic-gate 				return (ERROR);
9320Sstevel@tonic-gate 			} else {
9330Sstevel@tonic-gate 				if (*drvelf_type_ptr == ELFCLASS32)
9340Sstevel@tonic-gate 					return (NOERR);
9350Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_LOCATION),
9360Sstevel@tonic-gate 				    *drvelf_desc, drv_path);
9370Sstevel@tonic-gate 				return (ERROR);
9380Sstevel@tonic-gate 			}
9390Sstevel@tonic-gate 		} else {
9400Sstevel@tonic-gate 			token = strtok((char *)NULL, DIR_SEP);
9410Sstevel@tonic-gate 		}
9420Sstevel@tonic-gate 	}
9430Sstevel@tonic-gate 	return (ERROR);
9440Sstevel@tonic-gate }
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate /*
9470Sstevel@tonic-gate  * Creates a two-element linked list of isa-specific subdirectories to
9480Sstevel@tonic-gate  * search for each driver, which is is used by the function
9490Sstevel@tonic-gate  * module_not_found() to convert the isa-independent modpath into an
9500Sstevel@tonic-gate  * isa-specific path .  The list is ordered depending on the machine
9510Sstevel@tonic-gate  * architecture and instruction set architecture, corresponding to the
9520Sstevel@tonic-gate  * order in which module_not_found() will search for the driver.  This
9530Sstevel@tonic-gate  * routine relies on an architecture not having more than two
9540Sstevel@tonic-gate  * sub-architectures (e.g., sparc/sparcv9 or i386/amd64).
9550Sstevel@tonic-gate  */
9560Sstevel@tonic-gate int
isaspec_drvmod_discovery()9570Sstevel@tonic-gate isaspec_drvmod_discovery()
9580Sstevel@tonic-gate {
9590Sstevel@tonic-gate 	char arch[SYS_NMLN];
9600Sstevel@tonic-gate 
9610Sstevel@tonic-gate 	moddir = (struct drvmod_dir *)calloc(1, sizeof (struct drvmod_dir));
9620Sstevel@tonic-gate 	if (moddir == NULL) {
9630Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
9640Sstevel@tonic-gate 		return (ERROR);
9650Sstevel@tonic-gate 	}
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 	if (sysinfo(SI_ARCHITECTURE, arch, sizeof (arch)) == -1) {
9680Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_SYSINFO_ARCH));
9690Sstevel@tonic-gate 		return (ERROR);
9700Sstevel@tonic-gate 	}
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 	if (strcmp(arch, "sparc") == 0 || strcmp(arch, "i386") == 0) {
9730Sstevel@tonic-gate 		moddir->next = (struct drvmod_dir *)
9740Sstevel@tonic-gate 		    calloc(1, sizeof (struct drvmod_dir));
9750Sstevel@tonic-gate 		if (moddir->next == NULL) {
9760Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
9770Sstevel@tonic-gate 			return (ERROR);
9780Sstevel@tonic-gate 		}
9790Sstevel@tonic-gate 		if (kelf_type == ELFCLASS64) {
9800Sstevel@tonic-gate 			(void) strcpy(moddir->direc, DRVDIR64);
9810Sstevel@tonic-gate 			(void) strcpy(moddir->next->direc, "");
9820Sstevel@tonic-gate 		} else {
9830Sstevel@tonic-gate 			(void) strcpy(moddir->direc, "");
9840Sstevel@tonic-gate 			(void) strcpy(moddir->next->direc, DRVDIR64);
9850Sstevel@tonic-gate 		}
9860Sstevel@tonic-gate 		moddir->next->next = NULL;
9870Sstevel@tonic-gate 		return (NOERR);
9880Sstevel@tonic-gate 	} else {
9890Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_ARCH_NOT_SUPPORTED), arch);
9900Sstevel@tonic-gate 		return (ERROR);
9910Sstevel@tonic-gate 	}
9920Sstevel@tonic-gate }
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate void
remove_slashes(char * path)9950Sstevel@tonic-gate remove_slashes(char *path)
9960Sstevel@tonic-gate {
9970Sstevel@tonic-gate 	char *slash = path;
9980Sstevel@tonic-gate 	char *remain_str;
9990Sstevel@tonic-gate 	int pathlen;
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate 	while ((slash = strchr(slash, '/')) != NULL) {
10020Sstevel@tonic-gate 		remain_str = ++slash;
10030Sstevel@tonic-gate 		while (*remain_str == '/')
10040Sstevel@tonic-gate 			++remain_str;
10050Sstevel@tonic-gate 		if (slash != remain_str)
10060Sstevel@tonic-gate 			(void) strcpy(slash, remain_str);
10070Sstevel@tonic-gate 	}
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 	pathlen = strlen(path);
10100Sstevel@tonic-gate 	if ((pathlen > 1) && path[pathlen - 1] == '/')
10110Sstevel@tonic-gate 		path[pathlen - 1] = '\0';
10120Sstevel@tonic-gate }
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate /*
10150Sstevel@tonic-gate  * This is for ITU floppies to add packages to the miniroot
10160Sstevel@tonic-gate  */
10170Sstevel@tonic-gate static int
ignore_root_basedir(void)10180Sstevel@tonic-gate ignore_root_basedir(void)
10190Sstevel@tonic-gate {
10200Sstevel@tonic-gate 	struct stat statbuf;
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 	return (stat("/ADD_DRV_IGNORE_ROOT_BASEDIR", &statbuf) == 0);
10230Sstevel@tonic-gate }
1024