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