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