Home | History | Annotate | Download | only in modload
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <ctype.h>
     29 #include <unistd.h>
     30 #include <sys/sysmacros.h>
     31 #include <libintl.h>
     32 #include <wait.h>
     33 #include <string.h>
     34 #include <strings.h>
     35 #include <errno.h>
     36 #include <fcntl.h>
     37 #include <signal.h>
     38 #include <sys/buf.h>
     39 #include <sys/stat.h>
     40 #include <grp.h>
     41 #include "addrem.h"
     42 #include "errmsg.h"
     43 #include "plcysubr.h"
     44 
     45 /*
     46  * Macros to produce a quoted string containing the value of a
     47  * preprocessor macro. For example, if SIZE is defined to be 256,
     48  * VAL2STR(SIZE) is "256". This is used to construct format
     49  * strings for scanf-family functions below.
     50  * Note: For format string use, the argument to VAL2STR() must
     51  * be a numeric constant that is one less than the size of the
     52  * corresponding data buffer.
     53  */
     54 #define	VAL2STR_QUOTE(x)	#x
     55 #define	VAL2STR(x)		VAL2STR_QUOTE(x)
     56 
     57 /*
     58  * Convenience macro to determine if a character is a quote
     59  */
     60 #define	isquote(c)	(((c) == '"') || ((c) == '\''))
     61 
     62 
     63 static char *add_rem_lock;	/* lock file */
     64 static char *tmphold;		/* temporary file for updating */
     65 static int  add_rem_lock_fd = -1;
     66 
     67 static int get_cached_n_to_m_file(char *filename, char ***cache);
     68 static int get_name_to_major_entry(int *major_no, char *driver_name,
     69     char *file_name);
     70 
     71 static int is_blank(char *);
     72 
     73 /*ARGSUSED*/
     74 void
     75 log_minorperm_error(minorperm_err_t err, int key)
     76 {
     77 	switch (err) {
     78 	case MP_FOPEN_ERR:
     79 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
     80 		    MINOR_PERM_FILE);
     81 		break;
     82 	case MP_FCLOSE_ERR:
     83 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
     84 		    MINOR_PERM_FILE);
     85 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
     86 		break;
     87 	case MP_IGNORING_LINE_ERR:
     88 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
     89 		    MINOR_PERM_FILE);
     90 		break;
     91 	case MP_ALLOC_ERR:
     92 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
     93 		    MINOR_PERM_FILE);
     94 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
     95 		break;
     96 	case MP_NVLIST_ERR:
     97 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
     98 		    MINOR_PERM_FILE);
     99 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
    100 		break;
    101 	case MP_CANT_FIND_USER_ERR:
    102 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
    103 		    MINOR_PERM_FILE);
    104 		break;
    105 	case MP_CANT_FIND_GROUP_ERR:
    106 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
    107 		    MINOR_PERM_FILE);
    108 		break;
    109 	}
    110 }
    111 
    112 /*
    113  *  open file
    114  * for each entry in list
    115  *	where list entries are separated by <list_separator>
    116  * 	append entry : driver_name <entry_separator> entry
    117  * close file
    118  * return error/noerr
    119  */
    120 int
    121 append_to_file(
    122 	char *driver_name,
    123 	char *entry_list,
    124 	char *filename,
    125 	char list_separator,
    126 	char *entry_separator,
    127 	int quoted)
    128 {
    129 	int	len, line_len;
    130 	int	fpint;
    131 	char	*current_head, *previous_head;
    132 	char	*line, *one_entry;
    133 	FILE	*fp;
    134 
    135 	if ((fp = fopen(filename, "a")) == NULL) {
    136 		perror(NULL);
    137 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
    138 		    filename);
    139 		return (ERROR);
    140 	}
    141 
    142 	len = strlen(entry_list);
    143 
    144 	one_entry = calloc(len + 1, 1);
    145 	if (one_entry == NULL) {
    146 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
    147 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
    148 		(void) fclose(fp);
    149 		return (ERROR);
    150 	}
    151 
    152 	previous_head = entry_list;
    153 
    154 	line_len = strlen(driver_name) + len + 4;
    155 	if (quoted)
    156 		line_len += 2;
    157 
    158 	line = calloc(line_len, 1);
    159 	if (line == NULL) {
    160 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
    161 		(void) fclose(fp);
    162 		err_exit();
    163 	}
    164 
    165 	/*
    166 	 * get one entry at a time from list and append to <filename> file
    167 	 */
    168 
    169 	do {
    170 		bzero(one_entry, len + 1);
    171 		bzero(line, line_len);
    172 
    173 		current_head = get_entry(previous_head, one_entry,
    174 		    list_separator, quoted);
    175 		previous_head = current_head;
    176 
    177 		(void) snprintf(line, line_len,
    178 		    quoted ? "%s%s\"%s\"\n" : "%s%s%s\n",
    179 		    driver_name, entry_separator, one_entry);
    180 
    181 		if ((fputs(line, fp)) == EOF) {
    182 			perror(NULL);
    183 			(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
    184 			    filename);
    185 		}
    186 
    187 	} while (*current_head != '\0');
    188 
    189 
    190 	(void) fflush(fp);
    191 
    192 	fpint = fileno(fp);
    193 	(void) fsync(fpint);
    194 
    195 	(void) fclose(fp);
    196 
    197 	free(one_entry);
    198 	free(line);
    199 
    200 	return (NOERR);
    201 }
    202 
    203 /*
    204  *  open file
    205  * for each entry in list
    206  *	where list entries are separated by <list_separator>
    207  * 	append entry : driver_name <entry_separator> entry
    208  * close file
    209  * return error/noerr
    210  */
    211 int
    212 append_to_minor_perm(
    213 	char *driver_name,
    214 	char *entry_list,
    215 	char *filename)
    216 {
    217 	int	len, line_len;
    218 	int	fpint;
    219 	char	*current_head, *previous_head;
    220 	char	*line, *one_entry;
    221 	FILE	*fp;
    222 
    223 	if ((fp = fopen(filename, "a")) == NULL) {
    224 		perror(NULL);
    225 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
    226 		    filename);
    227 		return (ERROR);
    228 	}
    229 
    230 	len = strlen(entry_list);
    231 
    232 	one_entry = calloc(len + 1, 1);
    233 	if (one_entry == NULL) {
    234 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
    235 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
    236 		(void) fclose(fp);
    237 		return (ERROR);
    238 	}
    239 
    240 	previous_head = entry_list;
    241 
    242 	line_len = strlen(driver_name) + len + 4;
    243 	line = calloc(line_len, 1);
    244 	if (line == NULL) {
    245 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
    246 		(void) fclose(fp);
    247 		err_exit();
    248 	}
    249 
    250 	/*
    251 	 * get one entry at a time from list and append to <filename> file
    252 	 */
    253 	do {
    254 		bzero(one_entry, len + 1);
    255 		bzero(line, line_len);
    256 
    257 		current_head = get_perm_entry(previous_head, one_entry);
    258 		previous_head = current_head;
    259 
    260 		(void) snprintf(line, line_len, "%s:%s\n",
    261 		    driver_name, one_entry);
    262 
    263 		if ((fputs(line, fp)) == EOF) {
    264 			perror(NULL);
    265 			(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
    266 			    filename);
    267 		}
    268 
    269 	} while (*current_head != '\0');
    270 
    271 
    272 	(void) fflush(fp);
    273 
    274 	fpint = fileno(fp);
    275 	(void) fsync(fpint);
    276 
    277 	(void) fclose(fp);
    278 
    279 	free(one_entry);
    280 	free(line);
    281 
    282 	return (NOERR);
    283 }
    284 
    285 /*
    286  * Require exact match to delete a driver alias/permission entry.
    287  * Note line argument does not remain unchanged.  Return 1 if matched.
    288  */
    289 static int
    290 match_entry(char *line, char *match)
    291 {
    292 	char	*token, *p;
    293 	int	n;
    294 
    295 	/* skip any leading white space */
    296 	while (*line && isspace(*line))
    297 		line++;
    298 	/*
    299 	 * Find separator for driver name, either space or colon
    300 	 *	minor_perm: <driver>:<perm>
    301 	 *	driver_aliases: <driver> <alias>
    302 	 *	extra_privs: <driver>:<priv>
    303 	 */
    304 	if ((token = strpbrk(line, " :\t")) == NULL)
    305 		return (0);
    306 	token++;
    307 	/* skip leading white space and quotes */
    308 	while (*token && (isspace(*token) || isquote(*token)))
    309 		token++;
    310 	/* strip trailing newline, white space and quotes */
    311 	n = strlen(token);
    312 	p = token + n-1;
    313 	while (n > 0 && (*p == '\n' || isspace(*p) || isquote(*p))) {
    314 		*p-- = 0;
    315 		n--;
    316 	}
    317 	if (n == 0)
    318 		return (0);
    319 	return (strcmp(token, match) == 0);
    320 }
    321 
    322 /*
    323  *  open file
    324  * read thru file, deleting all entries if first
    325  *    entry = driver_name
    326  * close
    327  * if error, leave original file intact with message
    328  * assumption : drvconfig has been modified to work with clone
    329  *  entries in /etc/minor_perm as driver:mummble NOT
    330  *  clone:driver mummble
    331  * this implementation will NOT find clone entries
    332  * clone:driver mummble
    333  * match:
    334  *	delete just the matching entry
    335  *
    336  */
    337 int
    338 delete_entry(
    339 	char *oldfile,
    340 	char *driver_name,
    341 	char *marker,
    342 	char *match)
    343 {
    344 	int		rv, i;
    345 	int		status = NOERR;
    346 	int		drvr_found = 0;
    347 	boolean_t 	nomatch = B_TRUE;
    348 	char		*newfile, *tptr, *cp;
    349 	char		line[MAX_DBFILE_ENTRY];
    350 	char		drv[FILENAME_MAX + 1];
    351 	FILE		*fp, *newfp;
    352 	struct group	*sysgrp;
    353 	char		*copy;		/* same size as line */
    354 	char		*match2 = NULL;	/* match with quotes cleaned up */
    355 
    356 	/*
    357 	 * if match is specified, sanity check it and clean it
    358 	 * up by removing surrounding quotes as we require
    359 	 * an exact match.
    360 	 */
    361 	if (match) {
    362 		cp = match;
    363 		while (*cp && (isspace(*cp)))
    364 			cp++;
    365 		i = strlen(cp);
    366 		if (i > 0) {
    367 			if ((match2 = strdup(cp)) == NULL) {
    368 				perror(NULL);
    369 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
    370 				return (ERROR);
    371 			}
    372 			i = strlen(match2) - 1;
    373 			while (i >= 0 && (isspace(match2[i]))) {
    374 				match2[i] = 0;
    375 				i--;
    376 			}
    377 		}
    378 		if (match2 == NULL || (strlen(match2) == 0)) {
    379 			(void) fprintf(stderr,
    380 			    gettext(ERR_INT_UPDATE), oldfile);
    381 			return (ERROR);
    382 		}
    383 	}
    384 
    385 	if ((fp = fopen(oldfile, "r")) == NULL) {
    386 		perror(NULL);
    387 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
    388 		return (ERROR);
    389 	}
    390 
    391 	/* Space for defensive copy of input line */
    392 	copy = calloc(sizeof (line), 1);
    393 
    394 	/* Build filename for temporary file */
    395 	tptr = calloc(strlen(oldfile) + strlen(XEND) + 1, 1);
    396 	if (tptr == NULL || copy == NULL) {
    397 		perror(NULL);
    398 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
    399 		return (ERROR);
    400 	}
    401 
    402 	(void) strcpy(tptr, oldfile);
    403 	(void) strcat(tptr, XEND);
    404 
    405 	/*
    406 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
    407 	 * assume a gid of "sys" but we can't undo the damage on already
    408 	 * installed systems unless we force the issue.
    409 	 */
    410 	if ((sysgrp = getgrnam("sys")) != NULL) {
    411 		(void) setgid(sysgrp->gr_gid);
    412 	}
    413 
    414 	newfile = mktemp(tptr);
    415 
    416 	if ((newfp = fopen(newfile, "w")) == NULL) {
    417 		perror(NULL);
    418 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
    419 		    newfile);
    420 		return (ERROR);
    421 	}
    422 
    423 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
    424 		/* copy the whole line */
    425 		if (strlcpy(copy, line, sizeof (line)) >= sizeof (line)) {
    426 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
    427 			status = ERROR;
    428 			break;
    429 		}
    430 		/* cut off comments starting with '#' */
    431 		if ((cp = strchr(copy, '#')) != NULL)
    432 			*cp = '\0';
    433 		/* ignore comment or blank lines */
    434 		if (is_blank(copy)) {
    435 			if (fputs(line, newfp) == EOF) {
    436 				(void) fprintf(stderr, gettext(ERR_UPDATE),
    437 				    oldfile);
    438 				status = ERROR;
    439 			}
    440 			continue;
    441 		}
    442 
    443 		/* get the driver name */
    444 		if (sscanf(copy, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
    445 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
    446 			    oldfile, line);
    447 			status = ERROR;
    448 			break;
    449 		}
    450 
    451 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
    452 			drv[i] =  '\0';
    453 		}
    454 
    455 		if (strcmp(driver_name, drv) != 0) {
    456 			if ((fputs(line, newfp)) == EOF) {
    457 				(void) fprintf(stderr, gettext(ERR_UPDATE),
    458 				    oldfile);
    459 				status = ERROR;
    460 			}
    461 		} else {
    462 			drvr_found++;
    463 			if (match2) {	/* Just delete one entry */
    464 				/* for now delete just minor_perm and aliases */
    465 				if ((strcmp(oldfile, minor_perm) == 0) ||
    466 				    (strcmp(oldfile, extra_privs) == 0) ||
    467 				    (strcmp(oldfile, driver_aliases) == 0)) {
    468 
    469 					/* make defensive copy */
    470 					if (strlcpy(copy, line, sizeof (line))
    471 					    >= sizeof (line)) {
    472 						(void) fprintf(stderr,
    473 						    gettext(ERR_UPDATE),
    474 						    oldfile);
    475 						status = ERROR;
    476 						break;
    477 					}
    478 					if (match_entry(copy, match2)) {
    479 						nomatch = B_FALSE;
    480 					} else {
    481 						if ((fputs(line, newfp)) ==
    482 						    EOF) {
    483 							(void) fprintf(stderr,
    484 							    gettext(ERR_UPDATE),
    485 							    oldfile);
    486 							status = ERROR;
    487 						}
    488 						if (nomatch != B_FALSE)
    489 							nomatch = B_TRUE;
    490 					}
    491 				}
    492 			}
    493 
    494 		} /* end of else */
    495 	} /* end of while */
    496 
    497 	(void) fclose(fp);
    498 	free(tptr);
    499 	free(copy);
    500 	if (match2)
    501 		free(match2);
    502 
    503 	/* Make sure that the file is on disk */
    504 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
    505 		status = ERROR;
    506 	else
    507 		rv = NOERR;
    508 
    509 	(void) fclose(newfp);
    510 
    511 	/* no matching driver found */
    512 	rv = NOERR;
    513 	if (!drvr_found ||
    514 	    (nomatch == B_TRUE)) {
    515 		rv = NONE_FOUND;
    516 	}
    517 
    518 	/*
    519 	 * if error, leave original file, delete new file
    520 	 * if noerr, replace original file with new file
    521 	 */
    522 
    523 	if (status == NOERR) {
    524 		if (rename(oldfile, tmphold) == -1) {
    525 			perror(NULL);
    526 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
    527 			(void) unlink(newfile);
    528 			return (ERROR);
    529 		} else if (rename(newfile, oldfile) == -1) {
    530 			perror(NULL);
    531 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
    532 			(void) unlink(oldfile);
    533 			(void) unlink(newfile);
    534 			if (link(tmphold, oldfile) == -1) {
    535 				perror(NULL);
    536 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
    537 				    oldfile, tmphold);
    538 			}
    539 			return (ERROR);
    540 		}
    541 		(void) unlink(tmphold);
    542 	} else {
    543 		/*
    544 		 * since there's an error, leave file alone; remove
    545 		 * new file
    546 		 */
    547 		if (unlink(newfile) == -1) {
    548 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
    549 		}
    550 		return (ERROR);
    551 	}
    552 
    553 	return (rv);
    554 }
    555 
    556 
    557 /*
    558  * wrapper for call to get_name_to_major_entry(): given driver name,
    559  * retrieve major number.
    560  */
    561 int
    562 get_major_no(char *driver_name, char *file_name)
    563 {
    564 	int major = UNIQUE;
    565 
    566 	if (get_name_to_major_entry(&major, driver_name, file_name) == ERROR)
    567 		return (ERROR);
    568 	else
    569 		return (major);
    570 }
    571 
    572 /*
    573  * wrapper for call to get_name_to_major_entry(): given major number,
    574  * retrieve driver name.
    575  */
    576 int
    577 get_driver_name(int major, char *file_name, char *buf)
    578 {
    579 	if (major < 0)
    580 		return (ERROR);
    581 	return (get_name_to_major_entry(&major, buf, file_name));
    582 }
    583 
    584 
    585 /*
    586  * return pointer to cached name_to_major file - reads file into
    587  * cache if this has not already been done.  Since there may be
    588  * requests for multiple name_to_major files (rem_name_to_major,
    589  * name_to_major), this routine keeps a list of cached files.
    590  */
    591 static int
    592 get_cached_n_to_m_file(char *filename, char ***cache)
    593 {
    594 	struct n_to_m_cache {
    595 		char *file;
    596 		char **cached_file;
    597 		int size;
    598 		struct n_to_m_cache *next;
    599 	};
    600 	static struct n_to_m_cache *head = NULL;
    601 	struct n_to_m_cache *ptr;
    602 	FILE *fp;
    603 	char drv[FILENAME_MAX + 1];
    604 	char entry[FILENAME_MAX + 1];
    605 	char line[MAX_N2M_ALIAS_LINE], *cp;
    606 	int maj;
    607 	int size = 0;
    608 
    609 
    610 	/*
    611 	 * see if the file is already cached - either
    612 	 * rem_name_to_major or name_to_major
    613 	 */
    614 	ptr = head;
    615 	while (ptr != NULL) {
    616 		if (strcmp(ptr->file, filename) == 0)
    617 			break;
    618 		ptr = ptr->next;
    619 	}
    620 
    621 	if (ptr == NULL) {	/* we need to cache the contents */
    622 		if ((fp = fopen(filename, "r")) == NULL) {
    623 			perror(NULL);
    624 			(void) fprintf(stderr, gettext(ERR_CANT_OPEN),
    625 			    filename);
    626 			return (ERROR);
    627 		}
    628 
    629 		while (fgets(line, sizeof (line), fp) != NULL) {
    630 			/* cut off comments starting with '#' */
    631 			if ((cp = strchr(line, '#')) != NULL)
    632 				*cp = '\0';
    633 			/* ignore comment or blank lines */
    634 			if (is_blank(line))
    635 				continue;
    636 			/* sanity-check */
    637 			if (sscanf(line,
    638 			    "%" VAL2STR(FILENAME_MAX) "s"	/* drv */
    639 			    "%" VAL2STR(FILENAME_MAX) "s",	/* entry */
    640 			    drv, entry) != 2) {
    641 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
    642 				    filename, line);
    643 				continue;
    644 			}
    645 			maj = atoi(entry);
    646 			if (maj > size)
    647 				size = maj;
    648 		}
    649 
    650 		/* allocate struct to cache the file */
    651 		ptr = (struct n_to_m_cache *)calloc(1,
    652 		    sizeof (struct n_to_m_cache));
    653 		if (ptr == NULL) {
    654 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
    655 			return (ERROR);
    656 		}
    657 		ptr->size = size + 1;
    658 		/* allocate space to cache contents of file */
    659 		ptr->cached_file = (char **)calloc(ptr->size, sizeof (char *));
    660 		if (ptr->cached_file == NULL) {
    661 			free(ptr);
    662 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
    663 			return (ERROR);
    664 		}
    665 
    666 		rewind(fp);
    667 
    668 		/*
    669 		 * now fill the cache
    670 		 * the cache is an array of char pointers indexed by major
    671 		 * number
    672 		 */
    673 		while (fgets(line, sizeof (line), fp) != NULL) {
    674 			/* cut off comments starting with '#' */
    675 			if ((cp = strchr(line, '#')) != NULL)
    676 				*cp = '\0';
    677 			/* ignore comment or blank lines */
    678 			if (is_blank(line))
    679 				continue;
    680 			/* sanity-check */
    681 			if (sscanf(line,
    682 			    "%" VAL2STR(FILENAME_MAX) "s"	/* drv */
    683 			    "%" VAL2STR(FILENAME_MAX) "s",	/* entry */
    684 			    drv, entry) != 2) {
    685 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
    686 				    filename, line);
    687 				continue;
    688 			}
    689 			maj = atoi(entry);
    690 			if ((ptr->cached_file[maj] = strdup(drv)) == NULL) {
    691 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
    692 				free(ptr->cached_file);
    693 				free(ptr);
    694 				return (ERROR);
    695 			}
    696 			(void) strcpy(ptr->cached_file[maj], drv);
    697 		}
    698 		(void) fclose(fp);
    699 		/* link the cache struct into the list of cached files */
    700 		ptr->file = strdup(filename);
    701 		if (ptr->file == NULL) {
    702 			for (maj = 0; maj <= ptr->size; maj++)
    703 				free(ptr->cached_file[maj]);
    704 			free(ptr->cached_file);
    705 			free(ptr);
    706 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
    707 			return (ERROR);
    708 		}
    709 		ptr->next = head;
    710 		head = ptr;
    711 	}
    712 	/* return value pointer to contents of file */
    713 	*cache = ptr->cached_file;
    714 
    715 	/* return size */
    716 	return (ptr->size);
    717 }
    718 
    719 
    720 /*
    721  * Using get_cached_n_to_m_file(), retrieve maximum major number
    722  * found in the specificed file (name_to_major/rem_name_to_major).
    723  *
    724  * The return value is actually the size of the internal cache including 0.
    725  */
    726 int
    727 get_max_major(char *file_name)
    728 {
    729 	char **n_to_m_cache = NULL;
    730 
    731 	return (get_cached_n_to_m_file(file_name, &n_to_m_cache));
    732 }
    733 
    734 
    735 /*
    736  * searching name_to_major: if major_no == UNIQUE then the caller wants to
    737  * use the driver name as the key.  Otherwise, the caller wants to use
    738  * the major number as a key.
    739  *
    740  * This routine caches the contents of the name_to_major file on
    741  * first call.  And it could be generalized to deal with other
    742  * config files if necessary.
    743  */
    744 static int
    745 get_name_to_major_entry(int *major_no, char *driver_name, char *file_name)
    746 {
    747 	int maj;
    748 	char **n_to_m_cache = NULL;
    749 	int size = 0;
    750 
    751 	int ret = NOT_UNIQUE;
    752 
    753 	/*
    754 	 * read the file in - we cache it in case caller wants to
    755 	 * do multiple lookups
    756 	 */
    757 	size = get_cached_n_to_m_file(file_name, &n_to_m_cache);
    758 
    759 	if (size == ERROR)
    760 		return (ERROR);
    761 
    762 	/* search with driver name as key */
    763 	if (*major_no == UNIQUE) {
    764 		for (maj = 0; maj < size; maj++) {
    765 			if ((n_to_m_cache[maj] != NULL) &&
    766 			    (strcmp(driver_name, n_to_m_cache[maj]) == 0)) {
    767 				*major_no = maj;
    768 				break;
    769 			}
    770 		}
    771 		if (maj >= size)
    772 			ret = UNIQUE;
    773 	/* search with major number as key */
    774 	} else {
    775 		/*
    776 		 * Bugid 1254588, drvconfig dump core after loading driver
    777 		 * with major number bigger than entries defined in
    778 		 * /etc/name_to_major.
    779 		 */
    780 		if (*major_no >= size)
    781 			return (UNIQUE);
    782 
    783 		if (n_to_m_cache[*major_no] != NULL) {
    784 			(void) strcpy(driver_name, n_to_m_cache[*major_no]);
    785 		} else
    786 			ret = UNIQUE;
    787 	}
    788 	return (ret);
    789 }
    790 
    791 /*
    792  * Given pointer to begining of member 'n' in a space (or separator)
    793  * separated list, return pointer to member 'n+1', and establish member 'n'
    794  * in *current_entry.  If unquote, then we skip a leading quote and treat
    795  * the trailing quote as a separator (and skip).
    796  */
    797 char *
    798 get_entry(
    799 	char *prev_member,
    800 	char *current_entry,
    801 	char separator,
    802 	int  unquote)
    803 {
    804 	char	*ptr;
    805 	int	quoted = 0;
    806 
    807 	ptr = prev_member;
    808 
    809 	/* skip white space */
    810 	while (isspace(*ptr))
    811 		ptr++;
    812 
    813 	/* if unquote skip leading quote */
    814 	if (unquote && *ptr == '"') {
    815 		quoted++;
    816 		ptr++;
    817 	}
    818 
    819 	/* read thru the current entry looking for end, separator, or unquote */
    820 	while (*ptr &&
    821 	    (*ptr != separator) && (!isspace(*ptr)) &&
    822 	    (!quoted || (*ptr != '"'))) {
    823 		*current_entry++ = *ptr++;
    824 	}
    825 	*current_entry = '\0';
    826 
    827 	if (separator && (*ptr == separator))
    828 		ptr++;	/* skip over separator */
    829 	if (quoted && (*ptr == '"'))
    830 		ptr++;	/* skip over trailing quote */
    831 
    832 	/* skip white space */
    833 	while (isspace(*ptr))
    834 		ptr++;
    835 
    836 	return (ptr);
    837 }
    838 
    839 /*
    840  * A parser specific to the add_drv "-m permission" syntax:
    841  *
    842  *	-m '<minor-name> <permissions> <owner> <group>', ...
    843  *
    844  * One entry is parsed starting at prev_member and returned
    845  * in the string pointed at by current_entry.  A pointer
    846  * to the entry following is returned.
    847  */
    848 char *
    849 get_perm_entry(
    850 	char *prev_member,
    851 	char *current_entry)
    852 {
    853 	char	*ptr;
    854 	int	nfields = 0;
    855 	int	maxfields = 4;		/* fields in a permissions format */
    856 
    857 	ptr = prev_member;
    858 	while (isspace(*ptr))
    859 		ptr++;
    860 
    861 	while (*ptr) {
    862 		/* comma allowed in minor name token only */
    863 		if (*ptr == ',' && nfields > 0) {
    864 			break;
    865 		} else if (isspace(*ptr)) {
    866 			*current_entry++ = *ptr++;
    867 			while (isspace(*ptr))
    868 				ptr++;
    869 			if (++nfields == maxfields)
    870 				break;
    871 		} else
    872 			*current_entry++ = *ptr++;
    873 	}
    874 	*current_entry = '\0';
    875 
    876 	while (isspace(*ptr))
    877 		ptr++;
    878 	if (*ptr == ',') {
    879 		ptr++;	/* skip over optional trailing comma */
    880 	}
    881 	while (isspace(*ptr))
    882 		ptr++;
    883 
    884 	return (ptr);
    885 }
    886 
    887 void
    888 enter_lock(void)
    889 {
    890 	struct flock lock;
    891 
    892 	/*
    893 	 * Attempt to create the lock file.  Open the file itself,
    894 	 * and not a symlink to some other file.
    895 	 */
    896 	add_rem_lock_fd = open(add_rem_lock,
    897 	    O_CREAT|O_RDWR|O_NOFOLLOW|O_NOLINKS, S_IRUSR|S_IWUSR);
    898 	if (add_rem_lock_fd < 0) {
    899 		(void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
    900 		    add_rem_lock, strerror(errno));
    901 		exit(1);
    902 	}
    903 
    904 	lock.l_type = F_WRLCK;
    905 	lock.l_whence = SEEK_SET;
    906 	lock.l_start = 0;
    907 	lock.l_len = 0;
    908 
    909 	/* Try for the lock but don't wait. */
    910 	if (fcntl(add_rem_lock_fd, F_SETLK, &lock) == -1) {
    911 		if (errno == EACCES || errno == EAGAIN) {
    912 			(void) fprintf(stderr, gettext(ERR_PROG_IN_USE));
    913 		} else {
    914 			(void) fprintf(stderr, gettext(ERR_LOCK),
    915 			    add_rem_lock, strerror(errno));
    916 		}
    917 		exit(1);
    918 	}
    919 }
    920 
    921 void
    922 err_exit(void)
    923 {
    924 	/* release memory allocated for moddir */
    925 	cleanup_moddir();
    926 	/* remove add_drv/rem_drv lock */
    927 	exit_unlock();
    928 	exit(1);
    929 }
    930 
    931 void
    932 cleanup_moddir(void)
    933 {
    934 	struct drvmod_dir *walk_ptr;
    935 	struct drvmod_dir *free_ptr = moddir;
    936 
    937 	while (free_ptr != NULL) {
    938 		walk_ptr = free_ptr->next;
    939 		free(free_ptr);
    940 		free_ptr = walk_ptr;
    941 	}
    942 }
    943 
    944 void
    945 exit_unlock(void)
    946 {
    947 	struct flock unlock;
    948 
    949 	if (add_rem_lock_fd < 0)
    950 		return;
    951 
    952 	unlock.l_type = F_UNLCK;
    953 	unlock.l_whence = SEEK_SET;
    954 	unlock.l_start = 0;
    955 	unlock.l_len = 0;
    956 
    957 	if (fcntl(add_rem_lock_fd, F_SETLK, &unlock) == -1) {
    958 		(void) fprintf(stderr, gettext(ERR_UNLOCK),
    959 		    add_rem_lock, strerror(errno));
    960 	} else {
    961 		(void) close(add_rem_lock_fd);
    962 		add_rem_lock_fd = -1;
    963 	}
    964 }
    965 
    966 /*
    967  * error adding driver; need to back out any changes to files.
    968  * check flag to see which files need entries removed
    969  * entry removal based on driver name
    970  */
    971 void
    972 remove_entry(
    973 	int c_flag,
    974 	char *driver_name)
    975 {
    976 
    977 	if (c_flag & CLEAN_NAM_MAJ) {
    978 		if (delete_entry(name_to_major, driver_name, " ",
    979 		    NULL) == ERROR) {
    980 			(void) fprintf(stderr, gettext(ERR_NO_CLEAN),
    981 			    name_to_major, driver_name);
    982 		}
    983 	}
    984 
    985 	if (c_flag & CLEAN_DRV_ALIAS) {
    986 		if (delete_entry(driver_aliases, driver_name, " ",
    987 		    NULL) == ERROR) {
    988 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
    989 			    driver_name, driver_aliases);
    990 		}
    991 	}
    992 
    993 	if (c_flag & CLEAN_DRV_CLASSES) {
    994 		if (delete_entry(driver_classes, driver_name, "\t", NULL) ==
    995 		    ERROR) {
    996 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
    997 			    driver_name, driver_classes);
    998 		}
    999 	}
   1000 
   1001 	if (c_flag & CLEAN_MINOR_PERM) {
   1002 		if (delete_entry(minor_perm, driver_name, ":", NULL) == ERROR) {
   1003 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
   1004 			    driver_name, minor_perm);
   1005 		}
   1006 	}
   1007 	/*
   1008 	 * There's no point in removing entries from files that don't
   1009 	 * exist.  Prevent error messages by checking for file existence
   1010 	 * first.
   1011 	 */
   1012 	if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
   1013 	    access(device_policy, F_OK) == 0) {
   1014 		if (delete_plcy_entry(device_policy, driver_name) == ERROR) {
   1015 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
   1016 			    driver_name, device_policy);
   1017 		}
   1018 	}
   1019 	if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
   1020 	    access(extra_privs, F_OK) == 0) {
   1021 		if (delete_entry(extra_privs, driver_name, ":", NULL) ==
   1022 		    ERROR) {
   1023 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
   1024 			    driver_name, extra_privs);
   1025 		}
   1026 	}
   1027 }
   1028 
   1029 int
   1030 check_perms_aliases(
   1031 	int m_flag,
   1032 	int i_flag)
   1033 {
   1034 	/*
   1035 	 * If neither i_flag nor m_flag are specified no need to check the
   1036 	 * files for access permissions
   1037 	 */
   1038 	if (!m_flag && !i_flag)
   1039 		return (NOERR);
   1040 
   1041 	/* check minor_perm file : exits and is writable */
   1042 	if (m_flag) {
   1043 		if (access(minor_perm, R_OK | W_OK)) {
   1044 			perror(NULL);
   1045 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
   1046 			    minor_perm);
   1047 			return (ERROR);
   1048 		}
   1049 	}
   1050 
   1051 	/* check driver_aliases file : exits and is writable */
   1052 	if (i_flag) {
   1053 		if (access(driver_aliases, R_OK | W_OK)) {
   1054 			perror(NULL);
   1055 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
   1056 			    driver_aliases);
   1057 			return (ERROR);
   1058 		}
   1059 	}
   1060 
   1061 	return (NOERR);
   1062 }
   1063 
   1064 
   1065 int
   1066 check_name_to_major(int mode)
   1067 {
   1068 	/* check name_to_major file : exists and is writable */
   1069 	if (access(name_to_major, mode)) {
   1070 		perror(NULL);
   1071 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
   1072 		    name_to_major);
   1073 		return (ERROR);
   1074 	}
   1075 
   1076 	return (NOERR);
   1077 }
   1078 
   1079 
   1080 /*
   1081  * All this stuff is to support a server installing
   1082  * drivers on diskless clients.  When on the server
   1083  * need to prepend the basedir
   1084  */
   1085 int
   1086 build_filenames(char *basedir)
   1087 {
   1088 	int	len;
   1089 	int	driver_aliases_len;
   1090 	int	driver_classes_len;
   1091 	int	minor_perm_len;
   1092 	int	name_to_major_len;
   1093 	int	rem_name_to_major_len;
   1094 	int	add_rem_lock_len;
   1095 	int	tmphold_len;
   1096 	int	devfs_root_len;
   1097 	int	device_policy_len;
   1098 	int	extra_privs_len;
   1099 
   1100 	if (basedir == NULL) {
   1101 		driver_aliases = DRIVER_ALIAS;
   1102 		driver_classes = DRIVER_CLASSES;
   1103 		minor_perm = MINOR_PERM;
   1104 		name_to_major = NAM_TO_MAJ;
   1105 		rem_name_to_major = REM_NAM_TO_MAJ;
   1106 		add_rem_lock = ADD_REM_LOCK;
   1107 		tmphold = TMPHOLD;
   1108 		devfs_root = DEVFS_ROOT;
   1109 		device_policy = DEV_POLICY;
   1110 		extra_privs = EXTRA_PRIVS;
   1111 
   1112 	} else {
   1113 		len = strlen(basedir) + 1;
   1114 
   1115 		driver_aliases_len = len + sizeof (DRIVER_ALIAS);
   1116 		driver_classes_len = len + sizeof (DRIVER_CLASSES);
   1117 		minor_perm_len = len + sizeof (MINOR_PERM);
   1118 		name_to_major_len = len + sizeof (NAM_TO_MAJ);
   1119 		rem_name_to_major_len = len + sizeof (REM_NAM_TO_MAJ);
   1120 		add_rem_lock_len = len + sizeof (ADD_REM_LOCK);
   1121 		tmphold_len = len + sizeof (TMPHOLD);
   1122 		devfs_root_len = len + sizeof (DEVFS_ROOT);
   1123 		device_policy_len = len + sizeof (DEV_POLICY);
   1124 		extra_privs_len = len + sizeof (EXTRA_PRIVS);
   1125 
   1126 		driver_aliases = malloc(driver_aliases_len);
   1127 		driver_classes = malloc(driver_classes_len);
   1128 		minor_perm = malloc(minor_perm_len);
   1129 		name_to_major = malloc(name_to_major_len);
   1130 		rem_name_to_major = malloc(rem_name_to_major_len);
   1131 		add_rem_lock = malloc(add_rem_lock_len);
   1132 		tmphold = malloc(tmphold_len);
   1133 		devfs_root = malloc(devfs_root_len);
   1134 		device_policy = malloc(device_policy_len);
   1135 		extra_privs = malloc(extra_privs_len);
   1136 
   1137 		if ((driver_aliases == NULL) ||
   1138 		    (driver_classes == NULL) ||
   1139 		    (minor_perm == NULL) ||
   1140 		    (name_to_major == NULL) ||
   1141 		    (rem_name_to_major == NULL) ||
   1142 		    (add_rem_lock == NULL) ||
   1143 		    (tmphold == NULL) ||
   1144 		    (devfs_root == NULL) ||
   1145 		    (device_policy == NULL) ||
   1146 		    (extra_privs == NULL)) {
   1147 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
   1148 			return (ERROR);
   1149 		}
   1150 
   1151 		(void) snprintf(driver_aliases, driver_aliases_len,
   1152 		    "%s%s", basedir, DRIVER_ALIAS);
   1153 		(void) snprintf(driver_classes, driver_classes_len,
   1154 		    "%s%s", basedir, DRIVER_CLASSES);
   1155 		(void) snprintf(minor_perm, minor_perm_len,
   1156 		    "%s%s", basedir, MINOR_PERM);
   1157 		(void) snprintf(name_to_major, name_to_major_len,
   1158 		    "%s%s", basedir, NAM_TO_MAJ);
   1159 		(void) snprintf(rem_name_to_major, rem_name_to_major_len,
   1160 		    "%s%s", basedir, REM_NAM_TO_MAJ);
   1161 		(void) snprintf(add_rem_lock, add_rem_lock_len,
   1162 		    "%s%s", basedir, ADD_REM_LOCK);
   1163 		(void) snprintf(tmphold, tmphold_len,
   1164 		    "%s%s", basedir, TMPHOLD);
   1165 		(void) snprintf(devfs_root, devfs_root_len,
   1166 		    "%s%s", basedir, DEVFS_ROOT);
   1167 		(void) snprintf(device_policy, device_policy_len,
   1168 		    "%s%s", basedir, DEV_POLICY);
   1169 		(void) snprintf(extra_privs, extra_privs_len,
   1170 		    "%s%s", basedir, EXTRA_PRIVS);
   1171 	}
   1172 
   1173 	return (NOERR);
   1174 }
   1175 
   1176 static int
   1177 exec_command(char *path, char *cmdline[MAX_CMD_LINE])
   1178 {
   1179 	pid_t pid;
   1180 	uint_t stat_loc;
   1181 	int waitstat;
   1182 	int exit_status;
   1183 
   1184 	/* child */
   1185 	if ((pid = fork()) == 0) {
   1186 		(void) execv(path, cmdline);
   1187 		perror(NULL);
   1188 		return (ERROR);
   1189 	} else if (pid == -1) {
   1190 		/* fork failed */
   1191 		perror(NULL);
   1192 		(void) fprintf(stderr, gettext(ERR_FORK_FAIL), cmdline);
   1193 		return (ERROR);
   1194 	} else {
   1195 		/* parent */
   1196 		do {
   1197 			waitstat = waitpid(pid, (int *)&stat_loc, 0);
   1198 
   1199 		} while ((!WIFEXITED(stat_loc) &&
   1200 		    !WIFSIGNALED(stat_loc)) || (waitstat == 0));
   1201 
   1202 		exit_status = WEXITSTATUS(stat_loc);
   1203 
   1204 		return (exit_status);
   1205 	}
   1206 }
   1207 
   1208 /*
   1209  * Exec devfsadm to perform driver config/unconfig operation,
   1210  * adding or removing aliases.
   1211  */
   1212 static int
   1213 exec_devfsadm(
   1214 	boolean_t config,
   1215 	char *driver_name,
   1216 	major_t major_num,
   1217 	char *aliases,
   1218 	char *classes,
   1219 	int config_flags)
   1220 {
   1221 	int n = 0;
   1222 	char *cmdline[MAX_CMD_LINE];
   1223 	char maj_num[128];
   1224 	char *previous;
   1225 	char *current;
   1226 	int len;
   1227 	int rv;
   1228 
   1229 	/* build command line */
   1230 	cmdline[n++] = DRVCONFIG;
   1231 	if (config == B_FALSE) {
   1232 		cmdline[n++] = "-u";		/* unconfigure */
   1233 		if (config_flags & CONFIG_DRV_FORCE)
   1234 			cmdline[n++] = "-f";	/* force if currently in use */
   1235 	}
   1236 	if (config_flags & CONFIG_DRV_VERBOSE) {
   1237 		cmdline[n++] = "-v";
   1238 	}
   1239 	cmdline[n++] = "-b";
   1240 	if (classes) {
   1241 		cmdline[n++] = "-c";
   1242 		cmdline[n++] = classes;
   1243 	}
   1244 	cmdline[n++] = "-i";
   1245 	cmdline[n++] = driver_name;
   1246 	cmdline[n++] = "-m";
   1247 	(void) snprintf(maj_num, sizeof (maj_num), "%lu", major_num);
   1248 	cmdline[n++] = maj_num;
   1249 	if (config_flags & CONFIG_DRV_UPDATE_ONLY)
   1250 		cmdline[n++] = "-x";
   1251 
   1252 	if (aliases != NULL) {
   1253 		len = strlen(aliases);
   1254 		previous = aliases;
   1255 		do {
   1256 			cmdline[n++] = "-a";
   1257 			cmdline[n] = calloc(len + 1, 1);
   1258 			if (cmdline[n] == NULL) {
   1259 				(void) fprintf(stderr,
   1260 				    gettext(ERR_NO_MEM));
   1261 				return (ERROR);
   1262 			}
   1263 			current = get_entry(previous,
   1264 			    cmdline[n++], ' ', 0);
   1265 			previous = current;
   1266 
   1267 		} while (*current != '\0');
   1268 
   1269 	}
   1270 	cmdline[n] = (char *)0;
   1271 
   1272 	rv = exec_command(DRVCONFIG_PATH, cmdline);
   1273 	if (rv == NOERR)
   1274 		return (NOERR);
   1275 	return (ERROR);
   1276 }
   1277 
   1278 int
   1279 unconfig_driver(
   1280 	char *driver_name,
   1281 	major_t major_num,
   1282 	char *aliases,
   1283 	int config_flags)
   1284 {
   1285 	return (exec_devfsadm(B_FALSE, driver_name, major_num,
   1286 	    aliases, NULL, config_flags));
   1287 }
   1288 
   1289 /*
   1290  * check that major_num doesn't exceed maximum on this machine
   1291  * do this here to support add_drv on server for diskless clients
   1292  */
   1293 int
   1294 config_driver(
   1295 	char *driver_name,
   1296 	major_t major_num,
   1297 	char *aliases,
   1298 	char *classes,
   1299 	int cleanup_flag,
   1300 	int config_flags)
   1301 {
   1302 	int	max_dev;
   1303 	int	rv;
   1304 
   1305 	if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
   1306 		perror(NULL);
   1307 		(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
   1308 		return (ERROR);
   1309 	}
   1310 
   1311 	if (major_num >= max_dev) {
   1312 		(void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS),
   1313 		    major_num, max_dev);
   1314 		return (ERROR);
   1315 	}
   1316 
   1317 	/* bind major number and driver name */
   1318 	rv = exec_devfsadm(B_TRUE, driver_name, major_num,
   1319 	    aliases, classes, config_flags);
   1320 
   1321 	if (rv == NOERR)
   1322 		return (NOERR);
   1323 	perror(NULL);
   1324 	remove_entry(cleanup_flag, driver_name);
   1325 	return (ERROR);
   1326 }
   1327 
   1328 void
   1329 load_driver(char *driver_name, int verbose_flag)
   1330 {
   1331 	int n = 0;
   1332 	char *cmdline[MAX_CMD_LINE];
   1333 	int exec_status;
   1334 
   1335 	/* build command line */
   1336 	cmdline[n++] = DEVFSADM;
   1337 	if (verbose_flag) {
   1338 		cmdline[n++] = "-v";
   1339 	}
   1340 	cmdline[n++] = "-i";
   1341 	cmdline[n++] = driver_name;
   1342 	cmdline[n] = (char *)0;
   1343 
   1344 	exec_status = exec_command(DEVFSADM_PATH, cmdline);
   1345 
   1346 	if (exec_status != NOERR) {
   1347 		/* no clean : name and major number are bound */
   1348 		(void) fprintf(stderr, gettext(ERR_CONFIG), driver_name);
   1349 	}
   1350 }
   1351 
   1352 void
   1353 get_modid(char *driver_name, int *mod)
   1354 {
   1355 	struct modinfo	modinfo;
   1356 
   1357 	modinfo.mi_id = -1;
   1358 	modinfo.mi_info = MI_INFO_ALL;
   1359 	do {
   1360 		/*
   1361 		 * If we are at the end of the list of loaded modules
   1362 		 * then set *mod = -1 and return
   1363 		 */
   1364 		if (modctl(MODINFO, 0, &modinfo) < 0) {
   1365 			*mod = -1;
   1366 			return;
   1367 		}
   1368 
   1369 		*mod = modinfo.mi_id;
   1370 	} while (strcmp(driver_name, modinfo.mi_name) != 0);
   1371 }
   1372 
   1373 int
   1374 create_reconfig(char *basedir)
   1375 {
   1376 	char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1];
   1377 	FILE *reconfig_fp;
   1378 
   1379 	if (basedir != NULL) {
   1380 		(void) strcpy(reconfig_file, basedir);
   1381 		(void) strcat(reconfig_file, RECONFIGURE);
   1382 	} else {
   1383 		(void) strcpy(reconfig_file, RECONFIGURE);
   1384 	}
   1385 	if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL)
   1386 		return (ERROR);
   1387 
   1388 	(void) fclose(reconfig_fp);
   1389 	return (NOERR);
   1390 }
   1391 
   1392 
   1393 /*
   1394  * update_minor_entry:
   1395  *	open file
   1396  *	for each entry in list
   1397  *		where list entries are separated by <list_separator>
   1398  * 		modify entry : driver_name <entry_separator> entry
   1399  *	close file
   1400  *
   1401  *	return error/noerr
   1402  */
   1403 int
   1404 update_minor_entry(char *driver_name, char *perm_list)
   1405 {
   1406 	FILE	*fp;
   1407 	FILE	*newfp;
   1408 	int	match = 0;
   1409 	char	line[MAX_DBFILE_ENTRY];
   1410 	char	drv[FILENAME_MAX + 1];
   1411 	char	minor[FILENAME_MAX + 1];
   1412 	char	perm[OPT_LEN + 1];
   1413 	char	own[OPT_LEN + 1];
   1414 	char	grp[OPT_LEN + 1];
   1415 	int	status = NOERR, i;
   1416 	char	*newfile, *tptr;
   1417 	char	*cp, *dup, *drv_minor;
   1418 	struct group *sysgrp;
   1419 
   1420 	if ((fp = fopen(minor_perm, "r")) == NULL) {
   1421 		perror(NULL);
   1422 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
   1423 		    minor_perm);
   1424 
   1425 		return (ERROR);
   1426 	}
   1427 
   1428 	/*
   1429 	 * Build filename for temporary file
   1430 	 */
   1431 	if ((tptr = calloc(strlen(minor_perm) + strlen(XEND) + 1, 1)) == NULL) {
   1432 		perror(NULL);
   1433 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
   1434 	}
   1435 	(void) strcpy(tptr, minor_perm);
   1436 	(void) strcat(tptr, XEND);
   1437 
   1438 	/*
   1439 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
   1440 	 * assume a gid of "sys" but we can't undo the damage on already
   1441 	 * installed systems unless we force the issue.
   1442 	 */
   1443 	if ((sysgrp = getgrnam("sys")) != NULL) {
   1444 		(void) setgid(sysgrp->gr_gid);
   1445 	}
   1446 
   1447 	newfile = mktemp(tptr);
   1448 	if ((newfp = fopen(newfile, "w")) == NULL) {
   1449 		perror(NULL);
   1450 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
   1451 		    newfile);
   1452 		return (ERROR);
   1453 	}
   1454 
   1455 	if (sscanf(perm_list,
   1456 	    "%" VAL2STR(FILENAME_MAX) "s"	/* minor */
   1457 	    "%" VAL2STR(OPT_LEN) "s"		/* perm */
   1458 	    "%" VAL2STR(OPT_LEN) "s"		/* own */
   1459 	    "%" VAL2STR(OPT_LEN) "s",		/* grp */
   1460 	    minor, perm, own, grp) != 4) {
   1461 		status = ERROR;
   1462 	}
   1463 
   1464 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
   1465 		/* copy the whole line into dup */
   1466 		if ((dup = strdup(line)) == NULL) {
   1467 			perror(NULL);
   1468 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
   1469 			status = ERROR;
   1470 			break;
   1471 		}
   1472 		/* cut off comments starting with '#' */
   1473 		if ((cp = strchr(dup, '#')) != NULL)
   1474 			*cp = '\0';
   1475 		/* ignore comment or blank lines */
   1476 		if (is_blank(dup)) {
   1477 			if (fputs(line, newfp) == EOF) {
   1478 				(void) fprintf(stderr, gettext(ERR_UPDATE),
   1479 				    minor_perm);
   1480 				status = ERROR;
   1481 			}
   1482 			free(dup);
   1483 			continue;
   1484 		}
   1485 
   1486 		/* get the driver name */
   1487 		if (sscanf(dup, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
   1488 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
   1489 			    minor_perm, line);
   1490 			status = ERROR;
   1491 			free(dup);
   1492 			break;
   1493 		}
   1494 
   1495 		/*
   1496 		 * get the minor name; place the NULL character at the
   1497 		 * end of the driver name, then make the drv_minor
   1498 		 * point to the first character of the minor name.
   1499 		 * the line missing ':' must be treated as a broken one.
   1500 		 */
   1501 		i = strcspn(drv, ":");
   1502 		if (i == strlen(drv)) {
   1503 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
   1504 			    minor_perm, line);
   1505 			status = ERROR;
   1506 			free(dup);
   1507 			break;
   1508 		}
   1509 		drv[i] =  '\0';
   1510 		drv_minor = &drv[strlen(drv) + 1];
   1511 
   1512 		/*
   1513 		 * compare both of the driver name and the minor name.
   1514 		 * then the new line should be written to the file if
   1515 		 * both of them match
   1516 		 */
   1517 		if ((strcmp(drv, driver_name) == 0) &&
   1518 		    (strcmp(minor, drv_minor) == 0)) {
   1519 			/* if it has a comment, keep it */
   1520 			if (cp != NULL) {
   1521 				cp++; /* skip a terminator */
   1522 				(void) snprintf(line, sizeof (line),
   1523 				    "%s:%s %s %s %s #%s\n",
   1524 				    drv, minor, perm, own, grp, cp);
   1525 			} else {
   1526 				(void) snprintf(line, sizeof (line),
   1527 				    "%s:%s %s %s %s\n",
   1528 				    drv, minor, perm, own, grp);
   1529 			}
   1530 			match = 1;
   1531 		}
   1532 		free(dup);
   1533 
   1534 		/* update the file */
   1535 		if ((fputs(line, newfp)) == EOF) {
   1536 			(void) fprintf(stderr, gettext(ERR_UPDATE),
   1537 			    minor_perm);
   1538 			status = ERROR;
   1539 		}
   1540 	}
   1541 
   1542 	if (!match) {
   1543 		(void) bzero(line, sizeof (&line[0]));
   1544 		(void) snprintf(line, sizeof (line),
   1545 		    "%s:%s %s %s %s\n",
   1546 		    driver_name, minor, perm, own, grp);
   1547 
   1548 		/* add the new entry */
   1549 		if ((fputs(line, newfp)) == EOF) {
   1550 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
   1551 			status = ERROR;
   1552 		}
   1553 	}
   1554 
   1555 	(void) fclose(fp);
   1556 
   1557 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
   1558 		status = ERROR;
   1559 
   1560 	(void) fclose(newfp);
   1561 
   1562 	/*
   1563 	 * if error, leave original file, delete new file
   1564 	 * if noerr, replace original file with new file
   1565 	 */
   1566 	if (status == NOERR) {
   1567 		if (rename(minor_perm, tmphold) == -1) {
   1568 			perror(NULL);
   1569 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
   1570 			(void) unlink(newfile);
   1571 			return (ERROR);
   1572 		} else if (rename(newfile, minor_perm) == -1) {
   1573 			perror(NULL);
   1574 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
   1575 			(void) unlink(minor_perm);
   1576 			(void) unlink(newfile);
   1577 			if (link(tmphold, minor_perm) == -1) {
   1578 				perror(NULL);
   1579 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
   1580 				    minor_perm, tmphold);
   1581 			}
   1582 			return (ERROR);
   1583 		}
   1584 		(void) unlink(tmphold);
   1585 	} else {
   1586 		/*
   1587 		 * since there's an error, leave file alone; remove
   1588 		 * new file
   1589 		 */
   1590 		if (unlink(newfile) == -1) {
   1591 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
   1592 		}
   1593 		return (ERROR);
   1594 	}
   1595 
   1596 	return (NOERR);
   1597 
   1598 }
   1599 
   1600 
   1601 /*
   1602  * list_entry:
   1603  *	open file
   1604  *	read thru file, listing all entries if first entry = driver_name
   1605  *	close
   1606  */
   1607 void
   1608 list_entry(
   1609 	char *oldfile,
   1610 	char *driver_name,
   1611 	char *marker)
   1612 {
   1613 	FILE	*fp;
   1614 	int	i;
   1615 	char	line[MAX_DBFILE_ENTRY], *cp;
   1616 	char	drv[FILENAME_MAX + 1];
   1617 
   1618 	if ((fp = fopen(oldfile, "r")) == NULL) {
   1619 		perror(NULL);
   1620 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
   1621 
   1622 		return;
   1623 	}
   1624 
   1625 	while (fgets(line, sizeof (line), fp) != NULL) {
   1626 		/* cut off comments starting with '#' */
   1627 		if ((cp = strchr(line, '#')) != NULL)
   1628 			*cp = '\0';
   1629 		/* ignore comment or blank lines */
   1630 		if (is_blank(line))
   1631 			continue;
   1632 		/* sanity-check */
   1633 		if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
   1634 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
   1635 			    oldfile, line);
   1636 		}
   1637 
   1638 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
   1639 			drv[i] =  '\0';
   1640 		}
   1641 
   1642 		if (strcmp(driver_name, drv) == 0) {
   1643 			(void) fprintf(stdout, "%s", line);
   1644 		}
   1645 	}
   1646 
   1647 	(void) fclose(fp);
   1648 }
   1649 
   1650 static boolean_t
   1651 is_token(char *tok)
   1652 {
   1653 	/*
   1654 	 * Check the token here. According to IEEE1275 Open Firmware Boot
   1655 	 * Standard, the name is composed of 1 to 31 letters,
   1656 	 * digits and punctuation characters from the set ",._+-", and
   1657 	 * uppercase and lowercase characters are considered distinct.
   1658 	 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
   1659 	 * However, since either the definition of driver or aliase names is
   1660 	 * not known well, only '#' is avoided explicitly. (the kernel lexical
   1661 	 * analyzer treats it as a start of a comment)
   1662 	 */
   1663 	for (/* nothing */; *tok != '\0'; tok++)
   1664 		if (*tok == '#' || iscntrl(*tok))
   1665 			return (B_FALSE);
   1666 
   1667 	return (B_TRUE);
   1668 }
   1669 
   1670 /*
   1671  * check each entry in perm_list for:
   1672  *	4 arguments
   1673  *	permission arg is in valid range
   1674  * permlist entries separated by comma
   1675  * return ERROR/NOERR
   1676  */
   1677 int
   1678 check_perm_opts(char *perm_list)
   1679 {
   1680 	char *current_head;
   1681 	char *previous_head;
   1682 	char *one_entry;
   1683 	int len, scan_stat;
   1684 	char minor[FILENAME_MAX + 1];
   1685 	char perm[OPT_LEN + 1];
   1686 	char own[OPT_LEN + 1];
   1687 	char grp[OPT_LEN + 1];
   1688 	char dumb[OPT_LEN + 1];
   1689 	int status = NOERR;
   1690 	int intperm;
   1691 
   1692 	if ((len = strlen(perm_list)) == 0)
   1693 		return (ERROR);
   1694 
   1695 	if ((one_entry = calloc(len + 1, 1)) == NULL) {
   1696 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
   1697 		return (ERROR);
   1698 	}
   1699 
   1700 	previous_head = perm_list;
   1701 	current_head = perm_list;
   1702 
   1703 	while (*current_head != '\0') {
   1704 		bzero(one_entry, len + 1);
   1705 		current_head = get_perm_entry(previous_head, one_entry);
   1706 
   1707 		previous_head = current_head;
   1708 		scan_stat = sscanf(one_entry,
   1709 		    "%" VAL2STR(FILENAME_MAX) "s"	/* minor */
   1710 		    "%" VAL2STR(OPT_LEN) "s"		/* perm */
   1711 		    "%" VAL2STR(OPT_LEN) "s"		/* own */
   1712 		    "%" VAL2STR(OPT_LEN) "s"		/* grp */
   1713 		    "%" VAL2STR(OPT_LEN) "s",		/* dumb */
   1714 		    minor, perm, own, grp, dumb);
   1715 
   1716 		if (scan_stat < 4) {
   1717 			(void) fprintf(stderr, gettext(ERR_MIS_TOK),
   1718 			    "-m", one_entry);
   1719 			status = ERROR;
   1720 		}
   1721 		if (scan_stat > 4) {
   1722 			(void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS),
   1723 			    "-m", one_entry);
   1724 			status = ERROR;
   1725 		}
   1726 
   1727 		intperm = atoi(perm);
   1728 		if (intperm < 0000 || intperm > 4777) {
   1729 			(void) fprintf(stderr, gettext(ERR_BAD_MODE), perm);
   1730 			status = ERROR;
   1731 		}
   1732 	}
   1733 
   1734 	free(one_entry);
   1735 	return (status);
   1736 }
   1737 
   1738 
   1739 /*
   1740  * check each alias :
   1741  *	alias list members separated by white space
   1742  *	cannot exist as driver name in /etc/name_to_major
   1743  *	cannot exist as driver or alias name in /etc/driver_aliases
   1744  */
   1745 int
   1746 aliases_unique(char *aliases)
   1747 {
   1748 	char *current_head;
   1749 	char *previous_head;
   1750 	char *one_entry;
   1751 	int len;
   1752 	int is_unique;
   1753 	int err;
   1754 
   1755 	len = strlen(aliases);
   1756 
   1757 	one_entry = calloc(len + 1, 1);
   1758 	if (one_entry == NULL) {
   1759 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
   1760 		return (ERROR);
   1761 	}
   1762 
   1763 	previous_head = aliases;
   1764 
   1765 	do {
   1766 		bzero(one_entry, len+1);
   1767 		current_head = get_entry(previous_head, one_entry, ' ', 1);
   1768 		previous_head = current_head;
   1769 
   1770 		if ((unique_driver_name(one_entry, name_to_major,
   1771 		    &is_unique)) == ERROR)
   1772 			goto err_out;
   1773 
   1774 		if (is_unique != UNIQUE) {
   1775 			(void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ),
   1776 			    one_entry);
   1777 			goto err_out;
   1778 		}
   1779 
   1780 		if ((err = unique_drv_alias(one_entry)) != UNIQUE) {
   1781 			if (err == NOT_UNIQUE) {
   1782 				(void) fprintf(stderr,
   1783 				    gettext(ERR_ALIAS_IN_USE), one_entry);
   1784 			}
   1785 			goto err_out;
   1786 		}
   1787 
   1788 		if (!is_token(one_entry)) {
   1789 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
   1790 			    "-i", one_entry);
   1791 			goto err_out;
   1792 		}
   1793 
   1794 	} while (*current_head != '\0');
   1795 
   1796 	free(one_entry);
   1797 	return (NOERR);
   1798 
   1799 err_out:
   1800 	free(one_entry);
   1801 	return (ERROR);
   1802 }
   1803 
   1804 /*
   1805  * verify each alias :
   1806  *	alias list members separated by white space and quoted
   1807  *	exist as alias name in /etc/driver_aliases
   1808  */
   1809 int
   1810 aliases_exist(char *aliases)
   1811 {
   1812 	char *current_head;
   1813 	char *previous_head;
   1814 	char *one_entry;
   1815 	int len;
   1816 
   1817 	len = strlen(aliases);
   1818 
   1819 	one_entry = calloc(len + 1, 1);
   1820 	if (one_entry == NULL) {
   1821 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
   1822 		return (ERROR);
   1823 	}
   1824 
   1825 	previous_head = aliases;
   1826 
   1827 	do {
   1828 		bzero(one_entry, len+1);
   1829 		current_head = get_entry(previous_head, one_entry, ' ', 1);
   1830 		previous_head = current_head;
   1831 
   1832 		if (unique_drv_alias(one_entry) != NOT_UNIQUE)
   1833 			goto err_out;
   1834 
   1835 		if (!is_token(one_entry)) {
   1836 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
   1837 			    "-i", one_entry);
   1838 			goto err_out;
   1839 		}
   1840 
   1841 	} while (*current_head != '\0');
   1842 
   1843 	free(one_entry);
   1844 	return (NOERR);
   1845 
   1846 err_out:
   1847 	free(one_entry);
   1848 	return (ERROR);
   1849 }
   1850 
   1851 
   1852 /*
   1853  * check each alias :
   1854  *	if path-oriented alias, path exists
   1855  */
   1856 int
   1857 aliases_paths_exist(char *aliases)
   1858 {
   1859 	char *current_head;
   1860 	char *previous_head;
   1861 	char *one_entry;
   1862 	int i, len;
   1863 	char path[MAXPATHLEN];
   1864 	struct stat buf;
   1865 
   1866 	len = strlen(aliases);
   1867 
   1868 	one_entry = calloc(len + 1, 1);
   1869 	if (one_entry == NULL) {
   1870 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
   1871 		return (ERROR);
   1872 	}
   1873 
   1874 	previous_head = aliases;
   1875 
   1876 	do {
   1877 		for (i = 0; i <= len; i++)
   1878 			one_entry[i] = 0;
   1879 
   1880 		current_head = get_entry(previous_head, one_entry, ' ', 1);
   1881 		previous_head = current_head;
   1882 
   1883 		/* if the alias is a path, ensure that the path exists */
   1884 		if (*one_entry != '/')
   1885 			continue;
   1886 		(void) snprintf(path, sizeof (path), "/devices/%s", one_entry);
   1887 		if (stat(path, &buf) == 0)
   1888 			continue;
   1889 
   1890 		/* no device at specified path-oriented alias path */
   1891 		(void) fprintf(stderr, gettext(ERR_PATH_ORIENTED_ALIAS),
   1892 		    one_entry);
   1893 		free(one_entry);
   1894 		return (ERROR);
   1895 
   1896 	} while (*current_head != '\0');
   1897 
   1898 	free(one_entry);
   1899 
   1900 	return (NOERR);
   1901 }
   1902 
   1903 
   1904 int
   1905 update_driver_aliases(
   1906 	char *driver_name,
   1907 	char *aliases)
   1908 {
   1909 	/* make call to update the aliases file */
   1910 	return (append_to_file(driver_name, aliases, driver_aliases,
   1911 	    ' ', " ", 1));
   1912 }
   1913 
   1914 
   1915 /*
   1916  * Return:
   1917  *	ERROR in case of memory or read error
   1918  *	UNIQUE if there is no existing match to the supplied alias
   1919  *	NOT_UNIQUE if there is a match
   1920  * An error message is emitted in the case of ERROR,
   1921  * up to the caller otherwise.
   1922  */
   1923 int
   1924 unique_drv_alias(char *drv_alias)
   1925 {
   1926 	FILE *fp;
   1927 	char drv[FILENAME_MAX + 1];
   1928 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
   1929 	char alias[FILENAME_MAX + 1];
   1930 	char *a;
   1931 	int status = UNIQUE;
   1932 
   1933 	fp = fopen(driver_aliases, "r");
   1934 
   1935 	if (fp != NULL) {
   1936 		while ((fgets(line, sizeof (line), fp) != 0) &&
   1937 		    status == UNIQUE) {
   1938 			/* cut off comments starting with '#' */
   1939 			if ((cp = strchr(line, '#')) != NULL)
   1940 				*cp = '\0';
   1941 			/* ignore comment or blank lines */
   1942 			if (is_blank(line))
   1943 				continue;
   1944 			/* sanity-check */
   1945 			if (sscanf(line,
   1946 			    "%" VAL2STR(FILENAME_MAX) "s" 	/* drv */
   1947 			    "%" VAL2STR(FILENAME_MAX) "s",	/* alias */
   1948 			    drv, alias) != 2)
   1949 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
   1950 				    driver_aliases, line);
   1951 
   1952 			/* unquote for compare */
   1953 			if ((*alias == '"') &&
   1954 			    (*(alias + strlen(alias) - 1) == '"')) {
   1955 				a = &alias[1];
   1956 				alias[strlen(alias) - 1] = '\0';
   1957 			} else
   1958 				a = alias;
   1959 
   1960 			if ((strcmp(drv_alias, drv) == 0) ||
   1961 			    (strcmp(drv_alias, a) == 0)) {
   1962 				status = NOT_UNIQUE;
   1963 				break;
   1964 			}
   1965 		}
   1966 		(void) fclose(fp);
   1967 		return (status);
   1968 	} else {
   1969 		perror(NULL);
   1970 		(void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
   1971 		return (ERROR);
   1972 	}
   1973 }
   1974 
   1975 
   1976 /*
   1977  * search for driver_name in first field of file file_name
   1978  * searching name_to_major and driver_aliases: name separated
   1979  * from the remainder of the line by white space.
   1980  */
   1981 int
   1982 unique_driver_name(char *driver_name, char *file_name,
   1983 	int *is_unique)
   1984 {
   1985 	int ret, err;
   1986 
   1987 	if ((ret = get_major_no(driver_name, file_name)) == ERROR) {
   1988 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
   1989 		    file_name);
   1990 	} else {
   1991 		/* check alias file for name collision */
   1992 		if ((err = unique_drv_alias(driver_name)) != UNIQUE) {
   1993 			if (err == NOT_UNIQUE) {
   1994 				(void) fprintf(stderr,
   1995 				    gettext(ERR_ALIAS_IN_USE),
   1996 				    driver_name);
   1997 			}
   1998 			ret = ERROR;
   1999 		} else {
   2000 			if (ret != UNIQUE)
   2001 				*is_unique = NOT_UNIQUE;
   2002 			else
   2003 				*is_unique = ret;
   2004 			ret = NOERR;
   2005 		}
   2006 	}
   2007 	return (ret);
   2008 }
   2009 
   2010 /*
   2011  * returns:
   2012  *	SUCCESS - not an existing driver alias
   2013  *	NOT_UNIQUE - matching driver alias exists
   2014  *	ERROR - an error occurred
   2015  */
   2016 int
   2017 check_duplicate_driver_alias(char *driver_name, char *drv_alias)
   2018 {
   2019 	FILE *fp;
   2020 	char drv[FILENAME_MAX + 1];
   2021 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
   2022 	char alias[FILENAME_MAX + 1];
   2023 	char *a;
   2024 	int status = SUCCESS;
   2025 
   2026 	if ((fp = fopen(driver_aliases, "r")) == NULL) {
   2027 		perror(NULL);
   2028 		(void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
   2029 		return (ERROR);
   2030 	}
   2031 
   2032 	while (fgets(line, sizeof (line), fp) != 0) {
   2033 		/* cut off comments starting with '#' */
   2034 		if ((cp = strchr(line, '#')) != NULL)
   2035 			*cp = '\0';
   2036 		/* ignore comment or blank lines */
   2037 		if (is_blank(line))
   2038 			continue;
   2039 		/* sanity-check */
   2040 		if (sscanf(line,
   2041 		    "%" VAL2STR(FILENAME_MAX) "s"	/* drv */
   2042 		    "%" VAL2STR(FILENAME_MAX) "s",	/* alias */
   2043 		    drv, alias) != 2)
   2044 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
   2045 			    driver_aliases, line);
   2046 
   2047 		/* unquote for compare */
   2048 		if ((*alias == '"') &&
   2049 		    (*(alias + strlen(alias) - 1) == '"')) {
   2050 			a = &alias[1];
   2051 			alias[strlen(alias) - 1] = '\0';
   2052 		} else
   2053 			a = alias;
   2054 
   2055 		if ((strcmp(drv_alias, a) == 0) &&
   2056 		    (strcmp(drv, driver_name) == 0)) {
   2057 			status = NOT_UNIQUE;
   2058 		}
   2059 
   2060 		if ((strcmp(drv_alias, drv) == 0) ||
   2061 		    ((strcmp(drv_alias, a) == 0) &&
   2062 		    (strcmp(drv, driver_name) != 0))) {
   2063 			(void) fprintf(stderr,
   2064 			    gettext(ERR_ALIAS_IN_USE),
   2065 			    drv_alias);
   2066 			status = ERROR;
   2067 			goto done;
   2068 		}
   2069 	}
   2070 
   2071 done:
   2072 	(void) fclose(fp);
   2073 	return (status);
   2074 }
   2075 
   2076 int
   2077 trim_duplicate_aliases(char *driver_name, char *aliases, char **aliases2p)
   2078 {
   2079 	char *current_head;
   2080 	char *previous_head;
   2081 	char *one_entry;
   2082 	char *aliases2;
   2083 	int rv, len;
   2084 	int n = 0;
   2085 
   2086 	*aliases2p = NULL;
   2087 	len = strlen(aliases) + 1;
   2088 
   2089 	one_entry = calloc(len, 1);
   2090 	aliases2 = calloc(len, 1);
   2091 	if (one_entry == NULL || aliases2 == NULL) {
   2092 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
   2093 		return (ERROR);
   2094 	}
   2095 
   2096 	previous_head = aliases;
   2097 
   2098 	do {
   2099 		(void) bzero(one_entry, len);
   2100 		current_head = get_entry(previous_head, one_entry, ' ', 1);
   2101 		previous_head = current_head;
   2102 
   2103 		rv = check_duplicate_driver_alias(driver_name, one_entry);
   2104 		switch (rv) {
   2105 		case SUCCESS:
   2106 			/* not an existing driver alias: add it */
   2107 			if (n > 0) {
   2108 				if (strlcat(aliases2, " ", len) >= len)
   2109 					goto err;
   2110 			}
   2111 			if (strlcat(aliases2, one_entry, len) >= len)
   2112 				goto err;
   2113 			n++;
   2114 			break;
   2115 		case NOT_UNIQUE:
   2116 			/* matching driver alias exists: do not add it */
   2117 			break;
   2118 		case ERROR:
   2119 			/* error reading the alias file */
   2120 			goto err;
   2121 		default:
   2122 			goto err;
   2123 		}
   2124 
   2125 		if (!is_token(one_entry)) {
   2126 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
   2127 			    "-i", one_entry);
   2128 			goto err;
   2129 		}
   2130 	} while (*current_head != '\0');
   2131 
   2132 	/*
   2133 	 * If all the aliases listed are already
   2134 	 * present we actually have none to do.
   2135 	 */
   2136 	if (n == 0) {
   2137 		free(aliases2);
   2138 	} else {
   2139 		*aliases2p = aliases2;
   2140 	}
   2141 	free(one_entry);
   2142 	return (NOERR);
   2143 
   2144 err:
   2145 	free(aliases2);
   2146 	free(one_entry);
   2147 	return (ERROR);
   2148 }
   2149 
   2150 int
   2151 check_space_within_quote(char *str)
   2152 {
   2153 	register int i;
   2154 	register int len;
   2155 	int quoted = 0;
   2156 
   2157 	len = strlen(str);
   2158 	for (i = 0; i < len; i++, str++) {
   2159 		if (*str == '"') {
   2160 			if (quoted == 0)
   2161 				quoted++;
   2162 			else
   2163 				quoted--;
   2164 		} else if (*str == ' ' && quoted)
   2165 			return (ERROR);
   2166 	}
   2167 
   2168 	return (0);
   2169 }
   2170 
   2171 
   2172 /*
   2173  * get major number
   2174  * write driver_name major_num to name_to_major file
   2175  * major_num returned in major_num
   2176  * return success/failure
   2177  */
   2178 int
   2179 update_name_to_major(char *driver_name, major_t *major_num, int server)
   2180 {
   2181 	char major[MAX_STR_MAJOR + 1];
   2182 	struct stat buf;
   2183 	char *num_list;
   2184 	char drv_majnum_str[MAX_STR_MAJOR + 1];
   2185 	int new_maj = -1;
   2186 	int i, tmp = 0, is_unique, have_rem_n2m = 0;
   2187 	int max_dev = 0;
   2188 
   2189 	/*
   2190 	 * if driver_name already in rem_name_to_major
   2191 	 * 	delete entry from rem_nam_to_major
   2192 	 *	put entry into name_to_major
   2193 	 */
   2194 
   2195 	if (stat(rem_name_to_major, &buf) == 0) {
   2196 		have_rem_n2m = 1;
   2197 	}
   2198 
   2199 	if (have_rem_n2m) {
   2200 		if ((is_unique = get_major_no(driver_name, rem_name_to_major))
   2201 		    == ERROR)
   2202 			return (ERROR);
   2203 
   2204 		/*
   2205 		 * found a match in rem_name_to_major
   2206 		 */
   2207 		if (is_unique != UNIQUE) {
   2208 			char scratch[FILENAME_MAX];
   2209 
   2210 			/*
   2211 			 * If there is a match in /etc/rem_name_to_major then
   2212 			 * be paranoid: is that major number already in
   2213 			 * /etc/name_to_major (potentially under another name)?
   2214 			 */
   2215 			if (get_driver_name(is_unique, name_to_major,
   2216 			    scratch) != UNIQUE) {
   2217 				/*
   2218 				 * nuke the rem_name_to_major entry-- it
   2219 				 * isn't helpful.
   2220 				 */
   2221 				(void) delete_entry(rem_name_to_major,
   2222 				    driver_name, " ", NULL);
   2223 			} else {
   2224 				(void) snprintf(major, sizeof (major),
   2225 				    "%d", is_unique);
   2226 
   2227 				if (append_to_file(driver_name, major,
   2228 				    name_to_major, ' ', " ", 0) == ERROR) {
   2229 					(void) fprintf(stderr,
   2230 					    gettext(ERR_NO_UPDATE),
   2231 					    name_to_major);
   2232 					return (ERROR);
   2233 				}
   2234 
   2235 				if (delete_entry(rem_name_to_major,
   2236 				    driver_name, " ", NULL) == ERROR) {
   2237 					(void) fprintf(stderr,
   2238 					    gettext(ERR_DEL_ENTRY), driver_name,
   2239 					    rem_name_to_major);
   2240 					return (ERROR);
   2241 				}
   2242 
   2243 				/* found matching entry : no errors */
   2244 				*major_num = is_unique;
   2245 				return (NOERR);
   2246 			}
   2247 		}
   2248 	}
   2249 
   2250 	/*
   2251 	 * Bugid: 1264079
   2252 	 * In a server case (with -b option), we can't use modctl() to find
   2253 	 *    the maximum major number, we need to dig thru client's
   2254 	 *    /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
   2255 	 *
   2256 	 * if (server)
   2257 	 *    get maximum major number thru (rem_)name_to_major file on client
   2258 	 * else
   2259 	 *    get maximum major number allowable on current system using modctl
   2260 	 */
   2261 	if (server) {
   2262 		max_dev = 0;
   2263 		tmp = 0;
   2264 
   2265 		max_dev = get_max_major(name_to_major);
   2266 
   2267 		/* If rem_name_to_major exists, we need to check it too */
   2268 		if (have_rem_n2m) {
   2269 			tmp = get_max_major(rem_name_to_major);
   2270 
   2271 			/*
   2272 			 * If name_to_major is missing, we can get max_dev from
   2273 			 * /etc/rem_name_to_major.  If both missing, bail out!
   2274 			 */
   2275 			if ((max_dev == ERROR) && (tmp == ERROR)) {
   2276 				(void) fprintf(stderr,
   2277 				    gettext(ERR_CANT_ACCESS_FILE),
   2278 				    name_to_major);
   2279 				return (ERROR);
   2280 			}
   2281 
   2282 			/* guard against bigger maj_num in rem_name_to_major */
   2283 			if (tmp > max_dev)
   2284 				max_dev = tmp;
   2285 		} else {
   2286 			/*
   2287 			 * If we can't get major from name_to_major file
   2288 			 * and there is no /etc/rem_name_to_major file,
   2289 			 * then we don't have a max_dev, bail out quick!
   2290 			 */
   2291 			if (max_dev == ERROR)
   2292 				return (ERROR);
   2293 		}
   2294 
   2295 		/*
   2296 		 * In case there is no more slack in current name_to_major
   2297 		 * table, provide at least 1 extra entry so the add_drv can
   2298 		 * succeed.  Since only one add_drv process is allowed at one
   2299 		 * time, and hence max_dev will be re-calculated each time
   2300 		 * add_drv is ran, we don't need to worry about adding more
   2301 		 * than 1 extra slot for max_dev.
   2302 		 */
   2303 		max_dev++;
   2304 
   2305 	} else {
   2306 		if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
   2307 			perror(NULL);
   2308 			(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
   2309 			return (ERROR);
   2310 		}
   2311 	}
   2312 
   2313 	/*
   2314 	 * max_dev is really how many slots the kernel has allocated for
   2315 	 * devices... [0 , maxdev-1], not the largest available device num.
   2316 	 */
   2317 	if ((num_list = calloc(max_dev, 1)) == NULL) {
   2318 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
   2319 		return (ERROR);
   2320 	}
   2321 
   2322 	/*
   2323 	 * Populate the num_list array
   2324 	 */
   2325 	if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) {
   2326 		return (ERROR);
   2327 	}
   2328 	if (have_rem_n2m) {
   2329 		if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0)
   2330 			return (ERROR);
   2331 	}
   2332 
   2333 	/* find first free major number */
   2334 	for (i = 0; i < max_dev; i++) {
   2335 		if (num_list[i] != 1) {
   2336 			new_maj = i;
   2337 			break;
   2338 		}
   2339 	}
   2340 
   2341 	if (new_maj == -1) {
   2342 		(void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR));
   2343 		return (ERROR);
   2344 	}
   2345 
   2346 	(void) snprintf(drv_majnum_str, sizeof (drv_majnum_str),
   2347 	    "%d", new_maj);
   2348 	if (do_the_update(driver_name, drv_majnum_str) == ERROR) {
   2349 		return (ERROR);
   2350 	}
   2351 
   2352 	*major_num = new_maj;
   2353 	return (NOERR);
   2354 }
   2355 
   2356 
   2357 int
   2358 fill_n2m_array(char *filename, char **array, int *nelems)
   2359 {
   2360 	FILE *fp;
   2361 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
   2362 	char drv[FILENAME_MAX + 1];
   2363 	u_longlong_t dnum;
   2364 	major_t drv_majnum;
   2365 
   2366 	/*
   2367 	 * Read through the file, marking each major number found
   2368 	 * order is not relevant
   2369 	 */
   2370 	if ((fp = fopen(filename, "r")) == NULL) {
   2371 		perror(NULL);
   2372 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename);
   2373 		return (ERROR);
   2374 	}
   2375 
   2376 	while (fgets(line, sizeof (line), fp) != 0) {
   2377 		/* cut off comments starting with '#' */
   2378 		if ((cp = strchr(line, '#')) != NULL)
   2379 			*cp = '\0';
   2380 		/* ignore comment or blank lines */
   2381 		if (is_blank(line))
   2382 			continue;
   2383 		/* sanity-check */
   2384 		if (sscanf(line,
   2385 		    "%" VAL2STR(FILENAME_MAX) "s %llu", drv, &dnum) != 2) {
   2386 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
   2387 			    filename, line);
   2388 			(void) fclose(fp);
   2389 			return (ERROR);
   2390 		}
   2391 
   2392 		if (dnum > L_MAXMAJ32) {
   2393 			(void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv,
   2394 			    dnum, filename, L_MAXMAJ32);
   2395 			continue;
   2396 		}
   2397 		/*
   2398 		 * cast down to a major_t; we can be sure this is safe because
   2399 		 * of the above range-check.
   2400 		 */
   2401 		drv_majnum = (major_t)dnum;
   2402 
   2403 		if (drv_majnum >= *nelems) {
   2404 			/*
   2405 			 * Allocate some more space, up to drv_majnum + 1 so
   2406 			 * we can accomodate 0 through drv_majnum.
   2407 			 *
   2408 			 * Note that in the failure case, we leak all of the
   2409 			 * old contents of array.  It's ok, since we just
   2410 			 * wind up exiting immediately anyway.
   2411 			 */
   2412 			*nelems = drv_majnum + 1;
   2413 			*array = realloc(*array, *nelems);
   2414 			if (*array == NULL) {
   2415 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
   2416 				return (ERROR);
   2417 			}
   2418 		}
   2419 		(*array)[drv_majnum] = 1;
   2420 	}
   2421 
   2422 	(void) fclose(fp);
   2423 	return (0);
   2424 }
   2425 
   2426 
   2427 int
   2428 do_the_update(char *driver_name, char *major_number)
   2429 {
   2430 	return (append_to_file(driver_name, major_number, name_to_major,
   2431 	    ' ', " ", 0));
   2432 }
   2433 
   2434 /*
   2435  * is_blank() returns 1 (true) if a line specified is composed of
   2436  * whitespace characters only. otherwise, it returns 0 (false).
   2437  *
   2438  * Note. the argument (line) must be null-terminated.
   2439  */
   2440 static int
   2441 is_blank(char *line)
   2442 {
   2443 	for (/* nothing */; *line != '\0'; line++)
   2444 		if (!isspace(*line))
   2445 			return (0);
   2446 	return (1);
   2447 }
   2448