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