xref: /onnv-gate/usr/src/cmd/modload/drvsubr.c (revision 9268)
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 /*
228831SJerry.Gilliam@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <stdio.h>
270Sstevel@tonic-gate #include <stdlib.h>
282805Seota #include <ctype.h>
290Sstevel@tonic-gate #include <unistd.h>
300Sstevel@tonic-gate #include <sys/sysmacros.h>
310Sstevel@tonic-gate #include <libintl.h>
320Sstevel@tonic-gate #include <wait.h>
330Sstevel@tonic-gate #include <string.h>
348331SJerry.Gilliam@Sun.COM #include <strings.h>
350Sstevel@tonic-gate #include <errno.h>
360Sstevel@tonic-gate #include <fcntl.h>
370Sstevel@tonic-gate #include <signal.h>
380Sstevel@tonic-gate #include <sys/buf.h>
390Sstevel@tonic-gate #include <sys/stat.h>
400Sstevel@tonic-gate #include <grp.h>
410Sstevel@tonic-gate #include "addrem.h"
420Sstevel@tonic-gate #include "errmsg.h"
430Sstevel@tonic-gate #include "plcysubr.h"
440Sstevel@tonic-gate 
450Sstevel@tonic-gate static char *add_rem_lock;	/* lock file */
460Sstevel@tonic-gate static char *tmphold;		/* temperary file for updating */
473442Svikram static int  add_rem_lock_fd = -1;
480Sstevel@tonic-gate 
490Sstevel@tonic-gate static int get_cached_n_to_m_file(char *filename, char ***cache);
500Sstevel@tonic-gate static int get_name_to_major_entry(int *major_no, char *driver_name,
510Sstevel@tonic-gate     char *file_name);
520Sstevel@tonic-gate 
532805Seota static int is_blank(char *);
542805Seota 
550Sstevel@tonic-gate /*ARGSUSED*/
560Sstevel@tonic-gate void
570Sstevel@tonic-gate log_minorperm_error(minorperm_err_t err, int key)
580Sstevel@tonic-gate {
590Sstevel@tonic-gate 	switch (err) {
600Sstevel@tonic-gate 	case MP_FOPEN_ERR:
610Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
628331SJerry.Gilliam@Sun.COM 		    MINOR_PERM_FILE);
630Sstevel@tonic-gate 		break;
640Sstevel@tonic-gate 	case MP_FCLOSE_ERR:
650Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
668331SJerry.Gilliam@Sun.COM 		    MINOR_PERM_FILE);
670Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
680Sstevel@tonic-gate 		break;
690Sstevel@tonic-gate 	case MP_IGNORING_LINE_ERR:
700Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
718331SJerry.Gilliam@Sun.COM 		    MINOR_PERM_FILE);
720Sstevel@tonic-gate 		break;
730Sstevel@tonic-gate 	case MP_ALLOC_ERR:
740Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
758331SJerry.Gilliam@Sun.COM 		    MINOR_PERM_FILE);
760Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
770Sstevel@tonic-gate 		break;
780Sstevel@tonic-gate 	case MP_NVLIST_ERR:
790Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
808331SJerry.Gilliam@Sun.COM 		    MINOR_PERM_FILE);
810Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
820Sstevel@tonic-gate 		break;
830Sstevel@tonic-gate 	case MP_CANT_FIND_USER_ERR:
840Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
858331SJerry.Gilliam@Sun.COM 		    MINOR_PERM_FILE);
860Sstevel@tonic-gate 		break;
870Sstevel@tonic-gate 	case MP_CANT_FIND_GROUP_ERR:
880Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
898331SJerry.Gilliam@Sun.COM 		    MINOR_PERM_FILE);
900Sstevel@tonic-gate 		break;
910Sstevel@tonic-gate 	}
920Sstevel@tonic-gate }
930Sstevel@tonic-gate 
940Sstevel@tonic-gate /*
950Sstevel@tonic-gate  *  open file
960Sstevel@tonic-gate  * for each entry in list
970Sstevel@tonic-gate  *	where list entries are separated by <list_separator>
980Sstevel@tonic-gate  * 	append entry : driver_name <entry_separator> entry
990Sstevel@tonic-gate  * close file
1000Sstevel@tonic-gate  * return error/noerr
1010Sstevel@tonic-gate  */
1020Sstevel@tonic-gate int
1030Sstevel@tonic-gate append_to_file(
1040Sstevel@tonic-gate 	char *driver_name,
1050Sstevel@tonic-gate 	char *entry_list,
1060Sstevel@tonic-gate 	char *filename,
1070Sstevel@tonic-gate 	char list_separator,
1084145Scth 	char *entry_separator,
1094145Scth 	int quoted)
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,
1564145Scth 		    list_separator, quoted);
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);
1614145Scth 		if (quoted)
1624145Scth 			(void) strcat(line, "\"");
1630Sstevel@tonic-gate 		(void) strcat(line, one_entry);
1644145Scth 		if (quoted)
1654145Scth 			(void) strcat(line, "\"");
1660Sstevel@tonic-gate 		(void) strcat(line, "\n");
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 		if ((fputs(line, fp)) == EOF) {
1690Sstevel@tonic-gate 			perror(NULL);
1700Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
1710Sstevel@tonic-gate 			    filename);
1720Sstevel@tonic-gate 		}
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 	} while (*current_head != '\0');
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	(void) fflush(fp);
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	fpint = fileno(fp);
1800Sstevel@tonic-gate 	(void) fsync(fpint);
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	(void) fclose(fp);
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	free(one_entry);
1850Sstevel@tonic-gate 	free(line);
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 	return (NOERR);
1880Sstevel@tonic-gate }
1890Sstevel@tonic-gate 
1908456SJerry.Gilliam@Sun.COM /*
1918456SJerry.Gilliam@Sun.COM  * Require exact match to delete a driver alias/permission entry.
1928456SJerry.Gilliam@Sun.COM  * Note line argument does not remain unchanged.  Return 1 if matched.
1938456SJerry.Gilliam@Sun.COM  */
1948456SJerry.Gilliam@Sun.COM static int
1958456SJerry.Gilliam@Sun.COM match_entry(char *line, char *match)
1968456SJerry.Gilliam@Sun.COM {
1978456SJerry.Gilliam@Sun.COM 	char	*token, *p;
1988456SJerry.Gilliam@Sun.COM 	int	n;
1998456SJerry.Gilliam@Sun.COM 
2008456SJerry.Gilliam@Sun.COM 	/* skip any leading white space */
2018456SJerry.Gilliam@Sun.COM 	while (*line && ((*line == ' ') || (*line == '\t')))
2028456SJerry.Gilliam@Sun.COM 		line++;
2038456SJerry.Gilliam@Sun.COM 	/*
2048456SJerry.Gilliam@Sun.COM 	 * Find separator for driver name, either space or colon
2058456SJerry.Gilliam@Sun.COM 	 *	minor_perm: <driver>:<perm>
2068456SJerry.Gilliam@Sun.COM 	 *	driver_aliases: <driver> <alias>
2078456SJerry.Gilliam@Sun.COM 	 *	extra_privs: <driver>:<priv>
2088456SJerry.Gilliam@Sun.COM 	 */
2098456SJerry.Gilliam@Sun.COM 	if ((token = strpbrk(line, " :\t")) == NULL)
2108456SJerry.Gilliam@Sun.COM 		return (0);
2118456SJerry.Gilliam@Sun.COM 	token++;
2128456SJerry.Gilliam@Sun.COM 	/* skip leading white space and quotes */
2138456SJerry.Gilliam@Sun.COM 	while (*token && (*token == ' ' || *token == '\t' ||
2148456SJerry.Gilliam@Sun.COM 	    *token == '"' || *token == '\''))
2158456SJerry.Gilliam@Sun.COM 		token++;
2168456SJerry.Gilliam@Sun.COM 	/* strip trailing newline, white space and quotes */
2178456SJerry.Gilliam@Sun.COM 	n = strlen(token);
2188456SJerry.Gilliam@Sun.COM 	p = token + n-1;
2198456SJerry.Gilliam@Sun.COM 	while (n > 0 && (*p == '\n' || *p == ' ' || *p == '\t' ||
2208456SJerry.Gilliam@Sun.COM 	    *p == '"' || *p == '\'')) {
2218456SJerry.Gilliam@Sun.COM 		*p-- = 0;
2228456SJerry.Gilliam@Sun.COM 		n--;
2238456SJerry.Gilliam@Sun.COM 	}
2248456SJerry.Gilliam@Sun.COM 	if (n == 0)
2258456SJerry.Gilliam@Sun.COM 		return (0);
2268456SJerry.Gilliam@Sun.COM 	return (strcmp(token, match) == 0);
2278456SJerry.Gilliam@Sun.COM }
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate /*
2300Sstevel@tonic-gate  *  open file
2310Sstevel@tonic-gate  * read thru file, deleting all entries if first
2320Sstevel@tonic-gate  *    entry = driver_name
2330Sstevel@tonic-gate  * close
2340Sstevel@tonic-gate  * if error, leave original file intact with message
2350Sstevel@tonic-gate  * assumption : drvconfig has been modified to work with clone
2360Sstevel@tonic-gate  *  entries in /etc/minor_perm as driver:mummble NOT
2370Sstevel@tonic-gate  *  clone:driver mummble
2380Sstevel@tonic-gate  * this implementation will NOT find clone entries
2390Sstevel@tonic-gate  * clone:driver mummble
2400Sstevel@tonic-gate  * match:
2410Sstevel@tonic-gate  *	delete just the matching entry
2420Sstevel@tonic-gate  *
2430Sstevel@tonic-gate  */
2440Sstevel@tonic-gate int
2450Sstevel@tonic-gate delete_entry(
2460Sstevel@tonic-gate 	char *oldfile,
2470Sstevel@tonic-gate 	char *driver_name,
2480Sstevel@tonic-gate 	char *marker,
2490Sstevel@tonic-gate 	char *match)
2500Sstevel@tonic-gate {
2510Sstevel@tonic-gate 	int		rv, i;
2520Sstevel@tonic-gate 	int		status = NOERR;
2530Sstevel@tonic-gate 	int		drvr_found = 0;
2540Sstevel@tonic-gate 	boolean_t 	nomatch = B_TRUE;
2558456SJerry.Gilliam@Sun.COM 	char		*newfile, *tptr, *cp;
2560Sstevel@tonic-gate 	char		line[MAX_DBFILE_ENTRY], drv[FILENAME_MAX + 1];
2570Sstevel@tonic-gate 	FILE		*fp, *newfp;
2580Sstevel@tonic-gate 	struct group	*sysgrp;
2598456SJerry.Gilliam@Sun.COM 	char		*copy;		/* same size as line */
260*9268SJerry.Gilliam@Sun.COM 	char		*match2 = NULL;	/* match with quotes cleaned up */
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	/*
263*9268SJerry.Gilliam@Sun.COM 	 * if match is specified, sanity check it and clean it
264*9268SJerry.Gilliam@Sun.COM 	 * up by removing surrounding quotes as we require
265*9268SJerry.Gilliam@Sun.COM 	 * an exact match.
2660Sstevel@tonic-gate 	 */
267*9268SJerry.Gilliam@Sun.COM 	if (match) {
268*9268SJerry.Gilliam@Sun.COM 		cp = match;
269*9268SJerry.Gilliam@Sun.COM 		while (*cp && (*cp == '"' || *cp == '\''))
270*9268SJerry.Gilliam@Sun.COM 			cp++;
271*9268SJerry.Gilliam@Sun.COM 		i = strlen(cp);
272*9268SJerry.Gilliam@Sun.COM 		if (i > 0) {
273*9268SJerry.Gilliam@Sun.COM 			if ((match2 = strdup(cp)) == NULL) {
274*9268SJerry.Gilliam@Sun.COM 				perror(NULL);
275*9268SJerry.Gilliam@Sun.COM 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
276*9268SJerry.Gilliam@Sun.COM 				return (ERROR);
277*9268SJerry.Gilliam@Sun.COM 			}
278*9268SJerry.Gilliam@Sun.COM 			if ((cp = strchr(match2, '\'')) != NULL)
279*9268SJerry.Gilliam@Sun.COM 				*cp = 0;
280*9268SJerry.Gilliam@Sun.COM 			if ((cp = strchr(match2, '"')) != NULL)
281*9268SJerry.Gilliam@Sun.COM 				*cp = 0;
282*9268SJerry.Gilliam@Sun.COM 			if ((cp = strchr(match2, ' ')) != NULL)
283*9268SJerry.Gilliam@Sun.COM 				*cp = 0;
284*9268SJerry.Gilliam@Sun.COM 		}
285*9268SJerry.Gilliam@Sun.COM 		if (match2 == NULL || (strlen(match2) == 0)) {
286*9268SJerry.Gilliam@Sun.COM 			(void) fprintf(stderr,
287*9268SJerry.Gilliam@Sun.COM 			    gettext(ERR_INT_UPDATE), oldfile);
288*9268SJerry.Gilliam@Sun.COM 			return (ERROR);
289*9268SJerry.Gilliam@Sun.COM 		}
2900Sstevel@tonic-gate 	}
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	if ((fp = fopen(oldfile, "r")) == NULL) {
2930Sstevel@tonic-gate 		perror(NULL);
2940Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
2950Sstevel@tonic-gate 		return (ERROR);
2960Sstevel@tonic-gate 	}
2970Sstevel@tonic-gate 
2988456SJerry.Gilliam@Sun.COM 	/* Space for defensive copy of input line */
2998456SJerry.Gilliam@Sun.COM 	copy = calloc(sizeof (line), 1);
3000Sstevel@tonic-gate 
3018456SJerry.Gilliam@Sun.COM 	/* Build filename for temporary file */
3028456SJerry.Gilliam@Sun.COM 	tptr = calloc(strlen(oldfile) + strlen(XEND) + 1, 1);
3038456SJerry.Gilliam@Sun.COM 	if (tptr == NULL || copy == NULL) {
3040Sstevel@tonic-gate 		perror(NULL);
3050Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
3068456SJerry.Gilliam@Sun.COM 		return (ERROR);
3070Sstevel@tonic-gate 	}
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	(void) strcpy(tptr, oldfile);
3100Sstevel@tonic-gate 	(void) strcat(tptr, XEND);
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	/*
3130Sstevel@tonic-gate 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
3140Sstevel@tonic-gate 	 * assume a gid of "sys" but we can't undo the damage on already
3150Sstevel@tonic-gate 	 * installed systems unless we force the issue.
3160Sstevel@tonic-gate 	 */
3170Sstevel@tonic-gate 	if ((sysgrp = getgrnam("sys")) != NULL) {
3180Sstevel@tonic-gate 		(void) setgid(sysgrp->gr_gid);
3190Sstevel@tonic-gate 	}
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	newfile = mktemp(tptr);
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	if ((newfp = fopen(newfile, "w")) == NULL) {
3240Sstevel@tonic-gate 		perror(NULL);
3250Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
3260Sstevel@tonic-gate 		    newfile);
3270Sstevel@tonic-gate 		return (ERROR);
3280Sstevel@tonic-gate 	}
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
3318456SJerry.Gilliam@Sun.COM 		/* copy the whole line */
3328456SJerry.Gilliam@Sun.COM 		if (strlcpy(copy, line, sizeof (line)) >= sizeof (line)) {
3338456SJerry.Gilliam@Sun.COM 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
3342805Seota 			status = ERROR;
3352805Seota 			break;
3362805Seota 		}
3372805Seota 		/* cut off comments starting with '#' */
3388456SJerry.Gilliam@Sun.COM 		if ((cp = strchr(copy, '#')) != NULL)
3392805Seota 			*cp = '\0';
3402805Seota 		/* ignore comment or blank lines */
3418456SJerry.Gilliam@Sun.COM 		if (is_blank(copy)) {
3422805Seota 			if (fputs(line, newfp) == EOF) {
3430Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_UPDATE),
3440Sstevel@tonic-gate 				    oldfile);
3450Sstevel@tonic-gate 				status = ERROR;
3460Sstevel@tonic-gate 			}
3470Sstevel@tonic-gate 			continue;
3480Sstevel@tonic-gate 		}
3492805Seota 
3502805Seota 		/* get the driver name */
351*9268SJerry.Gilliam@Sun.COM 		/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
3528456SJerry.Gilliam@Sun.COM 		if (sscanf(copy, "%s", drv) != 1) {
3530Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
3540Sstevel@tonic-gate 			    oldfile, line);
3550Sstevel@tonic-gate 			status = ERROR;
3562805Seota 			break;
3570Sstevel@tonic-gate 		}
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
3600Sstevel@tonic-gate 			drv[i] =  '\0';
3610Sstevel@tonic-gate 		}
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 		if (strcmp(driver_name, drv) != 0) {
3640Sstevel@tonic-gate 			if ((fputs(line, newfp)) == EOF) {
3650Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_UPDATE),
3660Sstevel@tonic-gate 				    oldfile);
3670Sstevel@tonic-gate 				status = ERROR;
3680Sstevel@tonic-gate 			}
3690Sstevel@tonic-gate 		} else {
3700Sstevel@tonic-gate 			drvr_found++;
371*9268SJerry.Gilliam@Sun.COM 			if (match2) {	/* Just delete one entry */
3720Sstevel@tonic-gate 				/* for now delete just minor_perm and aliases */
3730Sstevel@tonic-gate 				if ((strcmp(oldfile, minor_perm) == 0) ||
3740Sstevel@tonic-gate 				    (strcmp(oldfile, extra_privs) == 0) ||
3750Sstevel@tonic-gate 				    (strcmp(oldfile, driver_aliases) == 0)) {
3768456SJerry.Gilliam@Sun.COM 
3778456SJerry.Gilliam@Sun.COM 					/* make defensive copy */
3788456SJerry.Gilliam@Sun.COM 					if (strlcpy(copy, line, sizeof (line))
3798456SJerry.Gilliam@Sun.COM 					    >= sizeof (line)) {
3808456SJerry.Gilliam@Sun.COM 						(void) fprintf(stderr,
3818456SJerry.Gilliam@Sun.COM 						    gettext(ERR_UPDATE),
3828456SJerry.Gilliam@Sun.COM 						    oldfile);
3838456SJerry.Gilliam@Sun.COM 						status = ERROR;
3848456SJerry.Gilliam@Sun.COM 						break;
3858456SJerry.Gilliam@Sun.COM 					}
386*9268SJerry.Gilliam@Sun.COM 					if (match_entry(copy, match2)) {
3870Sstevel@tonic-gate 						nomatch = B_FALSE;
3880Sstevel@tonic-gate 					} else {
3890Sstevel@tonic-gate 						if ((fputs(line, newfp)) ==
3900Sstevel@tonic-gate 						    EOF) {
3910Sstevel@tonic-gate 							(void) fprintf(stderr,
3920Sstevel@tonic-gate 							    gettext(ERR_UPDATE),
3930Sstevel@tonic-gate 							    oldfile);
3940Sstevel@tonic-gate 							status = ERROR;
3950Sstevel@tonic-gate 						}
3960Sstevel@tonic-gate 						if (nomatch != B_FALSE)
3970Sstevel@tonic-gate 							nomatch = B_TRUE;
3980Sstevel@tonic-gate 					}
3990Sstevel@tonic-gate 				}
4000Sstevel@tonic-gate 			}
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 		} /* end of else */
4030Sstevel@tonic-gate 	} /* end of while */
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 	(void) fclose(fp);
4068456SJerry.Gilliam@Sun.COM 	free(tptr);
4078456SJerry.Gilliam@Sun.COM 	free(copy);
408*9268SJerry.Gilliam@Sun.COM 	if (match2)
409*9268SJerry.Gilliam@Sun.COM 		free(match2);
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	/* Make sure that the file is on disk */
4120Sstevel@tonic-gate 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
4130Sstevel@tonic-gate 		status = ERROR;
4140Sstevel@tonic-gate 	else
4150Sstevel@tonic-gate 		rv = NOERR;
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 	(void) fclose(newfp);
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	/* no matching driver found */
4200Sstevel@tonic-gate 	rv = NOERR;
4210Sstevel@tonic-gate 	if (!drvr_found ||
4220Sstevel@tonic-gate 	    (nomatch == B_TRUE)) {
4230Sstevel@tonic-gate 		rv = NONE_FOUND;
4240Sstevel@tonic-gate 	}
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	/*
4270Sstevel@tonic-gate 	 * if error, leave original file, delete new file
4280Sstevel@tonic-gate 	 * if noerr, replace original file with new file
4290Sstevel@tonic-gate 	 */
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	if (status == NOERR) {
4320Sstevel@tonic-gate 		if (rename(oldfile, tmphold) == -1) {
4330Sstevel@tonic-gate 			perror(NULL);
4340Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
4350Sstevel@tonic-gate 			(void) unlink(newfile);
4360Sstevel@tonic-gate 			return (ERROR);
4370Sstevel@tonic-gate 		} else if (rename(newfile, oldfile) == -1) {
4380Sstevel@tonic-gate 			perror(NULL);
4390Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
4400Sstevel@tonic-gate 			(void) unlink(oldfile);
4410Sstevel@tonic-gate 			(void) unlink(newfile);
4420Sstevel@tonic-gate 			if (link(tmphold, oldfile) == -1) {
4430Sstevel@tonic-gate 				perror(NULL);
4440Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
4450Sstevel@tonic-gate 				    oldfile, tmphold);
4460Sstevel@tonic-gate 			}
4470Sstevel@tonic-gate 			return (ERROR);
4480Sstevel@tonic-gate 		}
4490Sstevel@tonic-gate 		(void) unlink(tmphold);
4500Sstevel@tonic-gate 	} else {
4510Sstevel@tonic-gate 		/*
4520Sstevel@tonic-gate 		 * since there's an error, leave file alone; remove
4530Sstevel@tonic-gate 		 * new file
4540Sstevel@tonic-gate 		 */
4550Sstevel@tonic-gate 		if (unlink(newfile) == -1) {
4560Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
4570Sstevel@tonic-gate 		}
4580Sstevel@tonic-gate 		return (ERROR);
4590Sstevel@tonic-gate 	}
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	return (rv);
4620Sstevel@tonic-gate }
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate /*
4660Sstevel@tonic-gate  * wrapper for call to get_name_to_major_entry(): given driver name,
4670Sstevel@tonic-gate  * retrieve major number.
4680Sstevel@tonic-gate  */
4690Sstevel@tonic-gate int
4700Sstevel@tonic-gate get_major_no(char *driver_name, char *file_name)
4710Sstevel@tonic-gate {
4720Sstevel@tonic-gate 	int major = UNIQUE;
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	if (get_name_to_major_entry(&major, driver_name, file_name) == ERROR)
4750Sstevel@tonic-gate 		return (ERROR);
4760Sstevel@tonic-gate 	else
4770Sstevel@tonic-gate 		return (major);
4780Sstevel@tonic-gate }
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate /*
4810Sstevel@tonic-gate  * wrapper for call to get_name_to_major_entry(): given major number,
4820Sstevel@tonic-gate  * retrieve driver name.
4830Sstevel@tonic-gate  */
4840Sstevel@tonic-gate int
4850Sstevel@tonic-gate get_driver_name(int major, char *file_name, char *buf)
4860Sstevel@tonic-gate {
4870Sstevel@tonic-gate 	if (major < 0)
4880Sstevel@tonic-gate 		return (ERROR);
4890Sstevel@tonic-gate 	return (get_name_to_major_entry(&major, buf, file_name));
4900Sstevel@tonic-gate }
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate /*
4940Sstevel@tonic-gate  * return pointer to cached name_to_major file - reads file into
4950Sstevel@tonic-gate  * cache if this has not already been done.  Since there may be
4960Sstevel@tonic-gate  * requests for multiple name_to_major files (rem_name_to_major,
4970Sstevel@tonic-gate  * name_to_major), this routine keeps a list of cached files.
4980Sstevel@tonic-gate  */
4990Sstevel@tonic-gate static int
5000Sstevel@tonic-gate get_cached_n_to_m_file(char *filename, char ***cache)
5010Sstevel@tonic-gate {
5020Sstevel@tonic-gate 	struct n_to_m_cache {
5030Sstevel@tonic-gate 		char *file;
5040Sstevel@tonic-gate 		char **cached_file;
5050Sstevel@tonic-gate 		int size;
5060Sstevel@tonic-gate 		struct n_to_m_cache *next;
5070Sstevel@tonic-gate 	};
5080Sstevel@tonic-gate 	static struct n_to_m_cache *head = NULL;
5090Sstevel@tonic-gate 	struct n_to_m_cache *ptr;
5100Sstevel@tonic-gate 	FILE *fp;
5110Sstevel@tonic-gate 	char drv[FILENAME_MAX + 1];
5120Sstevel@tonic-gate 	char entry[FILENAME_MAX + 1];
5132805Seota 	char line[MAX_N2M_ALIAS_LINE], *cp;
5140Sstevel@tonic-gate 	int maj;
5150Sstevel@tonic-gate 	int size = 0;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	/*
5190Sstevel@tonic-gate 	 * see if the file is already cached - either
5200Sstevel@tonic-gate 	 * rem_name_to_major or name_to_major
5210Sstevel@tonic-gate 	 */
5220Sstevel@tonic-gate 	ptr = head;
5230Sstevel@tonic-gate 	while (ptr != NULL) {
5240Sstevel@tonic-gate 		if (strcmp(ptr->file, filename) == 0)
5250Sstevel@tonic-gate 			break;
5260Sstevel@tonic-gate 		ptr = ptr->next;
5270Sstevel@tonic-gate 	}
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	if (ptr == NULL) {	/* we need to cache the contents */
5300Sstevel@tonic-gate 		if ((fp = fopen(filename, "r")) == NULL) {
5310Sstevel@tonic-gate 			perror(NULL);
5320Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_OPEN),
5330Sstevel@tonic-gate 			    filename);
5340Sstevel@tonic-gate 			return (ERROR);
5350Sstevel@tonic-gate 		}
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 		while (fgets(line, sizeof (line), fp) != NULL) {
5382805Seota 			/* cut off comments starting with '#' */
5392805Seota 			if ((cp = strchr(line, '#')) != NULL)
5402805Seota 				*cp = '\0';
5412805Seota 			/* ignore comment or blank lines */
5422805Seota 			if (is_blank(line))
5432805Seota 				continue;
5442805Seota 			/* sanity-check */
545*9268SJerry.Gilliam@Sun.COM 			/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
5460Sstevel@tonic-gate 			if (sscanf(line, "%s%s", drv, entry) != 2) {
5470Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
5480Sstevel@tonic-gate 				    filename, line);
5490Sstevel@tonic-gate 				continue;
5500Sstevel@tonic-gate 			}
5510Sstevel@tonic-gate 			maj = atoi(entry);
5520Sstevel@tonic-gate 			if (maj > size)
5530Sstevel@tonic-gate 				size = maj;
5540Sstevel@tonic-gate 		}
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 		/* allocate struct to cache the file */
5570Sstevel@tonic-gate 		ptr = (struct n_to_m_cache *)calloc(1,
5580Sstevel@tonic-gate 		    sizeof (struct n_to_m_cache));
5590Sstevel@tonic-gate 		if (ptr == NULL) {
5600Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
5610Sstevel@tonic-gate 			return (ERROR);
5620Sstevel@tonic-gate 		}
5630Sstevel@tonic-gate 		ptr->size = size + 1;
5640Sstevel@tonic-gate 		/* allocate space to cache contents of file */
5650Sstevel@tonic-gate 		ptr->cached_file = (char **)calloc(ptr->size, sizeof (char *));
5660Sstevel@tonic-gate 		if (ptr->cached_file == NULL) {
5670Sstevel@tonic-gate 			free(ptr);
5680Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
5690Sstevel@tonic-gate 			return (ERROR);
5700Sstevel@tonic-gate 		}
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 		rewind(fp);
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 		/*
5750Sstevel@tonic-gate 		 * now fill the cache
5760Sstevel@tonic-gate 		 * the cache is an array of char pointers indexed by major
5770Sstevel@tonic-gate 		 * number
5780Sstevel@tonic-gate 		 */
5790Sstevel@tonic-gate 		while (fgets(line, sizeof (line), fp) != NULL) {
5802805Seota 			/* cut off comments starting with '#' */
5812805Seota 			if ((cp = strchr(line, '#')) != NULL)
5822805Seota 				*cp = '\0';
5832805Seota 			/* ignore comment or blank lines */
5842805Seota 			if (is_blank(line))
5852805Seota 				continue;
5862805Seota 			/* sanity-check */
587*9268SJerry.Gilliam@Sun.COM 			/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
5880Sstevel@tonic-gate 			if (sscanf(line, "%s%s", drv, entry) != 2) {
5890Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
5900Sstevel@tonic-gate 				    filename, line);
5910Sstevel@tonic-gate 				continue;
5920Sstevel@tonic-gate 			}
5930Sstevel@tonic-gate 			maj = atoi(entry);
5940Sstevel@tonic-gate 			if ((ptr->cached_file[maj] = strdup(drv)) == NULL) {
5950Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
5960Sstevel@tonic-gate 				free(ptr->cached_file);
5970Sstevel@tonic-gate 				free(ptr);
5980Sstevel@tonic-gate 				return (ERROR);
5990Sstevel@tonic-gate 			}
6000Sstevel@tonic-gate 			(void) strcpy(ptr->cached_file[maj], drv);
6010Sstevel@tonic-gate 		}
6020Sstevel@tonic-gate 		(void) fclose(fp);
6030Sstevel@tonic-gate 		/* link the cache struct into the list of cached files */
6040Sstevel@tonic-gate 		ptr->file = strdup(filename);
6050Sstevel@tonic-gate 		if (ptr->file == NULL) {
6060Sstevel@tonic-gate 			for (maj = 0; maj <= ptr->size; maj++)
6070Sstevel@tonic-gate 				free(ptr->cached_file[maj]);
6080Sstevel@tonic-gate 			free(ptr->cached_file);
6090Sstevel@tonic-gate 			free(ptr);
6100Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
6110Sstevel@tonic-gate 			return (ERROR);
6120Sstevel@tonic-gate 		}
6130Sstevel@tonic-gate 		ptr->next = head;
6140Sstevel@tonic-gate 		head = ptr;
6150Sstevel@tonic-gate 	}
6160Sstevel@tonic-gate 	/* return value pointer to contents of file */
6170Sstevel@tonic-gate 	*cache = ptr->cached_file;
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	/* return size */
6200Sstevel@tonic-gate 	return (ptr->size);
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate /*
6250Sstevel@tonic-gate  * Using get_cached_n_to_m_file(), retrieve maximum major number
6260Sstevel@tonic-gate  * found in the specificed file (name_to_major/rem_name_to_major).
6270Sstevel@tonic-gate  *
6280Sstevel@tonic-gate  * The return value is actually the size of the internal cache including 0.
6290Sstevel@tonic-gate  */
6300Sstevel@tonic-gate int
6310Sstevel@tonic-gate get_max_major(char *file_name)
6320Sstevel@tonic-gate {
6330Sstevel@tonic-gate 	char **n_to_m_cache = NULL;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	return (get_cached_n_to_m_file(file_name, &n_to_m_cache));
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate /*
6400Sstevel@tonic-gate  * searching name_to_major: if major_no == UNIQUE then the caller wants to
6410Sstevel@tonic-gate  * use the driver name as the key.  Otherwise, the caller wants to use
6420Sstevel@tonic-gate  * the major number as a key.
6430Sstevel@tonic-gate  *
6440Sstevel@tonic-gate  * This routine caches the contents of the name_to_major file on
6450Sstevel@tonic-gate  * first call.  And it could be generalized to deal with other
6460Sstevel@tonic-gate  * config files if necessary.
6470Sstevel@tonic-gate  */
6480Sstevel@tonic-gate static int
6490Sstevel@tonic-gate get_name_to_major_entry(int *major_no, char *driver_name, char *file_name)
6500Sstevel@tonic-gate {
6510Sstevel@tonic-gate 	int maj;
6520Sstevel@tonic-gate 	char **n_to_m_cache = NULL;
6530Sstevel@tonic-gate 	int size = 0;
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 	int ret = NOT_UNIQUE;
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	/*
6580Sstevel@tonic-gate 	 * read the file in - we cache it in case caller wants to
6590Sstevel@tonic-gate 	 * do multiple lookups
6600Sstevel@tonic-gate 	 */
6610Sstevel@tonic-gate 	size = get_cached_n_to_m_file(file_name, &n_to_m_cache);
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	if (size == ERROR)
6640Sstevel@tonic-gate 		return (ERROR);
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 	/* search with driver name as key */
6670Sstevel@tonic-gate 	if (*major_no == UNIQUE) {
6680Sstevel@tonic-gate 		for (maj = 0; maj < size; maj++) {
6690Sstevel@tonic-gate 			if ((n_to_m_cache[maj] != NULL) &&
6700Sstevel@tonic-gate 			    (strcmp(driver_name, n_to_m_cache[maj]) == 0)) {
6710Sstevel@tonic-gate 				*major_no = maj;
6720Sstevel@tonic-gate 				break;
6730Sstevel@tonic-gate 			}
6740Sstevel@tonic-gate 		}
6750Sstevel@tonic-gate 		if (maj >= size)
6760Sstevel@tonic-gate 			ret = UNIQUE;
6770Sstevel@tonic-gate 	/* search with major number as key */
6780Sstevel@tonic-gate 	} else {
6790Sstevel@tonic-gate 		/*
6800Sstevel@tonic-gate 		 * Bugid 1254588, drvconfig dump core after loading driver
6810Sstevel@tonic-gate 		 * with major number bigger than entries defined in
6820Sstevel@tonic-gate 		 * /etc/name_to_major.
6830Sstevel@tonic-gate 		 */
6840Sstevel@tonic-gate 		if (*major_no >= size)
6850Sstevel@tonic-gate 			return (UNIQUE);
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 		if (n_to_m_cache[*major_no] != NULL) {
6880Sstevel@tonic-gate 			(void) strcpy(driver_name, n_to_m_cache[*major_no]);
6890Sstevel@tonic-gate 		} else
6900Sstevel@tonic-gate 			ret = UNIQUE;
6910Sstevel@tonic-gate 	}
6920Sstevel@tonic-gate 	return (ret);
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate /*
6964145Scth  * Given pointer to begining of member 'n' in a space (or separator)
6974145Scth  * separated list, return pointer to member 'n+1', and establish member 'n'
6984145Scth  * in *current_entry.  If unquote, then we skip a leading quote and treat
6994145Scth  * the trailing quote as a separator (and skip).
7000Sstevel@tonic-gate  */
7010Sstevel@tonic-gate char *
7020Sstevel@tonic-gate get_entry(
7030Sstevel@tonic-gate 	char *prev_member,
7040Sstevel@tonic-gate 	char *current_entry,
7054145Scth 	char separator,
7064145Scth 	int  unquote)
7070Sstevel@tonic-gate {
7084145Scth 	char	*ptr;
7094145Scth 	int	quoted = 0;
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	ptr = prev_member;
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	/* skip white space */
7140Sstevel@tonic-gate 	while (*ptr == '\t' || *ptr == ' ')
7150Sstevel@tonic-gate 		ptr++;
7160Sstevel@tonic-gate 
7174145Scth 	/* if unquote skip leading quote */
7184145Scth 	if (unquote && *ptr == '"') {
7194145Scth 		quoted++;
7204145Scth 		ptr++;
7214145Scth 	}
7224145Scth 
7234145Scth 	/* read thru the current entry looking for end, separator, or unquote */
7244145Scth 	while (*ptr &&
7254145Scth 	    (*ptr != separator) &&
7264145Scth 	    ((separator != ' ') || (*ptr != '\t')) &&
7274145Scth 	    (!quoted || (*ptr != '"'))) {
7280Sstevel@tonic-gate 		*current_entry++ = *ptr++;
7290Sstevel@tonic-gate 	}
7300Sstevel@tonic-gate 	*current_entry = '\0';
7310Sstevel@tonic-gate 
7324145Scth 	if (separator && (*ptr == separator))
7334145Scth 		ptr++;	/* skip over separator */
7344145Scth 	if (quoted && (*ptr == '"'))
7354145Scth 		ptr++;	/* skip over trailing quote */
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 	/* skip white space */
7380Sstevel@tonic-gate 	while (*ptr == '\t' || *ptr == ' ') {
7390Sstevel@tonic-gate 		ptr++;
7400Sstevel@tonic-gate 	}
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 	return (ptr);
7430Sstevel@tonic-gate }
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate void
7460Sstevel@tonic-gate enter_lock(void)
7470Sstevel@tonic-gate {
7483442Svikram 	struct flock lock;
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 	/*
7510Sstevel@tonic-gate 	 * attempt to create the lock file
7520Sstevel@tonic-gate 	 */
7533442Svikram 	add_rem_lock_fd = open(add_rem_lock, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
7543442Svikram 	if (add_rem_lock_fd < 0) {
7553442Svikram 		(void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
7563442Svikram 		    add_rem_lock, strerror(errno));
7573442Svikram 		exit(1);
7583442Svikram 	}
7593442Svikram 
7603442Svikram 	lock.l_type = F_WRLCK;
7613442Svikram 	lock.l_whence = SEEK_SET;
7623442Svikram 	lock.l_start = 0;
7633442Svikram 	lock.l_len = 0;
7643442Svikram 
7653442Svikram 	/* Try for the lock but don't wait. */
7663442Svikram 	if (fcntl(add_rem_lock_fd, F_SETLK, &lock) == -1) {
7673442Svikram 		if (errno == EACCES || errno == EAGAIN) {
7680Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_PROG_IN_USE));
7690Sstevel@tonic-gate 		} else {
7703442Svikram 			(void) fprintf(stderr, gettext(ERR_LOCK),
7713442Svikram 			    add_rem_lock, strerror(errno));
7720Sstevel@tonic-gate 		}
7730Sstevel@tonic-gate 		exit(1);
7740Sstevel@tonic-gate 	}
7750Sstevel@tonic-gate }
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate void
7780Sstevel@tonic-gate err_exit(void)
7790Sstevel@tonic-gate {
7800Sstevel@tonic-gate 	/* release memory allocated for moddir */
7810Sstevel@tonic-gate 	cleanup_moddir();
7820Sstevel@tonic-gate 	/* remove add_drv/rem_drv lock */
7830Sstevel@tonic-gate 	exit_unlock();
7840Sstevel@tonic-gate 	exit(1);
7850Sstevel@tonic-gate }
7860Sstevel@tonic-gate 
7870Sstevel@tonic-gate void
7880Sstevel@tonic-gate cleanup_moddir(void)
7890Sstevel@tonic-gate {
7900Sstevel@tonic-gate 	struct drvmod_dir *walk_ptr;
7910Sstevel@tonic-gate 	struct drvmod_dir *free_ptr = moddir;
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	while (free_ptr != NULL) {
7940Sstevel@tonic-gate 		walk_ptr = free_ptr->next;
7950Sstevel@tonic-gate 		free(free_ptr);
7960Sstevel@tonic-gate 		free_ptr = walk_ptr;
7970Sstevel@tonic-gate 	}
7980Sstevel@tonic-gate }
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate void
8010Sstevel@tonic-gate exit_unlock(void)
8020Sstevel@tonic-gate {
8033442Svikram 	struct flock unlock;
8043442Svikram 
8053442Svikram 	if (add_rem_lock_fd < 0)
8063442Svikram 		return;
8070Sstevel@tonic-gate 
8083442Svikram 	unlock.l_type = F_UNLCK;
8093442Svikram 	unlock.l_whence = SEEK_SET;
8103442Svikram 	unlock.l_start = 0;
8113442Svikram 	unlock.l_len = 0;
8123442Svikram 
8133442Svikram 	if (fcntl(add_rem_lock_fd, F_SETLK, &unlock) == -1) {
8143442Svikram 		(void) fprintf(stderr, gettext(ERR_UNLOCK),
8153442Svikram 		    add_rem_lock, strerror(errno));
8163442Svikram 	} else {
8173442Svikram 		(void) close(add_rem_lock_fd);
8183442Svikram 		add_rem_lock_fd = -1;
8190Sstevel@tonic-gate 	}
8200Sstevel@tonic-gate }
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate /*
8230Sstevel@tonic-gate  * error adding driver; need to back out any changes to files.
8240Sstevel@tonic-gate  * check flag to see which files need entries removed
8250Sstevel@tonic-gate  * entry removal based on driver name
8260Sstevel@tonic-gate  */
8270Sstevel@tonic-gate void
8280Sstevel@tonic-gate remove_entry(
8290Sstevel@tonic-gate 	int c_flag,
8300Sstevel@tonic-gate 	char *driver_name)
8310Sstevel@tonic-gate {
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	if (c_flag & CLEAN_NAM_MAJ) {
8340Sstevel@tonic-gate 		if (delete_entry(name_to_major, driver_name, " ",
8350Sstevel@tonic-gate 		    NULL) == ERROR) {
8360Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_CLEAN),
8370Sstevel@tonic-gate 			    name_to_major, driver_name);
8380Sstevel@tonic-gate 		}
8390Sstevel@tonic-gate 	}
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	if (c_flag & CLEAN_DRV_ALIAS) {
8420Sstevel@tonic-gate 		if (delete_entry(driver_aliases, driver_name, " ",
8430Sstevel@tonic-gate 		    NULL) == ERROR) {
8440Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
8450Sstevel@tonic-gate 			    driver_name, driver_aliases);
8460Sstevel@tonic-gate 		}
8470Sstevel@tonic-gate 	}
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	if (c_flag & CLEAN_DRV_CLASSES) {
8500Sstevel@tonic-gate 		if (delete_entry(driver_classes, driver_name, "\t", NULL) ==
8510Sstevel@tonic-gate 		    ERROR) {
8520Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
8530Sstevel@tonic-gate 			    driver_name, driver_classes);
8540Sstevel@tonic-gate 		}
8550Sstevel@tonic-gate 	}
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	if (c_flag & CLEAN_MINOR_PERM) {
8580Sstevel@tonic-gate 		if (delete_entry(minor_perm, driver_name, ":", NULL) == ERROR) {
8590Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
8600Sstevel@tonic-gate 			    driver_name, minor_perm);
8610Sstevel@tonic-gate 		}
8620Sstevel@tonic-gate 	}
8630Sstevel@tonic-gate 	/*
8640Sstevel@tonic-gate 	 * There's no point in removing entries from files that don't
8650Sstevel@tonic-gate 	 * exist.  Prevent error messages by checking for file existence
8660Sstevel@tonic-gate 	 * first.
8670Sstevel@tonic-gate 	 */
8680Sstevel@tonic-gate 	if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
8690Sstevel@tonic-gate 	    access(device_policy, F_OK) == 0) {
8700Sstevel@tonic-gate 		if (delete_plcy_entry(device_policy, driver_name) == ERROR) {
8710Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
8728331SJerry.Gilliam@Sun.COM 			    driver_name, device_policy);
8730Sstevel@tonic-gate 		}
8740Sstevel@tonic-gate 	}
8750Sstevel@tonic-gate 	if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
8760Sstevel@tonic-gate 	    access(extra_privs, F_OK) == 0) {
8770Sstevel@tonic-gate 		if (delete_entry(extra_privs, driver_name, ":", NULL) ==
8780Sstevel@tonic-gate 		    ERROR) {
8790Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
8808331SJerry.Gilliam@Sun.COM 			    driver_name, extra_privs);
8810Sstevel@tonic-gate 		}
8820Sstevel@tonic-gate 	}
8830Sstevel@tonic-gate }
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate int
8860Sstevel@tonic-gate check_perms_aliases(
8870Sstevel@tonic-gate 	int m_flag,
8880Sstevel@tonic-gate 	int i_flag)
8890Sstevel@tonic-gate {
8900Sstevel@tonic-gate 	/*
8910Sstevel@tonic-gate 	 * If neither i_flag nor m_flag are specified no need to check the
8920Sstevel@tonic-gate 	 * files for access permissions
8930Sstevel@tonic-gate 	 */
8940Sstevel@tonic-gate 	if (!m_flag && !i_flag)
8950Sstevel@tonic-gate 		return (NOERR);
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	/* check minor_perm file : exits and is writable */
8980Sstevel@tonic-gate 	if (m_flag) {
8990Sstevel@tonic-gate 		if (access(minor_perm, R_OK | W_OK)) {
9000Sstevel@tonic-gate 			perror(NULL);
9010Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
9020Sstevel@tonic-gate 			    minor_perm);
9030Sstevel@tonic-gate 			return (ERROR);
9040Sstevel@tonic-gate 		}
9050Sstevel@tonic-gate 	}
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 	/* check driver_aliases file : exits and is writable */
9080Sstevel@tonic-gate 	if (i_flag) {
9090Sstevel@tonic-gate 		if (access(driver_aliases, R_OK | W_OK)) {
9100Sstevel@tonic-gate 			perror(NULL);
9110Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
9120Sstevel@tonic-gate 			    driver_aliases);
9130Sstevel@tonic-gate 			return (ERROR);
9140Sstevel@tonic-gate 		}
9150Sstevel@tonic-gate 	}
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	return (NOERR);
9180Sstevel@tonic-gate }
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate int
9220Sstevel@tonic-gate check_name_to_major(int mode)
9230Sstevel@tonic-gate {
9240Sstevel@tonic-gate 	/* check name_to_major file : exists and is writable */
9250Sstevel@tonic-gate 	if (access(name_to_major, mode)) {
9260Sstevel@tonic-gate 		perror(NULL);
9270Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
9280Sstevel@tonic-gate 		    name_to_major);
9290Sstevel@tonic-gate 		return (ERROR);
9300Sstevel@tonic-gate 	}
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate 	return (NOERR);
9330Sstevel@tonic-gate }
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate /*
9370Sstevel@tonic-gate  * All this stuff is to support a server installing
9380Sstevel@tonic-gate  * drivers on diskless clients.  When on the server
9390Sstevel@tonic-gate  * need to prepend the basedir
9400Sstevel@tonic-gate  */
9410Sstevel@tonic-gate int
9420Sstevel@tonic-gate build_filenames(char *basedir)
9430Sstevel@tonic-gate {
944*9268SJerry.Gilliam@Sun.COM 	int	len;
945*9268SJerry.Gilliam@Sun.COM 	int	driver_aliases_len;
946*9268SJerry.Gilliam@Sun.COM 	int	driver_classes_len;
947*9268SJerry.Gilliam@Sun.COM 	int	minor_perm_len;
948*9268SJerry.Gilliam@Sun.COM 	int	name_to_major_len;
949*9268SJerry.Gilliam@Sun.COM 	int	rem_name_to_major_len;
950*9268SJerry.Gilliam@Sun.COM 	int	add_rem_lock_len;
951*9268SJerry.Gilliam@Sun.COM 	int	tmphold_len;
952*9268SJerry.Gilliam@Sun.COM 	int	devfs_root_len;
953*9268SJerry.Gilliam@Sun.COM 	int	device_policy_len;
954*9268SJerry.Gilliam@Sun.COM 	int	extra_privs_len;
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 	if (basedir == NULL) {
9570Sstevel@tonic-gate 		driver_aliases = DRIVER_ALIAS;
9580Sstevel@tonic-gate 		driver_classes = DRIVER_CLASSES;
9590Sstevel@tonic-gate 		minor_perm = MINOR_PERM;
9600Sstevel@tonic-gate 		name_to_major = NAM_TO_MAJ;
9610Sstevel@tonic-gate 		rem_name_to_major = REM_NAM_TO_MAJ;
9620Sstevel@tonic-gate 		add_rem_lock = ADD_REM_LOCK;
9630Sstevel@tonic-gate 		tmphold = TMPHOLD;
9640Sstevel@tonic-gate 		devfs_root = DEVFS_ROOT;
9650Sstevel@tonic-gate 		device_policy = DEV_POLICY;
9660Sstevel@tonic-gate 		extra_privs = EXTRA_PRIVS;
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	} else {
969*9268SJerry.Gilliam@Sun.COM 		len = strlen(basedir) + 1;
9700Sstevel@tonic-gate 
971*9268SJerry.Gilliam@Sun.COM 		driver_aliases_len = len + sizeof (DRIVER_ALIAS);
972*9268SJerry.Gilliam@Sun.COM 		driver_classes_len = len + sizeof (DRIVER_CLASSES);
973*9268SJerry.Gilliam@Sun.COM 		minor_perm_len = len + sizeof (MINOR_PERM);
974*9268SJerry.Gilliam@Sun.COM 		name_to_major_len = len + sizeof (NAM_TO_MAJ);
975*9268SJerry.Gilliam@Sun.COM 		rem_name_to_major_len = len + sizeof (REM_NAM_TO_MAJ);
976*9268SJerry.Gilliam@Sun.COM 		add_rem_lock_len = len + sizeof (ADD_REM_LOCK);
977*9268SJerry.Gilliam@Sun.COM 		tmphold_len = len + sizeof (TMPHOLD);
978*9268SJerry.Gilliam@Sun.COM 		devfs_root_len = len + sizeof (DEVFS_ROOT);
979*9268SJerry.Gilliam@Sun.COM 		device_policy_len = len + sizeof (DEV_POLICY);
980*9268SJerry.Gilliam@Sun.COM 		extra_privs_len = len + sizeof (EXTRA_PRIVS);
9810Sstevel@tonic-gate 
982*9268SJerry.Gilliam@Sun.COM 		driver_aliases = malloc(driver_aliases_len);
983*9268SJerry.Gilliam@Sun.COM 		driver_classes = malloc(driver_classes_len);
984*9268SJerry.Gilliam@Sun.COM 		minor_perm = malloc(minor_perm_len);
985*9268SJerry.Gilliam@Sun.COM 		name_to_major = malloc(name_to_major_len);
986*9268SJerry.Gilliam@Sun.COM 		rem_name_to_major = malloc(rem_name_to_major_len);
987*9268SJerry.Gilliam@Sun.COM 		add_rem_lock = malloc(add_rem_lock_len);
988*9268SJerry.Gilliam@Sun.COM 		tmphold = malloc(tmphold_len);
989*9268SJerry.Gilliam@Sun.COM 		devfs_root = malloc(devfs_root_len);
990*9268SJerry.Gilliam@Sun.COM 		device_policy = malloc(device_policy_len);
991*9268SJerry.Gilliam@Sun.COM 		extra_privs = malloc(extra_privs_len);
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate 		if ((driver_aliases == NULL) ||
9940Sstevel@tonic-gate 		    (driver_classes == NULL) ||
9950Sstevel@tonic-gate 		    (minor_perm == NULL) ||
9960Sstevel@tonic-gate 		    (name_to_major == NULL) ||
9970Sstevel@tonic-gate 		    (rem_name_to_major == NULL) ||
9980Sstevel@tonic-gate 		    (add_rem_lock == NULL) ||
9990Sstevel@tonic-gate 		    (tmphold == NULL) ||
10000Sstevel@tonic-gate 		    (devfs_root == NULL) ||
10010Sstevel@tonic-gate 		    (device_policy == NULL) ||
10020Sstevel@tonic-gate 		    (extra_privs == NULL)) {
10030Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
10040Sstevel@tonic-gate 			return (ERROR);
10050Sstevel@tonic-gate 		}
10060Sstevel@tonic-gate 
1007*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(driver_aliases, driver_aliases_len,
1008*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, DRIVER_ALIAS);
1009*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(driver_classes, driver_classes_len,
1010*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, DRIVER_CLASSES);
1011*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(minor_perm, minor_perm_len,
1012*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, MINOR_PERM);
1013*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(name_to_major, name_to_major_len,
1014*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, NAM_TO_MAJ);
1015*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(rem_name_to_major, rem_name_to_major_len,
1016*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, REM_NAM_TO_MAJ);
1017*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(add_rem_lock, add_rem_lock_len,
1018*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, ADD_REM_LOCK);
1019*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(tmphold, tmphold_len,
1020*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, TMPHOLD);
1021*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(devfs_root, devfs_root_len,
1022*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, DEVFS_ROOT);
1023*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(device_policy, device_policy_len,
1024*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, DEV_POLICY);
1025*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(extra_privs, extra_privs_len,
1026*9268SJerry.Gilliam@Sun.COM 		    "%s%s", basedir, EXTRA_PRIVS);
10270Sstevel@tonic-gate 	}
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 	return (NOERR);
10300Sstevel@tonic-gate }
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate static int
10330Sstevel@tonic-gate exec_command(char *path, char *cmdline[MAX_CMD_LINE])
10340Sstevel@tonic-gate {
10350Sstevel@tonic-gate 	pid_t pid;
10360Sstevel@tonic-gate 	uint_t stat_loc;
10370Sstevel@tonic-gate 	int waitstat;
10380Sstevel@tonic-gate 	int exit_status;
10390Sstevel@tonic-gate 
10400Sstevel@tonic-gate 	/* child */
10410Sstevel@tonic-gate 	if ((pid = fork()) == 0) {
10420Sstevel@tonic-gate 		(void) execv(path, cmdline);
10430Sstevel@tonic-gate 		perror(NULL);
10440Sstevel@tonic-gate 		return (ERROR);
10450Sstevel@tonic-gate 	} else if (pid == -1) {
10460Sstevel@tonic-gate 		/* fork failed */
10470Sstevel@tonic-gate 		perror(NULL);
10480Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_FORK_FAIL), cmdline);
10490Sstevel@tonic-gate 		return (ERROR);
10500Sstevel@tonic-gate 	} else {
10510Sstevel@tonic-gate 		/* parent */
10520Sstevel@tonic-gate 		do {
10530Sstevel@tonic-gate 			waitstat = waitpid(pid, (int *)&stat_loc, 0);
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 		} while ((!WIFEXITED(stat_loc) &&
10568331SJerry.Gilliam@Sun.COM 		    !WIFSIGNALED(stat_loc)) || (waitstat == 0));
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 		exit_status = WEXITSTATUS(stat_loc);
10590Sstevel@tonic-gate 
10600Sstevel@tonic-gate 		return (exit_status);
10610Sstevel@tonic-gate 	}
10620Sstevel@tonic-gate }
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate /*
10658831SJerry.Gilliam@Sun.COM  * Exec devfsadm to perform driver config/unconfig operation,
10668831SJerry.Gilliam@Sun.COM  * adding or removing aliases.
10670Sstevel@tonic-gate  */
10688831SJerry.Gilliam@Sun.COM static int
10698831SJerry.Gilliam@Sun.COM exec_devfsadm(
10708831SJerry.Gilliam@Sun.COM 	boolean_t config,
10710Sstevel@tonic-gate 	char *driver_name,
10720Sstevel@tonic-gate 	major_t major_num,
10730Sstevel@tonic-gate 	char *aliases,
10740Sstevel@tonic-gate 	char *classes,
10758831SJerry.Gilliam@Sun.COM 	int verbose_flag,
10768831SJerry.Gilliam@Sun.COM 	int force_flag)
10770Sstevel@tonic-gate {
10780Sstevel@tonic-gate 	int n = 0;
10790Sstevel@tonic-gate 	char *cmdline[MAX_CMD_LINE];
10800Sstevel@tonic-gate 	char maj_num[128];
10810Sstevel@tonic-gate 	char *previous;
10820Sstevel@tonic-gate 	char *current;
10830Sstevel@tonic-gate 	int len;
10848831SJerry.Gilliam@Sun.COM 	int rv;
10850Sstevel@tonic-gate 
10860Sstevel@tonic-gate 	/* build command line */
10870Sstevel@tonic-gate 	cmdline[n++] = DRVCONFIG;
10888831SJerry.Gilliam@Sun.COM 	if (config == B_FALSE) {
10898831SJerry.Gilliam@Sun.COM 		cmdline[n++] = "-u";		/* unconfigure */
10908831SJerry.Gilliam@Sun.COM 		if (force_flag)
10918831SJerry.Gilliam@Sun.COM 			cmdline[n++] = "-f";	/* force if currently in use */
10928831SJerry.Gilliam@Sun.COM 	}
10930Sstevel@tonic-gate 	if (verbose_flag) {
10940Sstevel@tonic-gate 		cmdline[n++] = "-v";
10950Sstevel@tonic-gate 	}
10960Sstevel@tonic-gate 	cmdline[n++] = "-b";
10970Sstevel@tonic-gate 	if (classes) {
10980Sstevel@tonic-gate 		cmdline[n++] = "-c";
10990Sstevel@tonic-gate 		cmdline[n++] = classes;
11000Sstevel@tonic-gate 	}
11010Sstevel@tonic-gate 	cmdline[n++] = "-i";
11020Sstevel@tonic-gate 	cmdline[n++] = driver_name;
11030Sstevel@tonic-gate 	cmdline[n++] = "-m";
1104*9268SJerry.Gilliam@Sun.COM 	(void) snprintf(maj_num, sizeof (maj_num), "%lu", major_num);
11050Sstevel@tonic-gate 	cmdline[n++] = maj_num;
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate 	if (aliases != NULL) {
11080Sstevel@tonic-gate 		len = strlen(aliases);
11090Sstevel@tonic-gate 		previous = aliases;
11100Sstevel@tonic-gate 		do {
11110Sstevel@tonic-gate 			cmdline[n++] = "-a";
11120Sstevel@tonic-gate 			cmdline[n] = calloc(len + 1, 1);
11130Sstevel@tonic-gate 			if (cmdline[n] == NULL) {
11140Sstevel@tonic-gate 				(void) fprintf(stderr,
11150Sstevel@tonic-gate 				    gettext(ERR_NO_MEM));
11160Sstevel@tonic-gate 				return (ERROR);
11170Sstevel@tonic-gate 			}
11180Sstevel@tonic-gate 			current = get_entry(previous,
11194145Scth 			    cmdline[n++], ' ', 0);
11200Sstevel@tonic-gate 			previous = current;
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 		} while (*current != '\0');
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 	}
11250Sstevel@tonic-gate 	cmdline[n] = (char *)0;
11260Sstevel@tonic-gate 
11278831SJerry.Gilliam@Sun.COM 	rv = exec_command(DRVCONFIG_PATH, cmdline);
11288831SJerry.Gilliam@Sun.COM 	if (rv == NOERR)
11298831SJerry.Gilliam@Sun.COM 		return (NOERR);
11308831SJerry.Gilliam@Sun.COM 	return (ERROR);
11318831SJerry.Gilliam@Sun.COM }
11328831SJerry.Gilliam@Sun.COM 
11338831SJerry.Gilliam@Sun.COM int
11348831SJerry.Gilliam@Sun.COM unconfig_driver(
11358831SJerry.Gilliam@Sun.COM 	char *driver_name,
11368831SJerry.Gilliam@Sun.COM 	major_t major_num,
11378831SJerry.Gilliam@Sun.COM 	char *aliases,
11388831SJerry.Gilliam@Sun.COM 	int verbose_flag,
11398831SJerry.Gilliam@Sun.COM 	int force_flag)
11408831SJerry.Gilliam@Sun.COM {
11418831SJerry.Gilliam@Sun.COM 	return (exec_devfsadm(B_FALSE, driver_name, major_num,
11428831SJerry.Gilliam@Sun.COM 	    aliases, NULL, verbose_flag, force_flag));
11438831SJerry.Gilliam@Sun.COM }
11440Sstevel@tonic-gate 
11458831SJerry.Gilliam@Sun.COM /*
11468831SJerry.Gilliam@Sun.COM  * check that major_num doesn't exceed maximum on this machine
11478831SJerry.Gilliam@Sun.COM  * do this here to support add_drv on server for diskless clients
11488831SJerry.Gilliam@Sun.COM  */
11498831SJerry.Gilliam@Sun.COM int
11508831SJerry.Gilliam@Sun.COM config_driver(
11518831SJerry.Gilliam@Sun.COM 	char *driver_name,
11528831SJerry.Gilliam@Sun.COM 	major_t major_num,
11538831SJerry.Gilliam@Sun.COM 	char *aliases,
11548831SJerry.Gilliam@Sun.COM 	char *classes,
11558831SJerry.Gilliam@Sun.COM 	int cleanup_flag,
11568831SJerry.Gilliam@Sun.COM 	int verbose_flag)
11578831SJerry.Gilliam@Sun.COM {
11588831SJerry.Gilliam@Sun.COM 	int	max_dev;
11598831SJerry.Gilliam@Sun.COM 	int	rv;
11608831SJerry.Gilliam@Sun.COM 
11618831SJerry.Gilliam@Sun.COM 	if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
11628831SJerry.Gilliam@Sun.COM 		perror(NULL);
11638831SJerry.Gilliam@Sun.COM 		(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
11648831SJerry.Gilliam@Sun.COM 		return (ERROR);
11658831SJerry.Gilliam@Sun.COM 	}
11668831SJerry.Gilliam@Sun.COM 
11678831SJerry.Gilliam@Sun.COM 	if (major_num >= max_dev) {
11688831SJerry.Gilliam@Sun.COM 		(void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS),
11698831SJerry.Gilliam@Sun.COM 		    major_num, max_dev);
11708831SJerry.Gilliam@Sun.COM 		return (ERROR);
11718831SJerry.Gilliam@Sun.COM 	}
11728831SJerry.Gilliam@Sun.COM 
11738831SJerry.Gilliam@Sun.COM 	/* bind major number and driver name */
11748831SJerry.Gilliam@Sun.COM 	rv = exec_devfsadm(B_TRUE, driver_name, major_num,
11758831SJerry.Gilliam@Sun.COM 	    aliases, classes, verbose_flag, 0);
11768831SJerry.Gilliam@Sun.COM 
11778831SJerry.Gilliam@Sun.COM 	if (rv == NOERR)
11780Sstevel@tonic-gate 		return (NOERR);
11790Sstevel@tonic-gate 	perror(NULL);
11800Sstevel@tonic-gate 	remove_entry(cleanup_flag, driver_name);
11810Sstevel@tonic-gate 	return (ERROR);
11820Sstevel@tonic-gate }
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate void
11850Sstevel@tonic-gate load_driver(char *driver_name, int verbose_flag)
11860Sstevel@tonic-gate {
11870Sstevel@tonic-gate 	int n = 0;
11880Sstevel@tonic-gate 	char *cmdline[MAX_CMD_LINE];
11890Sstevel@tonic-gate 	int exec_status;
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate 	/* build command line */
11920Sstevel@tonic-gate 	cmdline[n++] = DEVFSADM;
11930Sstevel@tonic-gate 	if (verbose_flag) {
11940Sstevel@tonic-gate 		cmdline[n++] = "-v";
11950Sstevel@tonic-gate 	}
11960Sstevel@tonic-gate 	cmdline[n++] = "-i";
11970Sstevel@tonic-gate 	cmdline[n++] = driver_name;
11980Sstevel@tonic-gate 	cmdline[n] = (char *)0;
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate 	exec_status = exec_command(DEVFSADM_PATH, cmdline);
12010Sstevel@tonic-gate 
12020Sstevel@tonic-gate 	if (exec_status != NOERR) {
12030Sstevel@tonic-gate 		/* no clean : name and major number are bound */
12048456SJerry.Gilliam@Sun.COM 		(void) fprintf(stderr, gettext(ERR_CONFIG), driver_name);
12050Sstevel@tonic-gate 	}
12060Sstevel@tonic-gate }
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate void
12090Sstevel@tonic-gate get_modid(char *driver_name, int *mod)
12100Sstevel@tonic-gate {
12110Sstevel@tonic-gate 	struct modinfo	modinfo;
12120Sstevel@tonic-gate 
12130Sstevel@tonic-gate 	modinfo.mi_id = -1;
12140Sstevel@tonic-gate 	modinfo.mi_info = MI_INFO_ALL;
12150Sstevel@tonic-gate 	do {
12160Sstevel@tonic-gate 		/*
12170Sstevel@tonic-gate 		 * If we are at the end of the list of loaded modules
12180Sstevel@tonic-gate 		 * then set *mod = -1 and return
12190Sstevel@tonic-gate 		 */
12200Sstevel@tonic-gate 		if (modctl(MODINFO, 0, &modinfo) < 0) {
12210Sstevel@tonic-gate 			*mod = -1;
12220Sstevel@tonic-gate 			return;
12230Sstevel@tonic-gate 		}
12240Sstevel@tonic-gate 
12250Sstevel@tonic-gate 		*mod = modinfo.mi_id;
12260Sstevel@tonic-gate 	} while (strcmp(driver_name, modinfo.mi_name) != 0);
12270Sstevel@tonic-gate }
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate int
12300Sstevel@tonic-gate create_reconfig(char *basedir)
12310Sstevel@tonic-gate {
12320Sstevel@tonic-gate 	char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1];
12330Sstevel@tonic-gate 	FILE *reconfig_fp;
12340Sstevel@tonic-gate 
12350Sstevel@tonic-gate 	if (basedir != NULL) {
12360Sstevel@tonic-gate 		(void) strcpy(reconfig_file, basedir);
12370Sstevel@tonic-gate 		(void) strcat(reconfig_file, RECONFIGURE);
12380Sstevel@tonic-gate 	} else {
12390Sstevel@tonic-gate 		(void) strcpy(reconfig_file, RECONFIGURE);
12400Sstevel@tonic-gate 	}
12410Sstevel@tonic-gate 	if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL)
12420Sstevel@tonic-gate 		return (ERROR);
12430Sstevel@tonic-gate 
12440Sstevel@tonic-gate 	(void) fclose(reconfig_fp);
12450Sstevel@tonic-gate 	return (NOERR);
12460Sstevel@tonic-gate }
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate /*
12500Sstevel@tonic-gate  * update_minor_entry:
12510Sstevel@tonic-gate  *	open file
12520Sstevel@tonic-gate  *	for each entry in list
12530Sstevel@tonic-gate  *		where list entries are separated by <list_separator>
12540Sstevel@tonic-gate  * 		modify entry : driver_name <entry_separator> entry
12550Sstevel@tonic-gate  *	close file
12560Sstevel@tonic-gate  *
12570Sstevel@tonic-gate  *	return error/noerr
12580Sstevel@tonic-gate  */
12590Sstevel@tonic-gate int
12600Sstevel@tonic-gate update_minor_entry(char *driver_name, char *perm_list)
12610Sstevel@tonic-gate {
12620Sstevel@tonic-gate 	FILE *fp;
12630Sstevel@tonic-gate 	FILE *newfp;
12640Sstevel@tonic-gate 	struct group *sysgrp;
12650Sstevel@tonic-gate 	int match = 0;
12662805Seota 	char line[MAX_DBFILE_ENTRY], *cp, *dup;
12672805Seota 	char drv[FILENAME_MAX + 1], *drv_minor;
12680Sstevel@tonic-gate 	char minor[FILENAME_MAX + 1], perm[OPT_LEN + 1];
12690Sstevel@tonic-gate 	char own[OPT_LEN + 1], grp[OPT_LEN + 1];
12700Sstevel@tonic-gate 	int status = NOERR, i;
12710Sstevel@tonic-gate 	char *newfile, *tptr;
12720Sstevel@tonic-gate 
12730Sstevel@tonic-gate 	if ((fp = fopen(minor_perm, "r")) == NULL) {
12740Sstevel@tonic-gate 		perror(NULL);
12750Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
12760Sstevel@tonic-gate 		    minor_perm);
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate 		return (ERROR);
12790Sstevel@tonic-gate 	}
12800Sstevel@tonic-gate 
12810Sstevel@tonic-gate 	/*
12820Sstevel@tonic-gate 	 * Build filename for temporary file
12830Sstevel@tonic-gate 	 */
12840Sstevel@tonic-gate 	if ((tptr = calloc(strlen(minor_perm) + strlen(XEND) + 1, 1)) == NULL) {
12850Sstevel@tonic-gate 		perror(NULL);
12860Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
12870Sstevel@tonic-gate 	}
12880Sstevel@tonic-gate 	(void) strcpy(tptr, minor_perm);
12890Sstevel@tonic-gate 	(void) strcat(tptr, XEND);
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 	/*
12920Sstevel@tonic-gate 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
12930Sstevel@tonic-gate 	 * assume a gid of "sys" but we can't undo the damage on already
12940Sstevel@tonic-gate 	 * installed systems unless we force the issue.
12950Sstevel@tonic-gate 	 */
12960Sstevel@tonic-gate 	if ((sysgrp = getgrnam("sys")) != NULL) {
12970Sstevel@tonic-gate 		(void) setgid(sysgrp->gr_gid);
12980Sstevel@tonic-gate 	}
12990Sstevel@tonic-gate 
13000Sstevel@tonic-gate 	newfile = mktemp(tptr);
13010Sstevel@tonic-gate 	if ((newfp = fopen(newfile, "w")) == NULL) {
13020Sstevel@tonic-gate 		perror(NULL);
13030Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
13040Sstevel@tonic-gate 		    newfile);
13050Sstevel@tonic-gate 		return (ERROR);
13060Sstevel@tonic-gate 	}
13070Sstevel@tonic-gate 
1308*9268SJerry.Gilliam@Sun.COM 	/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
13090Sstevel@tonic-gate 	if (sscanf(perm_list, "%s%s%s%s", minor, perm, own, grp) != 4) {
13100Sstevel@tonic-gate 		status = ERROR;
13110Sstevel@tonic-gate 	}
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
13142805Seota 		/* copy the whole line into dup */
13152805Seota 		if ((dup = strdup(line)) == NULL) {
13162805Seota 			perror(NULL);
13172805Seota 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
13182805Seota 			status = ERROR;
13192805Seota 			break;
13202805Seota 		}
13212805Seota 		/* cut off comments starting with '#' */
13222805Seota 		if ((cp = strchr(dup, '#')) != NULL)
13232805Seota 			*cp = '\0';
13242805Seota 		/* ignore comment or blank lines */
13252805Seota 		if (is_blank(dup)) {
13262805Seota 			if (fputs(line, newfp) == EOF) {
13270Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_UPDATE),
13280Sstevel@tonic-gate 				    minor_perm);
13290Sstevel@tonic-gate 				status = ERROR;
13300Sstevel@tonic-gate 			}
13312805Seota 			free(dup);
13320Sstevel@tonic-gate 			continue;
13330Sstevel@tonic-gate 		}
13340Sstevel@tonic-gate 
13352805Seota 		/* get the driver name */
1336*9268SJerry.Gilliam@Sun.COM 		/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
13372805Seota 		if (sscanf(dup, "%s", drv) != 1) {
13382805Seota 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
13392805Seota 			    minor_perm, line);
13402805Seota 			status = ERROR;
13412805Seota 			free(dup);
13422805Seota 			break;
13432805Seota 		}
13442805Seota 
13452805Seota 		/*
13462805Seota 		 * get the minor name; place the NULL character at the
13472805Seota 		 * end of the driver name, then make the drv_minor
13482805Seota 		 * point to the first character of the minor name.
13492805Seota 		 * the line missing ':' must be treated as a broken one.
13502805Seota 		 */
13512805Seota 		i = strcspn(drv, ":");
13522805Seota 		if (i == strlen(drv)) {
13530Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
13540Sstevel@tonic-gate 			    minor_perm, line);
13550Sstevel@tonic-gate 			status = ERROR;
13562805Seota 			free(dup);
13572805Seota 			break;
13580Sstevel@tonic-gate 		}
13592805Seota 		drv[i] =  '\0';
13602805Seota 		drv_minor = &drv[strlen(drv) + 1];
13610Sstevel@tonic-gate 
13622805Seota 		/*
13632805Seota 		 * compare both of the driver name and the minor name.
13642805Seota 		 * then the new line should be written to the file if
13652805Seota 		 * both of them match
13662805Seota 		 */
13670Sstevel@tonic-gate 		if ((strcmp(drv, driver_name) == 0) &&
13680Sstevel@tonic-gate 		    (strcmp(minor, drv_minor) == 0)) {
13692805Seota 			/* if it has a comment, keep it */
13702805Seota 			if (cp != NULL) {
13712805Seota 				cp++; /* skip a terminator */
1372*9268SJerry.Gilliam@Sun.COM 				(void) snprintf(line, sizeof (line),
1373*9268SJerry.Gilliam@Sun.COM 				    "%s:%s %s %s %s #%s\n",
13742805Seota 				    drv, minor, perm, own, grp, cp);
13752805Seota 			} else {
1376*9268SJerry.Gilliam@Sun.COM 				(void) snprintf(line, sizeof (line),
1377*9268SJerry.Gilliam@Sun.COM 				    "%s:%s %s %s %s\n",
13782805Seota 				    drv, minor, perm, own, grp);
13792805Seota 			}
13800Sstevel@tonic-gate 			match = 1;
13810Sstevel@tonic-gate 		}
13822805Seota 		free(dup);
13830Sstevel@tonic-gate 
13842805Seota 		/* update the file */
13850Sstevel@tonic-gate 		if ((fputs(line, newfp)) == EOF) {
13860Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE),
13870Sstevel@tonic-gate 			    minor_perm);
13880Sstevel@tonic-gate 			status = ERROR;
13890Sstevel@tonic-gate 		}
13900Sstevel@tonic-gate 	}
13910Sstevel@tonic-gate 
13920Sstevel@tonic-gate 	if (!match) {
13930Sstevel@tonic-gate 		(void) bzero(line, sizeof (&line[0]));
1394*9268SJerry.Gilliam@Sun.COM 		(void) snprintf(line, sizeof (line),
1395*9268SJerry.Gilliam@Sun.COM 		    "%s:%s %s %s %s\n",
13960Sstevel@tonic-gate 		    driver_name, minor, perm, own, grp);
13970Sstevel@tonic-gate 
13980Sstevel@tonic-gate 		/* add the new entry */
13990Sstevel@tonic-gate 		if ((fputs(line, newfp)) == EOF) {
14000Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
14010Sstevel@tonic-gate 			status = ERROR;
14020Sstevel@tonic-gate 		}
14030Sstevel@tonic-gate 	}
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate 	(void) fclose(fp);
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
14080Sstevel@tonic-gate 		status = ERROR;
14090Sstevel@tonic-gate 
14100Sstevel@tonic-gate 	(void) fclose(newfp);
14110Sstevel@tonic-gate 
14120Sstevel@tonic-gate 	/*
14130Sstevel@tonic-gate 	 * if error, leave original file, delete new file
14140Sstevel@tonic-gate 	 * if noerr, replace original file with new file
14150Sstevel@tonic-gate 	 */
14160Sstevel@tonic-gate 	if (status == NOERR) {
14170Sstevel@tonic-gate 		if (rename(minor_perm, tmphold) == -1) {
14180Sstevel@tonic-gate 			perror(NULL);
14190Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
14200Sstevel@tonic-gate 			(void) unlink(newfile);
14210Sstevel@tonic-gate 			return (ERROR);
14220Sstevel@tonic-gate 		} else if (rename(newfile, minor_perm) == -1) {
14230Sstevel@tonic-gate 			perror(NULL);
14240Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
14250Sstevel@tonic-gate 			(void) unlink(minor_perm);
14260Sstevel@tonic-gate 			(void) unlink(newfile);
14270Sstevel@tonic-gate 			if (link(tmphold, minor_perm) == -1) {
14280Sstevel@tonic-gate 				perror(NULL);
14290Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
14300Sstevel@tonic-gate 				    minor_perm, tmphold);
14310Sstevel@tonic-gate 			}
14320Sstevel@tonic-gate 			return (ERROR);
14330Sstevel@tonic-gate 		}
14340Sstevel@tonic-gate 		(void) unlink(tmphold);
14350Sstevel@tonic-gate 	} else {
14360Sstevel@tonic-gate 		/*
14370Sstevel@tonic-gate 		 * since there's an error, leave file alone; remove
14380Sstevel@tonic-gate 		 * new file
14390Sstevel@tonic-gate 		 */
14400Sstevel@tonic-gate 		if (unlink(newfile) == -1) {
14410Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
14420Sstevel@tonic-gate 		}
14430Sstevel@tonic-gate 		return (ERROR);
14440Sstevel@tonic-gate 	}
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate 	return (NOERR);
14470Sstevel@tonic-gate 
14480Sstevel@tonic-gate }
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 
14510Sstevel@tonic-gate /*
14520Sstevel@tonic-gate  * list_entry:
14530Sstevel@tonic-gate  *	open file
14540Sstevel@tonic-gate  *	read thru file, listing all entries if first entry = driver_name
14550Sstevel@tonic-gate  *	close
14560Sstevel@tonic-gate  */
14570Sstevel@tonic-gate void
14580Sstevel@tonic-gate list_entry(
14590Sstevel@tonic-gate 	char *oldfile,
14600Sstevel@tonic-gate 	char *driver_name,
14610Sstevel@tonic-gate 	char *marker)
14620Sstevel@tonic-gate {
14630Sstevel@tonic-gate 	FILE	*fp;
14640Sstevel@tonic-gate 	int	i;
14652805Seota 	char	line[MAX_DBFILE_ENTRY], *cp;
14660Sstevel@tonic-gate 	char	drv[FILENAME_MAX + 1];
14670Sstevel@tonic-gate 
14680Sstevel@tonic-gate 	if ((fp = fopen(oldfile, "r")) == NULL) {
14690Sstevel@tonic-gate 		perror(NULL);
14700Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 		return;
14730Sstevel@tonic-gate 	}
14740Sstevel@tonic-gate 
14750Sstevel@tonic-gate 	while (fgets(line, sizeof (line), fp) != NULL) {
14762805Seota 		/* cut off comments starting with '#' */
14772805Seota 		if ((cp = strchr(line, '#')) != NULL)
14782805Seota 			*cp = '\0';
14792805Seota 		/* ignore comment or blank lines */
14802805Seota 		if (is_blank(line))
14810Sstevel@tonic-gate 			continue;
14822805Seota 		/* sanity-check */
1483*9268SJerry.Gilliam@Sun.COM 		/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
14840Sstevel@tonic-gate 		if (sscanf(line, "%s", drv) != 1) {
14850Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
14860Sstevel@tonic-gate 			    oldfile, line);
14870Sstevel@tonic-gate 		}
14880Sstevel@tonic-gate 
14890Sstevel@tonic-gate 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
14900Sstevel@tonic-gate 			drv[i] =  '\0';
14910Sstevel@tonic-gate 		}
14920Sstevel@tonic-gate 
14930Sstevel@tonic-gate 		if (strcmp(driver_name, drv) == 0) {
14940Sstevel@tonic-gate 			(void) fprintf(stdout, "%s", line);
14950Sstevel@tonic-gate 		}
14960Sstevel@tonic-gate 	}
14970Sstevel@tonic-gate 
14980Sstevel@tonic-gate 	(void) fclose(fp);
14990Sstevel@tonic-gate }
15000Sstevel@tonic-gate 
15012805Seota static boolean_t
15022805Seota is_token(char *tok)
15032805Seota {
15042805Seota 	/*
15052805Seota 	 * Check the token here. According to IEEE1275 Open Firmware Boot
15062805Seota 	 * Standard, the name is composed of 1 to 31 letters,
15072805Seota 	 * digits and punctuation characters from the set ",._+-", and
15082805Seota 	 * uppercase and lowercase characters are considered distinct.
15092805Seota 	 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
15102805Seota 	 * However, since either the definition of driver or aliase names is
15112805Seota 	 * not known well, only '#' is avoided explicitly. (the kernel lexical
15122805Seota 	 * analyzer treats it as a start of a comment)
15132805Seota 	 */
15142805Seota 	for (/* nothing */; *tok != '\0'; tok++)
15152805Seota 		if (*tok == '#' || iscntrl(*tok))
15162805Seota 			return (B_FALSE);
15172805Seota 
15182805Seota 	return (B_TRUE);
15192805Seota }
15200Sstevel@tonic-gate 
15210Sstevel@tonic-gate /*
15220Sstevel@tonic-gate  * check each entry in perm_list for:
15230Sstevel@tonic-gate  *	4 arguments
15240Sstevel@tonic-gate  *	permission arg is in valid range
15250Sstevel@tonic-gate  * permlist entries separated by comma
15260Sstevel@tonic-gate  * return ERROR/NOERR
15270Sstevel@tonic-gate  */
15280Sstevel@tonic-gate int
15290Sstevel@tonic-gate check_perm_opts(char *perm_list)
15300Sstevel@tonic-gate {
15310Sstevel@tonic-gate 	char *current_head;
15320Sstevel@tonic-gate 	char *previous_head;
15330Sstevel@tonic-gate 	char *one_entry;
15340Sstevel@tonic-gate 	int i, len, scan_stat;
15350Sstevel@tonic-gate 	char minor[FILENAME_MAX + 1];
15360Sstevel@tonic-gate 	char perm[OPT_LEN + 1];
15370Sstevel@tonic-gate 	char own[OPT_LEN + 1];
15380Sstevel@tonic-gate 	char grp[OPT_LEN + 1];
15390Sstevel@tonic-gate 	char dumb[OPT_LEN + 1];
15400Sstevel@tonic-gate 	int status = NOERR;
15410Sstevel@tonic-gate 	int intperm;
15420Sstevel@tonic-gate 
15430Sstevel@tonic-gate 	len = strlen(perm_list);
15440Sstevel@tonic-gate 
15450Sstevel@tonic-gate 	if (len == 0) {
15460Sstevel@tonic-gate 		return (ERROR);
15470Sstevel@tonic-gate 	}
15480Sstevel@tonic-gate 
15490Sstevel@tonic-gate 	one_entry = calloc(len + 1, 1);
15500Sstevel@tonic-gate 	if (one_entry == NULL) {
15510Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
15520Sstevel@tonic-gate 		return (ERROR);
15530Sstevel@tonic-gate 	}
15540Sstevel@tonic-gate 
15550Sstevel@tonic-gate 	previous_head = perm_list;
15560Sstevel@tonic-gate 	current_head = perm_list;
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate 	while (*current_head != '\0') {
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate 		for (i = 0; i <= len; i++)
15610Sstevel@tonic-gate 			one_entry[i] = 0;
15620Sstevel@tonic-gate 
15634145Scth 		current_head = get_entry(previous_head, one_entry, ',', 0);
15640Sstevel@tonic-gate 
15650Sstevel@tonic-gate 		previous_head = current_head;
1566*9268SJerry.Gilliam@Sun.COM 		/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
15670Sstevel@tonic-gate 		scan_stat = sscanf(one_entry, "%s%s%s%s%s", minor, perm, own,
15680Sstevel@tonic-gate 		    grp, dumb);
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate 		if (scan_stat < 4) {
15710Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_MIS_TOK),
15720Sstevel@tonic-gate 			    "-m", one_entry);
15730Sstevel@tonic-gate 			status = ERROR;
15740Sstevel@tonic-gate 		}
15750Sstevel@tonic-gate 		if (scan_stat > 4) {
15760Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS),
15770Sstevel@tonic-gate 			    "-m", one_entry);
15780Sstevel@tonic-gate 			status = ERROR;
15790Sstevel@tonic-gate 		}
15800Sstevel@tonic-gate 
15810Sstevel@tonic-gate 		intperm = atoi(perm);
15820Sstevel@tonic-gate 		if (intperm < 0000 || intperm > 4777) {
15830Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_MODE), perm);
15840Sstevel@tonic-gate 			status = ERROR;
15850Sstevel@tonic-gate 		}
15860Sstevel@tonic-gate 	}
15870Sstevel@tonic-gate 
15880Sstevel@tonic-gate 	free(one_entry);
15890Sstevel@tonic-gate 	return (status);
15900Sstevel@tonic-gate }
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate 
15930Sstevel@tonic-gate /*
15940Sstevel@tonic-gate  * check each alias :
15950Sstevel@tonic-gate  *	alias list members separated by white space
15960Sstevel@tonic-gate  *	cannot exist as driver name in /etc/name_to_major
15970Sstevel@tonic-gate  *	cannot exist as driver or alias name in /etc/driver_aliases
15980Sstevel@tonic-gate  */
15990Sstevel@tonic-gate int
16000Sstevel@tonic-gate aliases_unique(char *aliases)
16010Sstevel@tonic-gate {
16020Sstevel@tonic-gate 	char *current_head;
16030Sstevel@tonic-gate 	char *previous_head;
16040Sstevel@tonic-gate 	char *one_entry;
16058831SJerry.Gilliam@Sun.COM 	int len;
16060Sstevel@tonic-gate 	int is_unique;
16078831SJerry.Gilliam@Sun.COM 	int err;
16080Sstevel@tonic-gate 
16090Sstevel@tonic-gate 	len = strlen(aliases);
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate 	one_entry = calloc(len + 1, 1);
16120Sstevel@tonic-gate 	if (one_entry == NULL) {
16130Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
16140Sstevel@tonic-gate 		return (ERROR);
16150Sstevel@tonic-gate 	}
16160Sstevel@tonic-gate 
16170Sstevel@tonic-gate 	previous_head = aliases;
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	do {
16208831SJerry.Gilliam@Sun.COM 		bzero(one_entry, len+1);
16214145Scth 		current_head = get_entry(previous_head, one_entry, ' ', 1);
16220Sstevel@tonic-gate 		previous_head = current_head;
16230Sstevel@tonic-gate 
16240Sstevel@tonic-gate 		if ((unique_driver_name(one_entry, name_to_major,
16258831SJerry.Gilliam@Sun.COM 		    &is_unique)) == ERROR)
16268831SJerry.Gilliam@Sun.COM 			goto err_out;
16270Sstevel@tonic-gate 
16280Sstevel@tonic-gate 		if (is_unique != UNIQUE) {
16290Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ),
16300Sstevel@tonic-gate 			    one_entry);
16318831SJerry.Gilliam@Sun.COM 			goto err_out;
16320Sstevel@tonic-gate 		}
16330Sstevel@tonic-gate 
16348831SJerry.Gilliam@Sun.COM 		if ((err = unique_drv_alias(one_entry)) != UNIQUE) {
16358831SJerry.Gilliam@Sun.COM 			if (err == NOT_UNIQUE) {
16368831SJerry.Gilliam@Sun.COM 				(void) fprintf(stderr,
16378831SJerry.Gilliam@Sun.COM 				    gettext(ERR_ALIAS_IN_USE), one_entry);
16388831SJerry.Gilliam@Sun.COM 			}
16398831SJerry.Gilliam@Sun.COM 			goto err_out;
16400Sstevel@tonic-gate 		}
16410Sstevel@tonic-gate 
16422805Seota 		if (!is_token(one_entry)) {
16432805Seota 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
16442805Seota 			    "-i", one_entry);
16458831SJerry.Gilliam@Sun.COM 			goto err_out;
16462805Seota 		}
16472805Seota 
16480Sstevel@tonic-gate 	} while (*current_head != '\0');
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate 	free(one_entry);
16510Sstevel@tonic-gate 	return (NOERR);
16520Sstevel@tonic-gate 
16538831SJerry.Gilliam@Sun.COM err_out:
16548831SJerry.Gilliam@Sun.COM 	free(one_entry);
16558831SJerry.Gilliam@Sun.COM 	return (ERROR);
16568831SJerry.Gilliam@Sun.COM }
16578831SJerry.Gilliam@Sun.COM 
16588831SJerry.Gilliam@Sun.COM /*
16598831SJerry.Gilliam@Sun.COM  * verify each alias :
16608831SJerry.Gilliam@Sun.COM  *	alias list members separated by white space and quoted
16618831SJerry.Gilliam@Sun.COM  *	exist as alias name in /etc/driver_aliases
16628831SJerry.Gilliam@Sun.COM  */
16638831SJerry.Gilliam@Sun.COM int
1664*9268SJerry.Gilliam@Sun.COM aliases_exist(char *aliases)
16658831SJerry.Gilliam@Sun.COM {
16668831SJerry.Gilliam@Sun.COM 	char *current_head;
16678831SJerry.Gilliam@Sun.COM 	char *previous_head;
16688831SJerry.Gilliam@Sun.COM 	char *one_entry;
16698831SJerry.Gilliam@Sun.COM 	int len;
16708831SJerry.Gilliam@Sun.COM 
16718831SJerry.Gilliam@Sun.COM 	len = strlen(aliases);
16728831SJerry.Gilliam@Sun.COM 
16738831SJerry.Gilliam@Sun.COM 	one_entry = calloc(len + 1, 1);
16748831SJerry.Gilliam@Sun.COM 	if (one_entry == NULL) {
16758831SJerry.Gilliam@Sun.COM 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
16768831SJerry.Gilliam@Sun.COM 		return (ERROR);
16778831SJerry.Gilliam@Sun.COM 	}
16788831SJerry.Gilliam@Sun.COM 
16798831SJerry.Gilliam@Sun.COM 	previous_head = aliases;
16808831SJerry.Gilliam@Sun.COM 
16818831SJerry.Gilliam@Sun.COM 	do {
16828831SJerry.Gilliam@Sun.COM 		bzero(one_entry, len+1);
16838831SJerry.Gilliam@Sun.COM 		current_head = get_entry(previous_head, one_entry, ' ', 1);
16848831SJerry.Gilliam@Sun.COM 		previous_head = current_head;
16858831SJerry.Gilliam@Sun.COM 
1686*9268SJerry.Gilliam@Sun.COM 		if (unique_drv_alias(one_entry) != NOT_UNIQUE)
16878831SJerry.Gilliam@Sun.COM 			goto err_out;
16888831SJerry.Gilliam@Sun.COM 
16898831SJerry.Gilliam@Sun.COM 		if (!is_token(one_entry)) {
16908831SJerry.Gilliam@Sun.COM 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
16918831SJerry.Gilliam@Sun.COM 			    "-i", one_entry);
16928831SJerry.Gilliam@Sun.COM 			goto err_out;
16938831SJerry.Gilliam@Sun.COM 		}
16948831SJerry.Gilliam@Sun.COM 
16958831SJerry.Gilliam@Sun.COM 	} while (*current_head != '\0');
16968831SJerry.Gilliam@Sun.COM 
16978831SJerry.Gilliam@Sun.COM 	free(one_entry);
16988831SJerry.Gilliam@Sun.COM 	return (NOERR);
16998831SJerry.Gilliam@Sun.COM 
17008831SJerry.Gilliam@Sun.COM err_out:
17018831SJerry.Gilliam@Sun.COM 	free(one_entry);
17028831SJerry.Gilliam@Sun.COM 	return (ERROR);
17030Sstevel@tonic-gate }
17040Sstevel@tonic-gate 
17050Sstevel@tonic-gate 
17064145Scth /*
17074145Scth  * check each alias :
17084145Scth  *	if path-oriented alias, path exists
17094145Scth  */
17104145Scth int
17114145Scth aliases_paths_exist(char *aliases)
17124145Scth {
17134145Scth 	char *current_head;
17144145Scth 	char *previous_head;
17154145Scth 	char *one_entry;
17164145Scth 	int i, len;
17174145Scth 	char path[MAXPATHLEN];
17184145Scth 	struct stat buf;
17194145Scth 
17204145Scth 	len = strlen(aliases);
17214145Scth 
17224145Scth 	one_entry = calloc(len + 1, 1);
17234145Scth 	if (one_entry == NULL) {
17244145Scth 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
17254145Scth 		return (ERROR);
17264145Scth 	}
17274145Scth 
17284145Scth 	previous_head = aliases;
17294145Scth 
17304145Scth 	do {
17314145Scth 		for (i = 0; i <= len; i++)
17324145Scth 			one_entry[i] = 0;
17334145Scth 
17344145Scth 		current_head = get_entry(previous_head, one_entry, ' ', 1);
17354145Scth 		previous_head = current_head;
17364145Scth 
17374145Scth 		/* if the alias is a path, ensure that the path exists */
17384145Scth 		if (*one_entry != '/')
17394145Scth 			continue;
17404145Scth 		(void) snprintf(path, sizeof (path), "/devices/%s", one_entry);
17414145Scth 		if (stat(path, &buf) == 0)
17424145Scth 			continue;
17434145Scth 
17444145Scth 		/* no device at specified path-oriented alias path */
17454145Scth 		(void) fprintf(stderr, gettext(ERR_PATH_ORIENTED_ALIAS),
17464145Scth 		    one_entry);
17474145Scth 		free(one_entry);
17484145Scth 		return (ERROR);
17494145Scth 
17504145Scth 	} while (*current_head != '\0');
17514145Scth 
17524145Scth 	free(one_entry);
17534145Scth 
17544145Scth 	return (NOERR);
17554145Scth }
17564145Scth 
17574145Scth 
17580Sstevel@tonic-gate int
17590Sstevel@tonic-gate update_driver_aliases(
17600Sstevel@tonic-gate 	char *driver_name,
17610Sstevel@tonic-gate 	char *aliases)
17620Sstevel@tonic-gate {
17630Sstevel@tonic-gate 	/* make call to update the aliases file */
17644145Scth 	return (append_to_file(driver_name, aliases, driver_aliases,
17654145Scth 	    ' ', " ", 1));
17660Sstevel@tonic-gate }
17670Sstevel@tonic-gate 
17680Sstevel@tonic-gate 
17698831SJerry.Gilliam@Sun.COM /*
17708831SJerry.Gilliam@Sun.COM  * Return:
17718831SJerry.Gilliam@Sun.COM  *	ERROR in case of memory or read error
17728831SJerry.Gilliam@Sun.COM  *	UNIQUE if there is no existing match to the supplied alias
17738831SJerry.Gilliam@Sun.COM  *	NOT_UNIQUE if there is a match
17748831SJerry.Gilliam@Sun.COM  * An error message is emitted in the case of ERROR,
17758831SJerry.Gilliam@Sun.COM  * up to the caller otherwise.
17768831SJerry.Gilliam@Sun.COM  */
17770Sstevel@tonic-gate int
17780Sstevel@tonic-gate unique_drv_alias(char *drv_alias)
17790Sstevel@tonic-gate {
17800Sstevel@tonic-gate 	FILE *fp;
17810Sstevel@tonic-gate 	char drv[FILENAME_MAX + 1];
17822805Seota 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
17830Sstevel@tonic-gate 	char alias[FILENAME_MAX + 1];
17844145Scth 	char *a;
17858831SJerry.Gilliam@Sun.COM 	int status = UNIQUE;
17860Sstevel@tonic-gate 
17870Sstevel@tonic-gate 	fp = fopen(driver_aliases, "r");
17880Sstevel@tonic-gate 
17890Sstevel@tonic-gate 	if (fp != NULL) {
17900Sstevel@tonic-gate 		while ((fgets(line, sizeof (line), fp) != 0) &&
17918831SJerry.Gilliam@Sun.COM 		    status == UNIQUE) {
17922805Seota 			/* cut off comments starting with '#' */
17932805Seota 			if ((cp = strchr(line, '#')) != NULL)
17942805Seota 				*cp = '\0';
17952805Seota 			/* ignore comment or blank lines */
17962805Seota 			if (is_blank(line))
17972805Seota 				continue;
17982805Seota 			/* sanity-check */
1799*9268SJerry.Gilliam@Sun.COM 			/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
18000Sstevel@tonic-gate 			if (sscanf(line, "%s %s", drv, alias) != 2)
18010Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
18020Sstevel@tonic-gate 				    driver_aliases, line);
18030Sstevel@tonic-gate 
18044145Scth 			/* unquote for compare */
18054145Scth 			if ((*alias == '"') &&
18064145Scth 			    (*(alias + strlen(alias) - 1) == '"')) {
18074145Scth 				a = &alias[1];
18084145Scth 				alias[strlen(alias) - 1] = '\0';
18094145Scth 			} else
18104145Scth 				a = alias;
18114145Scth 
18120Sstevel@tonic-gate 			if ((strcmp(drv_alias, drv) == 0) ||
18134145Scth 			    (strcmp(drv_alias, a) == 0)) {
18148831SJerry.Gilliam@Sun.COM 				status = NOT_UNIQUE;
18158831SJerry.Gilliam@Sun.COM 				break;
18160Sstevel@tonic-gate 			}
18170Sstevel@tonic-gate 		}
18180Sstevel@tonic-gate 		(void) fclose(fp);
18190Sstevel@tonic-gate 		return (status);
18200Sstevel@tonic-gate 	} else {
18210Sstevel@tonic-gate 		perror(NULL);
18220Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
18230Sstevel@tonic-gate 		return (ERROR);
18240Sstevel@tonic-gate 	}
18250Sstevel@tonic-gate }
18260Sstevel@tonic-gate 
18270Sstevel@tonic-gate 
18280Sstevel@tonic-gate /*
18290Sstevel@tonic-gate  * search for driver_name in first field of file file_name
18308831SJerry.Gilliam@Sun.COM  * searching name_to_major and driver_aliases: name separated
18318831SJerry.Gilliam@Sun.COM  * from the remainder of the line by white space.
18320Sstevel@tonic-gate  */
18330Sstevel@tonic-gate int
18340Sstevel@tonic-gate unique_driver_name(char *driver_name, char *file_name,
18350Sstevel@tonic-gate 	int *is_unique)
18360Sstevel@tonic-gate {
18378831SJerry.Gilliam@Sun.COM 	int ret, err;
18380Sstevel@tonic-gate 
18390Sstevel@tonic-gate 	if ((ret = get_major_no(driver_name, file_name)) == ERROR) {
18400Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
18410Sstevel@tonic-gate 		    file_name);
18420Sstevel@tonic-gate 	} else {
18430Sstevel@tonic-gate 		/* check alias file for name collision */
18448831SJerry.Gilliam@Sun.COM 		if ((err = unique_drv_alias(driver_name)) != UNIQUE) {
18458831SJerry.Gilliam@Sun.COM 			if (err == NOT_UNIQUE) {
18468831SJerry.Gilliam@Sun.COM 				(void) fprintf(stderr,
18478831SJerry.Gilliam@Sun.COM 				    gettext(ERR_ALIAS_IN_USE),
18488831SJerry.Gilliam@Sun.COM 				    driver_name);
18498831SJerry.Gilliam@Sun.COM 			}
18500Sstevel@tonic-gate 			ret = ERROR;
18510Sstevel@tonic-gate 		} else {
18520Sstevel@tonic-gate 			if (ret != UNIQUE)
18530Sstevel@tonic-gate 				*is_unique = NOT_UNIQUE;
18540Sstevel@tonic-gate 			else
18550Sstevel@tonic-gate 				*is_unique = ret;
18560Sstevel@tonic-gate 			ret = NOERR;
18570Sstevel@tonic-gate 		}
18580Sstevel@tonic-gate 	}
18590Sstevel@tonic-gate 	return (ret);
18600Sstevel@tonic-gate }
18610Sstevel@tonic-gate 
18628331SJerry.Gilliam@Sun.COM /*
18638331SJerry.Gilliam@Sun.COM  * returns:
18648331SJerry.Gilliam@Sun.COM  *	SUCCESS - not an existing driver alias
18658331SJerry.Gilliam@Sun.COM  *	NOT_UNIQUE - matching driver alias exists
18668331SJerry.Gilliam@Sun.COM  *	ERROR - an error occurred
18678331SJerry.Gilliam@Sun.COM  */
18688331SJerry.Gilliam@Sun.COM int
18698331SJerry.Gilliam@Sun.COM check_duplicate_driver_alias(char *driver_name, char *drv_alias)
18708331SJerry.Gilliam@Sun.COM {
18718331SJerry.Gilliam@Sun.COM 	FILE *fp;
18728331SJerry.Gilliam@Sun.COM 	char drv[FILENAME_MAX + 1];
18738331SJerry.Gilliam@Sun.COM 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
18748331SJerry.Gilliam@Sun.COM 	char alias[FILENAME_MAX + 1];
18758331SJerry.Gilliam@Sun.COM 	char *a;
18768331SJerry.Gilliam@Sun.COM 	int status = SUCCESS;
18778331SJerry.Gilliam@Sun.COM 
18788331SJerry.Gilliam@Sun.COM 	if ((fp = fopen(driver_aliases, "r")) == NULL) {
18798331SJerry.Gilliam@Sun.COM 		perror(NULL);
18808331SJerry.Gilliam@Sun.COM 		(void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
18818331SJerry.Gilliam@Sun.COM 		return (ERROR);
18828331SJerry.Gilliam@Sun.COM 	}
18838331SJerry.Gilliam@Sun.COM 
18848331SJerry.Gilliam@Sun.COM 	while (fgets(line, sizeof (line), fp) != 0) {
18858331SJerry.Gilliam@Sun.COM 		/* cut off comments starting with '#' */
18868331SJerry.Gilliam@Sun.COM 		if ((cp = strchr(line, '#')) != NULL)
18878331SJerry.Gilliam@Sun.COM 			*cp = '\0';
18888331SJerry.Gilliam@Sun.COM 		/* ignore comment or blank lines */
18898331SJerry.Gilliam@Sun.COM 		if (is_blank(line))
18908331SJerry.Gilliam@Sun.COM 			continue;
18918331SJerry.Gilliam@Sun.COM 		/* sanity-check */
1892*9268SJerry.Gilliam@Sun.COM 		/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
18938331SJerry.Gilliam@Sun.COM 		if (sscanf(line, "%s %s", drv, alias) != 2)
18948331SJerry.Gilliam@Sun.COM 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
18958331SJerry.Gilliam@Sun.COM 			    driver_aliases, line);
18968331SJerry.Gilliam@Sun.COM 
18978331SJerry.Gilliam@Sun.COM 		/* unquote for compare */
18988331SJerry.Gilliam@Sun.COM 		if ((*alias == '"') &&
18998331SJerry.Gilliam@Sun.COM 		    (*(alias + strlen(alias) - 1) == '"')) {
19008331SJerry.Gilliam@Sun.COM 			a = &alias[1];
19018331SJerry.Gilliam@Sun.COM 			alias[strlen(alias) - 1] = '\0';
19028331SJerry.Gilliam@Sun.COM 		} else
19038331SJerry.Gilliam@Sun.COM 			a = alias;
19048331SJerry.Gilliam@Sun.COM 
19058331SJerry.Gilliam@Sun.COM 		if ((strcmp(drv_alias, a) == 0) &&
19068331SJerry.Gilliam@Sun.COM 		    (strcmp(drv, driver_name) == 0)) {
19078331SJerry.Gilliam@Sun.COM 			status = NOT_UNIQUE;
19088331SJerry.Gilliam@Sun.COM 		}
19098331SJerry.Gilliam@Sun.COM 
19108331SJerry.Gilliam@Sun.COM 		if ((strcmp(drv_alias, drv) == 0) ||
19118331SJerry.Gilliam@Sun.COM 		    ((strcmp(drv_alias, a) == 0) &&
19128331SJerry.Gilliam@Sun.COM 		    (strcmp(drv, driver_name) != 0))) {
19138331SJerry.Gilliam@Sun.COM 			(void) fprintf(stderr,
19148331SJerry.Gilliam@Sun.COM 			    gettext(ERR_ALIAS_IN_USE),
19158331SJerry.Gilliam@Sun.COM 			    drv_alias);
19168331SJerry.Gilliam@Sun.COM 			status = ERROR;
19178331SJerry.Gilliam@Sun.COM 			goto done;
19188331SJerry.Gilliam@Sun.COM 		}
19198331SJerry.Gilliam@Sun.COM 	}
19208331SJerry.Gilliam@Sun.COM 
19218331SJerry.Gilliam@Sun.COM done:
19228331SJerry.Gilliam@Sun.COM 	(void) fclose(fp);
19238331SJerry.Gilliam@Sun.COM 	return (status);
19248331SJerry.Gilliam@Sun.COM }
19258331SJerry.Gilliam@Sun.COM 
19268331SJerry.Gilliam@Sun.COM int
19278331SJerry.Gilliam@Sun.COM trim_duplicate_aliases(char *driver_name, char *aliases, char **aliases2p)
19288331SJerry.Gilliam@Sun.COM {
19298331SJerry.Gilliam@Sun.COM 	char *current_head;
19308331SJerry.Gilliam@Sun.COM 	char *previous_head;
19318331SJerry.Gilliam@Sun.COM 	char *one_entry;
19328331SJerry.Gilliam@Sun.COM 	char *aliases2;
19338331SJerry.Gilliam@Sun.COM 	int rv, len;
19348331SJerry.Gilliam@Sun.COM 	int n = 0;
19358331SJerry.Gilliam@Sun.COM 
19368331SJerry.Gilliam@Sun.COM 	*aliases2p = NULL;
19378331SJerry.Gilliam@Sun.COM 	len = strlen(aliases) + 1;
19388331SJerry.Gilliam@Sun.COM 
19398331SJerry.Gilliam@Sun.COM 	one_entry = calloc(len, 1);
19408331SJerry.Gilliam@Sun.COM 	aliases2 = calloc(len, 1);
19418331SJerry.Gilliam@Sun.COM 	if (one_entry == NULL || aliases2 == NULL) {
19428331SJerry.Gilliam@Sun.COM 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
19438331SJerry.Gilliam@Sun.COM 		return (ERROR);
19448331SJerry.Gilliam@Sun.COM 	}
19458331SJerry.Gilliam@Sun.COM 
19468331SJerry.Gilliam@Sun.COM 	previous_head = aliases;
19478331SJerry.Gilliam@Sun.COM 
19488331SJerry.Gilliam@Sun.COM 	do {
19498331SJerry.Gilliam@Sun.COM 		(void) bzero(one_entry, len);
19508331SJerry.Gilliam@Sun.COM 		current_head = get_entry(previous_head, one_entry, ' ', 1);
19518331SJerry.Gilliam@Sun.COM 		previous_head = current_head;
19528331SJerry.Gilliam@Sun.COM 
19538331SJerry.Gilliam@Sun.COM 		rv = check_duplicate_driver_alias(driver_name, one_entry);
19548331SJerry.Gilliam@Sun.COM 		switch (rv) {
19558331SJerry.Gilliam@Sun.COM 		case SUCCESS:
19568331SJerry.Gilliam@Sun.COM 			/* not an existing driver alias: add it */
19578331SJerry.Gilliam@Sun.COM 			if (n > 0) {
19588331SJerry.Gilliam@Sun.COM 				if (strlcat(aliases2, " ", len) >= len)
19598331SJerry.Gilliam@Sun.COM 					goto err;
19608331SJerry.Gilliam@Sun.COM 			}
19618331SJerry.Gilliam@Sun.COM 			if (strlcat(aliases2, one_entry, len) >= len)
19628331SJerry.Gilliam@Sun.COM 				goto err;
19638331SJerry.Gilliam@Sun.COM 			n++;
19648331SJerry.Gilliam@Sun.COM 			break;
19658331SJerry.Gilliam@Sun.COM 		case NOT_UNIQUE:
19668331SJerry.Gilliam@Sun.COM 			/* matching driver alias exists: do not add it */
19678331SJerry.Gilliam@Sun.COM 			break;
19688331SJerry.Gilliam@Sun.COM 		case ERROR:
19698331SJerry.Gilliam@Sun.COM 			/* error reading the alias file */
19708331SJerry.Gilliam@Sun.COM 			goto err;
19718331SJerry.Gilliam@Sun.COM 		default:
19728331SJerry.Gilliam@Sun.COM 			goto err;
19738331SJerry.Gilliam@Sun.COM 		}
19748331SJerry.Gilliam@Sun.COM 
19758331SJerry.Gilliam@Sun.COM 		if (!is_token(one_entry)) {
19768331SJerry.Gilliam@Sun.COM 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
19778331SJerry.Gilliam@Sun.COM 			    "-i", one_entry);
19788331SJerry.Gilliam@Sun.COM 			goto err;
19798331SJerry.Gilliam@Sun.COM 		}
19808331SJerry.Gilliam@Sun.COM 	} while (*current_head != '\0');
19818331SJerry.Gilliam@Sun.COM 
19828331SJerry.Gilliam@Sun.COM 	/*
19838331SJerry.Gilliam@Sun.COM 	 * If all the aliases listed are already
19848331SJerry.Gilliam@Sun.COM 	 * present we actually have none to do.
19858331SJerry.Gilliam@Sun.COM 	 */
19868331SJerry.Gilliam@Sun.COM 	if (n == 0) {
19878331SJerry.Gilliam@Sun.COM 		free(aliases2);
19888331SJerry.Gilliam@Sun.COM 	} else {
19898331SJerry.Gilliam@Sun.COM 		*aliases2p = aliases2;
19908331SJerry.Gilliam@Sun.COM 	}
19918331SJerry.Gilliam@Sun.COM 	free(one_entry);
19928331SJerry.Gilliam@Sun.COM 	return (NOERR);
19938331SJerry.Gilliam@Sun.COM 
19948331SJerry.Gilliam@Sun.COM err:
19958331SJerry.Gilliam@Sun.COM 	free(aliases2);
19968331SJerry.Gilliam@Sun.COM 	free(one_entry);
19978331SJerry.Gilliam@Sun.COM 	return (ERROR);
19988331SJerry.Gilliam@Sun.COM }
19990Sstevel@tonic-gate 
20000Sstevel@tonic-gate int
20010Sstevel@tonic-gate check_space_within_quote(char *str)
20020Sstevel@tonic-gate {
20030Sstevel@tonic-gate 	register int i;
20040Sstevel@tonic-gate 	register int len;
20050Sstevel@tonic-gate 	int quoted = 0;
20060Sstevel@tonic-gate 
20070Sstevel@tonic-gate 	len = strlen(str);
20080Sstevel@tonic-gate 	for (i = 0; i < len; i++, str++) {
20090Sstevel@tonic-gate 		if (*str == '"') {
20100Sstevel@tonic-gate 			if (quoted == 0)
20110Sstevel@tonic-gate 				quoted++;
20120Sstevel@tonic-gate 			else
20130Sstevel@tonic-gate 				quoted--;
20140Sstevel@tonic-gate 		} else if (*str == ' ' && quoted)
20150Sstevel@tonic-gate 			return (ERROR);
20160Sstevel@tonic-gate 	}
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate 	return (0);
20190Sstevel@tonic-gate }
20200Sstevel@tonic-gate 
20210Sstevel@tonic-gate 
20220Sstevel@tonic-gate /*
20230Sstevel@tonic-gate  * get major number
20240Sstevel@tonic-gate  * write driver_name major_num to name_to_major file
20250Sstevel@tonic-gate  * major_num returned in major_num
20260Sstevel@tonic-gate  * return success/failure
20270Sstevel@tonic-gate  */
20280Sstevel@tonic-gate int
20290Sstevel@tonic-gate update_name_to_major(char *driver_name, major_t *major_num, int server)
20300Sstevel@tonic-gate {
20310Sstevel@tonic-gate 	char major[MAX_STR_MAJOR + 1];
20320Sstevel@tonic-gate 	struct stat buf;
20330Sstevel@tonic-gate 	char *num_list;
20340Sstevel@tonic-gate 	char drv_majnum_str[MAX_STR_MAJOR + 1];
20350Sstevel@tonic-gate 	int new_maj = -1;
20360Sstevel@tonic-gate 	int i, tmp = 0, is_unique, have_rem_n2m = 0;
20370Sstevel@tonic-gate 	int max_dev = 0;
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate 	/*
20400Sstevel@tonic-gate 	 * if driver_name already in rem_name_to_major
20410Sstevel@tonic-gate 	 * 	delete entry from rem_nam_to_major
20420Sstevel@tonic-gate 	 *	put entry into name_to_major
20430Sstevel@tonic-gate 	 */
20440Sstevel@tonic-gate 
20450Sstevel@tonic-gate 	if (stat(rem_name_to_major, &buf) == 0) {
20460Sstevel@tonic-gate 		have_rem_n2m = 1;
20470Sstevel@tonic-gate 	}
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate 	if (have_rem_n2m) {
20500Sstevel@tonic-gate 		if ((is_unique = get_major_no(driver_name, rem_name_to_major))
20510Sstevel@tonic-gate 		    == ERROR)
20520Sstevel@tonic-gate 			return (ERROR);
20530Sstevel@tonic-gate 
20540Sstevel@tonic-gate 		/*
20550Sstevel@tonic-gate 		 * found a match in rem_name_to_major
20560Sstevel@tonic-gate 		 */
20570Sstevel@tonic-gate 		if (is_unique != UNIQUE) {
20580Sstevel@tonic-gate 			char scratch[FILENAME_MAX];
20590Sstevel@tonic-gate 
20600Sstevel@tonic-gate 			/*
20610Sstevel@tonic-gate 			 * If there is a match in /etc/rem_name_to_major then
20620Sstevel@tonic-gate 			 * be paranoid: is that major number already in
20630Sstevel@tonic-gate 			 * /etc/name_to_major (potentially under another name)?
20640Sstevel@tonic-gate 			 */
20650Sstevel@tonic-gate 			if (get_driver_name(is_unique, name_to_major,
20660Sstevel@tonic-gate 			    scratch) != UNIQUE) {
20670Sstevel@tonic-gate 				/*
20680Sstevel@tonic-gate 				 * nuke the rem_name_to_major entry-- it
20690Sstevel@tonic-gate 				 * isn't helpful.
20700Sstevel@tonic-gate 				 */
20710Sstevel@tonic-gate 				(void) delete_entry(rem_name_to_major,
20720Sstevel@tonic-gate 				    driver_name, " ", NULL);
20730Sstevel@tonic-gate 			} else {
20740Sstevel@tonic-gate 				(void) snprintf(major, sizeof (major),
20750Sstevel@tonic-gate 				    "%d", is_unique);
20760Sstevel@tonic-gate 
20770Sstevel@tonic-gate 				if (append_to_file(driver_name, major,
20784145Scth 				    name_to_major, ' ', " ", 0) == ERROR) {
20790Sstevel@tonic-gate 					(void) fprintf(stderr,
20800Sstevel@tonic-gate 					    gettext(ERR_NO_UPDATE),
20810Sstevel@tonic-gate 					    name_to_major);
20820Sstevel@tonic-gate 					return (ERROR);
20830Sstevel@tonic-gate 				}
20840Sstevel@tonic-gate 
20850Sstevel@tonic-gate 				if (delete_entry(rem_name_to_major,
20860Sstevel@tonic-gate 				    driver_name, " ", NULL) == ERROR) {
20870Sstevel@tonic-gate 					(void) fprintf(stderr,
20880Sstevel@tonic-gate 					    gettext(ERR_DEL_ENTRY), driver_name,
20890Sstevel@tonic-gate 					    rem_name_to_major);
20900Sstevel@tonic-gate 					return (ERROR);
20910Sstevel@tonic-gate 				}
20920Sstevel@tonic-gate 
20930Sstevel@tonic-gate 				/* found matching entry : no errors */
20940Sstevel@tonic-gate 				*major_num = is_unique;
20950Sstevel@tonic-gate 				return (NOERR);
20960Sstevel@tonic-gate 			}
20970Sstevel@tonic-gate 		}
20980Sstevel@tonic-gate 	}
20990Sstevel@tonic-gate 
21000Sstevel@tonic-gate 	/*
21010Sstevel@tonic-gate 	 * Bugid: 1264079
21020Sstevel@tonic-gate 	 * In a server case (with -b option), we can't use modctl() to find
21030Sstevel@tonic-gate 	 *    the maximum major number, we need to dig thru client's
21040Sstevel@tonic-gate 	 *    /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
21050Sstevel@tonic-gate 	 *
21060Sstevel@tonic-gate 	 * if (server)
21070Sstevel@tonic-gate 	 *    get maximum major number thru (rem_)name_to_major file on client
21080Sstevel@tonic-gate 	 * else
21090Sstevel@tonic-gate 	 *    get maximum major number allowable on current system using modctl
21100Sstevel@tonic-gate 	 */
21110Sstevel@tonic-gate 	if (server) {
21120Sstevel@tonic-gate 		max_dev = 0;
21130Sstevel@tonic-gate 		tmp = 0;
21140Sstevel@tonic-gate 
21150Sstevel@tonic-gate 		max_dev = get_max_major(name_to_major);
21160Sstevel@tonic-gate 
21170Sstevel@tonic-gate 		/* If rem_name_to_major exists, we need to check it too */
21180Sstevel@tonic-gate 		if (have_rem_n2m) {
21190Sstevel@tonic-gate 			tmp = get_max_major(rem_name_to_major);
21200Sstevel@tonic-gate 
21210Sstevel@tonic-gate 			/*
21220Sstevel@tonic-gate 			 * If name_to_major is missing, we can get max_dev from
21230Sstevel@tonic-gate 			 * /etc/rem_name_to_major.  If both missing, bail out!
21240Sstevel@tonic-gate 			 */
21250Sstevel@tonic-gate 			if ((max_dev == ERROR) && (tmp == ERROR)) {
21260Sstevel@tonic-gate 				(void) fprintf(stderr,
21278331SJerry.Gilliam@Sun.COM 				    gettext(ERR_CANT_ACCESS_FILE),
21288331SJerry.Gilliam@Sun.COM 				    name_to_major);
21290Sstevel@tonic-gate 				return (ERROR);
21300Sstevel@tonic-gate 			}
21310Sstevel@tonic-gate 
21320Sstevel@tonic-gate 			/* guard against bigger maj_num in rem_name_to_major */
21330Sstevel@tonic-gate 			if (tmp > max_dev)
21340Sstevel@tonic-gate 				max_dev = tmp;
21350Sstevel@tonic-gate 		} else {
21360Sstevel@tonic-gate 			/*
21370Sstevel@tonic-gate 			 * If we can't get major from name_to_major file
21380Sstevel@tonic-gate 			 * and there is no /etc/rem_name_to_major file,
21390Sstevel@tonic-gate 			 * then we don't have a max_dev, bail out quick!
21400Sstevel@tonic-gate 			 */
21410Sstevel@tonic-gate 			if (max_dev == ERROR)
21420Sstevel@tonic-gate 				return (ERROR);
21430Sstevel@tonic-gate 		}
21440Sstevel@tonic-gate 
21450Sstevel@tonic-gate 		/*
21460Sstevel@tonic-gate 		 * In case there is no more slack in current name_to_major
21470Sstevel@tonic-gate 		 * table, provide at least 1 extra entry so the add_drv can
21480Sstevel@tonic-gate 		 * succeed.  Since only one add_drv process is allowed at one
21490Sstevel@tonic-gate 		 * time, and hence max_dev will be re-calculated each time
21500Sstevel@tonic-gate 		 * add_drv is ran, we don't need to worry about adding more
21510Sstevel@tonic-gate 		 * than 1 extra slot for max_dev.
21520Sstevel@tonic-gate 		 */
21530Sstevel@tonic-gate 		max_dev++;
21540Sstevel@tonic-gate 
21550Sstevel@tonic-gate 	} else {
21560Sstevel@tonic-gate 		if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
21570Sstevel@tonic-gate 			perror(NULL);
21580Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
21590Sstevel@tonic-gate 			return (ERROR);
21600Sstevel@tonic-gate 		}
21610Sstevel@tonic-gate 	}
21620Sstevel@tonic-gate 
21630Sstevel@tonic-gate 	/*
21640Sstevel@tonic-gate 	 * max_dev is really how many slots the kernel has allocated for
21650Sstevel@tonic-gate 	 * devices... [0 , maxdev-1], not the largest available device num.
21660Sstevel@tonic-gate 	 */
21670Sstevel@tonic-gate 	if ((num_list = calloc(max_dev, 1)) == NULL) {
21680Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
21690Sstevel@tonic-gate 		return (ERROR);
21700Sstevel@tonic-gate 	}
21710Sstevel@tonic-gate 
21720Sstevel@tonic-gate 	/*
21730Sstevel@tonic-gate 	 * Populate the num_list array
21740Sstevel@tonic-gate 	 */
21750Sstevel@tonic-gate 	if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) {
21760Sstevel@tonic-gate 		return (ERROR);
21770Sstevel@tonic-gate 	}
21780Sstevel@tonic-gate 	if (have_rem_n2m) {
21790Sstevel@tonic-gate 		if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0)
21800Sstevel@tonic-gate 			return (ERROR);
21810Sstevel@tonic-gate 	}
21820Sstevel@tonic-gate 
21830Sstevel@tonic-gate 	/* find first free major number */
21840Sstevel@tonic-gate 	for (i = 0; i < max_dev; i++) {
21850Sstevel@tonic-gate 		if (num_list[i] != 1) {
21860Sstevel@tonic-gate 			new_maj = i;
21870Sstevel@tonic-gate 			break;
21880Sstevel@tonic-gate 		}
21890Sstevel@tonic-gate 	}
21900Sstevel@tonic-gate 
21910Sstevel@tonic-gate 	if (new_maj == -1) {
21920Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR));
21930Sstevel@tonic-gate 		return (ERROR);
21940Sstevel@tonic-gate 	}
21950Sstevel@tonic-gate 
2196*9268SJerry.Gilliam@Sun.COM 	(void) snprintf(drv_majnum_str, sizeof (drv_majnum_str),
2197*9268SJerry.Gilliam@Sun.COM 	    "%d", new_maj);
21980Sstevel@tonic-gate 	if (do_the_update(driver_name, drv_majnum_str) == ERROR) {
21990Sstevel@tonic-gate 		return (ERROR);
22000Sstevel@tonic-gate 	}
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate 	*major_num = new_maj;
22030Sstevel@tonic-gate 	return (NOERR);
22040Sstevel@tonic-gate }
22050Sstevel@tonic-gate 
22060Sstevel@tonic-gate 
22070Sstevel@tonic-gate int
22080Sstevel@tonic-gate fill_n2m_array(char *filename, char **array, int *nelems)
22090Sstevel@tonic-gate {
22100Sstevel@tonic-gate 	FILE *fp;
22112805Seota 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
22120Sstevel@tonic-gate 	char drv[FILENAME_MAX + 1];
22130Sstevel@tonic-gate 	u_longlong_t dnum;
22140Sstevel@tonic-gate 	major_t drv_majnum;
22150Sstevel@tonic-gate 
22160Sstevel@tonic-gate 	/*
22170Sstevel@tonic-gate 	 * Read through the file, marking each major number found
22180Sstevel@tonic-gate 	 * order is not relevant
22190Sstevel@tonic-gate 	 */
22200Sstevel@tonic-gate 	if ((fp = fopen(filename, "r")) == NULL) {
22210Sstevel@tonic-gate 		perror(NULL);
22220Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename);
22230Sstevel@tonic-gate 		return (ERROR);
22240Sstevel@tonic-gate 	}
22250Sstevel@tonic-gate 
22260Sstevel@tonic-gate 	while (fgets(line, sizeof (line), fp) != 0) {
22272805Seota 		/* cut off comments starting with '#' */
22282805Seota 		if ((cp = strchr(line, '#')) != NULL)
22292805Seota 			*cp = '\0';
22302805Seota 		/* ignore comment or blank lines */
22312805Seota 		if (is_blank(line))
22322805Seota 			continue;
22332805Seota 		/* sanity-check */
2234*9268SJerry.Gilliam@Sun.COM 		/* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
22350Sstevel@tonic-gate 		if (sscanf(line, "%s %llu", drv, &dnum) != 2) {
22360Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
22370Sstevel@tonic-gate 			    filename, line);
22380Sstevel@tonic-gate 			(void) fclose(fp);
22390Sstevel@tonic-gate 			return (ERROR);
22400Sstevel@tonic-gate 		}
22410Sstevel@tonic-gate 
22420Sstevel@tonic-gate 		if (dnum > L_MAXMAJ32) {
22430Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv,
22440Sstevel@tonic-gate 			    dnum, filename, L_MAXMAJ32);
22450Sstevel@tonic-gate 			continue;
22460Sstevel@tonic-gate 		}
22470Sstevel@tonic-gate 		/*
22480Sstevel@tonic-gate 		 * cast down to a major_t; we can be sure this is safe because
22490Sstevel@tonic-gate 		 * of the above range-check.
22500Sstevel@tonic-gate 		 */
22510Sstevel@tonic-gate 		drv_majnum = (major_t)dnum;
22520Sstevel@tonic-gate 
22530Sstevel@tonic-gate 		if (drv_majnum >= *nelems) {
22540Sstevel@tonic-gate 			/*
22550Sstevel@tonic-gate 			 * Allocate some more space, up to drv_majnum + 1 so
22560Sstevel@tonic-gate 			 * we can accomodate 0 through drv_majnum.
22570Sstevel@tonic-gate 			 *
22580Sstevel@tonic-gate 			 * Note that in the failure case, we leak all of the
22590Sstevel@tonic-gate 			 * old contents of array.  It's ok, since we just
22600Sstevel@tonic-gate 			 * wind up exiting immediately anyway.
22610Sstevel@tonic-gate 			 */
22620Sstevel@tonic-gate 			*nelems = drv_majnum + 1;
22630Sstevel@tonic-gate 			*array = realloc(*array, *nelems);
22640Sstevel@tonic-gate 			if (*array == NULL) {
22650Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
22660Sstevel@tonic-gate 				return (ERROR);
22670Sstevel@tonic-gate 			}
22680Sstevel@tonic-gate 		}
22690Sstevel@tonic-gate 		(*array)[drv_majnum] = 1;
22700Sstevel@tonic-gate 	}
22710Sstevel@tonic-gate 
22720Sstevel@tonic-gate 	(void) fclose(fp);
22730Sstevel@tonic-gate 	return (0);
22740Sstevel@tonic-gate }
22750Sstevel@tonic-gate 
22760Sstevel@tonic-gate 
22770Sstevel@tonic-gate int
22780Sstevel@tonic-gate do_the_update(char *driver_name, char *major_number)
22790Sstevel@tonic-gate {
22800Sstevel@tonic-gate 	return (append_to_file(driver_name, major_number, name_to_major,
22814145Scth 	    ' ', " ", 0));
22820Sstevel@tonic-gate }
22832805Seota 
22842805Seota /*
22852805Seota  * is_blank() returns 1 (true) if a line specified is composed of
22862805Seota  * whitespace characters only. otherwise, it returns 0 (false).
22872805Seota  *
22882805Seota  * Note. the argument (line) must be null-terminated.
22892805Seota  */
22902805Seota static int
22912805Seota is_blank(char *line)
22922805Seota {
22932805Seota 	for (/* nothing */; *line != '\0'; line++)
22942805Seota 		if (!isspace(*line))
22952805Seota 			return (0);
22962805Seota 	return (1);
22972805Seota }
2298