xref: /illumos-gate/usr/src/cmd/modload/drvsubr.c (revision bb25c06c)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <sys/sysmacros.h>
33 #include <libintl.h>
34 #include <wait.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <signal.h>
39 #include <sys/buf.h>
40 #include <sys/stat.h>
41 #include <grp.h>
42 #include "addrem.h"
43 #include "errmsg.h"
44 #include "plcysubr.h"
45 
46 static char *add_rem_lock;	/* lock file */
47 static char *tmphold;		/* temperary file for updating */
48 
49 static int get_cached_n_to_m_file(char *filename, char ***cache);
50 static int get_name_to_major_entry(int *major_no, char *driver_name,
51     char *file_name);
52 
53 static int is_blank(char *);
54 
55 /*ARGSUSED*/
56 void
57 log_minorperm_error(minorperm_err_t err, int key)
58 {
59 	switch (err) {
60 	case MP_FOPEN_ERR:
61 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
62 			MINOR_PERM_FILE);
63 		break;
64 	case MP_FCLOSE_ERR:
65 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
66 			MINOR_PERM_FILE);
67 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
68 		break;
69 	case MP_IGNORING_LINE_ERR:
70 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
71 			MINOR_PERM_FILE);
72 		break;
73 	case MP_ALLOC_ERR:
74 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
75 			MINOR_PERM_FILE);
76 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
77 		break;
78 	case MP_NVLIST_ERR:
79 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
80 			MINOR_PERM_FILE);
81 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
82 		break;
83 	case MP_CANT_FIND_USER_ERR:
84 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
85 			MINOR_PERM_FILE);
86 		break;
87 	case MP_CANT_FIND_GROUP_ERR:
88 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
89 			MINOR_PERM_FILE);
90 		break;
91 	}
92 }
93 
94 /*
95  *  open file
96  * for each entry in list
97  *	where list entries are separated by <list_separator>
98  * 	append entry : driver_name <entry_separator> entry
99  * close file
100  * return error/noerr
101  */
102 int
103 append_to_file(
104 	char *driver_name,
105 	char *entry_list,
106 	char *filename,
107 	char list_separator,
108 	char *entry_separator)
109 {
110 	int	i, len;
111 	int	fpint;
112 	char	*current_head, *previous_head;
113 	char	*line, *one_entry;
114 	FILE	*fp;
115 
116 	if ((fp = fopen(filename, "a")) == NULL) {
117 		perror(NULL);
118 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
119 		    filename);
120 		return (ERROR);
121 	}
122 
123 	len = strlen(entry_list);
124 
125 	one_entry = calloc(len + 1, 1);
126 	if (one_entry == NULL) {
127 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
128 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
129 		(void) fclose(fp);
130 		return (ERROR);
131 	}
132 
133 	previous_head = entry_list;
134 
135 	line = calloc(strlen(driver_name) + len + 4, 1);
136 	if (line == NULL) {
137 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
138 		(void) fclose(fp);
139 		err_exit();
140 	}
141 
142 	/*
143 	 * get one entry at a time from list and append to <filename> file
144 	 */
145 
146 	do {
147 
148 		for (i = 0; i <= len; i++)
149 			one_entry[i] = 0;
150 
151 		for (i = 0; i <= (int)strlen(line); i++)
152 			line[i] = 0;
153 
154 		current_head = get_entry(previous_head, one_entry,
155 		    list_separator);
156 		previous_head = current_head;
157 
158 		(void) strcpy(line, driver_name);
159 		(void) strcat(line, entry_separator);
160 		(void) strcat(line, one_entry);
161 		(void) strcat(line, "\n");
162 
163 		if ((fputs(line, fp)) == EOF) {
164 			perror(NULL);
165 			(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
166 			    filename);
167 		}
168 
169 	} while (*current_head != '\0');
170 
171 
172 	(void) fflush(fp);
173 
174 	fpint = fileno(fp);
175 	(void) fsync(fpint);
176 
177 	(void) fclose(fp);
178 
179 	free(one_entry);
180 	free(line);
181 
182 	return (NOERR);
183 }
184 
185 
186 /*
187  *  open file
188  * read thru file, deleting all entries if first
189  *    entry = driver_name
190  * close
191  * if error, leave original file intact with message
192  * assumption : drvconfig has been modified to work with clone
193  *  entries in /etc/minor_perm as driver:mummble NOT
194  *  clone:driver mummble
195  * this implementation will NOT find clone entries
196  * clone:driver mummble
197  * match:
198  *	delete just the matching entry
199  *
200  */
201 int
202 delete_entry(
203 	char *oldfile,
204 	char *driver_name,
205 	char *marker,
206 	char *match)
207 {
208 	int		rv, i;
209 	int		status = NOERR;
210 	int		drvr_found = 0;
211 	boolean_t 	nomatch = B_TRUE;
212 	char		*newfile, *tptr, *cp, *dup;
213 	char		line[MAX_DBFILE_ENTRY], drv[FILENAME_MAX + 1];
214 	FILE		*fp, *newfp;
215 	struct group	*sysgrp;
216 
217 	/*
218 	 * check if match is specified and if it equals " "
219 	 * this is a special case handling as we do a strstr(3STRING)
220 	 * to match an entry. By default all entries are space separated
221 	 * and without this check all entries of the file could get deleted.
222 	 */
223 	if (match && (*match == ' ' && strlen(match) == 1)) {
224 		(void) fprintf(stderr, gettext(ERR_INT_UPDATE), oldfile);
225 		return (ERROR);
226 	}
227 
228 	if ((fp = fopen(oldfile, "r")) == NULL) {
229 		perror(NULL);
230 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
231 		return (ERROR);
232 	}
233 
234 	/*
235 	 * Build filename for temporary file
236 	 */
237 
238 	if ((tptr = calloc(strlen(oldfile) + strlen(XEND) + 1, 1)) == NULL) {
239 		perror(NULL);
240 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
241 	}
242 
243 	(void) strcpy(tptr, oldfile);
244 	(void) strcat(tptr, XEND);
245 
246 	/*
247 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
248 	 * assume a gid of "sys" but we can't undo the damage on already
249 	 * installed systems unless we force the issue.
250 	 */
251 	if ((sysgrp = getgrnam("sys")) != NULL) {
252 		(void) setgid(sysgrp->gr_gid);
253 	}
254 
255 	newfile = mktemp(tptr);
256 
257 	if ((newfp = fopen(newfile, "w")) == NULL) {
258 		perror(NULL);
259 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
260 		    newfile);
261 		return (ERROR);
262 	}
263 
264 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
265 		/* copy the whole line into dup */
266 		if ((dup = strdup(line)) == NULL) {
267 			perror(NULL);
268 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
269 			status = ERROR;
270 			break;
271 		}
272 		/* cut off comments starting with '#' */
273 		if ((cp = strchr(dup, '#')) != NULL)
274 			*cp = '\0';
275 		/* ignore comment or blank lines */
276 		if (is_blank(dup)) {
277 			if (fputs(line, newfp) == EOF) {
278 				(void) fprintf(stderr, gettext(ERR_UPDATE),
279 				    oldfile);
280 				status = ERROR;
281 			}
282 			free(dup);
283 			continue;
284 		}
285 
286 		/* get the driver name */
287 		if (sscanf(dup, "%s", drv) != 1) {
288 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
289 			    oldfile, line);
290 			status = ERROR;
291 			free(dup);
292 			break;
293 		}
294 		free(dup);
295 
296 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
297 			drv[i] =  '\0';
298 		}
299 
300 		if (strcmp(driver_name, drv) != 0) {
301 			if ((fputs(line, newfp)) == EOF) {
302 				(void) fprintf(stderr, gettext(ERR_UPDATE),
303 				    oldfile);
304 				status = ERROR;
305 			}
306 		} else {
307 			drvr_found++;
308 			if (match) {	/* Just delete one entry */
309 				/* for now delete just minor_perm and aliases */
310 				if ((strcmp(oldfile, minor_perm) == 0) ||
311 				    (strcmp(oldfile, extra_privs) == 0) ||
312 				    (strcmp(oldfile, driver_aliases) == 0)) {
313 					if (strstr(line, match)) {
314 						nomatch = B_FALSE;
315 					} else {
316 						if ((fputs(line, newfp)) ==
317 						    EOF) {
318 							(void) fprintf(stderr,
319 							    gettext(ERR_UPDATE),
320 							    oldfile);
321 							status = ERROR;
322 						}
323 						if (nomatch != B_FALSE)
324 							nomatch = B_TRUE;
325 					}
326 				}
327 			}
328 
329 		} /* end of else */
330 	} /* end of while */
331 
332 	(void) fclose(fp);
333 
334 	/* Make sure that the file is on disk */
335 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
336 		status = ERROR;
337 	else
338 		rv = NOERR;
339 
340 	(void) fclose(newfp);
341 
342 	/* no matching driver found */
343 	rv = NOERR;
344 	if (!drvr_found ||
345 	    (nomatch == B_TRUE)) {
346 		rv = NONE_FOUND;
347 	}
348 
349 	/*
350 	 * if error, leave original file, delete new file
351 	 * if noerr, replace original file with new file
352 	 */
353 
354 	if (status == NOERR) {
355 		if (rename(oldfile, tmphold) == -1) {
356 			perror(NULL);
357 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
358 			(void) unlink(newfile);
359 			return (ERROR);
360 		} else if (rename(newfile, oldfile) == -1) {
361 			perror(NULL);
362 			(void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
363 			(void) unlink(oldfile);
364 			(void) unlink(newfile);
365 			if (link(tmphold, oldfile) == -1) {
366 				perror(NULL);
367 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
368 				    oldfile, tmphold);
369 			}
370 			return (ERROR);
371 		}
372 		(void) unlink(tmphold);
373 	} else {
374 		/*
375 		 * since there's an error, leave file alone; remove
376 		 * new file
377 		 */
378 		if (unlink(newfile) == -1) {
379 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
380 		}
381 		return (ERROR);
382 	}
383 
384 	return (rv);
385 }
386 
387 
388 /*
389  * wrapper for call to get_name_to_major_entry(): given driver name,
390  * retrieve major number.
391  */
392 int
393 get_major_no(char *driver_name, char *file_name)
394 {
395 	int major = UNIQUE;
396 
397 	if (get_name_to_major_entry(&major, driver_name, file_name) == ERROR)
398 		return (ERROR);
399 	else
400 		return (major);
401 }
402 
403 /*
404  * wrapper for call to get_name_to_major_entry(): given major number,
405  * retrieve driver name.
406  */
407 int
408 get_driver_name(int major, char *file_name, char *buf)
409 {
410 	if (major < 0)
411 		return (ERROR);
412 	return (get_name_to_major_entry(&major, buf, file_name));
413 }
414 
415 
416 /*
417  * return pointer to cached name_to_major file - reads file into
418  * cache if this has not already been done.  Since there may be
419  * requests for multiple name_to_major files (rem_name_to_major,
420  * name_to_major), this routine keeps a list of cached files.
421  */
422 static int
423 get_cached_n_to_m_file(char *filename, char ***cache)
424 {
425 	struct n_to_m_cache {
426 		char *file;
427 		char **cached_file;
428 		int size;
429 		struct n_to_m_cache *next;
430 	};
431 	static struct n_to_m_cache *head = NULL;
432 	struct n_to_m_cache *ptr;
433 	FILE *fp;
434 	char drv[FILENAME_MAX + 1];
435 	char entry[FILENAME_MAX + 1];
436 	char line[MAX_N2M_ALIAS_LINE], *cp;
437 	int maj;
438 	int size = 0;
439 
440 
441 	/*
442 	 * see if the file is already cached - either
443 	 * rem_name_to_major or name_to_major
444 	 */
445 	ptr = head;
446 	while (ptr != NULL) {
447 		if (strcmp(ptr->file, filename) == 0)
448 			break;
449 		ptr = ptr->next;
450 	}
451 
452 	if (ptr == NULL) {	/* we need to cache the contents */
453 		if ((fp = fopen(filename, "r")) == NULL) {
454 			perror(NULL);
455 			(void) fprintf(stderr, gettext(ERR_CANT_OPEN),
456 			    filename);
457 			return (ERROR);
458 		}
459 
460 		while (fgets(line, sizeof (line), fp) != NULL) {
461 			/* cut off comments starting with '#' */
462 			if ((cp = strchr(line, '#')) != NULL)
463 				*cp = '\0';
464 			/* ignore comment or blank lines */
465 			if (is_blank(line))
466 				continue;
467 			/* sanity-check */
468 			if (sscanf(line, "%s%s", drv, entry) != 2) {
469 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
470 				    filename, line);
471 				continue;
472 			}
473 			maj = atoi(entry);
474 			if (maj > size)
475 				size = maj;
476 		}
477 
478 		/* allocate struct to cache the file */
479 		ptr = (struct n_to_m_cache *)calloc(1,
480 		    sizeof (struct n_to_m_cache));
481 		if (ptr == NULL) {
482 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
483 			return (ERROR);
484 		}
485 		ptr->size = size + 1;
486 		/* allocate space to cache contents of file */
487 		ptr->cached_file = (char **)calloc(ptr->size, sizeof (char *));
488 		if (ptr->cached_file == NULL) {
489 			free(ptr);
490 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
491 			return (ERROR);
492 		}
493 
494 		rewind(fp);
495 
496 		/*
497 		 * now fill the cache
498 		 * the cache is an array of char pointers indexed by major
499 		 * number
500 		 */
501 		while (fgets(line, sizeof (line), fp) != NULL) {
502 			/* cut off comments starting with '#' */
503 			if ((cp = strchr(line, '#')) != NULL)
504 				*cp = '\0';
505 			/* ignore comment or blank lines */
506 			if (is_blank(line))
507 				continue;
508 			/* sanity-check */
509 			if (sscanf(line, "%s%s", drv, entry) != 2) {
510 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
511 				    filename, line);
512 				continue;
513 			}
514 			maj = atoi(entry);
515 			if ((ptr->cached_file[maj] = strdup(drv)) == NULL) {
516 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
517 				free(ptr->cached_file);
518 				free(ptr);
519 				return (ERROR);
520 			}
521 			(void) strcpy(ptr->cached_file[maj], drv);
522 		}
523 		(void) fclose(fp);
524 		/* link the cache struct into the list of cached files */
525 		ptr->file = strdup(filename);
526 		if (ptr->file == NULL) {
527 			for (maj = 0; maj <= ptr->size; maj++)
528 				free(ptr->cached_file[maj]);
529 			free(ptr->cached_file);
530 			free(ptr);
531 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
532 			return (ERROR);
533 		}
534 		ptr->next = head;
535 		head = ptr;
536 	}
537 	/* return value pointer to contents of file */
538 	*cache = ptr->cached_file;
539 
540 	/* return size */
541 	return (ptr->size);
542 }
543 
544 
545 /*
546  * Using get_cached_n_to_m_file(), retrieve maximum major number
547  * found in the specificed file (name_to_major/rem_name_to_major).
548  *
549  * The return value is actually the size of the internal cache including 0.
550  */
551 int
552 get_max_major(char *file_name)
553 {
554 	char **n_to_m_cache = NULL;
555 
556 	return (get_cached_n_to_m_file(file_name, &n_to_m_cache));
557 }
558 
559 
560 /*
561  * searching name_to_major: if major_no == UNIQUE then the caller wants to
562  * use the driver name as the key.  Otherwise, the caller wants to use
563  * the major number as a key.
564  *
565  * This routine caches the contents of the name_to_major file on
566  * first call.  And it could be generalized to deal with other
567  * config files if necessary.
568  */
569 static int
570 get_name_to_major_entry(int *major_no, char *driver_name, char *file_name)
571 {
572 	int maj;
573 	char **n_to_m_cache = NULL;
574 	int size = 0;
575 
576 	int ret = NOT_UNIQUE;
577 
578 	/*
579 	 * read the file in - we cache it in case caller wants to
580 	 * do multiple lookups
581 	 */
582 	size = get_cached_n_to_m_file(file_name, &n_to_m_cache);
583 
584 	if (size == ERROR)
585 		return (ERROR);
586 
587 	/* search with driver name as key */
588 	if (*major_no == UNIQUE) {
589 		for (maj = 0; maj < size; maj++) {
590 			if ((n_to_m_cache[maj] != NULL) &&
591 			    (strcmp(driver_name, n_to_m_cache[maj]) == 0)) {
592 				*major_no = maj;
593 				break;
594 			}
595 		}
596 		if (maj >= size)
597 			ret = UNIQUE;
598 	/* search with major number as key */
599 	} else {
600 		/*
601 		 * Bugid 1254588, drvconfig dump core after loading driver
602 		 * with major number bigger than entries defined in
603 		 * /etc/name_to_major.
604 		 */
605 		if (*major_no >= size)
606 			return (UNIQUE);
607 
608 		if (n_to_m_cache[*major_no] != NULL) {
609 			(void) strcpy(driver_name, n_to_m_cache[*major_no]);
610 		} else
611 			ret = UNIQUE;
612 	}
613 	return (ret);
614 }
615 
616 /*
617  * given pointer to member n in space separated list, return pointer
618  * to member n+1, return member n
619  */
620 char *
621 get_entry(
622 	char *prev_member,
623 	char *current_entry,
624 	char separator)
625 {
626 	char *ptr;
627 
628 	ptr = prev_member;
629 
630 	/* skip white space */
631 	while (*ptr == '\t' || *ptr == ' ')
632 		ptr++;
633 
634 	/* read thru the current entry */
635 	while (*ptr != separator && *ptr != '\0') {
636 		*current_entry++ = *ptr++;
637 	}
638 	*current_entry = '\0';
639 
640 	if ((separator == ',') && (*ptr == separator))
641 		ptr++;	/* skip over comma */
642 
643 	/* skip white space */
644 	while (*ptr == '\t' || *ptr == ' ') {
645 		ptr++;
646 	}
647 
648 	return (ptr);
649 }
650 
651 /*ARGSUSED0*/
652 static void
653 signal_rtn(int sig)
654 {
655 	exit_unlock();
656 }
657 
658 void
659 enter_lock(void)
660 {
661 	int fd;
662 
663 	/*
664 	 * Setup handler to clean lock file in case user terminates
665 	 * the command.
666 	 */
667 	(void) sigset(SIGINT, signal_rtn);
668 	(void) sigset(SIGHUP, signal_rtn);
669 	(void) sigset(SIGTERM, signal_rtn);
670 
671 	/*
672 	 * attempt to create the lock file
673 	 */
674 	if ((fd = open(add_rem_lock, O_CREAT | O_EXCL | O_WRONLY,
675 	    S_IRUSR | S_IWUSR)) == -1) {
676 		if (errno == EEXIST) {
677 			(void) fprintf(stderr, gettext(ERR_PROG_IN_USE));
678 		} else {
679 			perror(gettext(ERR_LOCKFILE));
680 		}
681 		exit(1);
682 	}
683 	(void) close(fd);
684 }
685 
686 void
687 err_exit(void)
688 {
689 	/* release memory allocated for moddir */
690 	cleanup_moddir();
691 	/* remove add_drv/rem_drv lock */
692 	exit_unlock();
693 	exit(1);
694 }
695 
696 void
697 cleanup_moddir(void)
698 {
699 	struct drvmod_dir *walk_ptr;
700 	struct drvmod_dir *free_ptr = moddir;
701 
702 	while (free_ptr != NULL) {
703 		walk_ptr = free_ptr->next;
704 		free(free_ptr);
705 		free_ptr = walk_ptr;
706 	}
707 }
708 
709 void
710 exit_unlock(void)
711 {
712 	struct stat buf;
713 
714 	if (stat(add_rem_lock, &buf) == NOERR) {
715 		if (unlink(add_rem_lock) == -1) {
716 			(void) fprintf(stderr, gettext(ERR_REM_LOCK),
717 			    add_rem_lock);
718 		}
719 	}
720 }
721 
722 /*
723  * error adding driver; need to back out any changes to files.
724  * check flag to see which files need entries removed
725  * entry removal based on driver name
726  */
727 void
728 remove_entry(
729 	int c_flag,
730 	char *driver_name)
731 {
732 
733 	if (c_flag & CLEAN_NAM_MAJ) {
734 		if (delete_entry(name_to_major, driver_name, " ",
735 		    NULL) == ERROR) {
736 			(void) fprintf(stderr, gettext(ERR_NO_CLEAN),
737 			    name_to_major, driver_name);
738 		}
739 	}
740 
741 	if (c_flag & CLEAN_DRV_ALIAS) {
742 		if (delete_entry(driver_aliases, driver_name, " ",
743 		    NULL) == ERROR) {
744 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
745 			    driver_name, driver_aliases);
746 		}
747 	}
748 
749 	if (c_flag & CLEAN_DRV_CLASSES) {
750 		if (delete_entry(driver_classes, driver_name, "\t", NULL) ==
751 		    ERROR) {
752 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
753 			    driver_name, driver_classes);
754 		}
755 	}
756 
757 	if (c_flag & CLEAN_MINOR_PERM) {
758 		if (delete_entry(minor_perm, driver_name, ":", NULL) == ERROR) {
759 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
760 			    driver_name, minor_perm);
761 		}
762 	}
763 	/*
764 	 * There's no point in removing entries from files that don't
765 	 * exist.  Prevent error messages by checking for file existence
766 	 * first.
767 	 */
768 	if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
769 	    access(device_policy, F_OK) == 0) {
770 		if (delete_plcy_entry(device_policy, driver_name) == ERROR) {
771 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
772 				driver_name, device_policy);
773 		}
774 	}
775 	if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
776 	    access(extra_privs, F_OK) == 0) {
777 		if (delete_entry(extra_privs, driver_name, ":", NULL) ==
778 		    ERROR) {
779 			(void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
780 				driver_name, extra_privs);
781 		}
782 	}
783 }
784 
785 int
786 check_perms_aliases(
787 	int m_flag,
788 	int i_flag)
789 {
790 	/*
791 	 * If neither i_flag nor m_flag are specified no need to check the
792 	 * files for access permissions
793 	 */
794 	if (!m_flag && !i_flag)
795 		return (NOERR);
796 
797 	/* check minor_perm file : exits and is writable */
798 	if (m_flag) {
799 		if (access(minor_perm, R_OK | W_OK)) {
800 			perror(NULL);
801 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
802 			    minor_perm);
803 			return (ERROR);
804 		}
805 	}
806 
807 	/* check driver_aliases file : exits and is writable */
808 	if (i_flag) {
809 		if (access(driver_aliases, R_OK | W_OK)) {
810 			perror(NULL);
811 			(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
812 			    driver_aliases);
813 			return (ERROR);
814 		}
815 	}
816 
817 	return (NOERR);
818 }
819 
820 
821 int
822 check_name_to_major(int mode)
823 {
824 	/* check name_to_major file : exists and is writable */
825 	if (access(name_to_major, mode)) {
826 		perror(NULL);
827 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
828 		    name_to_major);
829 		return (ERROR);
830 	}
831 
832 	return (NOERR);
833 }
834 
835 
836 /*
837  * All this stuff is to support a server installing
838  * drivers on diskless clients.  When on the server
839  * need to prepend the basedir
840  */
841 int
842 build_filenames(char *basedir)
843 {
844 	int len;
845 
846 	if (basedir == NULL) {
847 		driver_aliases = DRIVER_ALIAS;
848 		driver_classes = DRIVER_CLASSES;
849 		minor_perm = MINOR_PERM;
850 		name_to_major = NAM_TO_MAJ;
851 		rem_name_to_major = REM_NAM_TO_MAJ;
852 		add_rem_lock = ADD_REM_LOCK;
853 		tmphold = TMPHOLD;
854 		devfs_root = DEVFS_ROOT;
855 		device_policy = DEV_POLICY;
856 		extra_privs = EXTRA_PRIVS;
857 
858 	} else {
859 		len = strlen(basedir);
860 
861 		driver_aliases = malloc(len + sizeof (DRIVER_ALIAS));
862 		driver_classes = malloc(len + sizeof (DRIVER_CLASSES));
863 		minor_perm = malloc(len + sizeof (MINOR_PERM));
864 		name_to_major = malloc(len + sizeof (NAM_TO_MAJ));
865 		rem_name_to_major = malloc(len + sizeof (REM_NAM_TO_MAJ));
866 		add_rem_lock = malloc(len + sizeof (ADD_REM_LOCK));
867 		tmphold = malloc(len + sizeof (TMPHOLD));
868 		devfs_root = malloc(len + sizeof (DEVFS_ROOT));
869 		device_policy = malloc(len + sizeof (DEV_POLICY));
870 		extra_privs = malloc(len + sizeof (EXTRA_PRIVS));
871 
872 
873 		if ((driver_aliases == NULL) ||
874 		    (driver_classes == NULL) ||
875 		    (minor_perm == NULL) ||
876 		    (name_to_major == NULL) ||
877 		    (rem_name_to_major == NULL) ||
878 		    (add_rem_lock == NULL) ||
879 		    (tmphold == NULL) ||
880 		    (devfs_root == NULL) ||
881 		    (device_policy == NULL) ||
882 		    (extra_privs == NULL)) {
883 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
884 			return (ERROR);
885 		}
886 
887 		(void) sprintf(driver_aliases, "%s%s", basedir, DRIVER_ALIAS);
888 		(void) sprintf(driver_classes, "%s%s", basedir, DRIVER_CLASSES);
889 		(void) sprintf(minor_perm, "%s%s", basedir, MINOR_PERM);
890 		(void) sprintf(name_to_major, "%s%s", basedir, NAM_TO_MAJ);
891 		(void) sprintf(rem_name_to_major, "%s%s", basedir,
892 				REM_NAM_TO_MAJ);
893 		(void) sprintf(add_rem_lock, "%s%s", basedir, ADD_REM_LOCK);
894 		(void) sprintf(tmphold, "%s%s", basedir, TMPHOLD);
895 		(void) sprintf(devfs_root, "%s%s", basedir, DEVFS_ROOT);
896 		(void) sprintf(device_policy, "%s%s", basedir, DEV_POLICY);
897 		(void) sprintf(extra_privs, "%s%s", basedir, EXTRA_PRIVS);
898 	}
899 
900 	return (NOERR);
901 }
902 
903 static int
904 exec_command(char *path, char *cmdline[MAX_CMD_LINE])
905 {
906 	pid_t pid;
907 	uint_t stat_loc;
908 	int waitstat;
909 	int exit_status;
910 
911 	/* child */
912 	if ((pid = fork()) == 0) {
913 		(void) execv(path, cmdline);
914 		perror(NULL);
915 		return (ERROR);
916 	} else if (pid == -1) {
917 		/* fork failed */
918 		perror(NULL);
919 		(void) fprintf(stderr, gettext(ERR_FORK_FAIL), cmdline);
920 		return (ERROR);
921 	} else {
922 		/* parent */
923 		do {
924 			waitstat = waitpid(pid, (int *)&stat_loc, 0);
925 
926 		} while ((!WIFEXITED(stat_loc) &&
927 			!WIFSIGNALED(stat_loc)) || (waitstat == 0));
928 
929 		exit_status = WEXITSTATUS(stat_loc);
930 
931 		return (exit_status);
932 	}
933 }
934 
935 /*
936  * check that major_num doesn't exceed maximum on this machine
937  * do this here to support add_drv on server for diskless clients
938  */
939 int
940 config_driver(
941 	char *driver_name,
942 	major_t major_num,
943 	char *aliases,
944 	char *classes,
945 	int cleanup_flag,
946 	int verbose_flag)
947 {
948 	int max_dev;
949 	int n = 0;
950 	char *cmdline[MAX_CMD_LINE];
951 	char maj_num[128];
952 	char *previous;
953 	char *current;
954 	int exec_status;
955 	int len;
956 
957 	if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
958 		perror(NULL);
959 		(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
960 		return (ERROR);
961 	}
962 
963 	if (major_num >= max_dev) {
964 		(void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS),
965 		    major_num, max_dev);
966 		return (ERROR);
967 	}
968 
969 	/* bind major number and driver name */
970 
971 	/* build command line */
972 	cmdline[n++] = DRVCONFIG;
973 	if (verbose_flag) {
974 		cmdline[n++] = "-v";
975 	}
976 	cmdline[n++] = "-b";
977 	if (classes) {
978 		cmdline[n++] = "-c";
979 		cmdline[n++] = classes;
980 	}
981 	cmdline[n++] = "-i";
982 	cmdline[n++] = driver_name;
983 	cmdline[n++] = "-m";
984 	(void) sprintf(maj_num, "%lu", major_num);
985 	cmdline[n++] = maj_num;
986 
987 	if (aliases != NULL) {
988 		len = strlen(aliases);
989 		previous = aliases;
990 		do {
991 			cmdline[n++] = "-a";
992 			cmdline[n] = calloc(len + 1, 1);
993 			if (cmdline[n] == NULL) {
994 				(void) fprintf(stderr,
995 				    gettext(ERR_NO_MEM));
996 				return (ERROR);
997 			}
998 			current = get_entry(previous,
999 			    cmdline[n++], ' ');
1000 			previous = current;
1001 
1002 		} while (*current != '\0');
1003 
1004 	}
1005 	cmdline[n] = (char *)0;
1006 
1007 	exec_status = exec_command(DRVCONFIG_PATH, cmdline);
1008 
1009 	if (exec_status == NOERR)
1010 		return (NOERR);
1011 	perror(NULL);
1012 	remove_entry(cleanup_flag, driver_name);
1013 	return (ERROR);
1014 }
1015 
1016 void
1017 load_driver(char *driver_name, int verbose_flag)
1018 {
1019 	int n = 0;
1020 	char *cmdline[MAX_CMD_LINE];
1021 	int exec_status;
1022 
1023 	/* build command line */
1024 	cmdline[n++] = DEVFSADM;
1025 	if (verbose_flag) {
1026 		cmdline[n++] = "-v";
1027 	}
1028 	cmdline[n++] = "-i";
1029 	cmdline[n++] = driver_name;
1030 	cmdline[n] = (char *)0;
1031 
1032 	exec_status = exec_command(DEVFSADM_PATH, cmdline);
1033 
1034 	if (exec_status != NOERR) {
1035 		/* no clean : name and major number are bound */
1036 		(void) fprintf(stderr, gettext(ERR_CONFIG),
1037 			driver_name);
1038 	}
1039 }
1040 
1041 void
1042 get_modid(char *driver_name, int *mod)
1043 {
1044 	struct modinfo	modinfo;
1045 
1046 	modinfo.mi_id = -1;
1047 	modinfo.mi_info = MI_INFO_ALL;
1048 	do {
1049 		/*
1050 		 * If we are at the end of the list of loaded modules
1051 		 * then set *mod = -1 and return
1052 		 */
1053 		if (modctl(MODINFO, 0, &modinfo) < 0) {
1054 			*mod = -1;
1055 			return;
1056 		}
1057 
1058 		*mod = modinfo.mi_id;
1059 	} while (strcmp(driver_name, modinfo.mi_name) != 0);
1060 }
1061 
1062 int
1063 create_reconfig(char *basedir)
1064 {
1065 	char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1];
1066 	FILE *reconfig_fp;
1067 
1068 	if (basedir != NULL) {
1069 		(void) strcpy(reconfig_file, basedir);
1070 		(void) strcat(reconfig_file, RECONFIGURE);
1071 	} else {
1072 		(void) strcpy(reconfig_file, RECONFIGURE);
1073 	}
1074 	if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL)
1075 		return (ERROR);
1076 
1077 	(void) fclose(reconfig_fp);
1078 	return (NOERR);
1079 }
1080 
1081 
1082 /*
1083  * update_minor_entry:
1084  *	open file
1085  *	for each entry in list
1086  *		where list entries are separated by <list_separator>
1087  * 		modify entry : driver_name <entry_separator> entry
1088  *	close file
1089  *
1090  *	return error/noerr
1091  */
1092 int
1093 update_minor_entry(char *driver_name, char *perm_list)
1094 {
1095 	FILE *fp;
1096 	FILE *newfp;
1097 	struct group *sysgrp;
1098 	int match = 0;
1099 	char line[MAX_DBFILE_ENTRY], *cp, *dup;
1100 	char drv[FILENAME_MAX + 1], *drv_minor;
1101 	char minor[FILENAME_MAX + 1], perm[OPT_LEN + 1];
1102 	char own[OPT_LEN + 1], grp[OPT_LEN + 1];
1103 	int status = NOERR, i;
1104 	char *newfile, *tptr;
1105 	extern void bzero();
1106 
1107 	if ((fp = fopen(minor_perm, "r")) == NULL) {
1108 		perror(NULL);
1109 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1110 		    minor_perm);
1111 
1112 		return (ERROR);
1113 	}
1114 
1115 	/*
1116 	 * Build filename for temporary file
1117 	 */
1118 	if ((tptr = calloc(strlen(minor_perm) + strlen(XEND) + 1, 1)) == NULL) {
1119 		perror(NULL);
1120 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1121 	}
1122 	(void) strcpy(tptr, minor_perm);
1123 	(void) strcat(tptr, XEND);
1124 
1125 	/*
1126 	 * Set gid so we preserve group attribute.  Ideally we wouldn't
1127 	 * assume a gid of "sys" but we can't undo the damage on already
1128 	 * installed systems unless we force the issue.
1129 	 */
1130 	if ((sysgrp = getgrnam("sys")) != NULL) {
1131 		(void) setgid(sysgrp->gr_gid);
1132 	}
1133 
1134 	newfile = mktemp(tptr);
1135 	if ((newfp = fopen(newfile, "w")) == NULL) {
1136 		perror(NULL);
1137 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1138 		    newfile);
1139 		return (ERROR);
1140 	}
1141 
1142 	if (sscanf(perm_list, "%s%s%s%s", minor, perm, own, grp) != 4) {
1143 		status = ERROR;
1144 	}
1145 
1146 	while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
1147 		/* copy the whole line into dup */
1148 		if ((dup = strdup(line)) == NULL) {
1149 			perror(NULL);
1150 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
1151 			status = ERROR;
1152 			break;
1153 		}
1154 		/* cut off comments starting with '#' */
1155 		if ((cp = strchr(dup, '#')) != NULL)
1156 			*cp = '\0';
1157 		/* ignore comment or blank lines */
1158 		if (is_blank(dup)) {
1159 			if (fputs(line, newfp) == EOF) {
1160 				(void) fprintf(stderr, gettext(ERR_UPDATE),
1161 				    minor_perm);
1162 				status = ERROR;
1163 			}
1164 			free(dup);
1165 			continue;
1166 		}
1167 
1168 		/* get the driver name */
1169 		if (sscanf(dup, "%s", drv) != 1) {
1170 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
1171 			    minor_perm, line);
1172 			status = ERROR;
1173 			free(dup);
1174 			break;
1175 		}
1176 
1177 		/*
1178 		 * get the minor name; place the NULL character at the
1179 		 * end of the driver name, then make the drv_minor
1180 		 * point to the first character of the minor name.
1181 		 * the line missing ':' must be treated as a broken one.
1182 		 */
1183 		i = strcspn(drv, ":");
1184 		if (i == strlen(drv)) {
1185 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
1186 			    minor_perm, line);
1187 			status = ERROR;
1188 			free(dup);
1189 			break;
1190 		}
1191 		drv[i] =  '\0';
1192 		drv_minor = &drv[strlen(drv) + 1];
1193 
1194 		/*
1195 		 * compare both of the driver name and the minor name.
1196 		 * then the new line should be written to the file if
1197 		 * both of them match
1198 		 */
1199 		if ((strcmp(drv, driver_name) == 0) &&
1200 		    (strcmp(minor, drv_minor) == 0)) {
1201 			/* if it has a comment, keep it */
1202 			if (cp != NULL) {
1203 				cp++; /* skip a terminator */
1204 				(void) sprintf(line, "%s:%s %s %s %s #%s\n",
1205 				    drv, minor, perm, own, grp, cp);
1206 			} else {
1207 				(void) sprintf(line, "%s:%s %s %s %s\n",
1208 				    drv, minor, perm, own, grp);
1209 			}
1210 			match = 1;
1211 		}
1212 		free(dup);
1213 
1214 		/* update the file */
1215 		if ((fputs(line, newfp)) == EOF) {
1216 			(void) fprintf(stderr, gettext(ERR_UPDATE),
1217 			    minor_perm);
1218 			status = ERROR;
1219 		}
1220 	}
1221 
1222 	if (!match) {
1223 		(void) bzero(line, sizeof (&line[0]));
1224 		(void) sprintf(line, "%s:%s %s %s %s\n",
1225 		    driver_name, minor, perm, own, grp);
1226 
1227 		/* add the new entry */
1228 		if ((fputs(line, newfp)) == EOF) {
1229 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1230 			status = ERROR;
1231 		}
1232 	}
1233 
1234 	(void) fclose(fp);
1235 
1236 	if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
1237 		status = ERROR;
1238 
1239 	(void) fclose(newfp);
1240 
1241 	/*
1242 	 * if error, leave original file, delete new file
1243 	 * if noerr, replace original file with new file
1244 	 */
1245 	if (status == NOERR) {
1246 		if (rename(minor_perm, tmphold) == -1) {
1247 			perror(NULL);
1248 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1249 			(void) unlink(newfile);
1250 			return (ERROR);
1251 		} else if (rename(newfile, minor_perm) == -1) {
1252 			perror(NULL);
1253 			(void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1254 			(void) unlink(minor_perm);
1255 			(void) unlink(newfile);
1256 			if (link(tmphold, minor_perm) == -1) {
1257 				perror(NULL);
1258 				(void) fprintf(stderr, gettext(ERR_BAD_LINK),
1259 				    minor_perm, tmphold);
1260 			}
1261 			return (ERROR);
1262 		}
1263 		(void) unlink(tmphold);
1264 	} else {
1265 		/*
1266 		 * since there's an error, leave file alone; remove
1267 		 * new file
1268 		 */
1269 		if (unlink(newfile) == -1) {
1270 			(void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
1271 		}
1272 		return (ERROR);
1273 	}
1274 
1275 	return (NOERR);
1276 
1277 }
1278 
1279 
1280 /*
1281  * list_entry:
1282  *	open file
1283  *	read thru file, listing all entries if first entry = driver_name
1284  *	close
1285  */
1286 void
1287 list_entry(
1288 	char *oldfile,
1289 	char *driver_name,
1290 	char *marker)
1291 {
1292 	FILE	*fp;
1293 	int	i;
1294 	char	line[MAX_DBFILE_ENTRY], *cp;
1295 	char	drv[FILENAME_MAX + 1];
1296 
1297 	if ((fp = fopen(oldfile, "r")) == NULL) {
1298 		perror(NULL);
1299 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
1300 
1301 		return;
1302 	}
1303 
1304 	while (fgets(line, sizeof (line), fp) != NULL) {
1305 		/* cut off comments starting with '#' */
1306 		if ((cp = strchr(line, '#')) != NULL)
1307 			*cp = '\0';
1308 		/* ignore comment or blank lines */
1309 		if (is_blank(line))
1310 			continue;
1311 		/* sanity-check */
1312 		if (sscanf(line, "%s", drv) != 1) {
1313 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
1314 			    oldfile, line);
1315 		}
1316 
1317 		for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
1318 			drv[i] =  '\0';
1319 		}
1320 
1321 		if (strcmp(driver_name, drv) == 0) {
1322 			(void) fprintf(stdout, "%s", line);
1323 		}
1324 	}
1325 
1326 	(void) fclose(fp);
1327 }
1328 
1329 static boolean_t
1330 is_token(char *tok)
1331 {
1332 	/*
1333 	 * Check the token here. According to IEEE1275 Open Firmware Boot
1334 	 * Standard, the name is composed of 1 to 31 letters,
1335 	 * digits and punctuation characters from the set ",._+-", and
1336 	 * uppercase and lowercase characters are considered distinct.
1337 	 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
1338 	 * However, since either the definition of driver or aliase names is
1339 	 * not known well, only '#' is avoided explicitly. (the kernel lexical
1340 	 * analyzer treats it as a start of a comment)
1341 	 */
1342 	for (/* nothing */; *tok != '\0'; tok++)
1343 		if (*tok == '#' || iscntrl(*tok))
1344 			return (B_FALSE);
1345 
1346 	return (B_TRUE);
1347 }
1348 
1349 /*
1350  * check each entry in perm_list for:
1351  *	4 arguments
1352  *	permission arg is in valid range
1353  * permlist entries separated by comma
1354  * return ERROR/NOERR
1355  */
1356 int
1357 check_perm_opts(char *perm_list)
1358 {
1359 	char *current_head;
1360 	char *previous_head;
1361 	char *one_entry;
1362 	int i, len, scan_stat;
1363 	char minor[FILENAME_MAX + 1];
1364 	char perm[OPT_LEN + 1];
1365 	char own[OPT_LEN + 1];
1366 	char grp[OPT_LEN + 1];
1367 	char dumb[OPT_LEN + 1];
1368 	int status = NOERR;
1369 	int intperm;
1370 
1371 	len = strlen(perm_list);
1372 
1373 	if (len == 0) {
1374 		return (ERROR);
1375 	}
1376 
1377 	one_entry = calloc(len + 1, 1);
1378 	if (one_entry == NULL) {
1379 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1380 		return (ERROR);
1381 	}
1382 
1383 	previous_head = perm_list;
1384 	current_head = perm_list;
1385 
1386 	while (*current_head != '\0') {
1387 
1388 		for (i = 0; i <= len; i++)
1389 			one_entry[i] = 0;
1390 
1391 		current_head = get_entry(previous_head, one_entry, ',');
1392 
1393 		previous_head = current_head;
1394 		scan_stat = sscanf(one_entry, "%s%s%s%s%s", minor, perm, own,
1395 		    grp, dumb);
1396 
1397 		if (scan_stat < 4) {
1398 			(void) fprintf(stderr, gettext(ERR_MIS_TOK),
1399 			    "-m", one_entry);
1400 			status = ERROR;
1401 		}
1402 		if (scan_stat > 4) {
1403 			(void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS),
1404 			    "-m", one_entry);
1405 			status = ERROR;
1406 		}
1407 
1408 		intperm = atoi(perm);
1409 		if (intperm < 0000 || intperm > 4777) {
1410 			(void) fprintf(stderr, gettext(ERR_BAD_MODE), perm);
1411 			status = ERROR;
1412 		}
1413 	}
1414 
1415 	free(one_entry);
1416 	return (status);
1417 }
1418 
1419 
1420 /*
1421  * check each alias :
1422  *	alias list members separated by white space
1423  *	cannot exist as driver name in /etc/name_to_major
1424  *	cannot exist as driver or alias name in /etc/driver_aliases
1425  */
1426 int
1427 aliases_unique(char *aliases)
1428 {
1429 	char *current_head;
1430 	char *previous_head;
1431 	char *one_entry;
1432 	int i, len;
1433 	int is_unique;
1434 
1435 	len = strlen(aliases);
1436 
1437 	one_entry = calloc(len + 1, 1);
1438 	if (one_entry == NULL) {
1439 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1440 		return (ERROR);
1441 	}
1442 
1443 	previous_head = aliases;
1444 
1445 	do {
1446 		for (i = 0; i <= len; i++)
1447 			one_entry[i] = 0;
1448 
1449 		current_head = get_entry(previous_head, one_entry, ' ');
1450 		previous_head = current_head;
1451 
1452 		if ((unique_driver_name(one_entry, name_to_major,
1453 		    &is_unique)) == ERROR) {
1454 			free(one_entry);
1455 			return (ERROR);
1456 		}
1457 
1458 		if (is_unique != UNIQUE) {
1459 			(void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ),
1460 			    one_entry);
1461 			free(one_entry);
1462 			return (ERROR);
1463 		}
1464 
1465 		if (unique_drv_alias(one_entry) != NOERR) {
1466 			free(one_entry);
1467 			return (ERROR);
1468 		}
1469 
1470 		if (!is_token(one_entry)) {
1471 			(void) fprintf(stderr, gettext(ERR_BAD_TOK),
1472 			    "-i", one_entry);
1473 			free(one_entry);
1474 			return (ERROR);
1475 		}
1476 
1477 	} while (*current_head != '\0');
1478 
1479 	free(one_entry);
1480 
1481 	return (NOERR);
1482 
1483 }
1484 
1485 
1486 int
1487 update_driver_aliases(
1488 	char *driver_name,
1489 	char *aliases)
1490 {
1491 	/* make call to update the aliases file */
1492 	return (append_to_file(driver_name, aliases, driver_aliases, ' ', " "));
1493 
1494 }
1495 
1496 
1497 int
1498 unique_drv_alias(char *drv_alias)
1499 {
1500 	FILE *fp;
1501 	char drv[FILENAME_MAX + 1];
1502 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
1503 	char alias[FILENAME_MAX + 1];
1504 	int status = NOERR;
1505 
1506 	fp = fopen(driver_aliases, "r");
1507 
1508 	if (fp != NULL) {
1509 		while ((fgets(line, sizeof (line), fp) != 0) &&
1510 		    status != ERROR) {
1511 			/* cut off comments starting with '#' */
1512 			if ((cp = strchr(line, '#')) != NULL)
1513 				*cp = '\0';
1514 			/* ignore comment or blank lines */
1515 			if (is_blank(line))
1516 				continue;
1517 			/* sanity-check */
1518 			if (sscanf(line, "%s %s", drv, alias) != 2)
1519 				(void) fprintf(stderr, gettext(ERR_BAD_LINE),
1520 				    driver_aliases, line);
1521 
1522 			if ((strcmp(drv_alias, drv) == 0) ||
1523 			    (strcmp(drv_alias, alias) == 0)) {
1524 				(void) fprintf(stderr,
1525 				    gettext(ERR_ALIAS_IN_USE),
1526 				    drv_alias);
1527 				status = ERROR;
1528 			}
1529 		}
1530 		(void) fclose(fp);
1531 		return (status);
1532 	} else {
1533 		perror(NULL);
1534 		(void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
1535 		return (ERROR);
1536 	}
1537 
1538 }
1539 
1540 
1541 /*
1542  * search for driver_name in first field of file file_name
1543  * searching name_to_major and driver_aliases: name separated from rest of
1544  * line by blank
1545  * if there return
1546  * else return
1547  */
1548 int
1549 unique_driver_name(char *driver_name, char *file_name,
1550 	int *is_unique)
1551 {
1552 	int ret;
1553 
1554 	if ((ret = get_major_no(driver_name, file_name)) == ERROR) {
1555 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1556 		    file_name);
1557 	} else {
1558 		/* XXX */
1559 		/* check alias file for name collision */
1560 		if (unique_drv_alias(driver_name) == ERROR) {
1561 			ret = ERROR;
1562 		} else {
1563 			if (ret != UNIQUE)
1564 				*is_unique = NOT_UNIQUE;
1565 			else
1566 				*is_unique = ret;
1567 			ret = NOERR;
1568 		}
1569 	}
1570 	return (ret);
1571 }
1572 
1573 
1574 int
1575 check_space_within_quote(char *str)
1576 {
1577 	register int i;
1578 	register int len;
1579 	int quoted = 0;
1580 
1581 	len = strlen(str);
1582 	for (i = 0; i < len; i++, str++) {
1583 		if (*str == '"') {
1584 			if (quoted == 0)
1585 				quoted++;
1586 			else
1587 				quoted--;
1588 		} else if (*str == ' ' && quoted)
1589 			return (ERROR);
1590 	}
1591 
1592 	return (0);
1593 }
1594 
1595 
1596 /*
1597  * get major number
1598  * write driver_name major_num to name_to_major file
1599  * major_num returned in major_num
1600  * return success/failure
1601  */
1602 int
1603 update_name_to_major(char *driver_name, major_t *major_num, int server)
1604 {
1605 	char major[MAX_STR_MAJOR + 1];
1606 	struct stat buf;
1607 	char *num_list;
1608 	char drv_majnum_str[MAX_STR_MAJOR + 1];
1609 	int new_maj = -1;
1610 	int i, tmp = 0, is_unique, have_rem_n2m = 0;
1611 	int max_dev = 0;
1612 
1613 	/*
1614 	 * if driver_name already in rem_name_to_major
1615 	 * 	delete entry from rem_nam_to_major
1616 	 *	put entry into name_to_major
1617 	 */
1618 
1619 	if (stat(rem_name_to_major, &buf) == 0) {
1620 		have_rem_n2m = 1;
1621 	}
1622 
1623 	if (have_rem_n2m) {
1624 		if ((is_unique = get_major_no(driver_name, rem_name_to_major))
1625 		    == ERROR)
1626 			return (ERROR);
1627 
1628 		/*
1629 		 * found a match in rem_name_to_major
1630 		 */
1631 		if (is_unique != UNIQUE) {
1632 			char scratch[FILENAME_MAX];
1633 
1634 			/*
1635 			 * If there is a match in /etc/rem_name_to_major then
1636 			 * be paranoid: is that major number already in
1637 			 * /etc/name_to_major (potentially under another name)?
1638 			 */
1639 			if (get_driver_name(is_unique, name_to_major,
1640 			    scratch) != UNIQUE) {
1641 				/*
1642 				 * nuke the rem_name_to_major entry-- it
1643 				 * isn't helpful.
1644 				 */
1645 				(void) delete_entry(rem_name_to_major,
1646 				    driver_name, " ", NULL);
1647 			} else {
1648 				(void) snprintf(major, sizeof (major),
1649 				    "%d", is_unique);
1650 
1651 				if (append_to_file(driver_name, major,
1652 				    name_to_major, ' ', " ") == ERROR) {
1653 					(void) fprintf(stderr,
1654 					    gettext(ERR_NO_UPDATE),
1655 					    name_to_major);
1656 					return (ERROR);
1657 				}
1658 
1659 				if (delete_entry(rem_name_to_major,
1660 				    driver_name, " ", NULL) == ERROR) {
1661 					(void) fprintf(stderr,
1662 					    gettext(ERR_DEL_ENTRY), driver_name,
1663 					    rem_name_to_major);
1664 					return (ERROR);
1665 				}
1666 
1667 				/* found matching entry : no errors */
1668 				*major_num = is_unique;
1669 				return (NOERR);
1670 			}
1671 		}
1672 	}
1673 
1674 	/*
1675 	 * Bugid: 1264079
1676 	 * In a server case (with -b option), we can't use modctl() to find
1677 	 *    the maximum major number, we need to dig thru client's
1678 	 *    /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
1679 	 *
1680 	 * if (server)
1681 	 *    get maximum major number thru (rem_)name_to_major file on client
1682 	 * else
1683 	 *    get maximum major number allowable on current system using modctl
1684 	 */
1685 	if (server) {
1686 		max_dev = 0;
1687 		tmp = 0;
1688 
1689 		max_dev = get_max_major(name_to_major);
1690 
1691 		/* If rem_name_to_major exists, we need to check it too */
1692 		if (have_rem_n2m) {
1693 			tmp = get_max_major(rem_name_to_major);
1694 
1695 			/*
1696 			 * If name_to_major is missing, we can get max_dev from
1697 			 * /etc/rem_name_to_major.  If both missing, bail out!
1698 			 */
1699 			if ((max_dev == ERROR) && (tmp == ERROR)) {
1700 				(void) fprintf(stderr,
1701 					gettext(ERR_CANT_ACCESS_FILE),
1702 					name_to_major);
1703 				return (ERROR);
1704 			}
1705 
1706 			/* guard against bigger maj_num in rem_name_to_major */
1707 			if (tmp > max_dev)
1708 				max_dev = tmp;
1709 		} else {
1710 			/*
1711 			 * If we can't get major from name_to_major file
1712 			 * and there is no /etc/rem_name_to_major file,
1713 			 * then we don't have a max_dev, bail out quick!
1714 			 */
1715 			if (max_dev == ERROR)
1716 				return (ERROR);
1717 		}
1718 
1719 		/*
1720 		 * In case there is no more slack in current name_to_major
1721 		 * table, provide at least 1 extra entry so the add_drv can
1722 		 * succeed.  Since only one add_drv process is allowed at one
1723 		 * time, and hence max_dev will be re-calculated each time
1724 		 * add_drv is ran, we don't need to worry about adding more
1725 		 * than 1 extra slot for max_dev.
1726 		 */
1727 		max_dev++;
1728 
1729 	} else {
1730 		if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
1731 			perror(NULL);
1732 			(void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
1733 			return (ERROR);
1734 		}
1735 	}
1736 
1737 	/*
1738 	 * max_dev is really how many slots the kernel has allocated for
1739 	 * devices... [0 , maxdev-1], not the largest available device num.
1740 	 */
1741 	if ((num_list = calloc(max_dev, 1)) == NULL) {
1742 		(void) fprintf(stderr, gettext(ERR_NO_MEM));
1743 		return (ERROR);
1744 	}
1745 
1746 	/*
1747 	 * Populate the num_list array
1748 	 */
1749 	if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) {
1750 		return (ERROR);
1751 	}
1752 	if (have_rem_n2m) {
1753 		if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0)
1754 			return (ERROR);
1755 	}
1756 
1757 	/* find first free major number */
1758 	for (i = 0; i < max_dev; i++) {
1759 		if (num_list[i] != 1) {
1760 			new_maj = i;
1761 			break;
1762 		}
1763 	}
1764 
1765 	if (new_maj == -1) {
1766 		(void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR));
1767 		return (ERROR);
1768 	}
1769 
1770 	(void) sprintf(drv_majnum_str, "%d", new_maj);
1771 	if (do_the_update(driver_name, drv_majnum_str) == ERROR) {
1772 		return (ERROR);
1773 	}
1774 
1775 	*major_num = new_maj;
1776 	return (NOERR);
1777 }
1778 
1779 
1780 int
1781 fill_n2m_array(char *filename, char **array, int *nelems)
1782 {
1783 	FILE *fp;
1784 	char line[MAX_N2M_ALIAS_LINE + 1], *cp;
1785 	char drv[FILENAME_MAX + 1];
1786 	u_longlong_t dnum;
1787 	major_t drv_majnum;
1788 
1789 	/*
1790 	 * Read through the file, marking each major number found
1791 	 * order is not relevant
1792 	 */
1793 	if ((fp = fopen(filename, "r")) == NULL) {
1794 		perror(NULL);
1795 		(void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename);
1796 		return (ERROR);
1797 	}
1798 
1799 	while (fgets(line, sizeof (line), fp) != 0) {
1800 		/* cut off comments starting with '#' */
1801 		if ((cp = strchr(line, '#')) != NULL)
1802 			*cp = '\0';
1803 		/* ignore comment or blank lines */
1804 		if (is_blank(line))
1805 			continue;
1806 		/* sanity-check */
1807 		if (sscanf(line, "%s %llu", drv, &dnum) != 2) {
1808 			(void) fprintf(stderr, gettext(ERR_BAD_LINE),
1809 			    filename, line);
1810 			(void) fclose(fp);
1811 			return (ERROR);
1812 		}
1813 
1814 		if (dnum > L_MAXMAJ32) {
1815 			(void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv,
1816 			    dnum, filename, L_MAXMAJ32);
1817 			continue;
1818 		}
1819 		/*
1820 		 * cast down to a major_t; we can be sure this is safe because
1821 		 * of the above range-check.
1822 		 */
1823 		drv_majnum = (major_t)dnum;
1824 
1825 		if (drv_majnum >= *nelems) {
1826 			/*
1827 			 * Allocate some more space, up to drv_majnum + 1 so
1828 			 * we can accomodate 0 through drv_majnum.
1829 			 *
1830 			 * Note that in the failure case, we leak all of the
1831 			 * old contents of array.  It's ok, since we just
1832 			 * wind up exiting immediately anyway.
1833 			 */
1834 			*nelems = drv_majnum + 1;
1835 			*array = realloc(*array, *nelems);
1836 			if (*array == NULL) {
1837 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
1838 				return (ERROR);
1839 			}
1840 		}
1841 		(*array)[drv_majnum] = 1;
1842 	}
1843 
1844 	(void) fclose(fp);
1845 	return (0);
1846 }
1847 
1848 
1849 int
1850 do_the_update(char *driver_name, char *major_number)
1851 {
1852 	return (append_to_file(driver_name, major_number, name_to_major,
1853 	    ' ', " "));
1854 }
1855 
1856 /*
1857  * is_blank() returns 1 (true) if a line specified is composed of
1858  * whitespace characters only. otherwise, it returns 0 (false).
1859  *
1860  * Note. the argument (line) must be null-terminated.
1861  */
1862 static int
1863 is_blank(char *line)
1864 {
1865 	for (/* nothing */; *line != '\0'; line++)
1866 		if (!isspace(*line))
1867 			return (0);
1868 	return (1);
1869 }
1870