Merge commit 'b1e7e97d3b60469b243b3b2e22c7d8cbd11c7c90'
[unleashed.git] / usr / src / cmd / modload / drvsubr.c
blob42d3f1cfaf8003fe42a56933e14ee9e65b6b02f8
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #include <unistd.h>
34 #include <sys/sysmacros.h>
35 #include <libintl.h>
36 #include <wait.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <signal.h>
42 #include <sys/buf.h>
43 #include <sys/stat.h>
44 #include <grp.h>
45 #include "addrem.h"
46 #include "errmsg.h"
47 #include "plcysubr.h"
50 * Macros to produce a quoted string containing the value of a
51 * preprocessor macro. For example, if SIZE is defined to be 256,
52 * VAL2STR(SIZE) is "256". This is used to construct format
53 * strings for scanf-family functions below.
54 * Note: For format string use, the argument to VAL2STR() must
55 * be a numeric constant that is one less than the size of the
56 * corresponding data buffer.
58 #define VAL2STR_QUOTE(x) #x
59 #define VAL2STR(x) VAL2STR_QUOTE(x)
62 * Convenience macro to determine if a character is a quote
64 #define isquote(c) (((c) == '"') || ((c) == '\''))
67 static char *add_rem_lock; /* lock file */
68 static int add_rem_lock_fd = -1;
70 static int get_cached_n_to_m_file(char *filename, char ***cache);
71 static int get_name_to_major_entry(int *major_no, char *driver_name,
72 char *file_name);
74 static int is_blank(char *);
76 /*ARGSUSED*/
77 void
78 log_minorperm_error(minorperm_err_t err, int key)
80 switch (err) {
81 case MP_FOPEN_ERR:
82 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
83 MINOR_PERM_FILE);
84 break;
85 case MP_FCLOSE_ERR:
86 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
87 MINOR_PERM_FILE);
88 (void) fprintf(stderr, gettext(ERR_NO_MEM));
89 break;
90 case MP_IGNORING_LINE_ERR:
91 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
92 MINOR_PERM_FILE);
93 break;
94 case MP_ALLOC_ERR:
95 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
96 MINOR_PERM_FILE);
97 (void) fprintf(stderr, gettext(ERR_NO_MEM));
98 break;
99 case MP_NVLIST_ERR:
100 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
101 MINOR_PERM_FILE);
102 (void) fprintf(stderr, gettext(ERR_NO_MEM));
103 break;
104 case MP_CANT_FIND_USER_ERR:
105 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
106 MINOR_PERM_FILE);
107 break;
108 case MP_CANT_FIND_GROUP_ERR:
109 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
110 MINOR_PERM_FILE);
111 break;
116 * open file
117 * for each entry in list
118 * where list entries are separated by <list_separator>
119 * append entry : driver_name <entry_separator> entry
120 * close file
121 * return error/noerr
124 append_to_file(
125 char *driver_name,
126 char *entry_list,
127 char *filename,
128 char list_separator,
129 char *entry_separator,
130 int quoted)
132 int len, line_len;
133 int fpint;
134 char *current_head, *previous_head;
135 char *line, *one_entry;
136 FILE *fp;
138 if ((fp = fopen(filename, "a")) == NULL) {
139 perror(NULL);
140 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
141 filename);
142 return (ERROR);
145 len = strlen(entry_list);
147 one_entry = calloc(len + 1, 1);
148 if (one_entry == NULL) {
149 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
150 (void) fprintf(stderr, gettext(ERR_NO_MEM));
151 (void) fclose(fp);
152 return (ERROR);
155 previous_head = entry_list;
157 line_len = strlen(driver_name) + len + 4;
158 if (quoted)
159 line_len += 2;
161 line = calloc(line_len, 1);
162 if (line == NULL) {
163 (void) fprintf(stderr, gettext(ERR_NO_MEM));
164 (void) fclose(fp);
165 err_exit();
169 * get one entry at a time from list and append to <filename> file
172 do {
173 bzero(one_entry, len + 1);
174 bzero(line, line_len);
176 current_head = get_entry(previous_head, one_entry,
177 list_separator, quoted);
178 previous_head = current_head;
180 (void) snprintf(line, line_len,
181 quoted ? "%s%s\"%s\"\n" : "%s%s%s\n",
182 driver_name, entry_separator, one_entry);
184 if ((fputs(line, fp)) == EOF) {
185 perror(NULL);
186 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
187 filename);
190 } while (*current_head != '\0');
193 (void) fflush(fp);
195 fpint = fileno(fp);
196 (void) fsync(fpint);
198 (void) fclose(fp);
200 free(one_entry);
201 free(line);
203 return (NOERR);
207 * open file
208 * for each entry in list
209 * where list entries are separated by <list_separator>
210 * append entry : driver_name <entry_separator> entry
211 * close file
212 * return error/noerr
215 append_to_minor_perm(
216 char *driver_name,
217 char *entry_list,
218 char *filename)
220 int len, line_len;
221 int fpint;
222 char *current_head, *previous_head;
223 char *line, *one_entry;
224 FILE *fp;
226 if ((fp = fopen(filename, "a")) == NULL) {
227 perror(NULL);
228 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
229 filename);
230 return (ERROR);
233 len = strlen(entry_list);
235 one_entry = calloc(len + 1, 1);
236 if (one_entry == NULL) {
237 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
238 (void) fprintf(stderr, gettext(ERR_NO_MEM));
239 (void) fclose(fp);
240 return (ERROR);
243 previous_head = entry_list;
245 line_len = strlen(driver_name) + len + 4;
246 line = calloc(line_len, 1);
247 if (line == NULL) {
248 (void) fprintf(stderr, gettext(ERR_NO_MEM));
249 (void) fclose(fp);
250 err_exit();
254 * get one entry at a time from list and append to <filename> file
256 do {
257 bzero(one_entry, len + 1);
258 bzero(line, line_len);
260 current_head = get_perm_entry(previous_head, one_entry);
261 previous_head = current_head;
263 (void) snprintf(line, line_len, "%s:%s\n",
264 driver_name, one_entry);
266 if ((fputs(line, fp)) == EOF) {
267 perror(NULL);
268 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
269 filename);
272 } while (*current_head != '\0');
275 (void) fflush(fp);
277 fpint = fileno(fp);
278 (void) fsync(fpint);
280 (void) fclose(fp);
282 free(one_entry);
283 free(line);
285 return (NOERR);
289 * Require exact match to delete a driver alias/permission entry.
290 * Note line argument does not remain unchanged. Return 1 if matched.
292 static int
293 match_entry(char *line, char *match)
295 char *token, *p;
296 int n;
298 /* skip any leading white space */
299 while (*line && isspace(*line))
300 line++;
302 * Find separator for driver name, either space or colon
303 * minor_perm: <driver>:<perm>
304 * driver_aliases: <driver> <alias>
305 * extra_privs: <driver>:<priv>
307 if ((token = strpbrk(line, " :\t")) == NULL)
308 return (0);
309 token++;
310 /* skip leading white space and quotes */
311 while (*token && (isspace(*token) || isquote(*token)))
312 token++;
313 /* strip trailing newline, white space and quotes */
314 n = strlen(token);
315 p = token + n-1;
316 while (n > 0 && (*p == '\n' || isspace(*p) || isquote(*p))) {
317 *p-- = 0;
318 n--;
320 if (n == 0)
321 return (0);
322 return (strcmp(token, match) == 0);
326 * open file
327 * read thru file, deleting all entries if first
328 * entry = driver_name
329 * close
330 * if error, leave original file intact with message
331 * assumption : drvconfig has been modified to work with clone
332 * entries in /etc/minor_perm as driver:mummble NOT
333 * clone:driver mummble
334 * this implementation will NOT find clone entries
335 * clone:driver mummble
336 * match:
337 * delete just the matching entry
341 delete_entry(
342 char *oldfile,
343 char *driver_name,
344 char *marker,
345 char *match)
347 int rv, i;
348 int status = NOERR;
349 int drvr_found = 0;
350 boolean_t nomatch = B_TRUE;
351 char newfile[MAXPATHLEN];
352 char *cp;
353 char line[MAX_DBFILE_ENTRY];
354 char drv[FILENAME_MAX + 1];
355 FILE *fp, *newfp;
356 struct group *sysgrp;
357 int newfd;
358 char *copy; /* same size as line */
359 char *match2 = NULL; /* match with quotes cleaned up */
362 * if match is specified, sanity check it and clean it
363 * up by removing surrounding quotes as we require
364 * an exact match.
366 if (match) {
367 cp = match;
368 while (*cp && (isspace(*cp)))
369 cp++;
370 i = strlen(cp);
371 if (i > 0) {
372 if ((match2 = strdup(cp)) == NULL) {
373 perror(NULL);
374 (void) fprintf(stderr, gettext(ERR_NO_MEM));
375 return (ERROR);
377 i = strlen(match2) - 1;
378 while (i >= 0 && (isspace(match2[i]))) {
379 match2[i] = 0;
380 i--;
383 if (match2 == NULL || (strlen(match2) == 0)) {
384 (void) fprintf(stderr,
385 gettext(ERR_INT_UPDATE), oldfile);
386 return (ERROR);
390 if ((fp = fopen(oldfile, "r")) == NULL) {
391 perror(NULL);
392 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
393 return (ERROR);
396 /* Space for defensive copy of input line */
397 if ((copy = calloc(sizeof (line), 1)) == NULL) {
398 perror(NULL);
399 (void) fprintf(stderr, gettext(ERR_NO_MEM));
400 return (ERROR);
403 /* Build filename for temporary file */
404 (void) snprintf(newfile, sizeof (newfile), "%s%s", oldfile, ".hold");
407 * Set gid so we preserve group attribute. Ideally we wouldn't
408 * assume a gid of "sys" but we can't undo the damage on already
409 * installed systems unless we force the issue.
411 if ((sysgrp = getgrnam("sys")) != NULL) {
412 (void) setgid(sysgrp->gr_gid);
415 if ((newfd = open(newfile, O_WRONLY | O_CREAT | O_EXCL, 0644)) < 0) {
416 if (errno == EEXIST) {
417 (void) fprintf(stderr, gettext(ERR_FILE_EXISTS),
418 newfile);
419 return (ERROR);
420 } else {
421 (void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
422 newfile);
423 return (ERROR);
427 if ((newfp = fdopen(newfd, "w")) == NULL) {
428 perror(NULL);
429 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
430 newfile);
431 (void) close(newfd);
432 return (ERROR);
435 while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
436 /* copy the whole line */
437 if (strlcpy(copy, line, sizeof (line)) >= sizeof (line)) {
438 (void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
439 status = ERROR;
440 break;
442 /* cut off comments starting with '#' */
443 if ((cp = strchr(copy, '#')) != NULL)
444 *cp = '\0';
445 /* ignore comment or blank lines */
446 if (is_blank(copy)) {
447 if (fputs(line, newfp) == EOF) {
448 (void) fprintf(stderr, gettext(ERR_UPDATE),
449 oldfile);
450 status = ERROR;
452 continue;
455 /* get the driver name */
456 if (sscanf(copy, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
457 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
458 oldfile, line);
459 status = ERROR;
460 break;
463 for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
464 drv[i] = '\0';
467 if (strcmp(driver_name, drv) != 0) {
468 if ((fputs(line, newfp)) == EOF) {
469 (void) fprintf(stderr, gettext(ERR_UPDATE),
470 oldfile);
471 status = ERROR;
473 } else {
474 drvr_found++;
475 if (match2) { /* Just delete one entry */
476 /* for now delete just minor_perm and aliases */
477 if ((strcmp(oldfile, minor_perm) == 0) ||
478 (strcmp(oldfile, extra_privs) == 0) ||
479 (strcmp(oldfile, driver_aliases) == 0)) {
481 /* make defensive copy */
482 if (strlcpy(copy, line, sizeof (line))
483 >= sizeof (line)) {
484 (void) fprintf(stderr,
485 gettext(ERR_UPDATE),
486 oldfile);
487 status = ERROR;
488 break;
490 if (match_entry(copy, match2)) {
491 nomatch = B_FALSE;
492 } else {
493 if ((fputs(line, newfp)) ==
494 EOF) {
495 (void) fprintf(stderr,
496 gettext(ERR_UPDATE),
497 oldfile);
498 status = ERROR;
500 if (nomatch != B_FALSE)
501 nomatch = B_TRUE;
506 } /* end of else */
507 } /* end of while */
509 (void) fclose(fp);
510 free(copy);
511 free(match2);
513 /* Make sure that the file is on disk */
514 if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
515 status = ERROR;
516 else
517 rv = NOERR;
519 (void) fclose(newfp);
521 /* no matching driver found */
522 rv = NOERR;
523 if (!drvr_found ||
524 (nomatch == B_TRUE)) {
525 rv = NONE_FOUND;
529 * if error, leave original file, delete new file
530 * if noerr, replace original file with new file
533 if (status == NOERR) {
534 if (rename(newfile, oldfile) == -1) {
535 perror(NULL);
536 (void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
537 (void) unlink(newfile);
538 return (ERROR);
540 } else {
542 * since there's an error, leave file alone; remove
543 * new file
545 if (unlink(newfile) == -1) {
546 (void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
548 return (ERROR);
551 return (rv);
556 * wrapper for call to get_name_to_major_entry(): given driver name,
557 * retrieve major number.
560 get_major_no(char *driver_name, char *file_name)
562 int major = UNIQUE;
564 if (get_name_to_major_entry(&major, driver_name, file_name) == ERROR)
565 return (ERROR);
566 else
567 return (major);
571 * wrapper for call to get_name_to_major_entry(): given major number,
572 * retrieve driver name.
575 get_driver_name(int major, char *file_name, char *buf)
577 if (major < 0)
578 return (ERROR);
579 return (get_name_to_major_entry(&major, buf, file_name));
584 * return pointer to cached name_to_major file - reads file into
585 * cache if this has not already been done. Since there may be
586 * requests for multiple name_to_major files (rem_name_to_major,
587 * name_to_major), this routine keeps a list of cached files.
589 static int
590 get_cached_n_to_m_file(char *filename, char ***cache)
592 struct n_to_m_cache {
593 char *file;
594 char **cached_file;
595 int size;
596 struct n_to_m_cache *next;
598 static struct n_to_m_cache *head = NULL;
599 struct n_to_m_cache *ptr;
600 FILE *fp;
601 char drv[FILENAME_MAX + 1];
602 char entry[FILENAME_MAX + 1];
603 char line[MAX_N2M_ALIAS_LINE], *cp;
604 int maj;
605 int size = 0;
609 * see if the file is already cached - either
610 * rem_name_to_major or name_to_major
612 ptr = head;
613 while (ptr != NULL) {
614 if (strcmp(ptr->file, filename) == 0)
615 break;
616 ptr = ptr->next;
619 if (ptr == NULL) { /* we need to cache the contents */
620 if ((fp = fopen(filename, "r")) == NULL) {
621 perror(NULL);
622 (void) fprintf(stderr, gettext(ERR_CANT_OPEN),
623 filename);
624 return (ERROR);
627 while (fgets(line, sizeof (line), fp) != NULL) {
628 /* cut off comments starting with '#' */
629 if ((cp = strchr(line, '#')) != NULL)
630 *cp = '\0';
631 /* ignore comment or blank lines */
632 if (is_blank(line))
633 continue;
634 /* sanity-check */
635 if (sscanf(line,
636 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
637 "%" VAL2STR(FILENAME_MAX) "s", /* entry */
638 drv, entry) != 2) {
639 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
640 filename, line);
641 continue;
643 maj = atoi(entry);
644 if (maj > size)
645 size = maj;
648 /* allocate struct to cache the file */
649 ptr = (struct n_to_m_cache *)calloc(1,
650 sizeof (struct n_to_m_cache));
651 if (ptr == NULL) {
652 (void) fprintf(stderr, gettext(ERR_NO_MEM));
653 return (ERROR);
655 ptr->size = size + 1;
656 /* allocate space to cache contents of file */
657 ptr->cached_file = (char **)calloc(ptr->size, sizeof (char *));
658 if (ptr->cached_file == NULL) {
659 free(ptr);
660 (void) fprintf(stderr, gettext(ERR_NO_MEM));
661 return (ERROR);
664 rewind(fp);
667 * now fill the cache
668 * the cache is an array of char pointers indexed by major
669 * number
671 while (fgets(line, sizeof (line), fp) != NULL) {
672 /* cut off comments starting with '#' */
673 if ((cp = strchr(line, '#')) != NULL)
674 *cp = '\0';
675 /* ignore comment or blank lines */
676 if (is_blank(line))
677 continue;
678 /* sanity-check */
679 if (sscanf(line,
680 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
681 "%" VAL2STR(FILENAME_MAX) "s", /* entry */
682 drv, entry) != 2) {
683 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
684 filename, line);
685 continue;
687 maj = atoi(entry);
688 if ((ptr->cached_file[maj] = strdup(drv)) == NULL) {
689 (void) fprintf(stderr, gettext(ERR_NO_MEM));
690 free(ptr->cached_file);
691 free(ptr);
692 return (ERROR);
694 (void) strcpy(ptr->cached_file[maj], drv);
696 (void) fclose(fp);
697 /* link the cache struct into the list of cached files */
698 ptr->file = strdup(filename);
699 if (ptr->file == NULL) {
700 for (maj = 0; maj <= ptr->size; maj++)
701 free(ptr->cached_file[maj]);
702 free(ptr->cached_file);
703 free(ptr);
704 (void) fprintf(stderr, gettext(ERR_NO_MEM));
705 return (ERROR);
707 ptr->next = head;
708 head = ptr;
710 /* return value pointer to contents of file */
711 *cache = ptr->cached_file;
713 /* return size */
714 return (ptr->size);
719 * Using get_cached_n_to_m_file(), retrieve maximum major number
720 * found in the specificed file (name_to_major/rem_name_to_major).
722 * The return value is actually the size of the internal cache including 0.
725 get_max_major(char *file_name)
727 char **n_to_m_cache = NULL;
729 return (get_cached_n_to_m_file(file_name, &n_to_m_cache));
734 * searching name_to_major: if major_no == UNIQUE then the caller wants to
735 * use the driver name as the key. Otherwise, the caller wants to use
736 * the major number as a key.
738 * This routine caches the contents of the name_to_major file on
739 * first call. And it could be generalized to deal with other
740 * config files if necessary.
742 static int
743 get_name_to_major_entry(int *major_no, char *driver_name, char *file_name)
745 int maj;
746 char **n_to_m_cache = NULL;
747 int size = 0;
749 int ret = NOT_UNIQUE;
752 * read the file in - we cache it in case caller wants to
753 * do multiple lookups
755 size = get_cached_n_to_m_file(file_name, &n_to_m_cache);
757 if (size == ERROR)
758 return (ERROR);
760 /* search with driver name as key */
761 if (*major_no == UNIQUE) {
762 for (maj = 0; maj < size; maj++) {
763 if ((n_to_m_cache[maj] != NULL) &&
764 (strcmp(driver_name, n_to_m_cache[maj]) == 0)) {
765 *major_no = maj;
766 break;
769 if (maj >= size)
770 ret = UNIQUE;
771 /* search with major number as key */
772 } else {
774 * Bugid 1254588, drvconfig dump core after loading driver
775 * with major number bigger than entries defined in
776 * /etc/name_to_major.
778 if (*major_no >= size)
779 return (UNIQUE);
781 if (n_to_m_cache[*major_no] != NULL) {
782 (void) strcpy(driver_name, n_to_m_cache[*major_no]);
783 } else
784 ret = UNIQUE;
786 return (ret);
790 * Given pointer to begining of member 'n' in a space (or separator)
791 * separated list, return pointer to member 'n+1', and establish member 'n'
792 * in *current_entry. If unquote, then we skip a leading quote and treat
793 * the trailing quote as a separator (and skip).
795 char *
796 get_entry(
797 char *prev_member,
798 char *current_entry,
799 char separator,
800 int unquote)
802 char *ptr;
803 int quoted = 0;
805 ptr = prev_member;
807 /* skip white space */
808 while (isspace(*ptr))
809 ptr++;
811 /* if unquote skip leading quote */
812 if (unquote && *ptr == '"') {
813 quoted++;
814 ptr++;
817 /* read thru the current entry looking for end, separator, or unquote */
818 while (*ptr &&
819 (*ptr != separator) && (!isspace(*ptr)) &&
820 (!quoted || (*ptr != '"'))) {
821 *current_entry++ = *ptr++;
823 *current_entry = '\0';
825 if (separator && (*ptr == separator))
826 ptr++; /* skip over separator */
827 if (quoted && (*ptr == '"'))
828 ptr++; /* skip over trailing quote */
830 /* skip white space */
831 while (isspace(*ptr))
832 ptr++;
834 return (ptr);
838 * A parser specific to the add_drv "-m permission" syntax:
840 * -m '<minor-name> <permissions> <owner> <group>', ...
842 * One entry is parsed starting at prev_member and returned
843 * in the string pointed at by current_entry. A pointer
844 * to the entry following is returned.
846 char *
847 get_perm_entry(
848 char *prev_member,
849 char *current_entry)
851 char *ptr;
852 int nfields = 0;
853 int maxfields = 4; /* fields in a permissions format */
855 ptr = prev_member;
856 while (isspace(*ptr))
857 ptr++;
859 while (*ptr) {
860 /* comma allowed in minor name token only */
861 if (*ptr == ',' && nfields > 0) {
862 break;
863 } else if (isspace(*ptr)) {
864 *current_entry++ = *ptr++;
865 while (isspace(*ptr))
866 ptr++;
867 if (++nfields == maxfields)
868 break;
869 } else
870 *current_entry++ = *ptr++;
872 *current_entry = '\0';
874 while (isspace(*ptr))
875 ptr++;
876 if (*ptr == ',') {
877 ptr++; /* skip over optional trailing comma */
879 while (isspace(*ptr))
880 ptr++;
882 return (ptr);
885 void
886 enter_lock(void)
888 struct flock lock;
891 * Attempt to create the lock file. Open the file itself,
892 * and not a symlink to some other file.
894 add_rem_lock_fd = open(add_rem_lock,
895 O_CREAT|O_RDWR|O_NOFOLLOW|O_NOLINKS, S_IRUSR|S_IWUSR);
896 if (add_rem_lock_fd < 0) {
897 (void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
898 add_rem_lock, strerror(errno));
899 exit(1);
902 lock.l_type = F_WRLCK;
903 lock.l_whence = SEEK_SET;
904 lock.l_start = 0;
905 lock.l_len = 0;
907 /* Try for the lock but don't wait. */
908 if (fcntl(add_rem_lock_fd, F_SETLK, &lock) == -1) {
909 if (errno == EACCES || errno == EAGAIN) {
910 (void) fprintf(stderr, gettext(ERR_PROG_IN_USE));
911 } else {
912 (void) fprintf(stderr, gettext(ERR_LOCK),
913 add_rem_lock, strerror(errno));
915 exit(1);
919 void
920 err_exit(void)
922 /* release memory allocated for moddir */
923 cleanup_moddir();
924 /* remove add_drv/rem_drv lock */
925 exit_unlock();
926 exit(1);
929 void
930 cleanup_moddir(void)
932 struct drvmod_dir *walk_ptr;
933 struct drvmod_dir *free_ptr = moddir;
935 while (free_ptr != NULL) {
936 walk_ptr = free_ptr->next;
937 free(free_ptr);
938 free_ptr = walk_ptr;
942 void
943 exit_unlock(void)
945 struct flock unlock;
947 if (add_rem_lock_fd < 0)
948 return;
950 unlock.l_type = F_UNLCK;
951 unlock.l_whence = SEEK_SET;
952 unlock.l_start = 0;
953 unlock.l_len = 0;
955 if (fcntl(add_rem_lock_fd, F_SETLK, &unlock) == -1) {
956 (void) fprintf(stderr, gettext(ERR_UNLOCK),
957 add_rem_lock, strerror(errno));
958 } else {
959 (void) close(add_rem_lock_fd);
960 add_rem_lock_fd = -1;
965 * error adding driver; need to back out any changes to files.
966 * check flag to see which files need entries removed
967 * entry removal based on driver name
969 void
970 remove_entry(
971 int c_flag,
972 char *driver_name)
975 if (c_flag & CLEAN_NAM_MAJ) {
976 if (delete_entry(name_to_major, driver_name, " ",
977 NULL) == ERROR) {
978 (void) fprintf(stderr, gettext(ERR_NO_CLEAN),
979 name_to_major, driver_name);
983 if (c_flag & CLEAN_DRV_ALIAS) {
984 if (delete_entry(driver_aliases, driver_name, " ",
985 NULL) == ERROR) {
986 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
987 driver_name, driver_aliases);
991 if (c_flag & CLEAN_DRV_CLASSES) {
992 if (delete_entry(driver_classes, driver_name, "\t", NULL) ==
993 ERROR) {
994 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
995 driver_name, driver_classes);
999 if (c_flag & CLEAN_MINOR_PERM) {
1000 if (delete_entry(minor_perm, driver_name, ":", NULL) == ERROR) {
1001 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1002 driver_name, minor_perm);
1006 * There's no point in removing entries from files that don't
1007 * exist. Prevent error messages by checking for file existence
1008 * first.
1010 if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
1011 access(device_policy, F_OK) == 0) {
1012 if (delete_plcy_entry(device_policy, driver_name) == ERROR) {
1013 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1014 driver_name, device_policy);
1017 if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
1018 access(extra_privs, F_OK) == 0) {
1019 if (delete_entry(extra_privs, driver_name, ":", NULL) ==
1020 ERROR) {
1021 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1022 driver_name, extra_privs);
1028 check_perms_aliases(
1029 int m_flag,
1030 int i_flag)
1033 * If neither i_flag nor m_flag are specified no need to check the
1034 * files for access permissions
1036 if (!m_flag && !i_flag)
1037 return (NOERR);
1039 /* check minor_perm file : exits and is writable */
1040 if (m_flag) {
1041 if (access(minor_perm, R_OK | W_OK)) {
1042 perror(NULL);
1043 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1044 minor_perm);
1045 return (ERROR);
1049 /* check driver_aliases file : exits and is writable */
1050 if (i_flag) {
1051 if (access(driver_aliases, R_OK | W_OK)) {
1052 perror(NULL);
1053 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1054 driver_aliases);
1055 return (ERROR);
1059 return (NOERR);
1064 check_name_to_major(int mode)
1066 /* check name_to_major file : exists and is writable */
1067 if (access(name_to_major, mode)) {
1068 perror(NULL);
1069 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1070 name_to_major);
1071 return (ERROR);
1074 return (NOERR);
1079 * All this stuff is to support a server installing
1080 * drivers on diskless clients. When on the server
1081 * need to prepend the basedir
1084 build_filenames(char *basedir)
1086 int len;
1087 int driver_aliases_len;
1088 int driver_classes_len;
1089 int minor_perm_len;
1090 int name_to_major_len;
1091 int rem_name_to_major_len;
1092 int add_rem_lock_len;
1093 int devfs_root_len;
1094 int device_policy_len;
1095 int extra_privs_len;
1097 if (basedir == NULL) {
1098 driver_aliases = DRIVER_ALIAS;
1099 driver_classes = DRIVER_CLASSES;
1100 minor_perm = MINOR_PERM;
1101 name_to_major = NAM_TO_MAJ;
1102 rem_name_to_major = REM_NAM_TO_MAJ;
1103 add_rem_lock = ADD_REM_LOCK;
1104 devfs_root = DEVFS_ROOT;
1105 device_policy = DEV_POLICY;
1106 extra_privs = EXTRA_PRIVS;
1108 } else {
1109 len = strlen(basedir) + 1;
1111 driver_aliases_len = len + sizeof (DRIVER_ALIAS);
1112 driver_classes_len = len + sizeof (DRIVER_CLASSES);
1113 minor_perm_len = len + sizeof (MINOR_PERM);
1114 name_to_major_len = len + sizeof (NAM_TO_MAJ);
1115 rem_name_to_major_len = len + sizeof (REM_NAM_TO_MAJ);
1116 add_rem_lock_len = len + sizeof (ADD_REM_LOCK);
1117 devfs_root_len = len + sizeof (DEVFS_ROOT);
1118 device_policy_len = len + sizeof (DEV_POLICY);
1119 extra_privs_len = len + sizeof (EXTRA_PRIVS);
1121 driver_aliases = malloc(driver_aliases_len);
1122 driver_classes = malloc(driver_classes_len);
1123 minor_perm = malloc(minor_perm_len);
1124 name_to_major = malloc(name_to_major_len);
1125 rem_name_to_major = malloc(rem_name_to_major_len);
1126 add_rem_lock = malloc(add_rem_lock_len);
1127 devfs_root = malloc(devfs_root_len);
1128 device_policy = malloc(device_policy_len);
1129 extra_privs = malloc(extra_privs_len);
1131 if ((driver_aliases == NULL) ||
1132 (driver_classes == NULL) ||
1133 (minor_perm == NULL) ||
1134 (name_to_major == NULL) ||
1135 (rem_name_to_major == NULL) ||
1136 (add_rem_lock == NULL) ||
1137 (devfs_root == NULL) ||
1138 (device_policy == NULL) ||
1139 (extra_privs == NULL)) {
1140 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1141 return (ERROR);
1144 (void) snprintf(driver_aliases, driver_aliases_len,
1145 "%s%s", basedir, DRIVER_ALIAS);
1146 (void) snprintf(driver_classes, driver_classes_len,
1147 "%s%s", basedir, DRIVER_CLASSES);
1148 (void) snprintf(minor_perm, minor_perm_len,
1149 "%s%s", basedir, MINOR_PERM);
1150 (void) snprintf(name_to_major, name_to_major_len,
1151 "%s%s", basedir, NAM_TO_MAJ);
1152 (void) snprintf(rem_name_to_major, rem_name_to_major_len,
1153 "%s%s", basedir, REM_NAM_TO_MAJ);
1154 (void) snprintf(add_rem_lock, add_rem_lock_len,
1155 "%s%s", basedir, ADD_REM_LOCK);
1156 (void) snprintf(devfs_root, devfs_root_len,
1157 "%s%s", basedir, DEVFS_ROOT);
1158 (void) snprintf(device_policy, device_policy_len,
1159 "%s%s", basedir, DEV_POLICY);
1160 (void) snprintf(extra_privs, extra_privs_len,
1161 "%s%s", basedir, EXTRA_PRIVS);
1164 return (NOERR);
1167 static int
1168 exec_command(char *path, char *cmdline[MAX_CMD_LINE])
1170 pid_t pid;
1171 uint_t stat_loc;
1172 int waitstat;
1173 int exit_status;
1175 /* child */
1176 if ((pid = fork()) == 0) {
1177 (void) execv(path, cmdline);
1178 perror(NULL);
1179 return (ERROR);
1180 } else if (pid == -1) {
1181 /* fork failed */
1182 perror(NULL);
1183 (void) fprintf(stderr, gettext(ERR_FORK_FAIL), cmdline);
1184 return (ERROR);
1185 } else {
1186 /* parent */
1187 do {
1188 waitstat = waitpid(pid, (int *)&stat_loc, 0);
1190 } while ((!WIFEXITED(stat_loc) &&
1191 !WIFSIGNALED(stat_loc)) || (waitstat == 0));
1193 exit_status = WEXITSTATUS(stat_loc);
1195 return (exit_status);
1200 * Exec devfsadm to perform driver config/unconfig operation,
1201 * adding or removing aliases.
1203 static int
1204 exec_devfsadm(
1205 boolean_t config,
1206 char *driver_name,
1207 major_t major_num,
1208 char *aliases,
1209 char *classes,
1210 int config_flags)
1212 int n = 0;
1213 char *cmdline[MAX_CMD_LINE];
1214 char maj_num[128];
1215 char *previous;
1216 char *current;
1217 int len;
1218 int rv;
1220 /* build command line */
1221 cmdline[n++] = DRVCONFIG;
1222 if (config == B_FALSE) {
1223 cmdline[n++] = "-u"; /* unconfigure */
1224 if (config_flags & CONFIG_DRV_FORCE)
1225 cmdline[n++] = "-f"; /* force if currently in use */
1227 if (config_flags & CONFIG_DRV_VERBOSE) {
1228 cmdline[n++] = "-v";
1230 cmdline[n++] = "-b";
1231 if (classes) {
1232 cmdline[n++] = "-c";
1233 cmdline[n++] = classes;
1235 cmdline[n++] = "-i";
1236 cmdline[n++] = driver_name;
1237 cmdline[n++] = "-m";
1238 (void) snprintf(maj_num, sizeof (maj_num), "%lu", major_num);
1239 cmdline[n++] = maj_num;
1240 if (config_flags & CONFIG_DRV_UPDATE_ONLY)
1241 cmdline[n++] = "-x";
1243 if (aliases != NULL) {
1244 len = strlen(aliases);
1245 previous = aliases;
1246 do {
1247 cmdline[n++] = "-a";
1248 cmdline[n] = calloc(len + 1, 1);
1249 if (cmdline[n] == NULL) {
1250 (void) fprintf(stderr,
1251 gettext(ERR_NO_MEM));
1252 return (ERROR);
1254 current = get_entry(previous,
1255 cmdline[n++], ' ', 0);
1256 previous = current;
1258 } while (*current != '\0');
1261 cmdline[n] = NULL;
1263 rv = exec_command(DRVCONFIG_PATH, cmdline);
1264 if (rv == NOERR)
1265 return (NOERR);
1266 return (ERROR);
1270 unconfig_driver(
1271 char *driver_name,
1272 major_t major_num,
1273 char *aliases,
1274 int config_flags)
1276 return (exec_devfsadm(B_FALSE, driver_name, major_num,
1277 aliases, NULL, config_flags));
1281 * check that major_num doesn't exceed maximum on this machine
1282 * do this here to support add_drv on server for diskless clients
1285 config_driver(
1286 char *driver_name,
1287 major_t major_num,
1288 char *aliases,
1289 char *classes,
1290 int cleanup_flag,
1291 int config_flags)
1293 int max_dev;
1294 int rv;
1296 if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
1297 perror(NULL);
1298 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
1299 return (ERROR);
1302 if (major_num >= max_dev) {
1303 (void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS),
1304 major_num, max_dev);
1305 return (ERROR);
1308 /* bind major number and driver name */
1309 rv = exec_devfsadm(B_TRUE, driver_name, major_num,
1310 aliases, classes, config_flags);
1312 if (rv == NOERR)
1313 return (NOERR);
1314 perror(NULL);
1315 remove_entry(cleanup_flag, driver_name);
1316 return (ERROR);
1319 void
1320 load_driver(char *driver_name, int verbose_flag)
1322 int n = 0;
1323 char *cmdline[MAX_CMD_LINE];
1324 int exec_status;
1326 /* build command line */
1327 cmdline[n++] = DEVFSADM;
1328 if (verbose_flag) {
1329 cmdline[n++] = "-v";
1331 cmdline[n++] = "-i";
1332 cmdline[n++] = driver_name;
1333 cmdline[n] = NULL;
1335 exec_status = exec_command(DEVFSADM_PATH, cmdline);
1337 if (exec_status != NOERR) {
1338 /* no clean : name and major number are bound */
1339 (void) fprintf(stderr, gettext(ERR_CONFIG), driver_name);
1343 void
1344 get_modid(char *driver_name, int *mod)
1346 struct modinfo modinfo;
1348 modinfo.mi_id = -1;
1349 modinfo.mi_info = MI_INFO_ALL;
1350 do {
1352 * If we are at the end of the list of loaded modules
1353 * then set *mod = -1 and return
1355 if (modctl(MODINFO, 0, &modinfo) < 0) {
1356 *mod = -1;
1357 return;
1360 *mod = modinfo.mi_id;
1361 } while (strcmp(driver_name, modinfo.mi_name) != 0);
1365 create_reconfig(char *basedir)
1367 char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1];
1368 FILE *reconfig_fp;
1370 if (basedir != NULL) {
1371 (void) strcpy(reconfig_file, basedir);
1372 (void) strcat(reconfig_file, RECONFIGURE);
1373 } else {
1374 (void) strcpy(reconfig_file, RECONFIGURE);
1376 if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL)
1377 return (ERROR);
1379 (void) fclose(reconfig_fp);
1380 return (NOERR);
1385 * update_minor_entry:
1386 * open file
1387 * for each entry in list
1388 * where list entries are separated by <list_separator>
1389 * modify entry : driver_name <entry_separator> entry
1390 * close file
1392 * return error/noerr
1395 update_minor_entry(char *driver_name, char *perm_list)
1397 FILE *fp;
1398 FILE *newfp;
1399 int match = 0;
1400 char line[MAX_DBFILE_ENTRY];
1401 char drv[FILENAME_MAX + 1];
1402 char minor[FILENAME_MAX + 1];
1403 char perm[OPT_LEN + 1];
1404 char own[OPT_LEN + 1];
1405 char grp[OPT_LEN + 1];
1406 int status = NOERR, i;
1407 char newfile[MAXPATHLEN];
1408 char *cp, *dup, *drv_minor;
1409 struct group *sysgrp;
1410 int newfd;
1412 if ((fp = fopen(minor_perm, "r")) == NULL) {
1413 perror(NULL);
1414 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1415 minor_perm);
1417 return (ERROR);
1420 /* Build filename for temporary file */
1421 (void) snprintf(newfile, sizeof (newfile), "%s%s", minor_perm, ".hold");
1424 * Set gid so we preserve group attribute. Ideally we wouldn't
1425 * assume a gid of "sys" but we can't undo the damage on already
1426 * installed systems unless we force the issue.
1428 if ((sysgrp = getgrnam("sys")) != NULL) {
1429 (void) setgid(sysgrp->gr_gid);
1432 if ((newfd = open(newfile, O_WRONLY | O_CREAT | O_EXCL, 0644)) < 0) {
1433 if (errno == EEXIST) {
1434 (void) fprintf(stderr, gettext(ERR_FILE_EXISTS),
1435 newfile);
1436 return (ERROR);
1437 } else {
1438 (void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
1439 newfile);
1440 return (ERROR);
1444 if ((newfp = fdopen(newfd, "w")) == NULL) {
1445 perror(NULL);
1446 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1447 newfile);
1448 (void) close(newfd);
1449 return (ERROR);
1452 if (sscanf(perm_list,
1453 "%" VAL2STR(FILENAME_MAX) "s" /* minor */
1454 "%" VAL2STR(OPT_LEN) "s" /* perm */
1455 "%" VAL2STR(OPT_LEN) "s" /* own */
1456 "%" VAL2STR(OPT_LEN) "s", /* grp */
1457 minor, perm, own, grp) != 4) {
1458 status = ERROR;
1461 while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
1462 /* copy the whole line into dup */
1463 if ((dup = strdup(line)) == NULL) {
1464 perror(NULL);
1465 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1466 status = ERROR;
1467 break;
1469 /* cut off comments starting with '#' */
1470 if ((cp = strchr(dup, '#')) != NULL)
1471 *cp = '\0';
1472 /* ignore comment or blank lines */
1473 if (is_blank(dup)) {
1474 if (fputs(line, newfp) == EOF) {
1475 (void) fprintf(stderr, gettext(ERR_UPDATE),
1476 minor_perm);
1477 status = ERROR;
1479 free(dup);
1480 continue;
1483 /* get the driver name */
1484 if (sscanf(dup, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
1485 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1486 minor_perm, line);
1487 status = ERROR;
1488 free(dup);
1489 break;
1493 * get the minor name; place the NULL character at the
1494 * end of the driver name, then make the drv_minor
1495 * point to the first character of the minor name.
1496 * the line missing ':' must be treated as a broken one.
1498 i = strcspn(drv, ":");
1499 if (i == strlen(drv)) {
1500 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1501 minor_perm, line);
1502 status = ERROR;
1503 free(dup);
1504 break;
1506 drv[i] = '\0';
1507 drv_minor = &drv[strlen(drv) + 1];
1510 * compare both of the driver name and the minor name.
1511 * then the new line should be written to the file if
1512 * both of them match
1514 if ((strcmp(drv, driver_name) == 0) &&
1515 (strcmp(minor, drv_minor) == 0)) {
1516 /* if it has a comment, keep it */
1517 if (cp != NULL) {
1518 cp++; /* skip a terminator */
1519 (void) snprintf(line, sizeof (line),
1520 "%s:%s %s %s %s #%s\n",
1521 drv, minor, perm, own, grp, cp);
1522 } else {
1523 (void) snprintf(line, sizeof (line),
1524 "%s:%s %s %s %s\n",
1525 drv, minor, perm, own, grp);
1527 match = 1;
1529 free(dup);
1531 /* update the file */
1532 if ((fputs(line, newfp)) == EOF) {
1533 (void) fprintf(stderr, gettext(ERR_UPDATE),
1534 minor_perm);
1535 status = ERROR;
1539 if (!match) {
1540 (void) bzero(line, sizeof (&line[0]));
1541 (void) snprintf(line, sizeof (line),
1542 "%s:%s %s %s %s\n",
1543 driver_name, minor, perm, own, grp);
1545 /* add the new entry */
1546 if ((fputs(line, newfp)) == EOF) {
1547 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1548 status = ERROR;
1552 (void) fclose(fp);
1554 if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
1555 status = ERROR;
1557 (void) fclose(newfp);
1560 * if error, leave original file, delete new file
1561 * if noerr, replace original file with new file
1563 if (status == NOERR) {
1564 if (rename(newfile, minor_perm) == -1) {
1565 perror(NULL);
1566 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1567 (void) unlink(newfile);
1568 return (ERROR);
1570 } else {
1572 * since there's an error, leave file alone; remove
1573 * new file
1575 if (unlink(newfile) == -1) {
1576 (void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
1578 return (ERROR);
1581 return (NOERR);
1587 * list_entry:
1588 * open file
1589 * read thru file, listing all entries if first entry = driver_name
1590 * close
1592 void
1593 list_entry(
1594 char *oldfile,
1595 char *driver_name,
1596 char *marker)
1598 FILE *fp;
1599 int i;
1600 char line[MAX_DBFILE_ENTRY], *cp;
1601 char drv[FILENAME_MAX + 1];
1603 if ((fp = fopen(oldfile, "r")) == NULL) {
1604 perror(NULL);
1605 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
1607 return;
1610 while (fgets(line, sizeof (line), fp) != NULL) {
1611 /* cut off comments starting with '#' */
1612 if ((cp = strchr(line, '#')) != NULL)
1613 *cp = '\0';
1614 /* ignore comment or blank lines */
1615 if (is_blank(line))
1616 continue;
1617 /* sanity-check */
1618 if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
1619 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1620 oldfile, line);
1623 for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
1624 drv[i] = '\0';
1627 if (strcmp(driver_name, drv) == 0) {
1628 (void) fprintf(stdout, "%s", line);
1632 (void) fclose(fp);
1635 static boolean_t
1636 is_token(char *tok)
1639 * Check the token here. According to IEEE1275 Open Firmware Boot
1640 * Standard, the name is composed of 1 to 31 letters,
1641 * digits and punctuation characters from the set ",._+-", and
1642 * uppercase and lowercase characters are considered distinct.
1643 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
1644 * However, since either the definition of driver or aliase names is
1645 * not known well, only '#' is avoided explicitly. (the kernel lexical
1646 * analyzer treats it as a start of a comment)
1648 for (/* nothing */; *tok != '\0'; tok++)
1649 if (*tok == '#' || iscntrl(*tok))
1650 return (B_FALSE);
1652 return (B_TRUE);
1656 * check each entry in perm_list for:
1657 * 4 arguments
1658 * permission arg is in valid range
1659 * permlist entries separated by comma
1660 * return ERROR/NOERR
1663 check_perm_opts(char *perm_list)
1665 char *current_head;
1666 char *previous_head;
1667 char *one_entry;
1668 int len, scan_stat;
1669 char minor[FILENAME_MAX + 1];
1670 char perm[OPT_LEN + 1];
1671 char own[OPT_LEN + 1];
1672 char grp[OPT_LEN + 1];
1673 char dumb[OPT_LEN + 1];
1674 int status = NOERR;
1675 int intperm;
1677 if ((len = strlen(perm_list)) == 0)
1678 return (ERROR);
1680 if ((one_entry = calloc(len + 1, 1)) == NULL) {
1681 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1682 return (ERROR);
1685 previous_head = perm_list;
1686 current_head = perm_list;
1688 while (*current_head != '\0') {
1689 bzero(one_entry, len + 1);
1690 current_head = get_perm_entry(previous_head, one_entry);
1692 previous_head = current_head;
1693 scan_stat = sscanf(one_entry,
1694 "%" VAL2STR(FILENAME_MAX) "s" /* minor */
1695 "%" VAL2STR(OPT_LEN) "s" /* perm */
1696 "%" VAL2STR(OPT_LEN) "s" /* own */
1697 "%" VAL2STR(OPT_LEN) "s" /* grp */
1698 "%" VAL2STR(OPT_LEN) "s", /* dumb */
1699 minor, perm, own, grp, dumb);
1701 if (scan_stat < 4) {
1702 (void) fprintf(stderr, gettext(ERR_MIS_TOK),
1703 "-m", one_entry);
1704 status = ERROR;
1706 if (scan_stat > 4) {
1707 (void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS),
1708 "-m", one_entry);
1709 status = ERROR;
1712 intperm = atoi(perm);
1713 if (intperm < 0000 || intperm > 4777) {
1714 (void) fprintf(stderr, gettext(ERR_BAD_MODE), perm);
1715 status = ERROR;
1719 free(one_entry);
1720 return (status);
1725 * check each alias :
1726 * alias list members separated by white space
1727 * cannot exist as driver name in /etc/name_to_major
1728 * cannot exist as driver or alias name in /etc/driver_aliases
1731 aliases_unique(char *aliases)
1733 char *current_head;
1734 char *previous_head;
1735 char *one_entry;
1736 int len;
1737 int is_unique;
1738 int err;
1740 len = strlen(aliases);
1742 one_entry = calloc(len + 1, 1);
1743 if (one_entry == NULL) {
1744 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1745 return (ERROR);
1748 previous_head = aliases;
1750 do {
1751 bzero(one_entry, len+1);
1752 current_head = get_entry(previous_head, one_entry, ' ', 1);
1753 previous_head = current_head;
1755 if ((unique_driver_name(one_entry, name_to_major,
1756 &is_unique)) == ERROR)
1757 goto err_out;
1759 if (is_unique != UNIQUE) {
1760 (void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ),
1761 one_entry);
1762 goto err_out;
1765 if ((err = unique_drv_alias(one_entry)) != UNIQUE) {
1766 if (err == NOT_UNIQUE) {
1767 (void) fprintf(stderr,
1768 gettext(ERR_ALIAS_IN_USE), one_entry);
1770 goto err_out;
1773 if (!is_token(one_entry)) {
1774 (void) fprintf(stderr, gettext(ERR_BAD_TOK),
1775 "-i", one_entry);
1776 goto err_out;
1779 } while (*current_head != '\0');
1781 free(one_entry);
1782 return (NOERR);
1784 err_out:
1785 free(one_entry);
1786 return (ERROR);
1790 * verify each alias :
1791 * alias list members separated by white space and quoted
1792 * exist as alias name in /etc/driver_aliases
1795 aliases_exist(char *aliases)
1797 char *current_head;
1798 char *previous_head;
1799 char *one_entry;
1800 int len;
1802 len = strlen(aliases);
1804 one_entry = calloc(len + 1, 1);
1805 if (one_entry == NULL) {
1806 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1807 return (ERROR);
1810 previous_head = aliases;
1812 do {
1813 bzero(one_entry, len+1);
1814 current_head = get_entry(previous_head, one_entry, ' ', 1);
1815 previous_head = current_head;
1817 if (unique_drv_alias(one_entry) != NOT_UNIQUE)
1818 goto err_out;
1820 if (!is_token(one_entry)) {
1821 (void) fprintf(stderr, gettext(ERR_BAD_TOK),
1822 "-i", one_entry);
1823 goto err_out;
1826 } while (*current_head != '\0');
1828 free(one_entry);
1829 return (NOERR);
1831 err_out:
1832 free(one_entry);
1833 return (ERROR);
1838 * check each alias :
1839 * if path-oriented alias, path exists
1842 aliases_paths_exist(char *aliases)
1844 char *current_head;
1845 char *previous_head;
1846 char *one_entry;
1847 int i, len;
1848 char path[MAXPATHLEN];
1849 struct stat buf;
1851 len = strlen(aliases);
1853 one_entry = calloc(len + 1, 1);
1854 if (one_entry == NULL) {
1855 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1856 return (ERROR);
1859 previous_head = aliases;
1861 do {
1862 for (i = 0; i <= len; i++)
1863 one_entry[i] = 0;
1865 current_head = get_entry(previous_head, one_entry, ' ', 1);
1866 previous_head = current_head;
1868 /* if the alias is a path, ensure that the path exists */
1869 if (*one_entry != '/')
1870 continue;
1871 (void) snprintf(path, sizeof (path), "/devices/%s", one_entry);
1872 if (stat(path, &buf) == 0)
1873 continue;
1875 /* no device at specified path-oriented alias path */
1876 (void) fprintf(stderr, gettext(ERR_PATH_ORIENTED_ALIAS),
1877 one_entry);
1878 free(one_entry);
1879 return (ERROR);
1881 } while (*current_head != '\0');
1883 free(one_entry);
1885 return (NOERR);
1890 update_driver_aliases(
1891 char *driver_name,
1892 char *aliases)
1894 /* make call to update the aliases file */
1895 return (append_to_file(driver_name, aliases, driver_aliases,
1896 ' ', " ", 1));
1901 * Return:
1902 * ERROR in case of memory or read error
1903 * UNIQUE if there is no existing match to the supplied alias
1904 * NOT_UNIQUE if there is a match
1905 * An error message is emitted in the case of ERROR,
1906 * up to the caller otherwise.
1909 unique_drv_alias(char *drv_alias)
1911 FILE *fp;
1912 char drv[FILENAME_MAX + 1];
1913 char line[MAX_N2M_ALIAS_LINE + 1], *cp;
1914 char alias[FILENAME_MAX + 1];
1915 char *a;
1916 int status = UNIQUE;
1918 fp = fopen(driver_aliases, "r");
1920 if (fp != NULL) {
1921 while ((fgets(line, sizeof (line), fp) != 0) &&
1922 status == UNIQUE) {
1923 /* cut off comments starting with '#' */
1924 if ((cp = strchr(line, '#')) != NULL)
1925 *cp = '\0';
1926 /* ignore comment or blank lines */
1927 if (is_blank(line))
1928 continue;
1929 /* sanity-check */
1930 if (sscanf(line,
1931 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
1932 "%" VAL2STR(FILENAME_MAX) "s", /* alias */
1933 drv, alias) != 2)
1934 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1935 driver_aliases, line);
1937 /* unquote for compare */
1938 if ((*alias == '"') &&
1939 (*(alias + strlen(alias) - 1) == '"')) {
1940 a = &alias[1];
1941 alias[strlen(alias) - 1] = '\0';
1942 } else
1943 a = alias;
1945 if ((strcmp(drv_alias, drv) == 0) ||
1946 (strcmp(drv_alias, a) == 0)) {
1947 status = NOT_UNIQUE;
1948 break;
1951 (void) fclose(fp);
1952 return (status);
1953 } else {
1954 perror(NULL);
1955 (void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
1956 return (ERROR);
1962 * search for driver_name in first field of file file_name
1963 * searching name_to_major and driver_aliases: name separated
1964 * from the remainder of the line by white space.
1967 unique_driver_name(char *driver_name, char *file_name,
1968 int *is_unique)
1970 int ret, err;
1972 if ((ret = get_major_no(driver_name, file_name)) == ERROR) {
1973 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1974 file_name);
1975 } else {
1976 /* check alias file for name collision */
1977 if ((err = unique_drv_alias(driver_name)) != UNIQUE) {
1978 if (err == NOT_UNIQUE) {
1979 (void) fprintf(stderr,
1980 gettext(ERR_ALIAS_IN_USE),
1981 driver_name);
1983 ret = ERROR;
1984 } else {
1985 if (ret != UNIQUE)
1986 *is_unique = NOT_UNIQUE;
1987 else
1988 *is_unique = ret;
1989 ret = NOERR;
1992 return (ret);
1996 * returns:
1997 * SUCCESS - not an existing driver alias
1998 * NOT_UNIQUE - matching driver alias exists
1999 * ERROR - an error occurred
2002 check_duplicate_driver_alias(char *driver_name, char *drv_alias)
2004 FILE *fp;
2005 char drv[FILENAME_MAX + 1];
2006 char line[MAX_N2M_ALIAS_LINE + 1], *cp;
2007 char alias[FILENAME_MAX + 1];
2008 char *a;
2009 int status = SUCCESS;
2011 if ((fp = fopen(driver_aliases, "r")) == NULL) {
2012 perror(NULL);
2013 (void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
2014 return (ERROR);
2017 while (fgets(line, sizeof (line), fp) != 0) {
2018 /* cut off comments starting with '#' */
2019 if ((cp = strchr(line, '#')) != NULL)
2020 *cp = '\0';
2021 /* ignore comment or blank lines */
2022 if (is_blank(line))
2023 continue;
2024 /* sanity-check */
2025 if (sscanf(line,
2026 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
2027 "%" VAL2STR(FILENAME_MAX) "s", /* alias */
2028 drv, alias) != 2)
2029 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
2030 driver_aliases, line);
2032 /* unquote for compare */
2033 if ((*alias == '"') &&
2034 (*(alias + strlen(alias) - 1) == '"')) {
2035 a = &alias[1];
2036 alias[strlen(alias) - 1] = '\0';
2037 } else
2038 a = alias;
2040 if ((strcmp(drv_alias, a) == 0) &&
2041 (strcmp(drv, driver_name) == 0)) {
2042 status = NOT_UNIQUE;
2045 if ((strcmp(drv_alias, drv) == 0) ||
2046 ((strcmp(drv_alias, a) == 0) &&
2047 (strcmp(drv, driver_name) != 0))) {
2048 (void) fprintf(stderr,
2049 gettext(ERR_ALIAS_IN_USE),
2050 drv_alias);
2051 status = ERROR;
2052 goto done;
2056 done:
2057 (void) fclose(fp);
2058 return (status);
2062 trim_duplicate_aliases(char *driver_name, char *aliases, char **aliases2p)
2064 char *current_head;
2065 char *previous_head;
2066 char *one_entry;
2067 char *aliases2;
2068 int rv, len;
2069 int n = 0;
2071 *aliases2p = NULL;
2072 len = strlen(aliases) + 1;
2074 one_entry = calloc(len, 1);
2075 aliases2 = calloc(len, 1);
2076 if (one_entry == NULL || aliases2 == NULL) {
2077 (void) fprintf(stderr, gettext(ERR_NO_MEM));
2078 return (ERROR);
2081 previous_head = aliases;
2083 do {
2084 (void) bzero(one_entry, len);
2085 current_head = get_entry(previous_head, one_entry, ' ', 1);
2086 previous_head = current_head;
2088 rv = check_duplicate_driver_alias(driver_name, one_entry);
2089 switch (rv) {
2090 case SUCCESS:
2091 /* not an existing driver alias: add it */
2092 if (n > 0) {
2093 if (strlcat(aliases2, " ", len) >= len)
2094 goto err;
2096 if (strlcat(aliases2, one_entry, len) >= len)
2097 goto err;
2098 n++;
2099 break;
2100 case NOT_UNIQUE:
2101 /* matching driver alias exists: do not add it */
2102 break;
2103 case ERROR:
2104 /* error reading the alias file */
2105 goto err;
2106 default:
2107 goto err;
2110 if (!is_token(one_entry)) {
2111 (void) fprintf(stderr, gettext(ERR_BAD_TOK),
2112 "-i", one_entry);
2113 goto err;
2115 } while (*current_head != '\0');
2118 * If all the aliases listed are already
2119 * present we actually have none to do.
2121 if (n == 0) {
2122 free(aliases2);
2123 } else {
2124 *aliases2p = aliases2;
2126 free(one_entry);
2127 return (NOERR);
2129 err:
2130 free(aliases2);
2131 free(one_entry);
2132 return (ERROR);
2136 check_space_within_quote(char *str)
2138 register int i;
2139 register int len;
2140 int quoted = 0;
2142 len = strlen(str);
2143 for (i = 0; i < len; i++, str++) {
2144 if (*str == '"') {
2145 if (quoted == 0)
2146 quoted++;
2147 else
2148 quoted--;
2149 } else if (*str == ' ' && quoted)
2150 return (ERROR);
2153 return (0);
2158 * get major number
2159 * write driver_name major_num to name_to_major file
2160 * major_num returned in major_num
2161 * return success/failure
2164 update_name_to_major(char *driver_name, major_t *major_num, int server)
2166 char major[MAX_STR_MAJOR + 1];
2167 struct stat buf;
2168 char *num_list;
2169 char drv_majnum_str[MAX_STR_MAJOR + 1];
2170 int new_maj = -1;
2171 int i, tmp = 0, is_unique, have_rem_n2m = 0;
2172 int max_dev = 0;
2175 * if driver_name already in rem_name_to_major
2176 * delete entry from rem_nam_to_major
2177 * put entry into name_to_major
2180 if (stat(rem_name_to_major, &buf) == 0) {
2181 have_rem_n2m = 1;
2184 if (have_rem_n2m) {
2185 if ((is_unique = get_major_no(driver_name, rem_name_to_major))
2186 == ERROR)
2187 return (ERROR);
2190 * found a match in rem_name_to_major
2192 if (is_unique != UNIQUE) {
2193 char scratch[FILENAME_MAX];
2196 * If there is a match in /etc/rem_name_to_major then
2197 * be paranoid: is that major number already in
2198 * /etc/name_to_major (potentially under another name)?
2200 if (get_driver_name(is_unique, name_to_major,
2201 scratch) != UNIQUE) {
2203 * nuke the rem_name_to_major entry-- it
2204 * isn't helpful.
2206 (void) delete_entry(rem_name_to_major,
2207 driver_name, " ", NULL);
2208 } else {
2209 (void) snprintf(major, sizeof (major),
2210 "%d", is_unique);
2212 if (append_to_file(driver_name, major,
2213 name_to_major, ' ', " ", 0) == ERROR) {
2214 (void) fprintf(stderr,
2215 gettext(ERR_NO_UPDATE),
2216 name_to_major);
2217 return (ERROR);
2220 if (delete_entry(rem_name_to_major,
2221 driver_name, " ", NULL) == ERROR) {
2222 (void) fprintf(stderr,
2223 gettext(ERR_DEL_ENTRY), driver_name,
2224 rem_name_to_major);
2225 return (ERROR);
2228 /* found matching entry : no errors */
2229 *major_num = is_unique;
2230 return (NOERR);
2236 * Bugid: 1264079
2237 * In a server case (with -b option), we can't use modctl() to find
2238 * the maximum major number, we need to dig thru client's
2239 * /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
2241 * if (server)
2242 * get maximum major number thru (rem_)name_to_major file on client
2243 * else
2244 * get maximum major number allowable on current system using modctl
2246 if (server) {
2247 max_dev = 0;
2248 tmp = 0;
2250 max_dev = get_max_major(name_to_major);
2252 /* If rem_name_to_major exists, we need to check it too */
2253 if (have_rem_n2m) {
2254 tmp = get_max_major(rem_name_to_major);
2257 * If name_to_major is missing, we can get max_dev from
2258 * /etc/rem_name_to_major. If both missing, bail out!
2260 if ((max_dev == ERROR) && (tmp == ERROR)) {
2261 (void) fprintf(stderr,
2262 gettext(ERR_CANT_ACCESS_FILE),
2263 name_to_major);
2264 return (ERROR);
2267 /* guard against bigger maj_num in rem_name_to_major */
2268 if (tmp > max_dev)
2269 max_dev = tmp;
2270 } else {
2272 * If we can't get major from name_to_major file
2273 * and there is no /etc/rem_name_to_major file,
2274 * then we don't have a max_dev, bail out quick!
2276 if (max_dev == ERROR)
2277 return (ERROR);
2281 * In case there is no more slack in current name_to_major
2282 * table, provide at least 1 extra entry so the add_drv can
2283 * succeed. Since only one add_drv process is allowed at one
2284 * time, and hence max_dev will be re-calculated each time
2285 * add_drv is ran, we don't need to worry about adding more
2286 * than 1 extra slot for max_dev.
2288 max_dev++;
2290 } else {
2291 if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
2292 perror(NULL);
2293 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
2294 return (ERROR);
2299 * max_dev is really how many slots the kernel has allocated for
2300 * devices... [0 , maxdev-1], not the largest available device num.
2302 if ((num_list = calloc(max_dev, 1)) == NULL) {
2303 (void) fprintf(stderr, gettext(ERR_NO_MEM));
2304 return (ERROR);
2308 * Populate the num_list array
2310 if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) {
2311 return (ERROR);
2313 if (have_rem_n2m) {
2314 if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0)
2315 return (ERROR);
2319 * Find the first free major number.
2321 * Note that we begin searching from 1, as drivers have developer the
2322 * erroneous assumption that a dev_t of 0 is invalid, and since we no
2323 * longer include system devices in the base files, a major number of
2324 * 0 may now be given to arbitrary devices.
2326 for (i = 1; i < max_dev; i++) {
2327 if (num_list[i] != 1) {
2328 new_maj = i;
2329 break;
2333 if (new_maj == -1) {
2334 (void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR));
2335 return (ERROR);
2338 (void) snprintf(drv_majnum_str, sizeof (drv_majnum_str),
2339 "%d", new_maj);
2340 if (do_the_update(driver_name, drv_majnum_str) == ERROR) {
2341 return (ERROR);
2344 *major_num = new_maj;
2345 return (NOERR);
2350 fill_n2m_array(char *filename, char **array, int *nelems)
2352 FILE *fp;
2353 char line[MAX_N2M_ALIAS_LINE + 1], *cp;
2354 char drv[FILENAME_MAX + 1];
2355 u_longlong_t dnum;
2356 major_t drv_majnum;
2359 * Read through the file, marking each major number found
2360 * order is not relevant
2362 if ((fp = fopen(filename, "r")) == NULL) {
2363 perror(NULL);
2364 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename);
2365 return (ERROR);
2368 while (fgets(line, sizeof (line), fp) != 0) {
2369 /* cut off comments starting with '#' */
2370 if ((cp = strchr(line, '#')) != NULL)
2371 *cp = '\0';
2372 /* ignore comment or blank lines */
2373 if (is_blank(line))
2374 continue;
2375 /* sanity-check */
2376 if (sscanf(line,
2377 "%" VAL2STR(FILENAME_MAX) "s %llu", drv, &dnum) != 2) {
2378 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
2379 filename, line);
2380 (void) fclose(fp);
2381 return (ERROR);
2384 if (dnum > L_MAXMAJ32) {
2385 (void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv,
2386 dnum, filename, L_MAXMAJ32);
2387 continue;
2390 * cast down to a major_t; we can be sure this is safe because
2391 * of the above range-check.
2393 drv_majnum = (major_t)dnum;
2395 if (drv_majnum >= *nelems) {
2397 * Allocate some more space, up to drv_majnum + 1 so
2398 * we can accomodate 0 through drv_majnum.
2400 * Note that in the failure case, we leak all of the
2401 * old contents of array. It's ok, since we just
2402 * wind up exiting immediately anyway.
2404 *nelems = drv_majnum + 1;
2405 *array = realloc(*array, *nelems);
2406 if (*array == NULL) {
2407 (void) fprintf(stderr, gettext(ERR_NO_MEM));
2408 return (ERROR);
2411 (*array)[drv_majnum] = 1;
2414 (void) fclose(fp);
2415 return (0);
2420 do_the_update(char *driver_name, char *major_number)
2422 return (append_to_file(driver_name, major_number, name_to_major,
2423 ' ', " ", 0));
2427 * is_blank() returns 1 (true) if a line specified is composed of
2428 * whitespace characters only. otherwise, it returns 0 (false).
2430 * Note. the argument (line) must be null-terminated.
2432 static int
2433 is_blank(char *line)
2435 for (/* nothing */; *line != '\0'; line++)
2436 if (!isspace(*line))
2437 return (0);
2438 return (1);