xref: /onnv-gate/usr/src/cmd/modload/drvsubr.c (revision 4145)
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 /*
223442Svikram  * 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 */
483442Svikram 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,
109*4145Scth 	char *entry_separator,
110*4145Scth 	int quoted)
1110Sstevel@tonic-gate {
1120Sstevel@tonic-gate 	int	i, len;
1130Sstevel@tonic-gate 	int	fpint;
1140Sstevel@tonic-gate 	char	*current_head, *previous_head;
1150Sstevel@tonic-gate 	char	*line, *one_entry;
1160Sstevel@tonic-gate 	FILE	*fp;
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 	if ((fp = fopen(filename, "a")) == NULL) {
1190Sstevel@tonic-gate 		perror(NULL);
1200Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1210Sstevel@tonic-gate 		    filename);
1220Sstevel@tonic-gate 		return (ERROR);
1230Sstevel@tonic-gate 	}
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate 	len = strlen(entry_list);
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	one_entry = calloc(len + 1, 1);
1280Sstevel@tonic-gate 	if (one_entry == NULL) {
1290Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
1300Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1310Sstevel@tonic-gate 		(void) fclose(fp);
1320Sstevel@tonic-gate 		return (ERROR);
1330Sstevel@tonic-gate 	}
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	previous_head = entry_list;
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate 	line = calloc(strlen(driver_name) + len + 4, 1);
1380Sstevel@tonic-gate 	if (line == NULL) {
1390Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1400Sstevel@tonic-gate 		(void) fclose(fp);
1410Sstevel@tonic-gate 		err_exit();
1420Sstevel@tonic-gate 	}
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 	/*
1450Sstevel@tonic-gate 	 * get one entry at a time from list and append to <filename> file
1460Sstevel@tonic-gate 	 */
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 	do {
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 		for (i = 0; i <= len; i++)
1510Sstevel@tonic-gate 			one_entry[i] = 0;
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 		for (i = 0; i <= (int)strlen(line); i++)
1540Sstevel@tonic-gate 			line[i] = 0;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 		current_head = get_entry(previous_head, one_entry,
157*4145Scth 		    list_separator, quoted);
1580Sstevel@tonic-gate 		previous_head = current_head;
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 		(void) strcpy(line, driver_name);
1610Sstevel@tonic-gate 		(void) strcat(line, entry_separator);
162*4145Scth 		if (quoted)
163*4145Scth 			(void) strcat(line, "\"");
1640Sstevel@tonic-gate 		(void) strcat(line, one_entry);
165*4145Scth 		if (quoted)
166*4145Scth 			(void) strcat(line, "\"");
1670Sstevel@tonic-gate 		(void) strcat(line, "\n");
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 		if ((fputs(line, fp)) == EOF) {
1700Sstevel@tonic-gate 			perror(NULL);
1710Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
1720Sstevel@tonic-gate 			    filename);
1730Sstevel@tonic-gate 		}
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	} while (*current_head != '\0');
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	(void) fflush(fp);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	fpint = fileno(fp);
1810Sstevel@tonic-gate 	(void) fsync(fpint);
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 	(void) fclose(fp);
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	free(one_entry);
1860Sstevel@tonic-gate 	free(line);
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	return (NOERR);
1890Sstevel@tonic-gate }
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate  *  open file
1940Sstevel@tonic-gate  * read thru file, deleting all entries if first
1950Sstevel@tonic-gate  *    entry = driver_name
1960Sstevel@tonic-gate  * close
1970Sstevel@tonic-gate  * if error, leave original file intact with message
1980Sstevel@tonic-gate  * assumption : drvconfig has been modified to work with clone
1990Sstevel@tonic-gate  *  entries in /etc/minor_perm as driver:mummble NOT
2000Sstevel@tonic-gate  *  clone:driver mummble
2010Sstevel@tonic-gate  * this implementation will NOT find clone entries
2020Sstevel@tonic-gate  * clone:driver mummble
2030Sstevel@tonic-gate  * match:
2040Sstevel@tonic-gate  *	delete just the matching entry
2050Sstevel@tonic-gate  *
2060Sstevel@tonic-gate  */
2070Sstevel@tonic-gate int
2080Sstevel@tonic-gate delete_entry(
2090Sstevel@tonic-gate 	char *oldfile,
2100Sstevel@tonic-gate 	char *driver_name,
2110Sstevel@tonic-gate 	char *marker,
2120Sstevel@tonic-gate 	char *match)
2130Sstevel@tonic-gate {
2140Sstevel@tonic-gate 	int		rv, i;
2150Sstevel@tonic-gate 	int		status = NOERR;
2160Sstevel@tonic-gate 	int		drvr_found = 0;
2170Sstevel@tonic-gate 	boolean_t 	nomatch = B_TRUE;
2182805Seota 	char		*newfile, *tptr, *cp, *dup;
2190Sstevel@tonic-gate 	char		line[MAX_DBFILE_ENTRY], drv[FILENAME_MAX + 1];
2200Sstevel@tonic-gate 	FILE		*fp, *newfp;
2210Sstevel@tonic-gate 	struct group	*sysgrp;
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	/*
2240Sstevel@tonic-gate 	 * check if match is specified and if it equals " "
2250Sstevel@tonic-gate 	 * this is a special case handling as we do a strstr(3STRING)
2260Sstevel@tonic-gate 	 * to match an entry. By default all entries are space separated
2270Sstevel@tonic-gate 	 * and without this check all entries of the file could get deleted.
2280Sstevel@tonic-gate 	 */
2290Sstevel@tonic-gate 	if (match && (*match == ' ' && strlen(match) == 1)) {
2300Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_INT_UPDATE), oldfile);
2310Sstevel@tonic-gate 		return (ERROR);
2320Sstevel@tonic-gate 	}
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	if ((fp = fopen(oldfile, "r")) == NULL) {
2350Sstevel@tonic-gate 		perror(NULL);
2360Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
2370Sstevel@tonic-gate 		return (ERROR);
2380Sstevel@tonic-gate 	}
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	/*
2410Sstevel@tonic-gate 	 * Build filename for temporary file
2420Sstevel@tonic-gate 	 */
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 	if ((tptr = calloc(strlen(oldfile) + strlen(XEND) + 1, 1)) == NULL) {
2450Sstevel@tonic-gate 		perror(NULL);
2460Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
2470Sstevel@tonic-gate 	}
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	(void) strcpy(tptr, oldfile);
2500Sstevel@tonic-gate 	(void) strcat(tptr, XEND);
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	/*
2530Sstevel@tonic-gate 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
2540Sstevel@tonic-gate 	 * assume a gid of "sys" but we can't undo the damage on already
2550Sstevel@tonic-gate 	 * installed systems unless we force the issue.
2560Sstevel@tonic-gate 	 */
2570Sstevel@tonic-gate 	if ((sysgrp = getgrnam("sys")) != NULL) {
2580Sstevel@tonic-gate 		(void) setgid(sysgrp->gr_gid);
2590Sstevel@tonic-gate 	}
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 	newfile = mktemp(tptr);
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	if ((newfp = fopen(newfile, "w")) == NULL) {
2640Sstevel@tonic-gate 		perror(NULL);
2650Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
2660Sstevel@tonic-gate 		    newfile);
2670Sstevel@tonic-gate 		return (ERROR);
2680Sstevel@tonic-gate 	}
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
2712805Seota 		/* copy the whole line into dup */
2722805Seota 		if ((dup = strdup(line)) == NULL) {
2732805Seota 			perror(NULL);
2742805Seota 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
2752805Seota 			status = ERROR;
2762805Seota 			break;
2772805Seota 		}
2782805Seota 		/* cut off comments starting with '#' */
2792805Seota 		if ((cp = strchr(dup, '#')) != NULL)
2802805Seota 			*cp = '\0';
2812805Seota 		/* ignore comment or blank lines */
2822805Seota 		if (is_blank(dup)) {
2832805Seota 			if (fputs(line, newfp) == EOF) {
2840Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_UPDATE),
2850Sstevel@tonic-gate 				    oldfile);
2860Sstevel@tonic-gate 				status = ERROR;
2870Sstevel@tonic-gate 			}
2882805Seota 			free(dup);
2890Sstevel@tonic-gate 			continue;
2900Sstevel@tonic-gate 		}
2912805Seota 
2922805Seota 		/* get the driver name */
2932805Seota 		if (sscanf(dup, "%s", drv) != 1) {
2940Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
2950Sstevel@tonic-gate 			    oldfile, line);
2960Sstevel@tonic-gate 			status = ERROR;
2972805Seota 			free(dup);
2982805Seota 			break;
2990Sstevel@tonic-gate 		}
3002805Seota 		free(dup);
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
3030Sstevel@tonic-gate 			drv[i] =  '\0';
3040Sstevel@tonic-gate 		}
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 		if (strcmp(driver_name, drv) != 0) {
3070Sstevel@tonic-gate 			if ((fputs(line, newfp)) == EOF) {
3080Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_UPDATE),
3090Sstevel@tonic-gate 				    oldfile);
3100Sstevel@tonic-gate 				status = ERROR;
3110Sstevel@tonic-gate 			}
3120Sstevel@tonic-gate 		} else {
3130Sstevel@tonic-gate 			drvr_found++;
3140Sstevel@tonic-gate 			if (match) {	/* Just delete one entry */
3150Sstevel@tonic-gate 				/* for now delete just minor_perm and aliases */
3160Sstevel@tonic-gate 				if ((strcmp(oldfile, minor_perm) == 0) ||
3170Sstevel@tonic-gate 				    (strcmp(oldfile, extra_privs) == 0) ||
3180Sstevel@tonic-gate 				    (strcmp(oldfile, driver_aliases) == 0)) {
3190Sstevel@tonic-gate 					if (strstr(line, match)) {
3200Sstevel@tonic-gate 						nomatch = B_FALSE;
3210Sstevel@tonic-gate 					} else {
3220Sstevel@tonic-gate 						if ((fputs(line, newfp)) ==
3230Sstevel@tonic-gate 						    EOF) {
3240Sstevel@tonic-gate 							(void) fprintf(stderr,
3250Sstevel@tonic-gate 							    gettext(ERR_UPDATE),
3260Sstevel@tonic-gate 							    oldfile);
3270Sstevel@tonic-gate 							status = ERROR;
3280Sstevel@tonic-gate 						}
3290Sstevel@tonic-gate 						if (nomatch != B_FALSE)
3300Sstevel@tonic-gate 							nomatch = B_TRUE;
3310Sstevel@tonic-gate 					}
3320Sstevel@tonic-gate 				}
3330Sstevel@tonic-gate 			}
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 		} /* end of else */
3360Sstevel@tonic-gate 	} /* end of while */
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	(void) fclose(fp);
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	/* Make sure that the file is on disk */
3410Sstevel@tonic-gate 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
3420Sstevel@tonic-gate 		status = ERROR;
3430Sstevel@tonic-gate 	else
3440Sstevel@tonic-gate 		rv = NOERR;
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	(void) fclose(newfp);
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	/* no matching driver found */
3490Sstevel@tonic-gate 	rv = NOERR;
3500Sstevel@tonic-gate 	if (!drvr_found ||
3510Sstevel@tonic-gate 	    (nomatch == B_TRUE)) {
3520Sstevel@tonic-gate 		rv = NONE_FOUND;
3530Sstevel@tonic-gate 	}
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	/*
3560Sstevel@tonic-gate 	 * if error, leave original file, delete new file
3570Sstevel@tonic-gate 	 * if noerr, replace original file with new file
3580Sstevel@tonic-gate 	 */
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 	if (status == NOERR) {
3610Sstevel@tonic-gate 		if (rename(oldfile, tmphold) == -1) {
3620Sstevel@tonic-gate 			perror(NULL);
3630Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
3640Sstevel@tonic-gate 			(void) unlink(newfile);
3650Sstevel@tonic-gate 			return (ERROR);
3660Sstevel@tonic-gate 		} else if (rename(newfile, oldfile) == -1) {
3670Sstevel@tonic-gate 			perror(NULL);
3680Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
3690Sstevel@tonic-gate 			(void) unlink(oldfile);
3700Sstevel@tonic-gate 			(void) unlink(newfile);
3710Sstevel@tonic-gate 			if (link(tmphold, oldfile) == -1) {
3720Sstevel@tonic-gate 				perror(NULL);
3730Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
3740Sstevel@tonic-gate 				    oldfile, tmphold);
3750Sstevel@tonic-gate 			}
3760Sstevel@tonic-gate 			return (ERROR);
3770Sstevel@tonic-gate 		}
3780Sstevel@tonic-gate 		(void) unlink(tmphold);
3790Sstevel@tonic-gate 	} else {
3800Sstevel@tonic-gate 		/*
3810Sstevel@tonic-gate 		 * since there's an error, leave file alone; remove
3820Sstevel@tonic-gate 		 * new file
3830Sstevel@tonic-gate 		 */
3840Sstevel@tonic-gate 		if (unlink(newfile) == -1) {
3850Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
3860Sstevel@tonic-gate 		}
3870Sstevel@tonic-gate 		return (ERROR);
3880Sstevel@tonic-gate 	}
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	return (rv);
3910Sstevel@tonic-gate }
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate /*
3950Sstevel@tonic-gate  * wrapper for call to get_name_to_major_entry(): given driver name,
3960Sstevel@tonic-gate  * retrieve major number.
3970Sstevel@tonic-gate  */
3980Sstevel@tonic-gate int
3990Sstevel@tonic-gate get_major_no(char *driver_name, char *file_name)
4000Sstevel@tonic-gate {
4010Sstevel@tonic-gate 	int major = UNIQUE;
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 	if (get_name_to_major_entry(&major, driver_name, file_name) == ERROR)
4040Sstevel@tonic-gate 		return (ERROR);
4050Sstevel@tonic-gate 	else
4060Sstevel@tonic-gate 		return (major);
4070Sstevel@tonic-gate }
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate /*
4100Sstevel@tonic-gate  * wrapper for call to get_name_to_major_entry(): given major number,
4110Sstevel@tonic-gate  * retrieve driver name.
4120Sstevel@tonic-gate  */
4130Sstevel@tonic-gate int
4140Sstevel@tonic-gate get_driver_name(int major, char *file_name, char *buf)
4150Sstevel@tonic-gate {
4160Sstevel@tonic-gate 	if (major < 0)
4170Sstevel@tonic-gate 		return (ERROR);
4180Sstevel@tonic-gate 	return (get_name_to_major_entry(&major, buf, file_name));
4190Sstevel@tonic-gate }
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate /*
4230Sstevel@tonic-gate  * return pointer to cached name_to_major file - reads file into
4240Sstevel@tonic-gate  * cache if this has not already been done.  Since there may be
4250Sstevel@tonic-gate  * requests for multiple name_to_major files (rem_name_to_major,
4260Sstevel@tonic-gate  * name_to_major), this routine keeps a list of cached files.
4270Sstevel@tonic-gate  */
4280Sstevel@tonic-gate static int
4290Sstevel@tonic-gate get_cached_n_to_m_file(char *filename, char ***cache)
4300Sstevel@tonic-gate {
4310Sstevel@tonic-gate 	struct n_to_m_cache {
4320Sstevel@tonic-gate 		char *file;
4330Sstevel@tonic-gate 		char **cached_file;
4340Sstevel@tonic-gate 		int size;
4350Sstevel@tonic-gate 		struct n_to_m_cache *next;
4360Sstevel@tonic-gate 	};
4370Sstevel@tonic-gate 	static struct n_to_m_cache *head = NULL;
4380Sstevel@tonic-gate 	struct n_to_m_cache *ptr;
4390Sstevel@tonic-gate 	FILE *fp;
4400Sstevel@tonic-gate 	char drv[FILENAME_MAX + 1];
4410Sstevel@tonic-gate 	char entry[FILENAME_MAX + 1];
4422805Seota 	char line[MAX_N2M_ALIAS_LINE], *cp;
4430Sstevel@tonic-gate 	int maj;
4440Sstevel@tonic-gate 	int size = 0;
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate 	/*
4480Sstevel@tonic-gate 	 * see if the file is already cached - either
4490Sstevel@tonic-gate 	 * rem_name_to_major or name_to_major
4500Sstevel@tonic-gate 	 */
4510Sstevel@tonic-gate 	ptr = head;
4520Sstevel@tonic-gate 	while (ptr != NULL) {
4530Sstevel@tonic-gate 		if (strcmp(ptr->file, filename) == 0)
4540Sstevel@tonic-gate 			break;
4550Sstevel@tonic-gate 		ptr = ptr->next;
4560Sstevel@tonic-gate 	}
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 	if (ptr == NULL) {	/* we need to cache the contents */
4590Sstevel@tonic-gate 		if ((fp = fopen(filename, "r")) == NULL) {
4600Sstevel@tonic-gate 			perror(NULL);
4610Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_OPEN),
4620Sstevel@tonic-gate 			    filename);
4630Sstevel@tonic-gate 			return (ERROR);
4640Sstevel@tonic-gate 		}
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 		while (fgets(line, sizeof (line), fp) != NULL) {
4672805Seota 			/* cut off comments starting with '#' */
4682805Seota 			if ((cp = strchr(line, '#')) != NULL)
4692805Seota 				*cp = '\0';
4702805Seota 			/* ignore comment or blank lines */
4712805Seota 			if (is_blank(line))
4722805Seota 				continue;
4732805Seota 			/* sanity-check */
4740Sstevel@tonic-gate 			if (sscanf(line, "%s%s", drv, entry) != 2) {
4750Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
4760Sstevel@tonic-gate 				    filename, line);
4770Sstevel@tonic-gate 				continue;
4780Sstevel@tonic-gate 			}
4790Sstevel@tonic-gate 			maj = atoi(entry);
4800Sstevel@tonic-gate 			if (maj > size)
4810Sstevel@tonic-gate 				size = maj;
4820Sstevel@tonic-gate 		}
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate 		/* allocate struct to cache the file */
4850Sstevel@tonic-gate 		ptr = (struct n_to_m_cache *)calloc(1,
4860Sstevel@tonic-gate 		    sizeof (struct n_to_m_cache));
4870Sstevel@tonic-gate 		if (ptr == NULL) {
4880Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
4890Sstevel@tonic-gate 			return (ERROR);
4900Sstevel@tonic-gate 		}
4910Sstevel@tonic-gate 		ptr->size = size + 1;
4920Sstevel@tonic-gate 		/* allocate space to cache contents of file */
4930Sstevel@tonic-gate 		ptr->cached_file = (char **)calloc(ptr->size, sizeof (char *));
4940Sstevel@tonic-gate 		if (ptr->cached_file == NULL) {
4950Sstevel@tonic-gate 			free(ptr);
4960Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
4970Sstevel@tonic-gate 			return (ERROR);
4980Sstevel@tonic-gate 		}
4990Sstevel@tonic-gate 
5000Sstevel@tonic-gate 		rewind(fp);
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 		/*
5030Sstevel@tonic-gate 		 * now fill the cache
5040Sstevel@tonic-gate 		 * the cache is an array of char pointers indexed by major
5050Sstevel@tonic-gate 		 * number
5060Sstevel@tonic-gate 		 */
5070Sstevel@tonic-gate 		while (fgets(line, sizeof (line), fp) != NULL) {
5082805Seota 			/* cut off comments starting with '#' */
5092805Seota 			if ((cp = strchr(line, '#')) != NULL)
5102805Seota 				*cp = '\0';
5112805Seota 			/* ignore comment or blank lines */
5122805Seota 			if (is_blank(line))
5132805Seota 				continue;
5142805Seota 			/* sanity-check */
5150Sstevel@tonic-gate 			if (sscanf(line, "%s%s", drv, entry) != 2) {
5160Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
5170Sstevel@tonic-gate 				    filename, line);
5180Sstevel@tonic-gate 				continue;
5190Sstevel@tonic-gate 			}
5200Sstevel@tonic-gate 			maj = atoi(entry);
5210Sstevel@tonic-gate 			if ((ptr->cached_file[maj] = strdup(drv)) == NULL) {
5220Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
5230Sstevel@tonic-gate 				free(ptr->cached_file);
5240Sstevel@tonic-gate 				free(ptr);
5250Sstevel@tonic-gate 				return (ERROR);
5260Sstevel@tonic-gate 			}
5270Sstevel@tonic-gate 			(void) strcpy(ptr->cached_file[maj], drv);
5280Sstevel@tonic-gate 		}
5290Sstevel@tonic-gate 		(void) fclose(fp);
5300Sstevel@tonic-gate 		/* link the cache struct into the list of cached files */
5310Sstevel@tonic-gate 		ptr->file = strdup(filename);
5320Sstevel@tonic-gate 		if (ptr->file == NULL) {
5330Sstevel@tonic-gate 			for (maj = 0; maj <= ptr->size; maj++)
5340Sstevel@tonic-gate 				free(ptr->cached_file[maj]);
5350Sstevel@tonic-gate 			free(ptr->cached_file);
5360Sstevel@tonic-gate 			free(ptr);
5370Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
5380Sstevel@tonic-gate 			return (ERROR);
5390Sstevel@tonic-gate 		}
5400Sstevel@tonic-gate 		ptr->next = head;
5410Sstevel@tonic-gate 		head = ptr;
5420Sstevel@tonic-gate 	}
5430Sstevel@tonic-gate 	/* return value pointer to contents of file */
5440Sstevel@tonic-gate 	*cache = ptr->cached_file;
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 	/* return size */
5470Sstevel@tonic-gate 	return (ptr->size);
5480Sstevel@tonic-gate }
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate /*
5520Sstevel@tonic-gate  * Using get_cached_n_to_m_file(), retrieve maximum major number
5530Sstevel@tonic-gate  * found in the specificed file (name_to_major/rem_name_to_major).
5540Sstevel@tonic-gate  *
5550Sstevel@tonic-gate  * The return value is actually the size of the internal cache including 0.
5560Sstevel@tonic-gate  */
5570Sstevel@tonic-gate int
5580Sstevel@tonic-gate get_max_major(char *file_name)
5590Sstevel@tonic-gate {
5600Sstevel@tonic-gate 	char **n_to_m_cache = NULL;
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	return (get_cached_n_to_m_file(file_name, &n_to_m_cache));
5630Sstevel@tonic-gate }
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate /*
5670Sstevel@tonic-gate  * searching name_to_major: if major_no == UNIQUE then the caller wants to
5680Sstevel@tonic-gate  * use the driver name as the key.  Otherwise, the caller wants to use
5690Sstevel@tonic-gate  * the major number as a key.
5700Sstevel@tonic-gate  *
5710Sstevel@tonic-gate  * This routine caches the contents of the name_to_major file on
5720Sstevel@tonic-gate  * first call.  And it could be generalized to deal with other
5730Sstevel@tonic-gate  * config files if necessary.
5740Sstevel@tonic-gate  */
5750Sstevel@tonic-gate static int
5760Sstevel@tonic-gate get_name_to_major_entry(int *major_no, char *driver_name, char *file_name)
5770Sstevel@tonic-gate {
5780Sstevel@tonic-gate 	int maj;
5790Sstevel@tonic-gate 	char **n_to_m_cache = NULL;
5800Sstevel@tonic-gate 	int size = 0;
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	int ret = NOT_UNIQUE;
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	/*
5850Sstevel@tonic-gate 	 * read the file in - we cache it in case caller wants to
5860Sstevel@tonic-gate 	 * do multiple lookups
5870Sstevel@tonic-gate 	 */
5880Sstevel@tonic-gate 	size = get_cached_n_to_m_file(file_name, &n_to_m_cache);
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	if (size == ERROR)
5910Sstevel@tonic-gate 		return (ERROR);
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	/* search with driver name as key */
5940Sstevel@tonic-gate 	if (*major_no == UNIQUE) {
5950Sstevel@tonic-gate 		for (maj = 0; maj < size; maj++) {
5960Sstevel@tonic-gate 			if ((n_to_m_cache[maj] != NULL) &&
5970Sstevel@tonic-gate 			    (strcmp(driver_name, n_to_m_cache[maj]) == 0)) {
5980Sstevel@tonic-gate 				*major_no = maj;
5990Sstevel@tonic-gate 				break;
6000Sstevel@tonic-gate 			}
6010Sstevel@tonic-gate 		}
6020Sstevel@tonic-gate 		if (maj >= size)
6030Sstevel@tonic-gate 			ret = UNIQUE;
6040Sstevel@tonic-gate 	/* search with major number as key */
6050Sstevel@tonic-gate 	} else {
6060Sstevel@tonic-gate 		/*
6070Sstevel@tonic-gate 		 * Bugid 1254588, drvconfig dump core after loading driver
6080Sstevel@tonic-gate 		 * with major number bigger than entries defined in
6090Sstevel@tonic-gate 		 * /etc/name_to_major.
6100Sstevel@tonic-gate 		 */
6110Sstevel@tonic-gate 		if (*major_no >= size)
6120Sstevel@tonic-gate 			return (UNIQUE);
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 		if (n_to_m_cache[*major_no] != NULL) {
6150Sstevel@tonic-gate 			(void) strcpy(driver_name, n_to_m_cache[*major_no]);
6160Sstevel@tonic-gate 		} else
6170Sstevel@tonic-gate 			ret = UNIQUE;
6180Sstevel@tonic-gate 	}
6190Sstevel@tonic-gate 	return (ret);
6200Sstevel@tonic-gate }
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate /*
623*4145Scth  * Given pointer to begining of member 'n' in a space (or separator)
624*4145Scth  * separated list, return pointer to member 'n+1', and establish member 'n'
625*4145Scth  * in *current_entry.  If unquote, then we skip a leading quote and treat
626*4145Scth  * the trailing quote as a separator (and skip).
6270Sstevel@tonic-gate  */
6280Sstevel@tonic-gate char *
6290Sstevel@tonic-gate get_entry(
6300Sstevel@tonic-gate 	char *prev_member,
6310Sstevel@tonic-gate 	char *current_entry,
632*4145Scth 	char separator,
633*4145Scth 	int  unquote)
6340Sstevel@tonic-gate {
635*4145Scth 	char	*ptr;
636*4145Scth 	int	quoted = 0;
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	ptr = prev_member;
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	/* skip white space */
6410Sstevel@tonic-gate 	while (*ptr == '\t' || *ptr == ' ')
6420Sstevel@tonic-gate 		ptr++;
6430Sstevel@tonic-gate 
644*4145Scth 	/* if unquote skip leading quote */
645*4145Scth 	if (unquote && *ptr == '"') {
646*4145Scth 		quoted++;
647*4145Scth 		ptr++;
648*4145Scth 	}
649*4145Scth 
650*4145Scth 	/* read thru the current entry looking for end, separator, or unquote */
651*4145Scth 	while (*ptr &&
652*4145Scth 	    (*ptr != separator) &&
653*4145Scth 	    ((separator != ' ') || (*ptr != '\t')) &&
654*4145Scth 	    (!quoted || (*ptr != '"'))) {
6550Sstevel@tonic-gate 		*current_entry++ = *ptr++;
6560Sstevel@tonic-gate 	}
6570Sstevel@tonic-gate 	*current_entry = '\0';
6580Sstevel@tonic-gate 
659*4145Scth 	if (separator && (*ptr == separator))
660*4145Scth 		ptr++;	/* skip over separator */
661*4145Scth 	if (quoted && (*ptr == '"'))
662*4145Scth 		ptr++;	/* skip over trailing quote */
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	/* skip white space */
6650Sstevel@tonic-gate 	while (*ptr == '\t' || *ptr == ' ') {
6660Sstevel@tonic-gate 		ptr++;
6670Sstevel@tonic-gate 	}
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate 	return (ptr);
6700Sstevel@tonic-gate }
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate void
6730Sstevel@tonic-gate enter_lock(void)
6740Sstevel@tonic-gate {
6753442Svikram 	struct flock lock;
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	/*
6780Sstevel@tonic-gate 	 * attempt to create the lock file
6790Sstevel@tonic-gate 	 */
6803442Svikram 	add_rem_lock_fd = open(add_rem_lock, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
6813442Svikram 	if (add_rem_lock_fd < 0) {
6823442Svikram 		(void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
6833442Svikram 		    add_rem_lock, strerror(errno));
6843442Svikram 		exit(1);
6853442Svikram 	}
6863442Svikram 
6873442Svikram 	lock.l_type = F_WRLCK;
6883442Svikram 	lock.l_whence = SEEK_SET;
6893442Svikram 	lock.l_start = 0;
6903442Svikram 	lock.l_len = 0;
6913442Svikram 
6923442Svikram 	/* Try for the lock but don't wait. */
6933442Svikram 	if (fcntl(add_rem_lock_fd, F_SETLK, &lock) == -1) {
6943442Svikram 		if (errno == EACCES || errno == EAGAIN) {
6950Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_PROG_IN_USE));
6960Sstevel@tonic-gate 		} else {
6973442Svikram 			(void) fprintf(stderr, gettext(ERR_LOCK),
6983442Svikram 			    add_rem_lock, strerror(errno));
6990Sstevel@tonic-gate 		}
7000Sstevel@tonic-gate 		exit(1);
7010Sstevel@tonic-gate 	}
7020Sstevel@tonic-gate }
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate void
7050Sstevel@tonic-gate err_exit(void)
7060Sstevel@tonic-gate {
7070Sstevel@tonic-gate 	/* release memory allocated for moddir */
7080Sstevel@tonic-gate 	cleanup_moddir();
7090Sstevel@tonic-gate 	/* remove add_drv/rem_drv lock */
7100Sstevel@tonic-gate 	exit_unlock();
7110Sstevel@tonic-gate 	exit(1);
7120Sstevel@tonic-gate }
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate void
7150Sstevel@tonic-gate cleanup_moddir(void)
7160Sstevel@tonic-gate {
7170Sstevel@tonic-gate 	struct drvmod_dir *walk_ptr;
7180Sstevel@tonic-gate 	struct drvmod_dir *free_ptr = moddir;
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	while (free_ptr != NULL) {
7210Sstevel@tonic-gate 		walk_ptr = free_ptr->next;
7220Sstevel@tonic-gate 		free(free_ptr);
7230Sstevel@tonic-gate 		free_ptr = walk_ptr;
7240Sstevel@tonic-gate 	}
7250Sstevel@tonic-gate }
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate void
7280Sstevel@tonic-gate exit_unlock(void)
7290Sstevel@tonic-gate {
7303442Svikram 	struct flock unlock;
7313442Svikram 
7323442Svikram 	if (add_rem_lock_fd < 0)
7333442Svikram 		return;
7340Sstevel@tonic-gate 
7353442Svikram 	unlock.l_type = F_UNLCK;
7363442Svikram 	unlock.l_whence = SEEK_SET;
7373442Svikram 	unlock.l_start = 0;
7383442Svikram 	unlock.l_len = 0;
7393442Svikram 
7403442Svikram 	if (fcntl(add_rem_lock_fd, F_SETLK, &unlock) == -1) {
7413442Svikram 		(void) fprintf(stderr, gettext(ERR_UNLOCK),
7423442Svikram 		    add_rem_lock, strerror(errno));
7433442Svikram 	} else {
7443442Svikram 		(void) close(add_rem_lock_fd);
7453442Svikram 		add_rem_lock_fd = -1;
7460Sstevel@tonic-gate 	}
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate /*
7500Sstevel@tonic-gate  * error adding driver; need to back out any changes to files.
7510Sstevel@tonic-gate  * check flag to see which files need entries removed
7520Sstevel@tonic-gate  * entry removal based on driver name
7530Sstevel@tonic-gate  */
7540Sstevel@tonic-gate void
7550Sstevel@tonic-gate remove_entry(
7560Sstevel@tonic-gate 	int c_flag,
7570Sstevel@tonic-gate 	char *driver_name)
7580Sstevel@tonic-gate {
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 	if (c_flag & CLEAN_NAM_MAJ) {
7610Sstevel@tonic-gate 		if (delete_entry(name_to_major, driver_name, " ",
7620Sstevel@tonic-gate 		    NULL) == ERROR) {
7630Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_CLEAN),
7640Sstevel@tonic-gate 			    name_to_major, driver_name);
7650Sstevel@tonic-gate 		}
7660Sstevel@tonic-gate 	}
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate 	if (c_flag & CLEAN_DRV_ALIAS) {
7690Sstevel@tonic-gate 		if (delete_entry(driver_aliases, driver_name, " ",
7700Sstevel@tonic-gate 		    NULL) == ERROR) {
7710Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
7720Sstevel@tonic-gate 			    driver_name, driver_aliases);
7730Sstevel@tonic-gate 		}
7740Sstevel@tonic-gate 	}
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	if (c_flag & CLEAN_DRV_CLASSES) {
7770Sstevel@tonic-gate 		if (delete_entry(driver_classes, driver_name, "\t", NULL) ==
7780Sstevel@tonic-gate 		    ERROR) {
7790Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
7800Sstevel@tonic-gate 			    driver_name, driver_classes);
7810Sstevel@tonic-gate 		}
7820Sstevel@tonic-gate 	}
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate 	if (c_flag & CLEAN_MINOR_PERM) {
7850Sstevel@tonic-gate 		if (delete_entry(minor_perm, driver_name, ":", NULL) == ERROR) {
7860Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
7870Sstevel@tonic-gate 			    driver_name, minor_perm);
7880Sstevel@tonic-gate 		}
7890Sstevel@tonic-gate 	}
7900Sstevel@tonic-gate 	/*
7910Sstevel@tonic-gate 	 * There's no point in removing entries from files that don't
7920Sstevel@tonic-gate 	 * exist.  Prevent error messages by checking for file existence
7930Sstevel@tonic-gate 	 * first.
7940Sstevel@tonic-gate 	 */
7950Sstevel@tonic-gate 	if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
7960Sstevel@tonic-gate 	    access(device_policy, F_OK) == 0) {
7970Sstevel@tonic-gate 		if (delete_plcy_entry(device_policy, driver_name) == ERROR) {
7980Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
7990Sstevel@tonic-gate 				driver_name, device_policy);
8000Sstevel@tonic-gate 		}
8010Sstevel@tonic-gate 	}
8020Sstevel@tonic-gate 	if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
8030Sstevel@tonic-gate 	    access(extra_privs, F_OK) == 0) {
8040Sstevel@tonic-gate 		if (delete_entry(extra_privs, driver_name, ":", NULL) ==
8050Sstevel@tonic-gate 		    ERROR) {
8060Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
8070Sstevel@tonic-gate 				driver_name, extra_privs);
8080Sstevel@tonic-gate 		}
8090Sstevel@tonic-gate 	}
8100Sstevel@tonic-gate }
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate int
8130Sstevel@tonic-gate check_perms_aliases(
8140Sstevel@tonic-gate 	int m_flag,
8150Sstevel@tonic-gate 	int i_flag)
8160Sstevel@tonic-gate {
8170Sstevel@tonic-gate 	/*
8180Sstevel@tonic-gate 	 * If neither i_flag nor m_flag are specified no need to check the
8190Sstevel@tonic-gate 	 * files for access permissions
8200Sstevel@tonic-gate 	 */
8210Sstevel@tonic-gate 	if (!m_flag && !i_flag)
8220Sstevel@tonic-gate 		return (NOERR);
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	/* check minor_perm file : exits and is writable */
8250Sstevel@tonic-gate 	if (m_flag) {
8260Sstevel@tonic-gate 		if (access(minor_perm, R_OK | W_OK)) {
8270Sstevel@tonic-gate 			perror(NULL);
8280Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
8290Sstevel@tonic-gate 			    minor_perm);
8300Sstevel@tonic-gate 			return (ERROR);
8310Sstevel@tonic-gate 		}
8320Sstevel@tonic-gate 	}
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	/* check driver_aliases file : exits and is writable */
8350Sstevel@tonic-gate 	if (i_flag) {
8360Sstevel@tonic-gate 		if (access(driver_aliases, R_OK | W_OK)) {
8370Sstevel@tonic-gate 			perror(NULL);
8380Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
8390Sstevel@tonic-gate 			    driver_aliases);
8400Sstevel@tonic-gate 			return (ERROR);
8410Sstevel@tonic-gate 		}
8420Sstevel@tonic-gate 	}
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate 	return (NOERR);
8450Sstevel@tonic-gate }
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate int
8490Sstevel@tonic-gate check_name_to_major(int mode)
8500Sstevel@tonic-gate {
8510Sstevel@tonic-gate 	/* check name_to_major file : exists and is writable */
8520Sstevel@tonic-gate 	if (access(name_to_major, mode)) {
8530Sstevel@tonic-gate 		perror(NULL);
8540Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
8550Sstevel@tonic-gate 		    name_to_major);
8560Sstevel@tonic-gate 		return (ERROR);
8570Sstevel@tonic-gate 	}
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	return (NOERR);
8600Sstevel@tonic-gate }
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate /*
8640Sstevel@tonic-gate  * All this stuff is to support a server installing
8650Sstevel@tonic-gate  * drivers on diskless clients.  When on the server
8660Sstevel@tonic-gate  * need to prepend the basedir
8670Sstevel@tonic-gate  */
8680Sstevel@tonic-gate int
8690Sstevel@tonic-gate build_filenames(char *basedir)
8700Sstevel@tonic-gate {
8710Sstevel@tonic-gate 	int len;
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 	if (basedir == NULL) {
8740Sstevel@tonic-gate 		driver_aliases = DRIVER_ALIAS;
8750Sstevel@tonic-gate 		driver_classes = DRIVER_CLASSES;
8760Sstevel@tonic-gate 		minor_perm = MINOR_PERM;
8770Sstevel@tonic-gate 		name_to_major = NAM_TO_MAJ;
8780Sstevel@tonic-gate 		rem_name_to_major = REM_NAM_TO_MAJ;
8790Sstevel@tonic-gate 		add_rem_lock = ADD_REM_LOCK;
8800Sstevel@tonic-gate 		tmphold = TMPHOLD;
8810Sstevel@tonic-gate 		devfs_root = DEVFS_ROOT;
8820Sstevel@tonic-gate 		device_policy = DEV_POLICY;
8830Sstevel@tonic-gate 		extra_privs = EXTRA_PRIVS;
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 	} else {
8860Sstevel@tonic-gate 		len = strlen(basedir);
8870Sstevel@tonic-gate 
8880Sstevel@tonic-gate 		driver_aliases = malloc(len + sizeof (DRIVER_ALIAS));
8890Sstevel@tonic-gate 		driver_classes = malloc(len + sizeof (DRIVER_CLASSES));
8900Sstevel@tonic-gate 		minor_perm = malloc(len + sizeof (MINOR_PERM));
8910Sstevel@tonic-gate 		name_to_major = malloc(len + sizeof (NAM_TO_MAJ));
8920Sstevel@tonic-gate 		rem_name_to_major = malloc(len + sizeof (REM_NAM_TO_MAJ));
8930Sstevel@tonic-gate 		add_rem_lock = malloc(len + sizeof (ADD_REM_LOCK));
8940Sstevel@tonic-gate 		tmphold = malloc(len + sizeof (TMPHOLD));
8950Sstevel@tonic-gate 		devfs_root = malloc(len + sizeof (DEVFS_ROOT));
8960Sstevel@tonic-gate 		device_policy = malloc(len + sizeof (DEV_POLICY));
8970Sstevel@tonic-gate 		extra_privs = malloc(len + sizeof (EXTRA_PRIVS));
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 		if ((driver_aliases == NULL) ||
9010Sstevel@tonic-gate 		    (driver_classes == NULL) ||
9020Sstevel@tonic-gate 		    (minor_perm == NULL) ||
9030Sstevel@tonic-gate 		    (name_to_major == NULL) ||
9040Sstevel@tonic-gate 		    (rem_name_to_major == NULL) ||
9050Sstevel@tonic-gate 		    (add_rem_lock == NULL) ||
9060Sstevel@tonic-gate 		    (tmphold == NULL) ||
9070Sstevel@tonic-gate 		    (devfs_root == NULL) ||
9080Sstevel@tonic-gate 		    (device_policy == NULL) ||
9090Sstevel@tonic-gate 		    (extra_privs == NULL)) {
9100Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
9110Sstevel@tonic-gate 			return (ERROR);
9120Sstevel@tonic-gate 		}
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 		(void) sprintf(driver_aliases, "%s%s", basedir, DRIVER_ALIAS);
9150Sstevel@tonic-gate 		(void) sprintf(driver_classes, "%s%s", basedir, DRIVER_CLASSES);
9160Sstevel@tonic-gate 		(void) sprintf(minor_perm, "%s%s", basedir, MINOR_PERM);
9170Sstevel@tonic-gate 		(void) sprintf(name_to_major, "%s%s", basedir, NAM_TO_MAJ);
9180Sstevel@tonic-gate 		(void) sprintf(rem_name_to_major, "%s%s", basedir,
9190Sstevel@tonic-gate 				REM_NAM_TO_MAJ);
9200Sstevel@tonic-gate 		(void) sprintf(add_rem_lock, "%s%s", basedir, ADD_REM_LOCK);
9210Sstevel@tonic-gate 		(void) sprintf(tmphold, "%s%s", basedir, TMPHOLD);
9220Sstevel@tonic-gate 		(void) sprintf(devfs_root, "%s%s", basedir, DEVFS_ROOT);
9230Sstevel@tonic-gate 		(void) sprintf(device_policy, "%s%s", basedir, DEV_POLICY);
9240Sstevel@tonic-gate 		(void) sprintf(extra_privs, "%s%s", basedir, EXTRA_PRIVS);
9250Sstevel@tonic-gate 	}
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 	return (NOERR);
9280Sstevel@tonic-gate }
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate static int
9310Sstevel@tonic-gate exec_command(char *path, char *cmdline[MAX_CMD_LINE])
9320Sstevel@tonic-gate {
9330Sstevel@tonic-gate 	pid_t pid;
9340Sstevel@tonic-gate 	uint_t stat_loc;
9350Sstevel@tonic-gate 	int waitstat;
9360Sstevel@tonic-gate 	int exit_status;
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 	/* child */
9390Sstevel@tonic-gate 	if ((pid = fork()) == 0) {
9400Sstevel@tonic-gate 		(void) execv(path, cmdline);
9410Sstevel@tonic-gate 		perror(NULL);
9420Sstevel@tonic-gate 		return (ERROR);
9430Sstevel@tonic-gate 	} else if (pid == -1) {
9440Sstevel@tonic-gate 		/* fork failed */
9450Sstevel@tonic-gate 		perror(NULL);
9460Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_FORK_FAIL), cmdline);
9470Sstevel@tonic-gate 		return (ERROR);
9480Sstevel@tonic-gate 	} else {
9490Sstevel@tonic-gate 		/* parent */
9500Sstevel@tonic-gate 		do {
9510Sstevel@tonic-gate 			waitstat = waitpid(pid, (int *)&stat_loc, 0);
9520Sstevel@tonic-gate 
9530Sstevel@tonic-gate 		} while ((!WIFEXITED(stat_loc) &&
9540Sstevel@tonic-gate 			!WIFSIGNALED(stat_loc)) || (waitstat == 0));
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 		exit_status = WEXITSTATUS(stat_loc);
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 		return (exit_status);
9590Sstevel@tonic-gate 	}
9600Sstevel@tonic-gate }
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate /*
9630Sstevel@tonic-gate  * check that major_num doesn't exceed maximum on this machine
9640Sstevel@tonic-gate  * do this here to support add_drv on server for diskless clients
9650Sstevel@tonic-gate  */
9660Sstevel@tonic-gate int
9670Sstevel@tonic-gate config_driver(
9680Sstevel@tonic-gate 	char *driver_name,
9690Sstevel@tonic-gate 	major_t major_num,
9700Sstevel@tonic-gate 	char *aliases,
9710Sstevel@tonic-gate 	char *classes,
9720Sstevel@tonic-gate 	int cleanup_flag,
9730Sstevel@tonic-gate 	int verbose_flag)
9740Sstevel@tonic-gate {
9750Sstevel@tonic-gate 	int max_dev;
9760Sstevel@tonic-gate 	int n = 0;
9770Sstevel@tonic-gate 	char *cmdline[MAX_CMD_LINE];
9780Sstevel@tonic-gate 	char maj_num[128];
9790Sstevel@tonic-gate 	char *previous;
9800Sstevel@tonic-gate 	char *current;
9810Sstevel@tonic-gate 	int exec_status;
9820Sstevel@tonic-gate 	int len;
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 	if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
9850Sstevel@tonic-gate 		perror(NULL);
9860Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
9870Sstevel@tonic-gate 		return (ERROR);
9880Sstevel@tonic-gate 	}
9890Sstevel@tonic-gate 
9900Sstevel@tonic-gate 	if (major_num >= max_dev) {
9910Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS),
9920Sstevel@tonic-gate 		    major_num, max_dev);
9930Sstevel@tonic-gate 		return (ERROR);
9940Sstevel@tonic-gate 	}
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	/* bind major number and driver name */
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 	/* build command line */
9990Sstevel@tonic-gate 	cmdline[n++] = DRVCONFIG;
10000Sstevel@tonic-gate 	if (verbose_flag) {
10010Sstevel@tonic-gate 		cmdline[n++] = "-v";
10020Sstevel@tonic-gate 	}
10030Sstevel@tonic-gate 	cmdline[n++] = "-b";
10040Sstevel@tonic-gate 	if (classes) {
10050Sstevel@tonic-gate 		cmdline[n++] = "-c";
10060Sstevel@tonic-gate 		cmdline[n++] = classes;
10070Sstevel@tonic-gate 	}
10080Sstevel@tonic-gate 	cmdline[n++] = "-i";
10090Sstevel@tonic-gate 	cmdline[n++] = driver_name;
10100Sstevel@tonic-gate 	cmdline[n++] = "-m";
10110Sstevel@tonic-gate 	(void) sprintf(maj_num, "%lu", major_num);
10120Sstevel@tonic-gate 	cmdline[n++] = maj_num;
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	if (aliases != NULL) {
10150Sstevel@tonic-gate 		len = strlen(aliases);
10160Sstevel@tonic-gate 		previous = aliases;
10170Sstevel@tonic-gate 		do {
10180Sstevel@tonic-gate 			cmdline[n++] = "-a";
10190Sstevel@tonic-gate 			cmdline[n] = calloc(len + 1, 1);
10200Sstevel@tonic-gate 			if (cmdline[n] == NULL) {
10210Sstevel@tonic-gate 				(void) fprintf(stderr,
10220Sstevel@tonic-gate 				    gettext(ERR_NO_MEM));
10230Sstevel@tonic-gate 				return (ERROR);
10240Sstevel@tonic-gate 			}
10250Sstevel@tonic-gate 			current = get_entry(previous,
1026*4145Scth 			    cmdline[n++], ' ', 0);
10270Sstevel@tonic-gate 			previous = current;
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 		} while (*current != '\0');
10300Sstevel@tonic-gate 
10310Sstevel@tonic-gate 	}
10320Sstevel@tonic-gate 	cmdline[n] = (char *)0;
10330Sstevel@tonic-gate 
10340Sstevel@tonic-gate 	exec_status = exec_command(DRVCONFIG_PATH, cmdline);
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	if (exec_status == NOERR)
10370Sstevel@tonic-gate 		return (NOERR);
10380Sstevel@tonic-gate 	perror(NULL);
10390Sstevel@tonic-gate 	remove_entry(cleanup_flag, driver_name);
10400Sstevel@tonic-gate 	return (ERROR);
10410Sstevel@tonic-gate }
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate void
10440Sstevel@tonic-gate load_driver(char *driver_name, int verbose_flag)
10450Sstevel@tonic-gate {
10460Sstevel@tonic-gate 	int n = 0;
10470Sstevel@tonic-gate 	char *cmdline[MAX_CMD_LINE];
10480Sstevel@tonic-gate 	int exec_status;
10490Sstevel@tonic-gate 
10500Sstevel@tonic-gate 	/* build command line */
10510Sstevel@tonic-gate 	cmdline[n++] = DEVFSADM;
10520Sstevel@tonic-gate 	if (verbose_flag) {
10530Sstevel@tonic-gate 		cmdline[n++] = "-v";
10540Sstevel@tonic-gate 	}
10550Sstevel@tonic-gate 	cmdline[n++] = "-i";
10560Sstevel@tonic-gate 	cmdline[n++] = driver_name;
10570Sstevel@tonic-gate 	cmdline[n] = (char *)0;
10580Sstevel@tonic-gate 
10590Sstevel@tonic-gate 	exec_status = exec_command(DEVFSADM_PATH, cmdline);
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 	if (exec_status != NOERR) {
10620Sstevel@tonic-gate 		/* no clean : name and major number are bound */
10630Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CONFIG),
10640Sstevel@tonic-gate 			driver_name);
10650Sstevel@tonic-gate 	}
10660Sstevel@tonic-gate }
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate void
10690Sstevel@tonic-gate get_modid(char *driver_name, int *mod)
10700Sstevel@tonic-gate {
10710Sstevel@tonic-gate 	struct modinfo	modinfo;
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate 	modinfo.mi_id = -1;
10740Sstevel@tonic-gate 	modinfo.mi_info = MI_INFO_ALL;
10750Sstevel@tonic-gate 	do {
10760Sstevel@tonic-gate 		/*
10770Sstevel@tonic-gate 		 * If we are at the end of the list of loaded modules
10780Sstevel@tonic-gate 		 * then set *mod = -1 and return
10790Sstevel@tonic-gate 		 */
10800Sstevel@tonic-gate 		if (modctl(MODINFO, 0, &modinfo) < 0) {
10810Sstevel@tonic-gate 			*mod = -1;
10820Sstevel@tonic-gate 			return;
10830Sstevel@tonic-gate 		}
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 		*mod = modinfo.mi_id;
10860Sstevel@tonic-gate 	} while (strcmp(driver_name, modinfo.mi_name) != 0);
10870Sstevel@tonic-gate }
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate int
10900Sstevel@tonic-gate create_reconfig(char *basedir)
10910Sstevel@tonic-gate {
10920Sstevel@tonic-gate 	char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1];
10930Sstevel@tonic-gate 	FILE *reconfig_fp;
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 	if (basedir != NULL) {
10960Sstevel@tonic-gate 		(void) strcpy(reconfig_file, basedir);
10970Sstevel@tonic-gate 		(void) strcat(reconfig_file, RECONFIGURE);
10980Sstevel@tonic-gate 	} else {
10990Sstevel@tonic-gate 		(void) strcpy(reconfig_file, RECONFIGURE);
11000Sstevel@tonic-gate 	}
11010Sstevel@tonic-gate 	if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL)
11020Sstevel@tonic-gate 		return (ERROR);
11030Sstevel@tonic-gate 
11040Sstevel@tonic-gate 	(void) fclose(reconfig_fp);
11050Sstevel@tonic-gate 	return (NOERR);
11060Sstevel@tonic-gate }
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 
11090Sstevel@tonic-gate /*
11100Sstevel@tonic-gate  * update_minor_entry:
11110Sstevel@tonic-gate  *	open file
11120Sstevel@tonic-gate  *	for each entry in list
11130Sstevel@tonic-gate  *		where list entries are separated by <list_separator>
11140Sstevel@tonic-gate  * 		modify entry : driver_name <entry_separator> entry
11150Sstevel@tonic-gate  *	close file
11160Sstevel@tonic-gate  *
11170Sstevel@tonic-gate  *	return error/noerr
11180Sstevel@tonic-gate  */
11190Sstevel@tonic-gate int
11200Sstevel@tonic-gate update_minor_entry(char *driver_name, char *perm_list)
11210Sstevel@tonic-gate {
11220Sstevel@tonic-gate 	FILE *fp;
11230Sstevel@tonic-gate 	FILE *newfp;
11240Sstevel@tonic-gate 	struct group *sysgrp;
11250Sstevel@tonic-gate 	int match = 0;
11262805Seota 	char line[MAX_DBFILE_ENTRY], *cp, *dup;
11272805Seota 	char drv[FILENAME_MAX + 1], *drv_minor;
11280Sstevel@tonic-gate 	char minor[FILENAME_MAX + 1], perm[OPT_LEN + 1];
11290Sstevel@tonic-gate 	char own[OPT_LEN + 1], grp[OPT_LEN + 1];
11300Sstevel@tonic-gate 	int status = NOERR, i;
11310Sstevel@tonic-gate 	char *newfile, *tptr;
11320Sstevel@tonic-gate 	extern void bzero();
11330Sstevel@tonic-gate 
11340Sstevel@tonic-gate 	if ((fp = fopen(minor_perm, "r")) == NULL) {
11350Sstevel@tonic-gate 		perror(NULL);
11360Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
11370Sstevel@tonic-gate 		    minor_perm);
11380Sstevel@tonic-gate 
11390Sstevel@tonic-gate 		return (ERROR);
11400Sstevel@tonic-gate 	}
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	/*
11430Sstevel@tonic-gate 	 * Build filename for temporary file
11440Sstevel@tonic-gate 	 */
11450Sstevel@tonic-gate 	if ((tptr = calloc(strlen(minor_perm) + strlen(XEND) + 1, 1)) == NULL) {
11460Sstevel@tonic-gate 		perror(NULL);
11470Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
11480Sstevel@tonic-gate 	}
11490Sstevel@tonic-gate 	(void) strcpy(tptr, minor_perm);
11500Sstevel@tonic-gate 	(void) strcat(tptr, XEND);
11510Sstevel@tonic-gate 
11520Sstevel@tonic-gate 	/*
11530Sstevel@tonic-gate 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
11540Sstevel@tonic-gate 	 * assume a gid of "sys" but we can't undo the damage on already
11550Sstevel@tonic-gate 	 * installed systems unless we force the issue.
11560Sstevel@tonic-gate 	 */
11570Sstevel@tonic-gate 	if ((sysgrp = getgrnam("sys")) != NULL) {
11580Sstevel@tonic-gate 		(void) setgid(sysgrp->gr_gid);
11590Sstevel@tonic-gate 	}
11600Sstevel@tonic-gate 
11610Sstevel@tonic-gate 	newfile = mktemp(tptr);
11620Sstevel@tonic-gate 	if ((newfp = fopen(newfile, "w")) == NULL) {
11630Sstevel@tonic-gate 		perror(NULL);
11640Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
11650Sstevel@tonic-gate 		    newfile);
11660Sstevel@tonic-gate 		return (ERROR);
11670Sstevel@tonic-gate 	}
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 	if (sscanf(perm_list, "%s%s%s%s", minor, perm, own, grp) != 4) {
11700Sstevel@tonic-gate 		status = ERROR;
11710Sstevel@tonic-gate 	}
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
11742805Seota 		/* copy the whole line into dup */
11752805Seota 		if ((dup = strdup(line)) == NULL) {
11762805Seota 			perror(NULL);
11772805Seota 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
11782805Seota 			status = ERROR;
11792805Seota 			break;
11802805Seota 		}
11812805Seota 		/* cut off comments starting with '#' */
11822805Seota 		if ((cp = strchr(dup, '#')) != NULL)
11832805Seota 			*cp = '\0';
11842805Seota 		/* ignore comment or blank lines */
11852805Seota 		if (is_blank(dup)) {
11862805Seota 			if (fputs(line, newfp) == EOF) {
11870Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_UPDATE),
11880Sstevel@tonic-gate 				    minor_perm);
11890Sstevel@tonic-gate 				status = ERROR;
11900Sstevel@tonic-gate 			}
11912805Seota 			free(dup);
11920Sstevel@tonic-gate 			continue;
11930Sstevel@tonic-gate 		}
11940Sstevel@tonic-gate 
11952805Seota 		/* get the driver name */
11962805Seota 		if (sscanf(dup, "%s", drv) != 1) {
11972805Seota 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
11982805Seota 			    minor_perm, line);
11992805Seota 			status = ERROR;
12002805Seota 			free(dup);
12012805Seota 			break;
12022805Seota 		}
12032805Seota 
12042805Seota 		/*
12052805Seota 		 * get the minor name; place the NULL character at the
12062805Seota 		 * end of the driver name, then make the drv_minor
12072805Seota 		 * point to the first character of the minor name.
12082805Seota 		 * the line missing ':' must be treated as a broken one.
12092805Seota 		 */
12102805Seota 		i = strcspn(drv, ":");
12112805Seota 		if (i == strlen(drv)) {
12120Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
12130Sstevel@tonic-gate 			    minor_perm, line);
12140Sstevel@tonic-gate 			status = ERROR;
12152805Seota 			free(dup);
12162805Seota 			break;
12170Sstevel@tonic-gate 		}
12182805Seota 		drv[i] =  '\0';
12192805Seota 		drv_minor = &drv[strlen(drv) + 1];
12200Sstevel@tonic-gate 
12212805Seota 		/*
12222805Seota 		 * compare both of the driver name and the minor name.
12232805Seota 		 * then the new line should be written to the file if
12242805Seota 		 * both of them match
12252805Seota 		 */
12260Sstevel@tonic-gate 		if ((strcmp(drv, driver_name) == 0) &&
12270Sstevel@tonic-gate 		    (strcmp(minor, drv_minor) == 0)) {
12282805Seota 			/* if it has a comment, keep it */
12292805Seota 			if (cp != NULL) {
12302805Seota 				cp++; /* skip a terminator */
12312805Seota 				(void) sprintf(line, "%s:%s %s %s %s #%s\n",
12322805Seota 				    drv, minor, perm, own, grp, cp);
12332805Seota 			} else {
12342805Seota 				(void) sprintf(line, "%s:%s %s %s %s\n",
12352805Seota 				    drv, minor, perm, own, grp);
12362805Seota 			}
12370Sstevel@tonic-gate 			match = 1;
12380Sstevel@tonic-gate 		}
12392805Seota 		free(dup);
12400Sstevel@tonic-gate 
12412805Seota 		/* update the file */
12420Sstevel@tonic-gate 		if ((fputs(line, newfp)) == EOF) {
12430Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE),
12440Sstevel@tonic-gate 			    minor_perm);
12450Sstevel@tonic-gate 			status = ERROR;
12460Sstevel@tonic-gate 		}
12470Sstevel@tonic-gate 	}
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate 	if (!match) {
12500Sstevel@tonic-gate 		(void) bzero(line, sizeof (&line[0]));
12510Sstevel@tonic-gate 		(void) sprintf(line, "%s:%s %s %s %s\n",
12520Sstevel@tonic-gate 		    driver_name, minor, perm, own, grp);
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 		/* add the new entry */
12550Sstevel@tonic-gate 		if ((fputs(line, newfp)) == EOF) {
12560Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
12570Sstevel@tonic-gate 			status = ERROR;
12580Sstevel@tonic-gate 		}
12590Sstevel@tonic-gate 	}
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 	(void) fclose(fp);
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
12640Sstevel@tonic-gate 		status = ERROR;
12650Sstevel@tonic-gate 
12660Sstevel@tonic-gate 	(void) fclose(newfp);
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 	/*
12690Sstevel@tonic-gate 	 * if error, leave original file, delete new file
12700Sstevel@tonic-gate 	 * if noerr, replace original file with new file
12710Sstevel@tonic-gate 	 */
12720Sstevel@tonic-gate 	if (status == NOERR) {
12730Sstevel@tonic-gate 		if (rename(minor_perm, tmphold) == -1) {
12740Sstevel@tonic-gate 			perror(NULL);
12750Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
12760Sstevel@tonic-gate 			(void) unlink(newfile);
12770Sstevel@tonic-gate 			return (ERROR);
12780Sstevel@tonic-gate 		} else if (rename(newfile, minor_perm) == -1) {
12790Sstevel@tonic-gate 			perror(NULL);
12800Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
12810Sstevel@tonic-gate 			(void) unlink(minor_perm);
12820Sstevel@tonic-gate 			(void) unlink(newfile);
12830Sstevel@tonic-gate 			if (link(tmphold, minor_perm) == -1) {
12840Sstevel@tonic-gate 				perror(NULL);
12850Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
12860Sstevel@tonic-gate 				    minor_perm, tmphold);
12870Sstevel@tonic-gate 			}
12880Sstevel@tonic-gate 			return (ERROR);
12890Sstevel@tonic-gate 		}
12900Sstevel@tonic-gate 		(void) unlink(tmphold);
12910Sstevel@tonic-gate 	} else {
12920Sstevel@tonic-gate 		/*
12930Sstevel@tonic-gate 		 * since there's an error, leave file alone; remove
12940Sstevel@tonic-gate 		 * new file
12950Sstevel@tonic-gate 		 */
12960Sstevel@tonic-gate 		if (unlink(newfile) == -1) {
12970Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
12980Sstevel@tonic-gate 		}
12990Sstevel@tonic-gate 		return (ERROR);
13000Sstevel@tonic-gate 	}
13010Sstevel@tonic-gate 
13020Sstevel@tonic-gate 	return (NOERR);
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate }
13050Sstevel@tonic-gate 
13060Sstevel@tonic-gate 
13070Sstevel@tonic-gate /*
13080Sstevel@tonic-gate  * list_entry:
13090Sstevel@tonic-gate  *	open file
13100Sstevel@tonic-gate  *	read thru file, listing all entries if first entry = driver_name
13110Sstevel@tonic-gate  *	close
13120Sstevel@tonic-gate  */
13130Sstevel@tonic-gate void
13140Sstevel@tonic-gate list_entry(
13150Sstevel@tonic-gate 	char *oldfile,
13160Sstevel@tonic-gate 	char *driver_name,
13170Sstevel@tonic-gate 	char *marker)
13180Sstevel@tonic-gate {
13190Sstevel@tonic-gate 	FILE	*fp;
13200Sstevel@tonic-gate 	int	i;
13212805Seota 	char	line[MAX_DBFILE_ENTRY], *cp;
13220Sstevel@tonic-gate 	char	drv[FILENAME_MAX + 1];
13230Sstevel@tonic-gate 
13240Sstevel@tonic-gate 	if ((fp = fopen(oldfile, "r")) == NULL) {
13250Sstevel@tonic-gate 		perror(NULL);
13260Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
13270Sstevel@tonic-gate 
13280Sstevel@tonic-gate 		return;
13290Sstevel@tonic-gate 	}
13300Sstevel@tonic-gate 
13310Sstevel@tonic-gate 	while (fgets(line, sizeof (line), fp) != NULL) {
13322805Seota 		/* cut off comments starting with '#' */
13332805Seota 		if ((cp = strchr(line, '#')) != NULL)
13342805Seota 			*cp = '\0';
13352805Seota 		/* ignore comment or blank lines */
13362805Seota 		if (is_blank(line))
13370Sstevel@tonic-gate 			continue;
13382805Seota 		/* sanity-check */
13390Sstevel@tonic-gate 		if (sscanf(line, "%s", drv) != 1) {
13400Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
13410Sstevel@tonic-gate 			    oldfile, line);
13420Sstevel@tonic-gate 		}
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
13450Sstevel@tonic-gate 			drv[i] =  '\0';
13460Sstevel@tonic-gate 		}
13470Sstevel@tonic-gate 
13480Sstevel@tonic-gate 		if (strcmp(driver_name, drv) == 0) {
13490Sstevel@tonic-gate 			(void) fprintf(stdout, "%s", line);
13500Sstevel@tonic-gate 		}
13510Sstevel@tonic-gate 	}
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 	(void) fclose(fp);
13540Sstevel@tonic-gate }
13550Sstevel@tonic-gate 
13562805Seota static boolean_t
13572805Seota is_token(char *tok)
13582805Seota {
13592805Seota 	/*
13602805Seota 	 * Check the token here. According to IEEE1275 Open Firmware Boot
13612805Seota 	 * Standard, the name is composed of 1 to 31 letters,
13622805Seota 	 * digits and punctuation characters from the set ",._+-", and
13632805Seota 	 * uppercase and lowercase characters are considered distinct.
13642805Seota 	 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
13652805Seota 	 * However, since either the definition of driver or aliase names is
13662805Seota 	 * not known well, only '#' is avoided explicitly. (the kernel lexical
13672805Seota 	 * analyzer treats it as a start of a comment)
13682805Seota 	 */
13692805Seota 	for (/* nothing */; *tok != '\0'; tok++)
13702805Seota 		if (*tok == '#' || iscntrl(*tok))
13712805Seota 			return (B_FALSE);
13722805Seota 
13732805Seota 	return (B_TRUE);
13742805Seota }
13750Sstevel@tonic-gate 
13760Sstevel@tonic-gate /*
13770Sstevel@tonic-gate  * check each entry in perm_list for:
13780Sstevel@tonic-gate  *	4 arguments
13790Sstevel@tonic-gate  *	permission arg is in valid range
13800Sstevel@tonic-gate  * permlist entries separated by comma
13810Sstevel@tonic-gate  * return ERROR/NOERR
13820Sstevel@tonic-gate  */
13830Sstevel@tonic-gate int
13840Sstevel@tonic-gate check_perm_opts(char *perm_list)
13850Sstevel@tonic-gate {
13860Sstevel@tonic-gate 	char *current_head;
13870Sstevel@tonic-gate 	char *previous_head;
13880Sstevel@tonic-gate 	char *one_entry;
13890Sstevel@tonic-gate 	int i, len, scan_stat;
13900Sstevel@tonic-gate 	char minor[FILENAME_MAX + 1];
13910Sstevel@tonic-gate 	char perm[OPT_LEN + 1];
13920Sstevel@tonic-gate 	char own[OPT_LEN + 1];
13930Sstevel@tonic-gate 	char grp[OPT_LEN + 1];
13940Sstevel@tonic-gate 	char dumb[OPT_LEN + 1];
13950Sstevel@tonic-gate 	int status = NOERR;
13960Sstevel@tonic-gate 	int intperm;
13970Sstevel@tonic-gate 
13980Sstevel@tonic-gate 	len = strlen(perm_list);
13990Sstevel@tonic-gate 
14000Sstevel@tonic-gate 	if (len == 0) {
14010Sstevel@tonic-gate 		return (ERROR);
14020Sstevel@tonic-gate 	}
14030Sstevel@tonic-gate 
14040Sstevel@tonic-gate 	one_entry = calloc(len + 1, 1);
14050Sstevel@tonic-gate 	if (one_entry == NULL) {
14060Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
14070Sstevel@tonic-gate 		return (ERROR);
14080Sstevel@tonic-gate 	}
14090Sstevel@tonic-gate 
14100Sstevel@tonic-gate 	previous_head = perm_list;
14110Sstevel@tonic-gate 	current_head = perm_list;
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 	while (*current_head != '\0') {
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate 		for (i = 0; i <= len; i++)
14160Sstevel@tonic-gate 			one_entry[i] = 0;
14170Sstevel@tonic-gate 
1418*4145Scth 		current_head = get_entry(previous_head, one_entry, ',', 0);
14190Sstevel@tonic-gate 
14200Sstevel@tonic-gate 		previous_head = current_head;
14210Sstevel@tonic-gate 		scan_stat = sscanf(one_entry, "%s%s%s%s%s", minor, perm, own,
14220Sstevel@tonic-gate 		    grp, dumb);
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 		if (scan_stat < 4) {
14250Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_MIS_TOK),
14260Sstevel@tonic-gate 			    "-m", one_entry);
14270Sstevel@tonic-gate 			status = ERROR;
14280Sstevel@tonic-gate 		}
14290Sstevel@tonic-gate 		if (scan_stat > 4) {
14300Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS),
14310Sstevel@tonic-gate 			    "-m", one_entry);
14320Sstevel@tonic-gate 			status = ERROR;
14330Sstevel@tonic-gate 		}
14340Sstevel@tonic-gate 
14350Sstevel@tonic-gate 		intperm = atoi(perm);
14360Sstevel@tonic-gate 		if (intperm < 0000 || intperm > 4777) {
14370Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_MODE), perm);
14380Sstevel@tonic-gate 			status = ERROR;
14390Sstevel@tonic-gate 		}
14400Sstevel@tonic-gate 	}
14410Sstevel@tonic-gate 
14420Sstevel@tonic-gate 	free(one_entry);
14430Sstevel@tonic-gate 	return (status);
14440Sstevel@tonic-gate }
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate 
14470Sstevel@tonic-gate /*
14480Sstevel@tonic-gate  * check each alias :
14490Sstevel@tonic-gate  *	alias list members separated by white space
14500Sstevel@tonic-gate  *	cannot exist as driver name in /etc/name_to_major
14510Sstevel@tonic-gate  *	cannot exist as driver or alias name in /etc/driver_aliases
14520Sstevel@tonic-gate  */
14530Sstevel@tonic-gate int
14540Sstevel@tonic-gate aliases_unique(char *aliases)
14550Sstevel@tonic-gate {
14560Sstevel@tonic-gate 	char *current_head;
14570Sstevel@tonic-gate 	char *previous_head;
14580Sstevel@tonic-gate 	char *one_entry;
14590Sstevel@tonic-gate 	int i, len;
14600Sstevel@tonic-gate 	int is_unique;
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 	len = strlen(aliases);
14630Sstevel@tonic-gate 
14640Sstevel@tonic-gate 	one_entry = calloc(len + 1, 1);
14650Sstevel@tonic-gate 	if (one_entry == NULL) {
14660Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
14670Sstevel@tonic-gate 		return (ERROR);
14680Sstevel@tonic-gate 	}
14690Sstevel@tonic-gate 
14700Sstevel@tonic-gate 	previous_head = aliases;
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 	do {
14730Sstevel@tonic-gate 		for (i = 0; i <= len; i++)
14740Sstevel@tonic-gate 			one_entry[i] = 0;
14750Sstevel@tonic-gate 
1476*4145Scth 		current_head = get_entry(previous_head, one_entry, ' ', 1);
14770Sstevel@tonic-gate 		previous_head = current_head;
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate 		if ((unique_driver_name(one_entry, name_to_major,
14800Sstevel@tonic-gate 		    &is_unique)) == ERROR) {
14810Sstevel@tonic-gate 			free(one_entry);
14820Sstevel@tonic-gate 			return (ERROR);
14830Sstevel@tonic-gate 		}
14840Sstevel@tonic-gate 
14850Sstevel@tonic-gate 		if (is_unique != UNIQUE) {
14860Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ),
14870Sstevel@tonic-gate 			    one_entry);
14880Sstevel@tonic-gate 			free(one_entry);
14890Sstevel@tonic-gate 			return (ERROR);
14900Sstevel@tonic-gate 		}
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 		if (unique_drv_alias(one_entry) != NOERR) {
14930Sstevel@tonic-gate 			free(one_entry);
14940Sstevel@tonic-gate 			return (ERROR);
14950Sstevel@tonic-gate 		}
14960Sstevel@tonic-gate 
14972805Seota 		if (!is_token(one_entry)) {
14982805Seota 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
14992805Seota 			    "-i", one_entry);
15002805Seota 			free(one_entry);
15012805Seota 			return (ERROR);
15022805Seota 		}
15032805Seota 
15040Sstevel@tonic-gate 	} while (*current_head != '\0');
15050Sstevel@tonic-gate 
15060Sstevel@tonic-gate 	free(one_entry);
15070Sstevel@tonic-gate 
15080Sstevel@tonic-gate 	return (NOERR);
15090Sstevel@tonic-gate 
15100Sstevel@tonic-gate }
15110Sstevel@tonic-gate 
15120Sstevel@tonic-gate 
1513*4145Scth /*
1514*4145Scth  * check each alias :
1515*4145Scth  *	if path-oriented alias, path exists
1516*4145Scth  */
1517*4145Scth int
1518*4145Scth aliases_paths_exist(char *aliases)
1519*4145Scth {
1520*4145Scth 	char *current_head;
1521*4145Scth 	char *previous_head;
1522*4145Scth 	char *one_entry;
1523*4145Scth 	int i, len;
1524*4145Scth 	char path[MAXPATHLEN];
1525*4145Scth 	struct stat buf;
1526*4145Scth 
1527*4145Scth 	len = strlen(aliases);
1528*4145Scth 
1529*4145Scth 	one_entry = calloc(len + 1, 1);
1530*4145Scth 	if (one_entry == NULL) {
1531*4145Scth 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1532*4145Scth 		return (ERROR);
1533*4145Scth 	}
1534*4145Scth 
1535*4145Scth 	previous_head = aliases;
1536*4145Scth 
1537*4145Scth 	do {
1538*4145Scth 		for (i = 0; i <= len; i++)
1539*4145Scth 			one_entry[i] = 0;
1540*4145Scth 
1541*4145Scth 		current_head = get_entry(previous_head, one_entry, ' ', 1);
1542*4145Scth 		previous_head = current_head;
1543*4145Scth 
1544*4145Scth 		/* if the alias is a path, ensure that the path exists */
1545*4145Scth 		if (*one_entry != '/')
1546*4145Scth 			continue;
1547*4145Scth 		(void) snprintf(path, sizeof (path), "/devices/%s", one_entry);
1548*4145Scth 		if (stat(path, &buf) == 0)
1549*4145Scth 			continue;
1550*4145Scth 
1551*4145Scth 		/* no device at specified path-oriented alias path */
1552*4145Scth 		(void) fprintf(stderr, gettext(ERR_PATH_ORIENTED_ALIAS),
1553*4145Scth 		    one_entry);
1554*4145Scth 		free(one_entry);
1555*4145Scth 		return (ERROR);
1556*4145Scth 
1557*4145Scth 	} while (*current_head != '\0');
1558*4145Scth 
1559*4145Scth 	free(one_entry);
1560*4145Scth 
1561*4145Scth 	return (NOERR);
1562*4145Scth }
1563*4145Scth 
1564*4145Scth 
15650Sstevel@tonic-gate int
15660Sstevel@tonic-gate update_driver_aliases(
15670Sstevel@tonic-gate 	char *driver_name,
15680Sstevel@tonic-gate 	char *aliases)
15690Sstevel@tonic-gate {
15700Sstevel@tonic-gate 	/* make call to update the aliases file */
1571*4145Scth 	return (append_to_file(driver_name, aliases, driver_aliases,
1572*4145Scth 	    ' ', " ", 1));
15730Sstevel@tonic-gate }
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate 
15760Sstevel@tonic-gate int
15770Sstevel@tonic-gate unique_drv_alias(char *drv_alias)
15780Sstevel@tonic-gate {
15790Sstevel@tonic-gate 	FILE *fp;
15800Sstevel@tonic-gate 	char drv[FILENAME_MAX + 1];
15812805Seota 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
15820Sstevel@tonic-gate 	char alias[FILENAME_MAX + 1];
1583*4145Scth 	char *a;
15840Sstevel@tonic-gate 	int status = NOERR;
15850Sstevel@tonic-gate 
15860Sstevel@tonic-gate 	fp = fopen(driver_aliases, "r");
15870Sstevel@tonic-gate 
15880Sstevel@tonic-gate 	if (fp != NULL) {
15890Sstevel@tonic-gate 		while ((fgets(line, sizeof (line), fp) != 0) &&
15900Sstevel@tonic-gate 		    status != ERROR) {
15912805Seota 			/* cut off comments starting with '#' */
15922805Seota 			if ((cp = strchr(line, '#')) != NULL)
15932805Seota 				*cp = '\0';
15942805Seota 			/* ignore comment or blank lines */
15952805Seota 			if (is_blank(line))
15962805Seota 				continue;
15972805Seota 			/* sanity-check */
15980Sstevel@tonic-gate 			if (sscanf(line, "%s %s", drv, alias) != 2)
15990Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
16000Sstevel@tonic-gate 				    driver_aliases, line);
16010Sstevel@tonic-gate 
1602*4145Scth 			/* unquote for compare */
1603*4145Scth 			if ((*alias == '"') &&
1604*4145Scth 			    (*(alias + strlen(alias) - 1) == '"')) {
1605*4145Scth 				a = &alias[1];
1606*4145Scth 				alias[strlen(alias) - 1] = '\0';
1607*4145Scth 			} else
1608*4145Scth 				a = alias;
1609*4145Scth 
16100Sstevel@tonic-gate 			if ((strcmp(drv_alias, drv) == 0) ||
1611*4145Scth 			    (strcmp(drv_alias, a) == 0)) {
16120Sstevel@tonic-gate 				(void) fprintf(stderr,
16130Sstevel@tonic-gate 				    gettext(ERR_ALIAS_IN_USE),
16140Sstevel@tonic-gate 				    drv_alias);
16150Sstevel@tonic-gate 				status = ERROR;
16160Sstevel@tonic-gate 			}
16170Sstevel@tonic-gate 		}
16180Sstevel@tonic-gate 		(void) fclose(fp);
16190Sstevel@tonic-gate 		return (status);
16200Sstevel@tonic-gate 	} else {
16210Sstevel@tonic-gate 		perror(NULL);
16220Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
16230Sstevel@tonic-gate 		return (ERROR);
16240Sstevel@tonic-gate 	}
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate }
16270Sstevel@tonic-gate 
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate /*
16300Sstevel@tonic-gate  * search for driver_name in first field of file file_name
16310Sstevel@tonic-gate  * searching name_to_major and driver_aliases: name separated from rest of
16320Sstevel@tonic-gate  * line by blank
16330Sstevel@tonic-gate  * if there return
16340Sstevel@tonic-gate  * else return
16350Sstevel@tonic-gate  */
16360Sstevel@tonic-gate int
16370Sstevel@tonic-gate unique_driver_name(char *driver_name, char *file_name,
16380Sstevel@tonic-gate 	int *is_unique)
16390Sstevel@tonic-gate {
16400Sstevel@tonic-gate 	int ret;
16410Sstevel@tonic-gate 
16420Sstevel@tonic-gate 	if ((ret = get_major_no(driver_name, file_name)) == ERROR) {
16430Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
16440Sstevel@tonic-gate 		    file_name);
16450Sstevel@tonic-gate 	} else {
16460Sstevel@tonic-gate 		/* XXX */
16470Sstevel@tonic-gate 		/* check alias file for name collision */
16480Sstevel@tonic-gate 		if (unique_drv_alias(driver_name) == ERROR) {
16490Sstevel@tonic-gate 			ret = ERROR;
16500Sstevel@tonic-gate 		} else {
16510Sstevel@tonic-gate 			if (ret != UNIQUE)
16520Sstevel@tonic-gate 				*is_unique = NOT_UNIQUE;
16530Sstevel@tonic-gate 			else
16540Sstevel@tonic-gate 				*is_unique = ret;
16550Sstevel@tonic-gate 			ret = NOERR;
16560Sstevel@tonic-gate 		}
16570Sstevel@tonic-gate 	}
16580Sstevel@tonic-gate 	return (ret);
16590Sstevel@tonic-gate }
16600Sstevel@tonic-gate 
16610Sstevel@tonic-gate 
16620Sstevel@tonic-gate int
16630Sstevel@tonic-gate check_space_within_quote(char *str)
16640Sstevel@tonic-gate {
16650Sstevel@tonic-gate 	register int i;
16660Sstevel@tonic-gate 	register int len;
16670Sstevel@tonic-gate 	int quoted = 0;
16680Sstevel@tonic-gate 
16690Sstevel@tonic-gate 	len = strlen(str);
16700Sstevel@tonic-gate 	for (i = 0; i < len; i++, str++) {
16710Sstevel@tonic-gate 		if (*str == '"') {
16720Sstevel@tonic-gate 			if (quoted == 0)
16730Sstevel@tonic-gate 				quoted++;
16740Sstevel@tonic-gate 			else
16750Sstevel@tonic-gate 				quoted--;
16760Sstevel@tonic-gate 		} else if (*str == ' ' && quoted)
16770Sstevel@tonic-gate 			return (ERROR);
16780Sstevel@tonic-gate 	}
16790Sstevel@tonic-gate 
16800Sstevel@tonic-gate 	return (0);
16810Sstevel@tonic-gate }
16820Sstevel@tonic-gate 
16830Sstevel@tonic-gate 
16840Sstevel@tonic-gate /*
16850Sstevel@tonic-gate  * get major number
16860Sstevel@tonic-gate  * write driver_name major_num to name_to_major file
16870Sstevel@tonic-gate  * major_num returned in major_num
16880Sstevel@tonic-gate  * return success/failure
16890Sstevel@tonic-gate  */
16900Sstevel@tonic-gate int
16910Sstevel@tonic-gate update_name_to_major(char *driver_name, major_t *major_num, int server)
16920Sstevel@tonic-gate {
16930Sstevel@tonic-gate 	char major[MAX_STR_MAJOR + 1];
16940Sstevel@tonic-gate 	struct stat buf;
16950Sstevel@tonic-gate 	char *num_list;
16960Sstevel@tonic-gate 	char drv_majnum_str[MAX_STR_MAJOR + 1];
16970Sstevel@tonic-gate 	int new_maj = -1;
16980Sstevel@tonic-gate 	int i, tmp = 0, is_unique, have_rem_n2m = 0;
16990Sstevel@tonic-gate 	int max_dev = 0;
17000Sstevel@tonic-gate 
17010Sstevel@tonic-gate 	/*
17020Sstevel@tonic-gate 	 * if driver_name already in rem_name_to_major
17030Sstevel@tonic-gate 	 * 	delete entry from rem_nam_to_major
17040Sstevel@tonic-gate 	 *	put entry into name_to_major
17050Sstevel@tonic-gate 	 */
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	if (stat(rem_name_to_major, &buf) == 0) {
17080Sstevel@tonic-gate 		have_rem_n2m = 1;
17090Sstevel@tonic-gate 	}
17100Sstevel@tonic-gate 
17110Sstevel@tonic-gate 	if (have_rem_n2m) {
17120Sstevel@tonic-gate 		if ((is_unique = get_major_no(driver_name, rem_name_to_major))
17130Sstevel@tonic-gate 		    == ERROR)
17140Sstevel@tonic-gate 			return (ERROR);
17150Sstevel@tonic-gate 
17160Sstevel@tonic-gate 		/*
17170Sstevel@tonic-gate 		 * found a match in rem_name_to_major
17180Sstevel@tonic-gate 		 */
17190Sstevel@tonic-gate 		if (is_unique != UNIQUE) {
17200Sstevel@tonic-gate 			char scratch[FILENAME_MAX];
17210Sstevel@tonic-gate 
17220Sstevel@tonic-gate 			/*
17230Sstevel@tonic-gate 			 * If there is a match in /etc/rem_name_to_major then
17240Sstevel@tonic-gate 			 * be paranoid: is that major number already in
17250Sstevel@tonic-gate 			 * /etc/name_to_major (potentially under another name)?
17260Sstevel@tonic-gate 			 */
17270Sstevel@tonic-gate 			if (get_driver_name(is_unique, name_to_major,
17280Sstevel@tonic-gate 			    scratch) != UNIQUE) {
17290Sstevel@tonic-gate 				/*
17300Sstevel@tonic-gate 				 * nuke the rem_name_to_major entry-- it
17310Sstevel@tonic-gate 				 * isn't helpful.
17320Sstevel@tonic-gate 				 */
17330Sstevel@tonic-gate 				(void) delete_entry(rem_name_to_major,
17340Sstevel@tonic-gate 				    driver_name, " ", NULL);
17350Sstevel@tonic-gate 			} else {
17360Sstevel@tonic-gate 				(void) snprintf(major, sizeof (major),
17370Sstevel@tonic-gate 				    "%d", is_unique);
17380Sstevel@tonic-gate 
17390Sstevel@tonic-gate 				if (append_to_file(driver_name, major,
1740*4145Scth 				    name_to_major, ' ', " ", 0) == ERROR) {
17410Sstevel@tonic-gate 					(void) fprintf(stderr,
17420Sstevel@tonic-gate 					    gettext(ERR_NO_UPDATE),
17430Sstevel@tonic-gate 					    name_to_major);
17440Sstevel@tonic-gate 					return (ERROR);
17450Sstevel@tonic-gate 				}
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 				if (delete_entry(rem_name_to_major,
17480Sstevel@tonic-gate 				    driver_name, " ", NULL) == ERROR) {
17490Sstevel@tonic-gate 					(void) fprintf(stderr,
17500Sstevel@tonic-gate 					    gettext(ERR_DEL_ENTRY), driver_name,
17510Sstevel@tonic-gate 					    rem_name_to_major);
17520Sstevel@tonic-gate 					return (ERROR);
17530Sstevel@tonic-gate 				}
17540Sstevel@tonic-gate 
17550Sstevel@tonic-gate 				/* found matching entry : no errors */
17560Sstevel@tonic-gate 				*major_num = is_unique;
17570Sstevel@tonic-gate 				return (NOERR);
17580Sstevel@tonic-gate 			}
17590Sstevel@tonic-gate 		}
17600Sstevel@tonic-gate 	}
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 	/*
17630Sstevel@tonic-gate 	 * Bugid: 1264079
17640Sstevel@tonic-gate 	 * In a server case (with -b option), we can't use modctl() to find
17650Sstevel@tonic-gate 	 *    the maximum major number, we need to dig thru client's
17660Sstevel@tonic-gate 	 *    /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
17670Sstevel@tonic-gate 	 *
17680Sstevel@tonic-gate 	 * if (server)
17690Sstevel@tonic-gate 	 *    get maximum major number thru (rem_)name_to_major file on client
17700Sstevel@tonic-gate 	 * else
17710Sstevel@tonic-gate 	 *    get maximum major number allowable on current system using modctl
17720Sstevel@tonic-gate 	 */
17730Sstevel@tonic-gate 	if (server) {
17740Sstevel@tonic-gate 		max_dev = 0;
17750Sstevel@tonic-gate 		tmp = 0;
17760Sstevel@tonic-gate 
17770Sstevel@tonic-gate 		max_dev = get_max_major(name_to_major);
17780Sstevel@tonic-gate 
17790Sstevel@tonic-gate 		/* If rem_name_to_major exists, we need to check it too */
17800Sstevel@tonic-gate 		if (have_rem_n2m) {
17810Sstevel@tonic-gate 			tmp = get_max_major(rem_name_to_major);
17820Sstevel@tonic-gate 
17830Sstevel@tonic-gate 			/*
17840Sstevel@tonic-gate 			 * If name_to_major is missing, we can get max_dev from
17850Sstevel@tonic-gate 			 * /etc/rem_name_to_major.  If both missing, bail out!
17860Sstevel@tonic-gate 			 */
17870Sstevel@tonic-gate 			if ((max_dev == ERROR) && (tmp == ERROR)) {
17880Sstevel@tonic-gate 				(void) fprintf(stderr,
17890Sstevel@tonic-gate 					gettext(ERR_CANT_ACCESS_FILE),
17900Sstevel@tonic-gate 					name_to_major);
17910Sstevel@tonic-gate 				return (ERROR);
17920Sstevel@tonic-gate 			}
17930Sstevel@tonic-gate 
17940Sstevel@tonic-gate 			/* guard against bigger maj_num in rem_name_to_major */
17950Sstevel@tonic-gate 			if (tmp > max_dev)
17960Sstevel@tonic-gate 				max_dev = tmp;
17970Sstevel@tonic-gate 		} else {
17980Sstevel@tonic-gate 			/*
17990Sstevel@tonic-gate 			 * If we can't get major from name_to_major file
18000Sstevel@tonic-gate 			 * and there is no /etc/rem_name_to_major file,
18010Sstevel@tonic-gate 			 * then we don't have a max_dev, bail out quick!
18020Sstevel@tonic-gate 			 */
18030Sstevel@tonic-gate 			if (max_dev == ERROR)
18040Sstevel@tonic-gate 				return (ERROR);
18050Sstevel@tonic-gate 		}
18060Sstevel@tonic-gate 
18070Sstevel@tonic-gate 		/*
18080Sstevel@tonic-gate 		 * In case there is no more slack in current name_to_major
18090Sstevel@tonic-gate 		 * table, provide at least 1 extra entry so the add_drv can
18100Sstevel@tonic-gate 		 * succeed.  Since only one add_drv process is allowed at one
18110Sstevel@tonic-gate 		 * time, and hence max_dev will be re-calculated each time
18120Sstevel@tonic-gate 		 * add_drv is ran, we don't need to worry about adding more
18130Sstevel@tonic-gate 		 * than 1 extra slot for max_dev.
18140Sstevel@tonic-gate 		 */
18150Sstevel@tonic-gate 		max_dev++;
18160Sstevel@tonic-gate 
18170Sstevel@tonic-gate 	} else {
18180Sstevel@tonic-gate 		if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
18190Sstevel@tonic-gate 			perror(NULL);
18200Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
18210Sstevel@tonic-gate 			return (ERROR);
18220Sstevel@tonic-gate 		}
18230Sstevel@tonic-gate 	}
18240Sstevel@tonic-gate 
18250Sstevel@tonic-gate 	/*
18260Sstevel@tonic-gate 	 * max_dev is really how many slots the kernel has allocated for
18270Sstevel@tonic-gate 	 * devices... [0 , maxdev-1], not the largest available device num.
18280Sstevel@tonic-gate 	 */
18290Sstevel@tonic-gate 	if ((num_list = calloc(max_dev, 1)) == NULL) {
18300Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
18310Sstevel@tonic-gate 		return (ERROR);
18320Sstevel@tonic-gate 	}
18330Sstevel@tonic-gate 
18340Sstevel@tonic-gate 	/*
18350Sstevel@tonic-gate 	 * Populate the num_list array
18360Sstevel@tonic-gate 	 */
18370Sstevel@tonic-gate 	if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) {
18380Sstevel@tonic-gate 		return (ERROR);
18390Sstevel@tonic-gate 	}
18400Sstevel@tonic-gate 	if (have_rem_n2m) {
18410Sstevel@tonic-gate 		if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0)
18420Sstevel@tonic-gate 			return (ERROR);
18430Sstevel@tonic-gate 	}
18440Sstevel@tonic-gate 
18450Sstevel@tonic-gate 	/* find first free major number */
18460Sstevel@tonic-gate 	for (i = 0; i < max_dev; i++) {
18470Sstevel@tonic-gate 		if (num_list[i] != 1) {
18480Sstevel@tonic-gate 			new_maj = i;
18490Sstevel@tonic-gate 			break;
18500Sstevel@tonic-gate 		}
18510Sstevel@tonic-gate 	}
18520Sstevel@tonic-gate 
18530Sstevel@tonic-gate 	if (new_maj == -1) {
18540Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR));
18550Sstevel@tonic-gate 		return (ERROR);
18560Sstevel@tonic-gate 	}
18570Sstevel@tonic-gate 
18580Sstevel@tonic-gate 	(void) sprintf(drv_majnum_str, "%d", new_maj);
18590Sstevel@tonic-gate 	if (do_the_update(driver_name, drv_majnum_str) == ERROR) {
18600Sstevel@tonic-gate 		return (ERROR);
18610Sstevel@tonic-gate 	}
18620Sstevel@tonic-gate 
18630Sstevel@tonic-gate 	*major_num = new_maj;
18640Sstevel@tonic-gate 	return (NOERR);
18650Sstevel@tonic-gate }
18660Sstevel@tonic-gate 
18670Sstevel@tonic-gate 
18680Sstevel@tonic-gate int
18690Sstevel@tonic-gate fill_n2m_array(char *filename, char **array, int *nelems)
18700Sstevel@tonic-gate {
18710Sstevel@tonic-gate 	FILE *fp;
18722805Seota 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
18730Sstevel@tonic-gate 	char drv[FILENAME_MAX + 1];
18740Sstevel@tonic-gate 	u_longlong_t dnum;
18750Sstevel@tonic-gate 	major_t drv_majnum;
18760Sstevel@tonic-gate 
18770Sstevel@tonic-gate 	/*
18780Sstevel@tonic-gate 	 * Read through the file, marking each major number found
18790Sstevel@tonic-gate 	 * order is not relevant
18800Sstevel@tonic-gate 	 */
18810Sstevel@tonic-gate 	if ((fp = fopen(filename, "r")) == NULL) {
18820Sstevel@tonic-gate 		perror(NULL);
18830Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename);
18840Sstevel@tonic-gate 		return (ERROR);
18850Sstevel@tonic-gate 	}
18860Sstevel@tonic-gate 
18870Sstevel@tonic-gate 	while (fgets(line, sizeof (line), fp) != 0) {
18882805Seota 		/* cut off comments starting with '#' */
18892805Seota 		if ((cp = strchr(line, '#')) != NULL)
18902805Seota 			*cp = '\0';
18912805Seota 		/* ignore comment or blank lines */
18922805Seota 		if (is_blank(line))
18932805Seota 			continue;
18942805Seota 		/* sanity-check */
18950Sstevel@tonic-gate 		if (sscanf(line, "%s %llu", drv, &dnum) != 2) {
18960Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
18970Sstevel@tonic-gate 			    filename, line);
18980Sstevel@tonic-gate 			(void) fclose(fp);
18990Sstevel@tonic-gate 			return (ERROR);
19000Sstevel@tonic-gate 		}
19010Sstevel@tonic-gate 
19020Sstevel@tonic-gate 		if (dnum > L_MAXMAJ32) {
19030Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv,
19040Sstevel@tonic-gate 			    dnum, filename, L_MAXMAJ32);
19050Sstevel@tonic-gate 			continue;
19060Sstevel@tonic-gate 		}
19070Sstevel@tonic-gate 		/*
19080Sstevel@tonic-gate 		 * cast down to a major_t; we can be sure this is safe because
19090Sstevel@tonic-gate 		 * of the above range-check.
19100Sstevel@tonic-gate 		 */
19110Sstevel@tonic-gate 		drv_majnum = (major_t)dnum;
19120Sstevel@tonic-gate 
19130Sstevel@tonic-gate 		if (drv_majnum >= *nelems) {
19140Sstevel@tonic-gate 			/*
19150Sstevel@tonic-gate 			 * Allocate some more space, up to drv_majnum + 1 so
19160Sstevel@tonic-gate 			 * we can accomodate 0 through drv_majnum.
19170Sstevel@tonic-gate 			 *
19180Sstevel@tonic-gate 			 * Note that in the failure case, we leak all of the
19190Sstevel@tonic-gate 			 * old contents of array.  It's ok, since we just
19200Sstevel@tonic-gate 			 * wind up exiting immediately anyway.
19210Sstevel@tonic-gate 			 */
19220Sstevel@tonic-gate 			*nelems = drv_majnum + 1;
19230Sstevel@tonic-gate 			*array = realloc(*array, *nelems);
19240Sstevel@tonic-gate 			if (*array == NULL) {
19250Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
19260Sstevel@tonic-gate 				return (ERROR);
19270Sstevel@tonic-gate 			}
19280Sstevel@tonic-gate 		}
19290Sstevel@tonic-gate 		(*array)[drv_majnum] = 1;
19300Sstevel@tonic-gate 	}
19310Sstevel@tonic-gate 
19320Sstevel@tonic-gate 	(void) fclose(fp);
19330Sstevel@tonic-gate 	return (0);
19340Sstevel@tonic-gate }
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 
19370Sstevel@tonic-gate int
19380Sstevel@tonic-gate do_the_update(char *driver_name, char *major_number)
19390Sstevel@tonic-gate {
19400Sstevel@tonic-gate 	return (append_to_file(driver_name, major_number, name_to_major,
1941*4145Scth 	    ' ', " ", 0));
19420Sstevel@tonic-gate }
19432805Seota 
19442805Seota /*
19452805Seota  * is_blank() returns 1 (true) if a line specified is composed of
19462805Seota  * whitespace characters only. otherwise, it returns 0 (false).
19472805Seota  *
19482805Seota  * Note. the argument (line) must be null-terminated.
19492805Seota  */
19502805Seota static int
19512805Seota is_blank(char *line)
19522805Seota {
19532805Seota 	for (/* nothing */; *line != '\0'; line++)
19542805Seota 		if (!isspace(*line))
19552805Seota 			return (0);
19562805Seota 	return (1);
19572805Seota }
1958