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