xref: /onnv-gate/usr/src/cmd/modload/drvsubr.c (revision 3442)
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
52805Seota  * Common Development and Distribution License (the "License").
62805Seota  * 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 /*
22*3442Svikram  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
260Sstevel@tonic-gate 
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <stdio.h>
290Sstevel@tonic-gate #include <stdlib.h>
302805Seota #include <ctype.h>
310Sstevel@tonic-gate #include <unistd.h>
320Sstevel@tonic-gate #include <sys/sysmacros.h>
330Sstevel@tonic-gate #include <libintl.h>
340Sstevel@tonic-gate #include <wait.h>
350Sstevel@tonic-gate #include <string.h>
360Sstevel@tonic-gate #include <errno.h>
370Sstevel@tonic-gate #include <fcntl.h>
380Sstevel@tonic-gate #include <signal.h>
390Sstevel@tonic-gate #include <sys/buf.h>
400Sstevel@tonic-gate #include <sys/stat.h>
410Sstevel@tonic-gate #include <grp.h>
420Sstevel@tonic-gate #include "addrem.h"
430Sstevel@tonic-gate #include "errmsg.h"
440Sstevel@tonic-gate #include "plcysubr.h"
450Sstevel@tonic-gate 
460Sstevel@tonic-gate static char *add_rem_lock;	/* lock file */
470Sstevel@tonic-gate static char *tmphold;		/* temperary file for updating */
48*3442Svikram static int  add_rem_lock_fd = -1;
490Sstevel@tonic-gate 
500Sstevel@tonic-gate static int get_cached_n_to_m_file(char *filename, char ***cache);
510Sstevel@tonic-gate static int get_name_to_major_entry(int *major_no, char *driver_name,
520Sstevel@tonic-gate     char *file_name);
530Sstevel@tonic-gate 
542805Seota static int is_blank(char *);
552805Seota 
560Sstevel@tonic-gate /*ARGSUSED*/
570Sstevel@tonic-gate void
580Sstevel@tonic-gate log_minorperm_error(minorperm_err_t err, int key)
590Sstevel@tonic-gate {
600Sstevel@tonic-gate 	switch (err) {
610Sstevel@tonic-gate 	case MP_FOPEN_ERR:
620Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
630Sstevel@tonic-gate 			MINOR_PERM_FILE);
640Sstevel@tonic-gate 		break;
650Sstevel@tonic-gate 	case MP_FCLOSE_ERR:
660Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
670Sstevel@tonic-gate 			MINOR_PERM_FILE);
680Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
690Sstevel@tonic-gate 		break;
700Sstevel@tonic-gate 	case MP_IGNORING_LINE_ERR:
710Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
720Sstevel@tonic-gate 			MINOR_PERM_FILE);
730Sstevel@tonic-gate 		break;
740Sstevel@tonic-gate 	case MP_ALLOC_ERR:
750Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
760Sstevel@tonic-gate 			MINOR_PERM_FILE);
770Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
780Sstevel@tonic-gate 		break;
790Sstevel@tonic-gate 	case MP_NVLIST_ERR:
800Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
810Sstevel@tonic-gate 			MINOR_PERM_FILE);
820Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
830Sstevel@tonic-gate 		break;
840Sstevel@tonic-gate 	case MP_CANT_FIND_USER_ERR:
850Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
860Sstevel@tonic-gate 			MINOR_PERM_FILE);
870Sstevel@tonic-gate 		break;
880Sstevel@tonic-gate 	case MP_CANT_FIND_GROUP_ERR:
890Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
900Sstevel@tonic-gate 			MINOR_PERM_FILE);
910Sstevel@tonic-gate 		break;
920Sstevel@tonic-gate 	}
930Sstevel@tonic-gate }
940Sstevel@tonic-gate 
950Sstevel@tonic-gate /*
960Sstevel@tonic-gate  *  open file
970Sstevel@tonic-gate  * for each entry in list
980Sstevel@tonic-gate  *	where list entries are separated by <list_separator>
990Sstevel@tonic-gate  * 	append entry : driver_name <entry_separator> entry
1000Sstevel@tonic-gate  * close file
1010Sstevel@tonic-gate  * return error/noerr
1020Sstevel@tonic-gate  */
1030Sstevel@tonic-gate int
1040Sstevel@tonic-gate append_to_file(
1050Sstevel@tonic-gate 	char *driver_name,
1060Sstevel@tonic-gate 	char *entry_list,
1070Sstevel@tonic-gate 	char *filename,
1080Sstevel@tonic-gate 	char list_separator,
1090Sstevel@tonic-gate 	char *entry_separator)
1100Sstevel@tonic-gate {
1110Sstevel@tonic-gate 	int	i, len;
1120Sstevel@tonic-gate 	int	fpint;
1130Sstevel@tonic-gate 	char	*current_head, *previous_head;
1140Sstevel@tonic-gate 	char	*line, *one_entry;
1150Sstevel@tonic-gate 	FILE	*fp;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	if ((fp = fopen(filename, "a")) == NULL) {
1180Sstevel@tonic-gate 		perror(NULL);
1190Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1200Sstevel@tonic-gate 		    filename);
1210Sstevel@tonic-gate 		return (ERROR);
1220Sstevel@tonic-gate 	}
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate 	len = strlen(entry_list);
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 	one_entry = calloc(len + 1, 1);
1270Sstevel@tonic-gate 	if (one_entry == NULL) {
1280Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
1290Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1300Sstevel@tonic-gate 		(void) fclose(fp);
1310Sstevel@tonic-gate 		return (ERROR);
1320Sstevel@tonic-gate 	}
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate 	previous_head = entry_list;
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 	line = calloc(strlen(driver_name) + len + 4, 1);
1370Sstevel@tonic-gate 	if (line == NULL) {
1380Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1390Sstevel@tonic-gate 		(void) fclose(fp);
1400Sstevel@tonic-gate 		err_exit();
1410Sstevel@tonic-gate 	}
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	/*
1440Sstevel@tonic-gate 	 * get one entry at a time from list and append to <filename> file
1450Sstevel@tonic-gate 	 */
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate 	do {
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 		for (i = 0; i <= len; i++)
1500Sstevel@tonic-gate 			one_entry[i] = 0;
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 		for (i = 0; i <= (int)strlen(line); i++)
1530Sstevel@tonic-gate 			line[i] = 0;
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 		current_head = get_entry(previous_head, one_entry,
1560Sstevel@tonic-gate 		    list_separator);
1570Sstevel@tonic-gate 		previous_head = current_head;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 		(void) strcpy(line, driver_name);
1600Sstevel@tonic-gate 		(void) strcat(line, entry_separator);
1610Sstevel@tonic-gate 		(void) strcat(line, one_entry);
1620Sstevel@tonic-gate 		(void) strcat(line, "\n");
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 		if ((fputs(line, fp)) == EOF) {
1650Sstevel@tonic-gate 			perror(NULL);
1660Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
1670Sstevel@tonic-gate 			    filename);
1680Sstevel@tonic-gate 		}
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	} while (*current_head != '\0');
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	(void) fflush(fp);
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	fpint = fileno(fp);
1760Sstevel@tonic-gate 	(void) fsync(fpint);
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	(void) fclose(fp);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	free(one_entry);
1810Sstevel@tonic-gate 	free(line);
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 	return (NOERR);
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate /*
1880Sstevel@tonic-gate  *  open file
1890Sstevel@tonic-gate  * read thru file, deleting all entries if first
1900Sstevel@tonic-gate  *    entry = driver_name
1910Sstevel@tonic-gate  * close
1920Sstevel@tonic-gate  * if error, leave original file intact with message
1930Sstevel@tonic-gate  * assumption : drvconfig has been modified to work with clone
1940Sstevel@tonic-gate  *  entries in /etc/minor_perm as driver:mummble NOT
1950Sstevel@tonic-gate  *  clone:driver mummble
1960Sstevel@tonic-gate  * this implementation will NOT find clone entries
1970Sstevel@tonic-gate  * clone:driver mummble
1980Sstevel@tonic-gate  * match:
1990Sstevel@tonic-gate  *	delete just the matching entry
2000Sstevel@tonic-gate  *
2010Sstevel@tonic-gate  */
2020Sstevel@tonic-gate int
2030Sstevel@tonic-gate delete_entry(
2040Sstevel@tonic-gate 	char *oldfile,
2050Sstevel@tonic-gate 	char *driver_name,
2060Sstevel@tonic-gate 	char *marker,
2070Sstevel@tonic-gate 	char *match)
2080Sstevel@tonic-gate {
2090Sstevel@tonic-gate 	int		rv, i;
2100Sstevel@tonic-gate 	int		status = NOERR;
2110Sstevel@tonic-gate 	int		drvr_found = 0;
2120Sstevel@tonic-gate 	boolean_t 	nomatch = B_TRUE;
2132805Seota 	char		*newfile, *tptr, *cp, *dup;
2140Sstevel@tonic-gate 	char		line[MAX_DBFILE_ENTRY], drv[FILENAME_MAX + 1];
2150Sstevel@tonic-gate 	FILE		*fp, *newfp;
2160Sstevel@tonic-gate 	struct group	*sysgrp;
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	/*
2190Sstevel@tonic-gate 	 * check if match is specified and if it equals " "
2200Sstevel@tonic-gate 	 * this is a special case handling as we do a strstr(3STRING)
2210Sstevel@tonic-gate 	 * to match an entry. By default all entries are space separated
2220Sstevel@tonic-gate 	 * and without this check all entries of the file could get deleted.
2230Sstevel@tonic-gate 	 */
2240Sstevel@tonic-gate 	if (match && (*match == ' ' && strlen(match) == 1)) {
2250Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_INT_UPDATE), oldfile);
2260Sstevel@tonic-gate 		return (ERROR);
2270Sstevel@tonic-gate 	}
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	if ((fp = fopen(oldfile, "r")) == NULL) {
2300Sstevel@tonic-gate 		perror(NULL);
2310Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
2320Sstevel@tonic-gate 		return (ERROR);
2330Sstevel@tonic-gate 	}
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	/*
2360Sstevel@tonic-gate 	 * Build filename for temporary file
2370Sstevel@tonic-gate 	 */
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	if ((tptr = calloc(strlen(oldfile) + strlen(XEND) + 1, 1)) == NULL) {
2400Sstevel@tonic-gate 		perror(NULL);
2410Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
2420Sstevel@tonic-gate 	}
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 	(void) strcpy(tptr, oldfile);
2450Sstevel@tonic-gate 	(void) strcat(tptr, XEND);
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	/*
2480Sstevel@tonic-gate 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
2490Sstevel@tonic-gate 	 * assume a gid of "sys" but we can't undo the damage on already
2500Sstevel@tonic-gate 	 * installed systems unless we force the issue.
2510Sstevel@tonic-gate 	 */
2520Sstevel@tonic-gate 	if ((sysgrp = getgrnam("sys")) != NULL) {
2530Sstevel@tonic-gate 		(void) setgid(sysgrp->gr_gid);
2540Sstevel@tonic-gate 	}
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	newfile = mktemp(tptr);
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	if ((newfp = fopen(newfile, "w")) == NULL) {
2590Sstevel@tonic-gate 		perror(NULL);
2600Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
2610Sstevel@tonic-gate 		    newfile);
2620Sstevel@tonic-gate 		return (ERROR);
2630Sstevel@tonic-gate 	}
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
2662805Seota 		/* copy the whole line into dup */
2672805Seota 		if ((dup = strdup(line)) == NULL) {
2682805Seota 			perror(NULL);
2692805Seota 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
2702805Seota 			status = ERROR;
2712805Seota 			break;
2722805Seota 		}
2732805Seota 		/* cut off comments starting with '#' */
2742805Seota 		if ((cp = strchr(dup, '#')) != NULL)
2752805Seota 			*cp = '\0';
2762805Seota 		/* ignore comment or blank lines */
2772805Seota 		if (is_blank(dup)) {
2782805Seota 			if (fputs(line, newfp) == EOF) {
2790Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_UPDATE),
2800Sstevel@tonic-gate 				    oldfile);
2810Sstevel@tonic-gate 				status = ERROR;
2820Sstevel@tonic-gate 			}
2832805Seota 			free(dup);
2840Sstevel@tonic-gate 			continue;
2850Sstevel@tonic-gate 		}
2862805Seota 
2872805Seota 		/* get the driver name */
2882805Seota 		if (sscanf(dup, "%s", drv) != 1) {
2890Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
2900Sstevel@tonic-gate 			    oldfile, line);
2910Sstevel@tonic-gate 			status = ERROR;
2922805Seota 			free(dup);
2932805Seota 			break;
2940Sstevel@tonic-gate 		}
2952805Seota 		free(dup);
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
2980Sstevel@tonic-gate 			drv[i] =  '\0';
2990Sstevel@tonic-gate 		}
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 		if (strcmp(driver_name, drv) != 0) {
3020Sstevel@tonic-gate 			if ((fputs(line, newfp)) == EOF) {
3030Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_UPDATE),
3040Sstevel@tonic-gate 				    oldfile);
3050Sstevel@tonic-gate 				status = ERROR;
3060Sstevel@tonic-gate 			}
3070Sstevel@tonic-gate 		} else {
3080Sstevel@tonic-gate 			drvr_found++;
3090Sstevel@tonic-gate 			if (match) {	/* Just delete one entry */
3100Sstevel@tonic-gate 				/* for now delete just minor_perm and aliases */
3110Sstevel@tonic-gate 				if ((strcmp(oldfile, minor_perm) == 0) ||
3120Sstevel@tonic-gate 				    (strcmp(oldfile, extra_privs) == 0) ||
3130Sstevel@tonic-gate 				    (strcmp(oldfile, driver_aliases) == 0)) {
3140Sstevel@tonic-gate 					if (strstr(line, match)) {
3150Sstevel@tonic-gate 						nomatch = B_FALSE;
3160Sstevel@tonic-gate 					} else {
3170Sstevel@tonic-gate 						if ((fputs(line, newfp)) ==
3180Sstevel@tonic-gate 						    EOF) {
3190Sstevel@tonic-gate 							(void) fprintf(stderr,
3200Sstevel@tonic-gate 							    gettext(ERR_UPDATE),
3210Sstevel@tonic-gate 							    oldfile);
3220Sstevel@tonic-gate 							status = ERROR;
3230Sstevel@tonic-gate 						}
3240Sstevel@tonic-gate 						if (nomatch != B_FALSE)
3250Sstevel@tonic-gate 							nomatch = B_TRUE;
3260Sstevel@tonic-gate 					}
3270Sstevel@tonic-gate 				}
3280Sstevel@tonic-gate 			}
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 		} /* end of else */
3310Sstevel@tonic-gate 	} /* end of while */
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 	(void) fclose(fp);
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 	/* Make sure that the file is on disk */
3360Sstevel@tonic-gate 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
3370Sstevel@tonic-gate 		status = ERROR;
3380Sstevel@tonic-gate 	else
3390Sstevel@tonic-gate 		rv = NOERR;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	(void) fclose(newfp);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	/* no matching driver found */
3440Sstevel@tonic-gate 	rv = NOERR;
3450Sstevel@tonic-gate 	if (!drvr_found ||
3460Sstevel@tonic-gate 	    (nomatch == B_TRUE)) {
3470Sstevel@tonic-gate 		rv = NONE_FOUND;
3480Sstevel@tonic-gate 	}
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	/*
3510Sstevel@tonic-gate 	 * if error, leave original file, delete new file
3520Sstevel@tonic-gate 	 * if noerr, replace original file with new file
3530Sstevel@tonic-gate 	 */
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	if (status == NOERR) {
3560Sstevel@tonic-gate 		if (rename(oldfile, tmphold) == -1) {
3570Sstevel@tonic-gate 			perror(NULL);
3580Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
3590Sstevel@tonic-gate 			(void) unlink(newfile);
3600Sstevel@tonic-gate 			return (ERROR);
3610Sstevel@tonic-gate 		} else if (rename(newfile, oldfile) == -1) {
3620Sstevel@tonic-gate 			perror(NULL);
3630Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
3640Sstevel@tonic-gate 			(void) unlink(oldfile);
3650Sstevel@tonic-gate 			(void) unlink(newfile);
3660Sstevel@tonic-gate 			if (link(tmphold, oldfile) == -1) {
3670Sstevel@tonic-gate 				perror(NULL);
3680Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
3690Sstevel@tonic-gate 				    oldfile, tmphold);
3700Sstevel@tonic-gate 			}
3710Sstevel@tonic-gate 			return (ERROR);
3720Sstevel@tonic-gate 		}
3730Sstevel@tonic-gate 		(void) unlink(tmphold);
3740Sstevel@tonic-gate 	} else {
3750Sstevel@tonic-gate 		/*
3760Sstevel@tonic-gate 		 * since there's an error, leave file alone; remove
3770Sstevel@tonic-gate 		 * new file
3780Sstevel@tonic-gate 		 */
3790Sstevel@tonic-gate 		if (unlink(newfile) == -1) {
3800Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
3810Sstevel@tonic-gate 		}
3820Sstevel@tonic-gate 		return (ERROR);
3830Sstevel@tonic-gate 	}
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 	return (rv);
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate /*
3900Sstevel@tonic-gate  * wrapper for call to get_name_to_major_entry(): given driver name,
3910Sstevel@tonic-gate  * retrieve major number.
3920Sstevel@tonic-gate  */
3930Sstevel@tonic-gate int
3940Sstevel@tonic-gate get_major_no(char *driver_name, char *file_name)
3950Sstevel@tonic-gate {
3960Sstevel@tonic-gate 	int major = UNIQUE;
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	if (get_name_to_major_entry(&major, driver_name, file_name) == ERROR)
3990Sstevel@tonic-gate 		return (ERROR);
4000Sstevel@tonic-gate 	else
4010Sstevel@tonic-gate 		return (major);
4020Sstevel@tonic-gate }
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate /*
4050Sstevel@tonic-gate  * wrapper for call to get_name_to_major_entry(): given major number,
4060Sstevel@tonic-gate  * retrieve driver name.
4070Sstevel@tonic-gate  */
4080Sstevel@tonic-gate int
4090Sstevel@tonic-gate get_driver_name(int major, char *file_name, char *buf)
4100Sstevel@tonic-gate {
4110Sstevel@tonic-gate 	if (major < 0)
4120Sstevel@tonic-gate 		return (ERROR);
4130Sstevel@tonic-gate 	return (get_name_to_major_entry(&major, buf, file_name));
4140Sstevel@tonic-gate }
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate /*
4180Sstevel@tonic-gate  * return pointer to cached name_to_major file - reads file into
4190Sstevel@tonic-gate  * cache if this has not already been done.  Since there may be
4200Sstevel@tonic-gate  * requests for multiple name_to_major files (rem_name_to_major,
4210Sstevel@tonic-gate  * name_to_major), this routine keeps a list of cached files.
4220Sstevel@tonic-gate  */
4230Sstevel@tonic-gate static int
4240Sstevel@tonic-gate get_cached_n_to_m_file(char *filename, char ***cache)
4250Sstevel@tonic-gate {
4260Sstevel@tonic-gate 	struct n_to_m_cache {
4270Sstevel@tonic-gate 		char *file;
4280Sstevel@tonic-gate 		char **cached_file;
4290Sstevel@tonic-gate 		int size;
4300Sstevel@tonic-gate 		struct n_to_m_cache *next;
4310Sstevel@tonic-gate 	};
4320Sstevel@tonic-gate 	static struct n_to_m_cache *head = NULL;
4330Sstevel@tonic-gate 	struct n_to_m_cache *ptr;
4340Sstevel@tonic-gate 	FILE *fp;
4350Sstevel@tonic-gate 	char drv[FILENAME_MAX + 1];
4360Sstevel@tonic-gate 	char entry[FILENAME_MAX + 1];
4372805Seota 	char line[MAX_N2M_ALIAS_LINE], *cp;
4380Sstevel@tonic-gate 	int maj;
4390Sstevel@tonic-gate 	int size = 0;
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	/*
4430Sstevel@tonic-gate 	 * see if the file is already cached - either
4440Sstevel@tonic-gate 	 * rem_name_to_major or name_to_major
4450Sstevel@tonic-gate 	 */
4460Sstevel@tonic-gate 	ptr = head;
4470Sstevel@tonic-gate 	while (ptr != NULL) {
4480Sstevel@tonic-gate 		if (strcmp(ptr->file, filename) == 0)
4490Sstevel@tonic-gate 			break;
4500Sstevel@tonic-gate 		ptr = ptr->next;
4510Sstevel@tonic-gate 	}
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	if (ptr == NULL) {	/* we need to cache the contents */
4540Sstevel@tonic-gate 		if ((fp = fopen(filename, "r")) == NULL) {
4550Sstevel@tonic-gate 			perror(NULL);
4560Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_OPEN),
4570Sstevel@tonic-gate 			    filename);
4580Sstevel@tonic-gate 			return (ERROR);
4590Sstevel@tonic-gate 		}
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 		while (fgets(line, sizeof (line), fp) != NULL) {
4622805Seota 			/* cut off comments starting with '#' */
4632805Seota 			if ((cp = strchr(line, '#')) != NULL)
4642805Seota 				*cp = '\0';
4652805Seota 			/* ignore comment or blank lines */
4662805Seota 			if (is_blank(line))
4672805Seota 				continue;
4682805Seota 			/* sanity-check */
4690Sstevel@tonic-gate 			if (sscanf(line, "%s%s", drv, entry) != 2) {
4700Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
4710Sstevel@tonic-gate 				    filename, line);
4720Sstevel@tonic-gate 				continue;
4730Sstevel@tonic-gate 			}
4740Sstevel@tonic-gate 			maj = atoi(entry);
4750Sstevel@tonic-gate 			if (maj > size)
4760Sstevel@tonic-gate 				size = maj;
4770Sstevel@tonic-gate 		}
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 		/* allocate struct to cache the file */
4800Sstevel@tonic-gate 		ptr = (struct n_to_m_cache *)calloc(1,
4810Sstevel@tonic-gate 		    sizeof (struct n_to_m_cache));
4820Sstevel@tonic-gate 		if (ptr == NULL) {
4830Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
4840Sstevel@tonic-gate 			return (ERROR);
4850Sstevel@tonic-gate 		}
4860Sstevel@tonic-gate 		ptr->size = size + 1;
4870Sstevel@tonic-gate 		/* allocate space to cache contents of file */
4880Sstevel@tonic-gate 		ptr->cached_file = (char **)calloc(ptr->size, sizeof (char *));
4890Sstevel@tonic-gate 		if (ptr->cached_file == NULL) {
4900Sstevel@tonic-gate 			free(ptr);
4910Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
4920Sstevel@tonic-gate 			return (ERROR);
4930Sstevel@tonic-gate 		}
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 		rewind(fp);
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 		/*
4980Sstevel@tonic-gate 		 * now fill the cache
4990Sstevel@tonic-gate 		 * the cache is an array of char pointers indexed by major
5000Sstevel@tonic-gate 		 * number
5010Sstevel@tonic-gate 		 */
5020Sstevel@tonic-gate 		while (fgets(line, sizeof (line), fp) != NULL) {
5032805Seota 			/* cut off comments starting with '#' */
5042805Seota 			if ((cp = strchr(line, '#')) != NULL)
5052805Seota 				*cp = '\0';
5062805Seota 			/* ignore comment or blank lines */
5072805Seota 			if (is_blank(line))
5082805Seota 				continue;
5092805Seota 			/* sanity-check */
5100Sstevel@tonic-gate 			if (sscanf(line, "%s%s", drv, entry) != 2) {
5110Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
5120Sstevel@tonic-gate 				    filename, line);
5130Sstevel@tonic-gate 				continue;
5140Sstevel@tonic-gate 			}
5150Sstevel@tonic-gate 			maj = atoi(entry);
5160Sstevel@tonic-gate 			if ((ptr->cached_file[maj] = strdup(drv)) == NULL) {
5170Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
5180Sstevel@tonic-gate 				free(ptr->cached_file);
5190Sstevel@tonic-gate 				free(ptr);
5200Sstevel@tonic-gate 				return (ERROR);
5210Sstevel@tonic-gate 			}
5220Sstevel@tonic-gate 			(void) strcpy(ptr->cached_file[maj], drv);
5230Sstevel@tonic-gate 		}
5240Sstevel@tonic-gate 		(void) fclose(fp);
5250Sstevel@tonic-gate 		/* link the cache struct into the list of cached files */
5260Sstevel@tonic-gate 		ptr->file = strdup(filename);
5270Sstevel@tonic-gate 		if (ptr->file == NULL) {
5280Sstevel@tonic-gate 			for (maj = 0; maj <= ptr->size; maj++)
5290Sstevel@tonic-gate 				free(ptr->cached_file[maj]);
5300Sstevel@tonic-gate 			free(ptr->cached_file);
5310Sstevel@tonic-gate 			free(ptr);
5320Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
5330Sstevel@tonic-gate 			return (ERROR);
5340Sstevel@tonic-gate 		}
5350Sstevel@tonic-gate 		ptr->next = head;
5360Sstevel@tonic-gate 		head = ptr;
5370Sstevel@tonic-gate 	}
5380Sstevel@tonic-gate 	/* return value pointer to contents of file */
5390Sstevel@tonic-gate 	*cache = ptr->cached_file;
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	/* return size */
5420Sstevel@tonic-gate 	return (ptr->size);
5430Sstevel@tonic-gate }
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate /*
5470Sstevel@tonic-gate  * Using get_cached_n_to_m_file(), retrieve maximum major number
5480Sstevel@tonic-gate  * found in the specificed file (name_to_major/rem_name_to_major).
5490Sstevel@tonic-gate  *
5500Sstevel@tonic-gate  * The return value is actually the size of the internal cache including 0.
5510Sstevel@tonic-gate  */
5520Sstevel@tonic-gate int
5530Sstevel@tonic-gate get_max_major(char *file_name)
5540Sstevel@tonic-gate {
5550Sstevel@tonic-gate 	char **n_to_m_cache = NULL;
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	return (get_cached_n_to_m_file(file_name, &n_to_m_cache));
5580Sstevel@tonic-gate }
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 
5610Sstevel@tonic-gate /*
5620Sstevel@tonic-gate  * searching name_to_major: if major_no == UNIQUE then the caller wants to
5630Sstevel@tonic-gate  * use the driver name as the key.  Otherwise, the caller wants to use
5640Sstevel@tonic-gate  * the major number as a key.
5650Sstevel@tonic-gate  *
5660Sstevel@tonic-gate  * This routine caches the contents of the name_to_major file on
5670Sstevel@tonic-gate  * first call.  And it could be generalized to deal with other
5680Sstevel@tonic-gate  * config files if necessary.
5690Sstevel@tonic-gate  */
5700Sstevel@tonic-gate static int
5710Sstevel@tonic-gate get_name_to_major_entry(int *major_no, char *driver_name, char *file_name)
5720Sstevel@tonic-gate {
5730Sstevel@tonic-gate 	int maj;
5740Sstevel@tonic-gate 	char **n_to_m_cache = NULL;
5750Sstevel@tonic-gate 	int size = 0;
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	int ret = NOT_UNIQUE;
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 	/*
5800Sstevel@tonic-gate 	 * read the file in - we cache it in case caller wants to
5810Sstevel@tonic-gate 	 * do multiple lookups
5820Sstevel@tonic-gate 	 */
5830Sstevel@tonic-gate 	size = get_cached_n_to_m_file(file_name, &n_to_m_cache);
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 	if (size == ERROR)
5860Sstevel@tonic-gate 		return (ERROR);
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 	/* search with driver name as key */
5890Sstevel@tonic-gate 	if (*major_no == UNIQUE) {
5900Sstevel@tonic-gate 		for (maj = 0; maj < size; maj++) {
5910Sstevel@tonic-gate 			if ((n_to_m_cache[maj] != NULL) &&
5920Sstevel@tonic-gate 			    (strcmp(driver_name, n_to_m_cache[maj]) == 0)) {
5930Sstevel@tonic-gate 				*major_no = maj;
5940Sstevel@tonic-gate 				break;
5950Sstevel@tonic-gate 			}
5960Sstevel@tonic-gate 		}
5970Sstevel@tonic-gate 		if (maj >= size)
5980Sstevel@tonic-gate 			ret = UNIQUE;
5990Sstevel@tonic-gate 	/* search with major number as key */
6000Sstevel@tonic-gate 	} else {
6010Sstevel@tonic-gate 		/*
6020Sstevel@tonic-gate 		 * Bugid 1254588, drvconfig dump core after loading driver
6030Sstevel@tonic-gate 		 * with major number bigger than entries defined in
6040Sstevel@tonic-gate 		 * /etc/name_to_major.
6050Sstevel@tonic-gate 		 */
6060Sstevel@tonic-gate 		if (*major_no >= size)
6070Sstevel@tonic-gate 			return (UNIQUE);
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 		if (n_to_m_cache[*major_no] != NULL) {
6100Sstevel@tonic-gate 			(void) strcpy(driver_name, n_to_m_cache[*major_no]);
6110Sstevel@tonic-gate 		} else
6120Sstevel@tonic-gate 			ret = UNIQUE;
6130Sstevel@tonic-gate 	}
6140Sstevel@tonic-gate 	return (ret);
6150Sstevel@tonic-gate }
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate /*
6180Sstevel@tonic-gate  * given pointer to member n in space separated list, return pointer
6190Sstevel@tonic-gate  * to member n+1, return member n
6200Sstevel@tonic-gate  */
6210Sstevel@tonic-gate char *
6220Sstevel@tonic-gate get_entry(
6230Sstevel@tonic-gate 	char *prev_member,
6240Sstevel@tonic-gate 	char *current_entry,
6250Sstevel@tonic-gate 	char separator)
6260Sstevel@tonic-gate {
6270Sstevel@tonic-gate 	char *ptr;
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	ptr = prev_member;
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 	/* skip white space */
6320Sstevel@tonic-gate 	while (*ptr == '\t' || *ptr == ' ')
6330Sstevel@tonic-gate 		ptr++;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	/* read thru the current entry */
6360Sstevel@tonic-gate 	while (*ptr != separator && *ptr != '\0') {
6370Sstevel@tonic-gate 		*current_entry++ = *ptr++;
6380Sstevel@tonic-gate 	}
6390Sstevel@tonic-gate 	*current_entry = '\0';
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	if ((separator == ',') && (*ptr == separator))
6420Sstevel@tonic-gate 		ptr++;	/* skip over comma */
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	/* skip white space */
6450Sstevel@tonic-gate 	while (*ptr == '\t' || *ptr == ' ') {
6460Sstevel@tonic-gate 		ptr++;
6470Sstevel@tonic-gate 	}
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	return (ptr);
6500Sstevel@tonic-gate }
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate void
6530Sstevel@tonic-gate enter_lock(void)
6540Sstevel@tonic-gate {
655*3442Svikram 	struct flock lock;
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	/*
6580Sstevel@tonic-gate 	 * attempt to create the lock file
6590Sstevel@tonic-gate 	 */
660*3442Svikram 	add_rem_lock_fd = open(add_rem_lock, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
661*3442Svikram 	if (add_rem_lock_fd < 0) {
662*3442Svikram 		(void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
663*3442Svikram 		    add_rem_lock, strerror(errno));
664*3442Svikram 		exit(1);
665*3442Svikram 	}
666*3442Svikram 
667*3442Svikram 	lock.l_type = F_WRLCK;
668*3442Svikram 	lock.l_whence = SEEK_SET;
669*3442Svikram 	lock.l_start = 0;
670*3442Svikram 	lock.l_len = 0;
671*3442Svikram 
672*3442Svikram 	/* Try for the lock but don't wait. */
673*3442Svikram 	if (fcntl(add_rem_lock_fd, F_SETLK, &lock) == -1) {
674*3442Svikram 		if (errno == EACCES || errno == EAGAIN) {
6750Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_PROG_IN_USE));
6760Sstevel@tonic-gate 		} else {
677*3442Svikram 			(void) fprintf(stderr, gettext(ERR_LOCK),
678*3442Svikram 			    add_rem_lock, strerror(errno));
6790Sstevel@tonic-gate 		}
6800Sstevel@tonic-gate 		exit(1);
6810Sstevel@tonic-gate 	}
6820Sstevel@tonic-gate }
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate void
6850Sstevel@tonic-gate err_exit(void)
6860Sstevel@tonic-gate {
6870Sstevel@tonic-gate 	/* release memory allocated for moddir */
6880Sstevel@tonic-gate 	cleanup_moddir();
6890Sstevel@tonic-gate 	/* remove add_drv/rem_drv lock */
6900Sstevel@tonic-gate 	exit_unlock();
6910Sstevel@tonic-gate 	exit(1);
6920Sstevel@tonic-gate }
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate void
6950Sstevel@tonic-gate cleanup_moddir(void)
6960Sstevel@tonic-gate {
6970Sstevel@tonic-gate 	struct drvmod_dir *walk_ptr;
6980Sstevel@tonic-gate 	struct drvmod_dir *free_ptr = moddir;
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 	while (free_ptr != NULL) {
7010Sstevel@tonic-gate 		walk_ptr = free_ptr->next;
7020Sstevel@tonic-gate 		free(free_ptr);
7030Sstevel@tonic-gate 		free_ptr = walk_ptr;
7040Sstevel@tonic-gate 	}
7050Sstevel@tonic-gate }
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate void
7080Sstevel@tonic-gate exit_unlock(void)
7090Sstevel@tonic-gate {
710*3442Svikram 	struct flock unlock;
711*3442Svikram 
712*3442Svikram 	if (add_rem_lock_fd < 0)
713*3442Svikram 		return;
7140Sstevel@tonic-gate 
715*3442Svikram 	unlock.l_type = F_UNLCK;
716*3442Svikram 	unlock.l_whence = SEEK_SET;
717*3442Svikram 	unlock.l_start = 0;
718*3442Svikram 	unlock.l_len = 0;
719*3442Svikram 
720*3442Svikram 	if (fcntl(add_rem_lock_fd, F_SETLK, &unlock) == -1) {
721*3442Svikram 		(void) fprintf(stderr, gettext(ERR_UNLOCK),
722*3442Svikram 		    add_rem_lock, strerror(errno));
723*3442Svikram 	} else {
724*3442Svikram 		(void) close(add_rem_lock_fd);
725*3442Svikram 		add_rem_lock_fd = -1;
7260Sstevel@tonic-gate 	}
7270Sstevel@tonic-gate }
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate /*
7300Sstevel@tonic-gate  * error adding driver; need to back out any changes to files.
7310Sstevel@tonic-gate  * check flag to see which files need entries removed
7320Sstevel@tonic-gate  * entry removal based on driver name
7330Sstevel@tonic-gate  */
7340Sstevel@tonic-gate void
7350Sstevel@tonic-gate remove_entry(
7360Sstevel@tonic-gate 	int c_flag,
7370Sstevel@tonic-gate 	char *driver_name)
7380Sstevel@tonic-gate {
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	if (c_flag & CLEAN_NAM_MAJ) {
7410Sstevel@tonic-gate 		if (delete_entry(name_to_major, driver_name, " ",
7420Sstevel@tonic-gate 		    NULL) == ERROR) {
7430Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_CLEAN),
7440Sstevel@tonic-gate 			    name_to_major, driver_name);
7450Sstevel@tonic-gate 		}
7460Sstevel@tonic-gate 	}
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 	if (c_flag & CLEAN_DRV_ALIAS) {
7490Sstevel@tonic-gate 		if (delete_entry(driver_aliases, driver_name, " ",
7500Sstevel@tonic-gate 		    NULL) == ERROR) {
7510Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
7520Sstevel@tonic-gate 			    driver_name, driver_aliases);
7530Sstevel@tonic-gate 		}
7540Sstevel@tonic-gate 	}
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate 	if (c_flag & CLEAN_DRV_CLASSES) {
7570Sstevel@tonic-gate 		if (delete_entry(driver_classes, driver_name, "\t", NULL) ==
7580Sstevel@tonic-gate 		    ERROR) {
7590Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
7600Sstevel@tonic-gate 			    driver_name, driver_classes);
7610Sstevel@tonic-gate 		}
7620Sstevel@tonic-gate 	}
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 	if (c_flag & CLEAN_MINOR_PERM) {
7650Sstevel@tonic-gate 		if (delete_entry(minor_perm, driver_name, ":", NULL) == ERROR) {
7660Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
7670Sstevel@tonic-gate 			    driver_name, minor_perm);
7680Sstevel@tonic-gate 		}
7690Sstevel@tonic-gate 	}
7700Sstevel@tonic-gate 	/*
7710Sstevel@tonic-gate 	 * There's no point in removing entries from files that don't
7720Sstevel@tonic-gate 	 * exist.  Prevent error messages by checking for file existence
7730Sstevel@tonic-gate 	 * first.
7740Sstevel@tonic-gate 	 */
7750Sstevel@tonic-gate 	if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
7760Sstevel@tonic-gate 	    access(device_policy, F_OK) == 0) {
7770Sstevel@tonic-gate 		if (delete_plcy_entry(device_policy, driver_name) == ERROR) {
7780Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
7790Sstevel@tonic-gate 				driver_name, device_policy);
7800Sstevel@tonic-gate 		}
7810Sstevel@tonic-gate 	}
7820Sstevel@tonic-gate 	if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
7830Sstevel@tonic-gate 	    access(extra_privs, F_OK) == 0) {
7840Sstevel@tonic-gate 		if (delete_entry(extra_privs, driver_name, ":", NULL) ==
7850Sstevel@tonic-gate 		    ERROR) {
7860Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
7870Sstevel@tonic-gate 				driver_name, extra_privs);
7880Sstevel@tonic-gate 		}
7890Sstevel@tonic-gate 	}
7900Sstevel@tonic-gate }
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate int
7930Sstevel@tonic-gate check_perms_aliases(
7940Sstevel@tonic-gate 	int m_flag,
7950Sstevel@tonic-gate 	int i_flag)
7960Sstevel@tonic-gate {
7970Sstevel@tonic-gate 	/*
7980Sstevel@tonic-gate 	 * If neither i_flag nor m_flag are specified no need to check the
7990Sstevel@tonic-gate 	 * files for access permissions
8000Sstevel@tonic-gate 	 */
8010Sstevel@tonic-gate 	if (!m_flag && !i_flag)
8020Sstevel@tonic-gate 		return (NOERR);
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 	/* check minor_perm file : exits and is writable */
8050Sstevel@tonic-gate 	if (m_flag) {
8060Sstevel@tonic-gate 		if (access(minor_perm, R_OK | W_OK)) {
8070Sstevel@tonic-gate 			perror(NULL);
8080Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
8090Sstevel@tonic-gate 			    minor_perm);
8100Sstevel@tonic-gate 			return (ERROR);
8110Sstevel@tonic-gate 		}
8120Sstevel@tonic-gate 	}
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	/* check driver_aliases file : exits and is writable */
8150Sstevel@tonic-gate 	if (i_flag) {
8160Sstevel@tonic-gate 		if (access(driver_aliases, R_OK | W_OK)) {
8170Sstevel@tonic-gate 			perror(NULL);
8180Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
8190Sstevel@tonic-gate 			    driver_aliases);
8200Sstevel@tonic-gate 			return (ERROR);
8210Sstevel@tonic-gate 		}
8220Sstevel@tonic-gate 	}
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	return (NOERR);
8250Sstevel@tonic-gate }
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate int
8290Sstevel@tonic-gate check_name_to_major(int mode)
8300Sstevel@tonic-gate {
8310Sstevel@tonic-gate 	/* check name_to_major file : exists and is writable */
8320Sstevel@tonic-gate 	if (access(name_to_major, mode)) {
8330Sstevel@tonic-gate 		perror(NULL);
8340Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
8350Sstevel@tonic-gate 		    name_to_major);
8360Sstevel@tonic-gate 		return (ERROR);
8370Sstevel@tonic-gate 	}
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	return (NOERR);
8400Sstevel@tonic-gate }
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate /*
8440Sstevel@tonic-gate  * All this stuff is to support a server installing
8450Sstevel@tonic-gate  * drivers on diskless clients.  When on the server
8460Sstevel@tonic-gate  * need to prepend the basedir
8470Sstevel@tonic-gate  */
8480Sstevel@tonic-gate int
8490Sstevel@tonic-gate build_filenames(char *basedir)
8500Sstevel@tonic-gate {
8510Sstevel@tonic-gate 	int len;
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 	if (basedir == NULL) {
8540Sstevel@tonic-gate 		driver_aliases = DRIVER_ALIAS;
8550Sstevel@tonic-gate 		driver_classes = DRIVER_CLASSES;
8560Sstevel@tonic-gate 		minor_perm = MINOR_PERM;
8570Sstevel@tonic-gate 		name_to_major = NAM_TO_MAJ;
8580Sstevel@tonic-gate 		rem_name_to_major = REM_NAM_TO_MAJ;
8590Sstevel@tonic-gate 		add_rem_lock = ADD_REM_LOCK;
8600Sstevel@tonic-gate 		tmphold = TMPHOLD;
8610Sstevel@tonic-gate 		devfs_root = DEVFS_ROOT;
8620Sstevel@tonic-gate 		device_policy = DEV_POLICY;
8630Sstevel@tonic-gate 		extra_privs = EXTRA_PRIVS;
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 	} else {
8660Sstevel@tonic-gate 		len = strlen(basedir);
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 		driver_aliases = malloc(len + sizeof (DRIVER_ALIAS));
8690Sstevel@tonic-gate 		driver_classes = malloc(len + sizeof (DRIVER_CLASSES));
8700Sstevel@tonic-gate 		minor_perm = malloc(len + sizeof (MINOR_PERM));
8710Sstevel@tonic-gate 		name_to_major = malloc(len + sizeof (NAM_TO_MAJ));
8720Sstevel@tonic-gate 		rem_name_to_major = malloc(len + sizeof (REM_NAM_TO_MAJ));
8730Sstevel@tonic-gate 		add_rem_lock = malloc(len + sizeof (ADD_REM_LOCK));
8740Sstevel@tonic-gate 		tmphold = malloc(len + sizeof (TMPHOLD));
8750Sstevel@tonic-gate 		devfs_root = malloc(len + sizeof (DEVFS_ROOT));
8760Sstevel@tonic-gate 		device_policy = malloc(len + sizeof (DEV_POLICY));
8770Sstevel@tonic-gate 		extra_privs = malloc(len + sizeof (EXTRA_PRIVS));
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 		if ((driver_aliases == NULL) ||
8810Sstevel@tonic-gate 		    (driver_classes == NULL) ||
8820Sstevel@tonic-gate 		    (minor_perm == NULL) ||
8830Sstevel@tonic-gate 		    (name_to_major == NULL) ||
8840Sstevel@tonic-gate 		    (rem_name_to_major == NULL) ||
8850Sstevel@tonic-gate 		    (add_rem_lock == NULL) ||
8860Sstevel@tonic-gate 		    (tmphold == NULL) ||
8870Sstevel@tonic-gate 		    (devfs_root == NULL) ||
8880Sstevel@tonic-gate 		    (device_policy == NULL) ||
8890Sstevel@tonic-gate 		    (extra_privs == NULL)) {
8900Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
8910Sstevel@tonic-gate 			return (ERROR);
8920Sstevel@tonic-gate 		}
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 		(void) sprintf(driver_aliases, "%s%s", basedir, DRIVER_ALIAS);
8950Sstevel@tonic-gate 		(void) sprintf(driver_classes, "%s%s", basedir, DRIVER_CLASSES);
8960Sstevel@tonic-gate 		(void) sprintf(minor_perm, "%s%s", basedir, MINOR_PERM);
8970Sstevel@tonic-gate 		(void) sprintf(name_to_major, "%s%s", basedir, NAM_TO_MAJ);
8980Sstevel@tonic-gate 		(void) sprintf(rem_name_to_major, "%s%s", basedir,
8990Sstevel@tonic-gate 				REM_NAM_TO_MAJ);
9000Sstevel@tonic-gate 		(void) sprintf(add_rem_lock, "%s%s", basedir, ADD_REM_LOCK);
9010Sstevel@tonic-gate 		(void) sprintf(tmphold, "%s%s", basedir, TMPHOLD);
9020Sstevel@tonic-gate 		(void) sprintf(devfs_root, "%s%s", basedir, DEVFS_ROOT);
9030Sstevel@tonic-gate 		(void) sprintf(device_policy, "%s%s", basedir, DEV_POLICY);
9040Sstevel@tonic-gate 		(void) sprintf(extra_privs, "%s%s", basedir, EXTRA_PRIVS);
9050Sstevel@tonic-gate 	}
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 	return (NOERR);
9080Sstevel@tonic-gate }
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate static int
9110Sstevel@tonic-gate exec_command(char *path, char *cmdline[MAX_CMD_LINE])
9120Sstevel@tonic-gate {
9130Sstevel@tonic-gate 	pid_t pid;
9140Sstevel@tonic-gate 	uint_t stat_loc;
9150Sstevel@tonic-gate 	int waitstat;
9160Sstevel@tonic-gate 	int exit_status;
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 	/* child */
9190Sstevel@tonic-gate 	if ((pid = fork()) == 0) {
9200Sstevel@tonic-gate 		(void) execv(path, cmdline);
9210Sstevel@tonic-gate 		perror(NULL);
9220Sstevel@tonic-gate 		return (ERROR);
9230Sstevel@tonic-gate 	} else if (pid == -1) {
9240Sstevel@tonic-gate 		/* fork failed */
9250Sstevel@tonic-gate 		perror(NULL);
9260Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_FORK_FAIL), cmdline);
9270Sstevel@tonic-gate 		return (ERROR);
9280Sstevel@tonic-gate 	} else {
9290Sstevel@tonic-gate 		/* parent */
9300Sstevel@tonic-gate 		do {
9310Sstevel@tonic-gate 			waitstat = waitpid(pid, (int *)&stat_loc, 0);
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate 		} while ((!WIFEXITED(stat_loc) &&
9340Sstevel@tonic-gate 			!WIFSIGNALED(stat_loc)) || (waitstat == 0));
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate 		exit_status = WEXITSTATUS(stat_loc);
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 		return (exit_status);
9390Sstevel@tonic-gate 	}
9400Sstevel@tonic-gate }
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate /*
9430Sstevel@tonic-gate  * check that major_num doesn't exceed maximum on this machine
9440Sstevel@tonic-gate  * do this here to support add_drv on server for diskless clients
9450Sstevel@tonic-gate  */
9460Sstevel@tonic-gate int
9470Sstevel@tonic-gate config_driver(
9480Sstevel@tonic-gate 	char *driver_name,
9490Sstevel@tonic-gate 	major_t major_num,
9500Sstevel@tonic-gate 	char *aliases,
9510Sstevel@tonic-gate 	char *classes,
9520Sstevel@tonic-gate 	int cleanup_flag,
9530Sstevel@tonic-gate 	int verbose_flag)
9540Sstevel@tonic-gate {
9550Sstevel@tonic-gate 	int max_dev;
9560Sstevel@tonic-gate 	int n = 0;
9570Sstevel@tonic-gate 	char *cmdline[MAX_CMD_LINE];
9580Sstevel@tonic-gate 	char maj_num[128];
9590Sstevel@tonic-gate 	char *previous;
9600Sstevel@tonic-gate 	char *current;
9610Sstevel@tonic-gate 	int exec_status;
9620Sstevel@tonic-gate 	int len;
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate 	if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
9650Sstevel@tonic-gate 		perror(NULL);
9660Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
9670Sstevel@tonic-gate 		return (ERROR);
9680Sstevel@tonic-gate 	}
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	if (major_num >= max_dev) {
9710Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS),
9720Sstevel@tonic-gate 		    major_num, max_dev);
9730Sstevel@tonic-gate 		return (ERROR);
9740Sstevel@tonic-gate 	}
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate 	/* bind major number and driver name */
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	/* build command line */
9790Sstevel@tonic-gate 	cmdline[n++] = DRVCONFIG;
9800Sstevel@tonic-gate 	if (verbose_flag) {
9810Sstevel@tonic-gate 		cmdline[n++] = "-v";
9820Sstevel@tonic-gate 	}
9830Sstevel@tonic-gate 	cmdline[n++] = "-b";
9840Sstevel@tonic-gate 	if (classes) {
9850Sstevel@tonic-gate 		cmdline[n++] = "-c";
9860Sstevel@tonic-gate 		cmdline[n++] = classes;
9870Sstevel@tonic-gate 	}
9880Sstevel@tonic-gate 	cmdline[n++] = "-i";
9890Sstevel@tonic-gate 	cmdline[n++] = driver_name;
9900Sstevel@tonic-gate 	cmdline[n++] = "-m";
9910Sstevel@tonic-gate 	(void) sprintf(maj_num, "%lu", major_num);
9920Sstevel@tonic-gate 	cmdline[n++] = maj_num;
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 	if (aliases != NULL) {
9950Sstevel@tonic-gate 		len = strlen(aliases);
9960Sstevel@tonic-gate 		previous = aliases;
9970Sstevel@tonic-gate 		do {
9980Sstevel@tonic-gate 			cmdline[n++] = "-a";
9990Sstevel@tonic-gate 			cmdline[n] = calloc(len + 1, 1);
10000Sstevel@tonic-gate 			if (cmdline[n] == NULL) {
10010Sstevel@tonic-gate 				(void) fprintf(stderr,
10020Sstevel@tonic-gate 				    gettext(ERR_NO_MEM));
10030Sstevel@tonic-gate 				return (ERROR);
10040Sstevel@tonic-gate 			}
10050Sstevel@tonic-gate 			current = get_entry(previous,
10060Sstevel@tonic-gate 			    cmdline[n++], ' ');
10070Sstevel@tonic-gate 			previous = current;
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 		} while (*current != '\0');
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 	}
10120Sstevel@tonic-gate 	cmdline[n] = (char *)0;
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	exec_status = exec_command(DRVCONFIG_PATH, cmdline);
10150Sstevel@tonic-gate 
10160Sstevel@tonic-gate 	if (exec_status == NOERR)
10170Sstevel@tonic-gate 		return (NOERR);
10180Sstevel@tonic-gate 	perror(NULL);
10190Sstevel@tonic-gate 	remove_entry(cleanup_flag, driver_name);
10200Sstevel@tonic-gate 	return (ERROR);
10210Sstevel@tonic-gate }
10220Sstevel@tonic-gate 
10230Sstevel@tonic-gate void
10240Sstevel@tonic-gate load_driver(char *driver_name, int verbose_flag)
10250Sstevel@tonic-gate {
10260Sstevel@tonic-gate 	int n = 0;
10270Sstevel@tonic-gate 	char *cmdline[MAX_CMD_LINE];
10280Sstevel@tonic-gate 	int exec_status;
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 	/* build command line */
10310Sstevel@tonic-gate 	cmdline[n++] = DEVFSADM;
10320Sstevel@tonic-gate 	if (verbose_flag) {
10330Sstevel@tonic-gate 		cmdline[n++] = "-v";
10340Sstevel@tonic-gate 	}
10350Sstevel@tonic-gate 	cmdline[n++] = "-i";
10360Sstevel@tonic-gate 	cmdline[n++] = driver_name;
10370Sstevel@tonic-gate 	cmdline[n] = (char *)0;
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 	exec_status = exec_command(DEVFSADM_PATH, cmdline);
10400Sstevel@tonic-gate 
10410Sstevel@tonic-gate 	if (exec_status != NOERR) {
10420Sstevel@tonic-gate 		/* no clean : name and major number are bound */
10430Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CONFIG),
10440Sstevel@tonic-gate 			driver_name);
10450Sstevel@tonic-gate 	}
10460Sstevel@tonic-gate }
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate void
10490Sstevel@tonic-gate get_modid(char *driver_name, int *mod)
10500Sstevel@tonic-gate {
10510Sstevel@tonic-gate 	struct modinfo	modinfo;
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 	modinfo.mi_id = -1;
10540Sstevel@tonic-gate 	modinfo.mi_info = MI_INFO_ALL;
10550Sstevel@tonic-gate 	do {
10560Sstevel@tonic-gate 		/*
10570Sstevel@tonic-gate 		 * If we are at the end of the list of loaded modules
10580Sstevel@tonic-gate 		 * then set *mod = -1 and return
10590Sstevel@tonic-gate 		 */
10600Sstevel@tonic-gate 		if (modctl(MODINFO, 0, &modinfo) < 0) {
10610Sstevel@tonic-gate 			*mod = -1;
10620Sstevel@tonic-gate 			return;
10630Sstevel@tonic-gate 		}
10640Sstevel@tonic-gate 
10650Sstevel@tonic-gate 		*mod = modinfo.mi_id;
10660Sstevel@tonic-gate 	} while (strcmp(driver_name, modinfo.mi_name) != 0);
10670Sstevel@tonic-gate }
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate int
10700Sstevel@tonic-gate create_reconfig(char *basedir)
10710Sstevel@tonic-gate {
10720Sstevel@tonic-gate 	char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1];
10730Sstevel@tonic-gate 	FILE *reconfig_fp;
10740Sstevel@tonic-gate 
10750Sstevel@tonic-gate 	if (basedir != NULL) {
10760Sstevel@tonic-gate 		(void) strcpy(reconfig_file, basedir);
10770Sstevel@tonic-gate 		(void) strcat(reconfig_file, RECONFIGURE);
10780Sstevel@tonic-gate 	} else {
10790Sstevel@tonic-gate 		(void) strcpy(reconfig_file, RECONFIGURE);
10800Sstevel@tonic-gate 	}
10810Sstevel@tonic-gate 	if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL)
10820Sstevel@tonic-gate 		return (ERROR);
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate 	(void) fclose(reconfig_fp);
10850Sstevel@tonic-gate 	return (NOERR);
10860Sstevel@tonic-gate }
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate /*
10900Sstevel@tonic-gate  * update_minor_entry:
10910Sstevel@tonic-gate  *	open file
10920Sstevel@tonic-gate  *	for each entry in list
10930Sstevel@tonic-gate  *		where list entries are separated by <list_separator>
10940Sstevel@tonic-gate  * 		modify entry : driver_name <entry_separator> entry
10950Sstevel@tonic-gate  *	close file
10960Sstevel@tonic-gate  *
10970Sstevel@tonic-gate  *	return error/noerr
10980Sstevel@tonic-gate  */
10990Sstevel@tonic-gate int
11000Sstevel@tonic-gate update_minor_entry(char *driver_name, char *perm_list)
11010Sstevel@tonic-gate {
11020Sstevel@tonic-gate 	FILE *fp;
11030Sstevel@tonic-gate 	FILE *newfp;
11040Sstevel@tonic-gate 	struct group *sysgrp;
11050Sstevel@tonic-gate 	int match = 0;
11062805Seota 	char line[MAX_DBFILE_ENTRY], *cp, *dup;
11072805Seota 	char drv[FILENAME_MAX + 1], *drv_minor;
11080Sstevel@tonic-gate 	char minor[FILENAME_MAX + 1], perm[OPT_LEN + 1];
11090Sstevel@tonic-gate 	char own[OPT_LEN + 1], grp[OPT_LEN + 1];
11100Sstevel@tonic-gate 	int status = NOERR, i;
11110Sstevel@tonic-gate 	char *newfile, *tptr;
11120Sstevel@tonic-gate 	extern void bzero();
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 	if ((fp = fopen(minor_perm, "r")) == NULL) {
11150Sstevel@tonic-gate 		perror(NULL);
11160Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
11170Sstevel@tonic-gate 		    minor_perm);
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 		return (ERROR);
11200Sstevel@tonic-gate 	}
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	/*
11230Sstevel@tonic-gate 	 * Build filename for temporary file
11240Sstevel@tonic-gate 	 */
11250Sstevel@tonic-gate 	if ((tptr = calloc(strlen(minor_perm) + strlen(XEND) + 1, 1)) == NULL) {
11260Sstevel@tonic-gate 		perror(NULL);
11270Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
11280Sstevel@tonic-gate 	}
11290Sstevel@tonic-gate 	(void) strcpy(tptr, minor_perm);
11300Sstevel@tonic-gate 	(void) strcat(tptr, XEND);
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate 	/*
11330Sstevel@tonic-gate 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
11340Sstevel@tonic-gate 	 * assume a gid of "sys" but we can't undo the damage on already
11350Sstevel@tonic-gate 	 * installed systems unless we force the issue.
11360Sstevel@tonic-gate 	 */
11370Sstevel@tonic-gate 	if ((sysgrp = getgrnam("sys")) != NULL) {
11380Sstevel@tonic-gate 		(void) setgid(sysgrp->gr_gid);
11390Sstevel@tonic-gate 	}
11400Sstevel@tonic-gate 
11410Sstevel@tonic-gate 	newfile = mktemp(tptr);
11420Sstevel@tonic-gate 	if ((newfp = fopen(newfile, "w")) == NULL) {
11430Sstevel@tonic-gate 		perror(NULL);
11440Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
11450Sstevel@tonic-gate 		    newfile);
11460Sstevel@tonic-gate 		return (ERROR);
11470Sstevel@tonic-gate 	}
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate 	if (sscanf(perm_list, "%s%s%s%s", minor, perm, own, grp) != 4) {
11500Sstevel@tonic-gate 		status = ERROR;
11510Sstevel@tonic-gate 	}
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
11542805Seota 		/* copy the whole line into dup */
11552805Seota 		if ((dup = strdup(line)) == NULL) {
11562805Seota 			perror(NULL);
11572805Seota 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
11582805Seota 			status = ERROR;
11592805Seota 			break;
11602805Seota 		}
11612805Seota 		/* cut off comments starting with '#' */
11622805Seota 		if ((cp = strchr(dup, '#')) != NULL)
11632805Seota 			*cp = '\0';
11642805Seota 		/* ignore comment or blank lines */
11652805Seota 		if (is_blank(dup)) {
11662805Seota 			if (fputs(line, newfp) == EOF) {
11670Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_UPDATE),
11680Sstevel@tonic-gate 				    minor_perm);
11690Sstevel@tonic-gate 				status = ERROR;
11700Sstevel@tonic-gate 			}
11712805Seota 			free(dup);
11720Sstevel@tonic-gate 			continue;
11730Sstevel@tonic-gate 		}
11740Sstevel@tonic-gate 
11752805Seota 		/* get the driver name */
11762805Seota 		if (sscanf(dup, "%s", drv) != 1) {
11772805Seota 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
11782805Seota 			    minor_perm, line);
11792805Seota 			status = ERROR;
11802805Seota 			free(dup);
11812805Seota 			break;
11822805Seota 		}
11832805Seota 
11842805Seota 		/*
11852805Seota 		 * get the minor name; place the NULL character at the
11862805Seota 		 * end of the driver name, then make the drv_minor
11872805Seota 		 * point to the first character of the minor name.
11882805Seota 		 * the line missing ':' must be treated as a broken one.
11892805Seota 		 */
11902805Seota 		i = strcspn(drv, ":");
11912805Seota 		if (i == strlen(drv)) {
11920Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
11930Sstevel@tonic-gate 			    minor_perm, line);
11940Sstevel@tonic-gate 			status = ERROR;
11952805Seota 			free(dup);
11962805Seota 			break;
11970Sstevel@tonic-gate 		}
11982805Seota 		drv[i] =  '\0';
11992805Seota 		drv_minor = &drv[strlen(drv) + 1];
12000Sstevel@tonic-gate 
12012805Seota 		/*
12022805Seota 		 * compare both of the driver name and the minor name.
12032805Seota 		 * then the new line should be written to the file if
12042805Seota 		 * both of them match
12052805Seota 		 */
12060Sstevel@tonic-gate 		if ((strcmp(drv, driver_name) == 0) &&
12070Sstevel@tonic-gate 		    (strcmp(minor, drv_minor) == 0)) {
12082805Seota 			/* if it has a comment, keep it */
12092805Seota 			if (cp != NULL) {
12102805Seota 				cp++; /* skip a terminator */
12112805Seota 				(void) sprintf(line, "%s:%s %s %s %s #%s\n",
12122805Seota 				    drv, minor, perm, own, grp, cp);
12132805Seota 			} else {
12142805Seota 				(void) sprintf(line, "%s:%s %s %s %s\n",
12152805Seota 				    drv, minor, perm, own, grp);
12162805Seota 			}
12170Sstevel@tonic-gate 			match = 1;
12180Sstevel@tonic-gate 		}
12192805Seota 		free(dup);
12200Sstevel@tonic-gate 
12212805Seota 		/* update the file */
12220Sstevel@tonic-gate 		if ((fputs(line, newfp)) == EOF) {
12230Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE),
12240Sstevel@tonic-gate 			    minor_perm);
12250Sstevel@tonic-gate 			status = ERROR;
12260Sstevel@tonic-gate 		}
12270Sstevel@tonic-gate 	}
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate 	if (!match) {
12300Sstevel@tonic-gate 		(void) bzero(line, sizeof (&line[0]));
12310Sstevel@tonic-gate 		(void) sprintf(line, "%s:%s %s %s %s\n",
12320Sstevel@tonic-gate 		    driver_name, minor, perm, own, grp);
12330Sstevel@tonic-gate 
12340Sstevel@tonic-gate 		/* add the new entry */
12350Sstevel@tonic-gate 		if ((fputs(line, newfp)) == EOF) {
12360Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
12370Sstevel@tonic-gate 			status = ERROR;
12380Sstevel@tonic-gate 		}
12390Sstevel@tonic-gate 	}
12400Sstevel@tonic-gate 
12410Sstevel@tonic-gate 	(void) fclose(fp);
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
12440Sstevel@tonic-gate 		status = ERROR;
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 	(void) fclose(newfp);
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate 	/*
12490Sstevel@tonic-gate 	 * if error, leave original file, delete new file
12500Sstevel@tonic-gate 	 * if noerr, replace original file with new file
12510Sstevel@tonic-gate 	 */
12520Sstevel@tonic-gate 	if (status == NOERR) {
12530Sstevel@tonic-gate 		if (rename(minor_perm, tmphold) == -1) {
12540Sstevel@tonic-gate 			perror(NULL);
12550Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
12560Sstevel@tonic-gate 			(void) unlink(newfile);
12570Sstevel@tonic-gate 			return (ERROR);
12580Sstevel@tonic-gate 		} else if (rename(newfile, minor_perm) == -1) {
12590Sstevel@tonic-gate 			perror(NULL);
12600Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
12610Sstevel@tonic-gate 			(void) unlink(minor_perm);
12620Sstevel@tonic-gate 			(void) unlink(newfile);
12630Sstevel@tonic-gate 			if (link(tmphold, minor_perm) == -1) {
12640Sstevel@tonic-gate 				perror(NULL);
12650Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
12660Sstevel@tonic-gate 				    minor_perm, tmphold);
12670Sstevel@tonic-gate 			}
12680Sstevel@tonic-gate 			return (ERROR);
12690Sstevel@tonic-gate 		}
12700Sstevel@tonic-gate 		(void) unlink(tmphold);
12710Sstevel@tonic-gate 	} else {
12720Sstevel@tonic-gate 		/*
12730Sstevel@tonic-gate 		 * since there's an error, leave file alone; remove
12740Sstevel@tonic-gate 		 * new file
12750Sstevel@tonic-gate 		 */
12760Sstevel@tonic-gate 		if (unlink(newfile) == -1) {
12770Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
12780Sstevel@tonic-gate 		}
12790Sstevel@tonic-gate 		return (ERROR);
12800Sstevel@tonic-gate 	}
12810Sstevel@tonic-gate 
12820Sstevel@tonic-gate 	return (NOERR);
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate }
12850Sstevel@tonic-gate 
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate /*
12880Sstevel@tonic-gate  * list_entry:
12890Sstevel@tonic-gate  *	open file
12900Sstevel@tonic-gate  *	read thru file, listing all entries if first entry = driver_name
12910Sstevel@tonic-gate  *	close
12920Sstevel@tonic-gate  */
12930Sstevel@tonic-gate void
12940Sstevel@tonic-gate list_entry(
12950Sstevel@tonic-gate 	char *oldfile,
12960Sstevel@tonic-gate 	char *driver_name,
12970Sstevel@tonic-gate 	char *marker)
12980Sstevel@tonic-gate {
12990Sstevel@tonic-gate 	FILE	*fp;
13000Sstevel@tonic-gate 	int	i;
13012805Seota 	char	line[MAX_DBFILE_ENTRY], *cp;
13020Sstevel@tonic-gate 	char	drv[FILENAME_MAX + 1];
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate 	if ((fp = fopen(oldfile, "r")) == NULL) {
13050Sstevel@tonic-gate 		perror(NULL);
13060Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
13070Sstevel@tonic-gate 
13080Sstevel@tonic-gate 		return;
13090Sstevel@tonic-gate 	}
13100Sstevel@tonic-gate 
13110Sstevel@tonic-gate 	while (fgets(line, sizeof (line), fp) != NULL) {
13122805Seota 		/* cut off comments starting with '#' */
13132805Seota 		if ((cp = strchr(line, '#')) != NULL)
13142805Seota 			*cp = '\0';
13152805Seota 		/* ignore comment or blank lines */
13162805Seota 		if (is_blank(line))
13170Sstevel@tonic-gate 			continue;
13182805Seota 		/* sanity-check */
13190Sstevel@tonic-gate 		if (sscanf(line, "%s", drv) != 1) {
13200Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
13210Sstevel@tonic-gate 			    oldfile, line);
13220Sstevel@tonic-gate 		}
13230Sstevel@tonic-gate 
13240Sstevel@tonic-gate 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
13250Sstevel@tonic-gate 			drv[i] =  '\0';
13260Sstevel@tonic-gate 		}
13270Sstevel@tonic-gate 
13280Sstevel@tonic-gate 		if (strcmp(driver_name, drv) == 0) {
13290Sstevel@tonic-gate 			(void) fprintf(stdout, "%s", line);
13300Sstevel@tonic-gate 		}
13310Sstevel@tonic-gate 	}
13320Sstevel@tonic-gate 
13330Sstevel@tonic-gate 	(void) fclose(fp);
13340Sstevel@tonic-gate }
13350Sstevel@tonic-gate 
13362805Seota static boolean_t
13372805Seota is_token(char *tok)
13382805Seota {
13392805Seota 	/*
13402805Seota 	 * Check the token here. According to IEEE1275 Open Firmware Boot
13412805Seota 	 * Standard, the name is composed of 1 to 31 letters,
13422805Seota 	 * digits and punctuation characters from the set ",._+-", and
13432805Seota 	 * uppercase and lowercase characters are considered distinct.
13442805Seota 	 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
13452805Seota 	 * However, since either the definition of driver or aliase names is
13462805Seota 	 * not known well, only '#' is avoided explicitly. (the kernel lexical
13472805Seota 	 * analyzer treats it as a start of a comment)
13482805Seota 	 */
13492805Seota 	for (/* nothing */; *tok != '\0'; tok++)
13502805Seota 		if (*tok == '#' || iscntrl(*tok))
13512805Seota 			return (B_FALSE);
13522805Seota 
13532805Seota 	return (B_TRUE);
13542805Seota }
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate /*
13570Sstevel@tonic-gate  * check each entry in perm_list for:
13580Sstevel@tonic-gate  *	4 arguments
13590Sstevel@tonic-gate  *	permission arg is in valid range
13600Sstevel@tonic-gate  * permlist entries separated by comma
13610Sstevel@tonic-gate  * return ERROR/NOERR
13620Sstevel@tonic-gate  */
13630Sstevel@tonic-gate int
13640Sstevel@tonic-gate check_perm_opts(char *perm_list)
13650Sstevel@tonic-gate {
13660Sstevel@tonic-gate 	char *current_head;
13670Sstevel@tonic-gate 	char *previous_head;
13680Sstevel@tonic-gate 	char *one_entry;
13690Sstevel@tonic-gate 	int i, len, scan_stat;
13700Sstevel@tonic-gate 	char minor[FILENAME_MAX + 1];
13710Sstevel@tonic-gate 	char perm[OPT_LEN + 1];
13720Sstevel@tonic-gate 	char own[OPT_LEN + 1];
13730Sstevel@tonic-gate 	char grp[OPT_LEN + 1];
13740Sstevel@tonic-gate 	char dumb[OPT_LEN + 1];
13750Sstevel@tonic-gate 	int status = NOERR;
13760Sstevel@tonic-gate 	int intperm;
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 	len = strlen(perm_list);
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 	if (len == 0) {
13810Sstevel@tonic-gate 		return (ERROR);
13820Sstevel@tonic-gate 	}
13830Sstevel@tonic-gate 
13840Sstevel@tonic-gate 	one_entry = calloc(len + 1, 1);
13850Sstevel@tonic-gate 	if (one_entry == NULL) {
13860Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
13870Sstevel@tonic-gate 		return (ERROR);
13880Sstevel@tonic-gate 	}
13890Sstevel@tonic-gate 
13900Sstevel@tonic-gate 	previous_head = perm_list;
13910Sstevel@tonic-gate 	current_head = perm_list;
13920Sstevel@tonic-gate 
13930Sstevel@tonic-gate 	while (*current_head != '\0') {
13940Sstevel@tonic-gate 
13950Sstevel@tonic-gate 		for (i = 0; i <= len; i++)
13960Sstevel@tonic-gate 			one_entry[i] = 0;
13970Sstevel@tonic-gate 
13980Sstevel@tonic-gate 		current_head = get_entry(previous_head, one_entry, ',');
13990Sstevel@tonic-gate 
14000Sstevel@tonic-gate 		previous_head = current_head;
14010Sstevel@tonic-gate 		scan_stat = sscanf(one_entry, "%s%s%s%s%s", minor, perm, own,
14020Sstevel@tonic-gate 		    grp, dumb);
14030Sstevel@tonic-gate 
14040Sstevel@tonic-gate 		if (scan_stat < 4) {
14050Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_MIS_TOK),
14060Sstevel@tonic-gate 			    "-m", one_entry);
14070Sstevel@tonic-gate 			status = ERROR;
14080Sstevel@tonic-gate 		}
14090Sstevel@tonic-gate 		if (scan_stat > 4) {
14100Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS),
14110Sstevel@tonic-gate 			    "-m", one_entry);
14120Sstevel@tonic-gate 			status = ERROR;
14130Sstevel@tonic-gate 		}
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate 		intperm = atoi(perm);
14160Sstevel@tonic-gate 		if (intperm < 0000 || intperm > 4777) {
14170Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_MODE), perm);
14180Sstevel@tonic-gate 			status = ERROR;
14190Sstevel@tonic-gate 		}
14200Sstevel@tonic-gate 	}
14210Sstevel@tonic-gate 
14220Sstevel@tonic-gate 	free(one_entry);
14230Sstevel@tonic-gate 	return (status);
14240Sstevel@tonic-gate }
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate /*
14280Sstevel@tonic-gate  * check each alias :
14290Sstevel@tonic-gate  *	alias list members separated by white space
14300Sstevel@tonic-gate  *	cannot exist as driver name in /etc/name_to_major
14310Sstevel@tonic-gate  *	cannot exist as driver or alias name in /etc/driver_aliases
14320Sstevel@tonic-gate  */
14330Sstevel@tonic-gate int
14340Sstevel@tonic-gate aliases_unique(char *aliases)
14350Sstevel@tonic-gate {
14360Sstevel@tonic-gate 	char *current_head;
14370Sstevel@tonic-gate 	char *previous_head;
14380Sstevel@tonic-gate 	char *one_entry;
14390Sstevel@tonic-gate 	int i, len;
14400Sstevel@tonic-gate 	int is_unique;
14410Sstevel@tonic-gate 
14420Sstevel@tonic-gate 	len = strlen(aliases);
14430Sstevel@tonic-gate 
14440Sstevel@tonic-gate 	one_entry = calloc(len + 1, 1);
14450Sstevel@tonic-gate 	if (one_entry == NULL) {
14460Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
14470Sstevel@tonic-gate 		return (ERROR);
14480Sstevel@tonic-gate 	}
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 	previous_head = aliases;
14510Sstevel@tonic-gate 
14520Sstevel@tonic-gate 	do {
14530Sstevel@tonic-gate 		for (i = 0; i <= len; i++)
14540Sstevel@tonic-gate 			one_entry[i] = 0;
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate 		current_head = get_entry(previous_head, one_entry, ' ');
14570Sstevel@tonic-gate 		previous_head = current_head;
14580Sstevel@tonic-gate 
14590Sstevel@tonic-gate 		if ((unique_driver_name(one_entry, name_to_major,
14600Sstevel@tonic-gate 		    &is_unique)) == ERROR) {
14610Sstevel@tonic-gate 			free(one_entry);
14620Sstevel@tonic-gate 			return (ERROR);
14630Sstevel@tonic-gate 		}
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 		if (is_unique != UNIQUE) {
14660Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ),
14670Sstevel@tonic-gate 			    one_entry);
14680Sstevel@tonic-gate 			free(one_entry);
14690Sstevel@tonic-gate 			return (ERROR);
14700Sstevel@tonic-gate 		}
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 		if (unique_drv_alias(one_entry) != NOERR) {
14730Sstevel@tonic-gate 			free(one_entry);
14740Sstevel@tonic-gate 			return (ERROR);
14750Sstevel@tonic-gate 		}
14760Sstevel@tonic-gate 
14772805Seota 		if (!is_token(one_entry)) {
14782805Seota 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
14792805Seota 			    "-i", one_entry);
14802805Seota 			free(one_entry);
14812805Seota 			return (ERROR);
14822805Seota 		}
14832805Seota 
14840Sstevel@tonic-gate 	} while (*current_head != '\0');
14850Sstevel@tonic-gate 
14860Sstevel@tonic-gate 	free(one_entry);
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate 	return (NOERR);
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate }
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 
14930Sstevel@tonic-gate int
14940Sstevel@tonic-gate update_driver_aliases(
14950Sstevel@tonic-gate 	char *driver_name,
14960Sstevel@tonic-gate 	char *aliases)
14970Sstevel@tonic-gate {
14980Sstevel@tonic-gate 	/* make call to update the aliases file */
14990Sstevel@tonic-gate 	return (append_to_file(driver_name, aliases, driver_aliases, ' ', " "));
15000Sstevel@tonic-gate 
15010Sstevel@tonic-gate }
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate int
15050Sstevel@tonic-gate unique_drv_alias(char *drv_alias)
15060Sstevel@tonic-gate {
15070Sstevel@tonic-gate 	FILE *fp;
15080Sstevel@tonic-gate 	char drv[FILENAME_MAX + 1];
15092805Seota 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
15100Sstevel@tonic-gate 	char alias[FILENAME_MAX + 1];
15110Sstevel@tonic-gate 	int status = NOERR;
15120Sstevel@tonic-gate 
15130Sstevel@tonic-gate 	fp = fopen(driver_aliases, "r");
15140Sstevel@tonic-gate 
15150Sstevel@tonic-gate 	if (fp != NULL) {
15160Sstevel@tonic-gate 		while ((fgets(line, sizeof (line), fp) != 0) &&
15170Sstevel@tonic-gate 		    status != ERROR) {
15182805Seota 			/* cut off comments starting with '#' */
15192805Seota 			if ((cp = strchr(line, '#')) != NULL)
15202805Seota 				*cp = '\0';
15212805Seota 			/* ignore comment or blank lines */
15222805Seota 			if (is_blank(line))
15232805Seota 				continue;
15242805Seota 			/* sanity-check */
15250Sstevel@tonic-gate 			if (sscanf(line, "%s %s", drv, alias) != 2)
15260Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
15270Sstevel@tonic-gate 				    driver_aliases, line);
15280Sstevel@tonic-gate 
15290Sstevel@tonic-gate 			if ((strcmp(drv_alias, drv) == 0) ||
15300Sstevel@tonic-gate 			    (strcmp(drv_alias, alias) == 0)) {
15310Sstevel@tonic-gate 				(void) fprintf(stderr,
15320Sstevel@tonic-gate 				    gettext(ERR_ALIAS_IN_USE),
15330Sstevel@tonic-gate 				    drv_alias);
15340Sstevel@tonic-gate 				status = ERROR;
15350Sstevel@tonic-gate 			}
15360Sstevel@tonic-gate 		}
15370Sstevel@tonic-gate 		(void) fclose(fp);
15380Sstevel@tonic-gate 		return (status);
15390Sstevel@tonic-gate 	} else {
15400Sstevel@tonic-gate 		perror(NULL);
15410Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
15420Sstevel@tonic-gate 		return (ERROR);
15430Sstevel@tonic-gate 	}
15440Sstevel@tonic-gate 
15450Sstevel@tonic-gate }
15460Sstevel@tonic-gate 
15470Sstevel@tonic-gate 
15480Sstevel@tonic-gate /*
15490Sstevel@tonic-gate  * search for driver_name in first field of file file_name
15500Sstevel@tonic-gate  * searching name_to_major and driver_aliases: name separated from rest of
15510Sstevel@tonic-gate  * line by blank
15520Sstevel@tonic-gate  * if there return
15530Sstevel@tonic-gate  * else return
15540Sstevel@tonic-gate  */
15550Sstevel@tonic-gate int
15560Sstevel@tonic-gate unique_driver_name(char *driver_name, char *file_name,
15570Sstevel@tonic-gate 	int *is_unique)
15580Sstevel@tonic-gate {
15590Sstevel@tonic-gate 	int ret;
15600Sstevel@tonic-gate 
15610Sstevel@tonic-gate 	if ((ret = get_major_no(driver_name, file_name)) == ERROR) {
15620Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
15630Sstevel@tonic-gate 		    file_name);
15640Sstevel@tonic-gate 	} else {
15650Sstevel@tonic-gate 		/* XXX */
15660Sstevel@tonic-gate 		/* check alias file for name collision */
15670Sstevel@tonic-gate 		if (unique_drv_alias(driver_name) == ERROR) {
15680Sstevel@tonic-gate 			ret = ERROR;
15690Sstevel@tonic-gate 		} else {
15700Sstevel@tonic-gate 			if (ret != UNIQUE)
15710Sstevel@tonic-gate 				*is_unique = NOT_UNIQUE;
15720Sstevel@tonic-gate 			else
15730Sstevel@tonic-gate 				*is_unique = ret;
15740Sstevel@tonic-gate 			ret = NOERR;
15750Sstevel@tonic-gate 		}
15760Sstevel@tonic-gate 	}
15770Sstevel@tonic-gate 	return (ret);
15780Sstevel@tonic-gate }
15790Sstevel@tonic-gate 
15800Sstevel@tonic-gate 
15810Sstevel@tonic-gate int
15820Sstevel@tonic-gate check_space_within_quote(char *str)
15830Sstevel@tonic-gate {
15840Sstevel@tonic-gate 	register int i;
15850Sstevel@tonic-gate 	register int len;
15860Sstevel@tonic-gate 	int quoted = 0;
15870Sstevel@tonic-gate 
15880Sstevel@tonic-gate 	len = strlen(str);
15890Sstevel@tonic-gate 	for (i = 0; i < len; i++, str++) {
15900Sstevel@tonic-gate 		if (*str == '"') {
15910Sstevel@tonic-gate 			if (quoted == 0)
15920Sstevel@tonic-gate 				quoted++;
15930Sstevel@tonic-gate 			else
15940Sstevel@tonic-gate 				quoted--;
15950Sstevel@tonic-gate 		} else if (*str == ' ' && quoted)
15960Sstevel@tonic-gate 			return (ERROR);
15970Sstevel@tonic-gate 	}
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 	return (0);
16000Sstevel@tonic-gate }
16010Sstevel@tonic-gate 
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate /*
16040Sstevel@tonic-gate  * get major number
16050Sstevel@tonic-gate  * write driver_name major_num to name_to_major file
16060Sstevel@tonic-gate  * major_num returned in major_num
16070Sstevel@tonic-gate  * return success/failure
16080Sstevel@tonic-gate  */
16090Sstevel@tonic-gate int
16100Sstevel@tonic-gate update_name_to_major(char *driver_name, major_t *major_num, int server)
16110Sstevel@tonic-gate {
16120Sstevel@tonic-gate 	char major[MAX_STR_MAJOR + 1];
16130Sstevel@tonic-gate 	struct stat buf;
16140Sstevel@tonic-gate 	char *num_list;
16150Sstevel@tonic-gate 	char drv_majnum_str[MAX_STR_MAJOR + 1];
16160Sstevel@tonic-gate 	int new_maj = -1;
16170Sstevel@tonic-gate 	int i, tmp = 0, is_unique, have_rem_n2m = 0;
16180Sstevel@tonic-gate 	int max_dev = 0;
16190Sstevel@tonic-gate 
16200Sstevel@tonic-gate 	/*
16210Sstevel@tonic-gate 	 * if driver_name already in rem_name_to_major
16220Sstevel@tonic-gate 	 * 	delete entry from rem_nam_to_major
16230Sstevel@tonic-gate 	 *	put entry into name_to_major
16240Sstevel@tonic-gate 	 */
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate 	if (stat(rem_name_to_major, &buf) == 0) {
16270Sstevel@tonic-gate 		have_rem_n2m = 1;
16280Sstevel@tonic-gate 	}
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate 	if (have_rem_n2m) {
16310Sstevel@tonic-gate 		if ((is_unique = get_major_no(driver_name, rem_name_to_major))
16320Sstevel@tonic-gate 		    == ERROR)
16330Sstevel@tonic-gate 			return (ERROR);
16340Sstevel@tonic-gate 
16350Sstevel@tonic-gate 		/*
16360Sstevel@tonic-gate 		 * found a match in rem_name_to_major
16370Sstevel@tonic-gate 		 */
16380Sstevel@tonic-gate 		if (is_unique != UNIQUE) {
16390Sstevel@tonic-gate 			char scratch[FILENAME_MAX];
16400Sstevel@tonic-gate 
16410Sstevel@tonic-gate 			/*
16420Sstevel@tonic-gate 			 * If there is a match in /etc/rem_name_to_major then
16430Sstevel@tonic-gate 			 * be paranoid: is that major number already in
16440Sstevel@tonic-gate 			 * /etc/name_to_major (potentially under another name)?
16450Sstevel@tonic-gate 			 */
16460Sstevel@tonic-gate 			if (get_driver_name(is_unique, name_to_major,
16470Sstevel@tonic-gate 			    scratch) != UNIQUE) {
16480Sstevel@tonic-gate 				/*
16490Sstevel@tonic-gate 				 * nuke the rem_name_to_major entry-- it
16500Sstevel@tonic-gate 				 * isn't helpful.
16510Sstevel@tonic-gate 				 */
16520Sstevel@tonic-gate 				(void) delete_entry(rem_name_to_major,
16530Sstevel@tonic-gate 				    driver_name, " ", NULL);
16540Sstevel@tonic-gate 			} else {
16550Sstevel@tonic-gate 				(void) snprintf(major, sizeof (major),
16560Sstevel@tonic-gate 				    "%d", is_unique);
16570Sstevel@tonic-gate 
16580Sstevel@tonic-gate 				if (append_to_file(driver_name, major,
16590Sstevel@tonic-gate 				    name_to_major, ' ', " ") == ERROR) {
16600Sstevel@tonic-gate 					(void) fprintf(stderr,
16610Sstevel@tonic-gate 					    gettext(ERR_NO_UPDATE),
16620Sstevel@tonic-gate 					    name_to_major);
16630Sstevel@tonic-gate 					return (ERROR);
16640Sstevel@tonic-gate 				}
16650Sstevel@tonic-gate 
16660Sstevel@tonic-gate 				if (delete_entry(rem_name_to_major,
16670Sstevel@tonic-gate 				    driver_name, " ", NULL) == ERROR) {
16680Sstevel@tonic-gate 					(void) fprintf(stderr,
16690Sstevel@tonic-gate 					    gettext(ERR_DEL_ENTRY), driver_name,
16700Sstevel@tonic-gate 					    rem_name_to_major);
16710Sstevel@tonic-gate 					return (ERROR);
16720Sstevel@tonic-gate 				}
16730Sstevel@tonic-gate 
16740Sstevel@tonic-gate 				/* found matching entry : no errors */
16750Sstevel@tonic-gate 				*major_num = is_unique;
16760Sstevel@tonic-gate 				return (NOERR);
16770Sstevel@tonic-gate 			}
16780Sstevel@tonic-gate 		}
16790Sstevel@tonic-gate 	}
16800Sstevel@tonic-gate 
16810Sstevel@tonic-gate 	/*
16820Sstevel@tonic-gate 	 * Bugid: 1264079
16830Sstevel@tonic-gate 	 * In a server case (with -b option), we can't use modctl() to find
16840Sstevel@tonic-gate 	 *    the maximum major number, we need to dig thru client's
16850Sstevel@tonic-gate 	 *    /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
16860Sstevel@tonic-gate 	 *
16870Sstevel@tonic-gate 	 * if (server)
16880Sstevel@tonic-gate 	 *    get maximum major number thru (rem_)name_to_major file on client
16890Sstevel@tonic-gate 	 * else
16900Sstevel@tonic-gate 	 *    get maximum major number allowable on current system using modctl
16910Sstevel@tonic-gate 	 */
16920Sstevel@tonic-gate 	if (server) {
16930Sstevel@tonic-gate 		max_dev = 0;
16940Sstevel@tonic-gate 		tmp = 0;
16950Sstevel@tonic-gate 
16960Sstevel@tonic-gate 		max_dev = get_max_major(name_to_major);
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate 		/* If rem_name_to_major exists, we need to check it too */
16990Sstevel@tonic-gate 		if (have_rem_n2m) {
17000Sstevel@tonic-gate 			tmp = get_max_major(rem_name_to_major);
17010Sstevel@tonic-gate 
17020Sstevel@tonic-gate 			/*
17030Sstevel@tonic-gate 			 * If name_to_major is missing, we can get max_dev from
17040Sstevel@tonic-gate 			 * /etc/rem_name_to_major.  If both missing, bail out!
17050Sstevel@tonic-gate 			 */
17060Sstevel@tonic-gate 			if ((max_dev == ERROR) && (tmp == ERROR)) {
17070Sstevel@tonic-gate 				(void) fprintf(stderr,
17080Sstevel@tonic-gate 					gettext(ERR_CANT_ACCESS_FILE),
17090Sstevel@tonic-gate 					name_to_major);
17100Sstevel@tonic-gate 				return (ERROR);
17110Sstevel@tonic-gate 			}
17120Sstevel@tonic-gate 
17130Sstevel@tonic-gate 			/* guard against bigger maj_num in rem_name_to_major */
17140Sstevel@tonic-gate 			if (tmp > max_dev)
17150Sstevel@tonic-gate 				max_dev = tmp;
17160Sstevel@tonic-gate 		} else {
17170Sstevel@tonic-gate 			/*
17180Sstevel@tonic-gate 			 * If we can't get major from name_to_major file
17190Sstevel@tonic-gate 			 * and there is no /etc/rem_name_to_major file,
17200Sstevel@tonic-gate 			 * then we don't have a max_dev, bail out quick!
17210Sstevel@tonic-gate 			 */
17220Sstevel@tonic-gate 			if (max_dev == ERROR)
17230Sstevel@tonic-gate 				return (ERROR);
17240Sstevel@tonic-gate 		}
17250Sstevel@tonic-gate 
17260Sstevel@tonic-gate 		/*
17270Sstevel@tonic-gate 		 * In case there is no more slack in current name_to_major
17280Sstevel@tonic-gate 		 * table, provide at least 1 extra entry so the add_drv can
17290Sstevel@tonic-gate 		 * succeed.  Since only one add_drv process is allowed at one
17300Sstevel@tonic-gate 		 * time, and hence max_dev will be re-calculated each time
17310Sstevel@tonic-gate 		 * add_drv is ran, we don't need to worry about adding more
17320Sstevel@tonic-gate 		 * than 1 extra slot for max_dev.
17330Sstevel@tonic-gate 		 */
17340Sstevel@tonic-gate 		max_dev++;
17350Sstevel@tonic-gate 
17360Sstevel@tonic-gate 	} else {
17370Sstevel@tonic-gate 		if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
17380Sstevel@tonic-gate 			perror(NULL);
17390Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
17400Sstevel@tonic-gate 			return (ERROR);
17410Sstevel@tonic-gate 		}
17420Sstevel@tonic-gate 	}
17430Sstevel@tonic-gate 
17440Sstevel@tonic-gate 	/*
17450Sstevel@tonic-gate 	 * max_dev is really how many slots the kernel has allocated for
17460Sstevel@tonic-gate 	 * devices... [0 , maxdev-1], not the largest available device num.
17470Sstevel@tonic-gate 	 */
17480Sstevel@tonic-gate 	if ((num_list = calloc(max_dev, 1)) == NULL) {
17490Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
17500Sstevel@tonic-gate 		return (ERROR);
17510Sstevel@tonic-gate 	}
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate 	/*
17540Sstevel@tonic-gate 	 * Populate the num_list array
17550Sstevel@tonic-gate 	 */
17560Sstevel@tonic-gate 	if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) {
17570Sstevel@tonic-gate 		return (ERROR);
17580Sstevel@tonic-gate 	}
17590Sstevel@tonic-gate 	if (have_rem_n2m) {
17600Sstevel@tonic-gate 		if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0)
17610Sstevel@tonic-gate 			return (ERROR);
17620Sstevel@tonic-gate 	}
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate 	/* find first free major number */
17650Sstevel@tonic-gate 	for (i = 0; i < max_dev; i++) {
17660Sstevel@tonic-gate 		if (num_list[i] != 1) {
17670Sstevel@tonic-gate 			new_maj = i;
17680Sstevel@tonic-gate 			break;
17690Sstevel@tonic-gate 		}
17700Sstevel@tonic-gate 	}
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate 	if (new_maj == -1) {
17730Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR));
17740Sstevel@tonic-gate 		return (ERROR);
17750Sstevel@tonic-gate 	}
17760Sstevel@tonic-gate 
17770Sstevel@tonic-gate 	(void) sprintf(drv_majnum_str, "%d", new_maj);
17780Sstevel@tonic-gate 	if (do_the_update(driver_name, drv_majnum_str) == ERROR) {
17790Sstevel@tonic-gate 		return (ERROR);
17800Sstevel@tonic-gate 	}
17810Sstevel@tonic-gate 
17820Sstevel@tonic-gate 	*major_num = new_maj;
17830Sstevel@tonic-gate 	return (NOERR);
17840Sstevel@tonic-gate }
17850Sstevel@tonic-gate 
17860Sstevel@tonic-gate 
17870Sstevel@tonic-gate int
17880Sstevel@tonic-gate fill_n2m_array(char *filename, char **array, int *nelems)
17890Sstevel@tonic-gate {
17900Sstevel@tonic-gate 	FILE *fp;
17912805Seota 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
17920Sstevel@tonic-gate 	char drv[FILENAME_MAX + 1];
17930Sstevel@tonic-gate 	u_longlong_t dnum;
17940Sstevel@tonic-gate 	major_t drv_majnum;
17950Sstevel@tonic-gate 
17960Sstevel@tonic-gate 	/*
17970Sstevel@tonic-gate 	 * Read through the file, marking each major number found
17980Sstevel@tonic-gate 	 * order is not relevant
17990Sstevel@tonic-gate 	 */
18000Sstevel@tonic-gate 	if ((fp = fopen(filename, "r")) == NULL) {
18010Sstevel@tonic-gate 		perror(NULL);
18020Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename);
18030Sstevel@tonic-gate 		return (ERROR);
18040Sstevel@tonic-gate 	}
18050Sstevel@tonic-gate 
18060Sstevel@tonic-gate 	while (fgets(line, sizeof (line), fp) != 0) {
18072805Seota 		/* cut off comments starting with '#' */
18082805Seota 		if ((cp = strchr(line, '#')) != NULL)
18092805Seota 			*cp = '\0';
18102805Seota 		/* ignore comment or blank lines */
18112805Seota 		if (is_blank(line))
18122805Seota 			continue;
18132805Seota 		/* sanity-check */
18140Sstevel@tonic-gate 		if (sscanf(line, "%s %llu", drv, &dnum) != 2) {
18150Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
18160Sstevel@tonic-gate 			    filename, line);
18170Sstevel@tonic-gate 			(void) fclose(fp);
18180Sstevel@tonic-gate 			return (ERROR);
18190Sstevel@tonic-gate 		}
18200Sstevel@tonic-gate 
18210Sstevel@tonic-gate 		if (dnum > L_MAXMAJ32) {
18220Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv,
18230Sstevel@tonic-gate 			    dnum, filename, L_MAXMAJ32);
18240Sstevel@tonic-gate 			continue;
18250Sstevel@tonic-gate 		}
18260Sstevel@tonic-gate 		/*
18270Sstevel@tonic-gate 		 * cast down to a major_t; we can be sure this is safe because
18280Sstevel@tonic-gate 		 * of the above range-check.
18290Sstevel@tonic-gate 		 */
18300Sstevel@tonic-gate 		drv_majnum = (major_t)dnum;
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate 		if (drv_majnum >= *nelems) {
18330Sstevel@tonic-gate 			/*
18340Sstevel@tonic-gate 			 * Allocate some more space, up to drv_majnum + 1 so
18350Sstevel@tonic-gate 			 * we can accomodate 0 through drv_majnum.
18360Sstevel@tonic-gate 			 *
18370Sstevel@tonic-gate 			 * Note that in the failure case, we leak all of the
18380Sstevel@tonic-gate 			 * old contents of array.  It's ok, since we just
18390Sstevel@tonic-gate 			 * wind up exiting immediately anyway.
18400Sstevel@tonic-gate 			 */
18410Sstevel@tonic-gate 			*nelems = drv_majnum + 1;
18420Sstevel@tonic-gate 			*array = realloc(*array, *nelems);
18430Sstevel@tonic-gate 			if (*array == NULL) {
18440Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
18450Sstevel@tonic-gate 				return (ERROR);
18460Sstevel@tonic-gate 			}
18470Sstevel@tonic-gate 		}
18480Sstevel@tonic-gate 		(*array)[drv_majnum] = 1;
18490Sstevel@tonic-gate 	}
18500Sstevel@tonic-gate 
18510Sstevel@tonic-gate 	(void) fclose(fp);
18520Sstevel@tonic-gate 	return (0);
18530Sstevel@tonic-gate }
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate int
18570Sstevel@tonic-gate do_the_update(char *driver_name, char *major_number)
18580Sstevel@tonic-gate {
18590Sstevel@tonic-gate 	return (append_to_file(driver_name, major_number, name_to_major,
18600Sstevel@tonic-gate 	    ' ', " "));
18610Sstevel@tonic-gate }
18622805Seota 
18632805Seota /*
18642805Seota  * is_blank() returns 1 (true) if a line specified is composed of
18652805Seota  * whitespace characters only. otherwise, it returns 0 (false).
18662805Seota  *
18672805Seota  * Note. the argument (line) must be null-terminated.
18682805Seota  */
18692805Seota static int
18702805Seota is_blank(char *line)
18712805Seota {
18722805Seota 	for (/* nothing */; *line != '\0'; line++)
18732805Seota 		if (!isspace(*line))
18742805Seota 			return (0);
18752805Seota 	return (1);
18762805Seota }
1877