8672 proc_t changes broke genunix dmods and walker
[unleashed.git] / usr / src / cmd / modload / drvsubr.c
blob83282f471f12021a85812c977d2c3f446b936dcf
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 if (match2)
512 free(match2);
514 /* Make sure that the file is on disk */
515 if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
516 status = ERROR;
517 else
518 rv = NOERR;
520 (void) fclose(newfp);
522 /* no matching driver found */
523 rv = NOERR;
524 if (!drvr_found ||
525 (nomatch == B_TRUE)) {
526 rv = NONE_FOUND;
530 * if error, leave original file, delete new file
531 * if noerr, replace original file with new file
534 if (status == NOERR) {
535 if (rename(newfile, oldfile) == -1) {
536 perror(NULL);
537 (void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
538 (void) unlink(newfile);
539 return (ERROR);
541 } else {
543 * since there's an error, leave file alone; remove
544 * new file
546 if (unlink(newfile) == -1) {
547 (void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
549 return (ERROR);
552 return (rv);
557 * wrapper for call to get_name_to_major_entry(): given driver name,
558 * retrieve major number.
561 get_major_no(char *driver_name, char *file_name)
563 int major = UNIQUE;
565 if (get_name_to_major_entry(&major, driver_name, file_name) == ERROR)
566 return (ERROR);
567 else
568 return (major);
572 * wrapper for call to get_name_to_major_entry(): given major number,
573 * retrieve driver name.
576 get_driver_name(int major, char *file_name, char *buf)
578 if (major < 0)
579 return (ERROR);
580 return (get_name_to_major_entry(&major, buf, file_name));
585 * return pointer to cached name_to_major file - reads file into
586 * cache if this has not already been done. Since there may be
587 * requests for multiple name_to_major files (rem_name_to_major,
588 * name_to_major), this routine keeps a list of cached files.
590 static int
591 get_cached_n_to_m_file(char *filename, char ***cache)
593 struct n_to_m_cache {
594 char *file;
595 char **cached_file;
596 int size;
597 struct n_to_m_cache *next;
599 static struct n_to_m_cache *head = NULL;
600 struct n_to_m_cache *ptr;
601 FILE *fp;
602 char drv[FILENAME_MAX + 1];
603 char entry[FILENAME_MAX + 1];
604 char line[MAX_N2M_ALIAS_LINE], *cp;
605 int maj;
606 int size = 0;
610 * see if the file is already cached - either
611 * rem_name_to_major or name_to_major
613 ptr = head;
614 while (ptr != NULL) {
615 if (strcmp(ptr->file, filename) == 0)
616 break;
617 ptr = ptr->next;
620 if (ptr == NULL) { /* we need to cache the contents */
621 if ((fp = fopen(filename, "r")) == NULL) {
622 perror(NULL);
623 (void) fprintf(stderr, gettext(ERR_CANT_OPEN),
624 filename);
625 return (ERROR);
628 while (fgets(line, sizeof (line), fp) != NULL) {
629 /* cut off comments starting with '#' */
630 if ((cp = strchr(line, '#')) != NULL)
631 *cp = '\0';
632 /* ignore comment or blank lines */
633 if (is_blank(line))
634 continue;
635 /* sanity-check */
636 if (sscanf(line,
637 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
638 "%" VAL2STR(FILENAME_MAX) "s", /* entry */
639 drv, entry) != 2) {
640 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
641 filename, line);
642 continue;
644 maj = atoi(entry);
645 if (maj > size)
646 size = maj;
649 /* allocate struct to cache the file */
650 ptr = (struct n_to_m_cache *)calloc(1,
651 sizeof (struct n_to_m_cache));
652 if (ptr == NULL) {
653 (void) fprintf(stderr, gettext(ERR_NO_MEM));
654 return (ERROR);
656 ptr->size = size + 1;
657 /* allocate space to cache contents of file */
658 ptr->cached_file = (char **)calloc(ptr->size, sizeof (char *));
659 if (ptr->cached_file == NULL) {
660 free(ptr);
661 (void) fprintf(stderr, gettext(ERR_NO_MEM));
662 return (ERROR);
665 rewind(fp);
668 * now fill the cache
669 * the cache is an array of char pointers indexed by major
670 * number
672 while (fgets(line, sizeof (line), fp) != NULL) {
673 /* cut off comments starting with '#' */
674 if ((cp = strchr(line, '#')) != NULL)
675 *cp = '\0';
676 /* ignore comment or blank lines */
677 if (is_blank(line))
678 continue;
679 /* sanity-check */
680 if (sscanf(line,
681 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
682 "%" VAL2STR(FILENAME_MAX) "s", /* entry */
683 drv, entry) != 2) {
684 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
685 filename, line);
686 continue;
688 maj = atoi(entry);
689 if ((ptr->cached_file[maj] = strdup(drv)) == NULL) {
690 (void) fprintf(stderr, gettext(ERR_NO_MEM));
691 free(ptr->cached_file);
692 free(ptr);
693 return (ERROR);
695 (void) strcpy(ptr->cached_file[maj], drv);
697 (void) fclose(fp);
698 /* link the cache struct into the list of cached files */
699 ptr->file = strdup(filename);
700 if (ptr->file == NULL) {
701 for (maj = 0; maj <= ptr->size; maj++)
702 free(ptr->cached_file[maj]);
703 free(ptr->cached_file);
704 free(ptr);
705 (void) fprintf(stderr, gettext(ERR_NO_MEM));
706 return (ERROR);
708 ptr->next = head;
709 head = ptr;
711 /* return value pointer to contents of file */
712 *cache = ptr->cached_file;
714 /* return size */
715 return (ptr->size);
720 * Using get_cached_n_to_m_file(), retrieve maximum major number
721 * found in the specificed file (name_to_major/rem_name_to_major).
723 * The return value is actually the size of the internal cache including 0.
726 get_max_major(char *file_name)
728 char **n_to_m_cache = NULL;
730 return (get_cached_n_to_m_file(file_name, &n_to_m_cache));
735 * searching name_to_major: if major_no == UNIQUE then the caller wants to
736 * use the driver name as the key. Otherwise, the caller wants to use
737 * the major number as a key.
739 * This routine caches the contents of the name_to_major file on
740 * first call. And it could be generalized to deal with other
741 * config files if necessary.
743 static int
744 get_name_to_major_entry(int *major_no, char *driver_name, char *file_name)
746 int maj;
747 char **n_to_m_cache = NULL;
748 int size = 0;
750 int ret = NOT_UNIQUE;
753 * read the file in - we cache it in case caller wants to
754 * do multiple lookups
756 size = get_cached_n_to_m_file(file_name, &n_to_m_cache);
758 if (size == ERROR)
759 return (ERROR);
761 /* search with driver name as key */
762 if (*major_no == UNIQUE) {
763 for (maj = 0; maj < size; maj++) {
764 if ((n_to_m_cache[maj] != NULL) &&
765 (strcmp(driver_name, n_to_m_cache[maj]) == 0)) {
766 *major_no = maj;
767 break;
770 if (maj >= size)
771 ret = UNIQUE;
772 /* search with major number as key */
773 } else {
775 * Bugid 1254588, drvconfig dump core after loading driver
776 * with major number bigger than entries defined in
777 * /etc/name_to_major.
779 if (*major_no >= size)
780 return (UNIQUE);
782 if (n_to_m_cache[*major_no] != NULL) {
783 (void) strcpy(driver_name, n_to_m_cache[*major_no]);
784 } else
785 ret = UNIQUE;
787 return (ret);
791 * Given pointer to begining of member 'n' in a space (or separator)
792 * separated list, return pointer to member 'n+1', and establish member 'n'
793 * in *current_entry. If unquote, then we skip a leading quote and treat
794 * the trailing quote as a separator (and skip).
796 char *
797 get_entry(
798 char *prev_member,
799 char *current_entry,
800 char separator,
801 int unquote)
803 char *ptr;
804 int quoted = 0;
806 ptr = prev_member;
808 /* skip white space */
809 while (isspace(*ptr))
810 ptr++;
812 /* if unquote skip leading quote */
813 if (unquote && *ptr == '"') {
814 quoted++;
815 ptr++;
818 /* read thru the current entry looking for end, separator, or unquote */
819 while (*ptr &&
820 (*ptr != separator) && (!isspace(*ptr)) &&
821 (!quoted || (*ptr != '"'))) {
822 *current_entry++ = *ptr++;
824 *current_entry = '\0';
826 if (separator && (*ptr == separator))
827 ptr++; /* skip over separator */
828 if (quoted && (*ptr == '"'))
829 ptr++; /* skip over trailing quote */
831 /* skip white space */
832 while (isspace(*ptr))
833 ptr++;
835 return (ptr);
839 * A parser specific to the add_drv "-m permission" syntax:
841 * -m '<minor-name> <permissions> <owner> <group>', ...
843 * One entry is parsed starting at prev_member and returned
844 * in the string pointed at by current_entry. A pointer
845 * to the entry following is returned.
847 char *
848 get_perm_entry(
849 char *prev_member,
850 char *current_entry)
852 char *ptr;
853 int nfields = 0;
854 int maxfields = 4; /* fields in a permissions format */
856 ptr = prev_member;
857 while (isspace(*ptr))
858 ptr++;
860 while (*ptr) {
861 /* comma allowed in minor name token only */
862 if (*ptr == ',' && nfields > 0) {
863 break;
864 } else if (isspace(*ptr)) {
865 *current_entry++ = *ptr++;
866 while (isspace(*ptr))
867 ptr++;
868 if (++nfields == maxfields)
869 break;
870 } else
871 *current_entry++ = *ptr++;
873 *current_entry = '\0';
875 while (isspace(*ptr))
876 ptr++;
877 if (*ptr == ',') {
878 ptr++; /* skip over optional trailing comma */
880 while (isspace(*ptr))
881 ptr++;
883 return (ptr);
886 void
887 enter_lock(void)
889 struct flock lock;
892 * Attempt to create the lock file. Open the file itself,
893 * and not a symlink to some other file.
895 add_rem_lock_fd = open(add_rem_lock,
896 O_CREAT|O_RDWR|O_NOFOLLOW|O_NOLINKS, S_IRUSR|S_IWUSR);
897 if (add_rem_lock_fd < 0) {
898 (void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
899 add_rem_lock, strerror(errno));
900 exit(1);
903 lock.l_type = F_WRLCK;
904 lock.l_whence = SEEK_SET;
905 lock.l_start = 0;
906 lock.l_len = 0;
908 /* Try for the lock but don't wait. */
909 if (fcntl(add_rem_lock_fd, F_SETLK, &lock) == -1) {
910 if (errno == EACCES || errno == EAGAIN) {
911 (void) fprintf(stderr, gettext(ERR_PROG_IN_USE));
912 } else {
913 (void) fprintf(stderr, gettext(ERR_LOCK),
914 add_rem_lock, strerror(errno));
916 exit(1);
920 void
921 err_exit(void)
923 /* release memory allocated for moddir */
924 cleanup_moddir();
925 /* remove add_drv/rem_drv lock */
926 exit_unlock();
927 exit(1);
930 void
931 cleanup_moddir(void)
933 struct drvmod_dir *walk_ptr;
934 struct drvmod_dir *free_ptr = moddir;
936 while (free_ptr != NULL) {
937 walk_ptr = free_ptr->next;
938 free(free_ptr);
939 free_ptr = walk_ptr;
943 void
944 exit_unlock(void)
946 struct flock unlock;
948 if (add_rem_lock_fd < 0)
949 return;
951 unlock.l_type = F_UNLCK;
952 unlock.l_whence = SEEK_SET;
953 unlock.l_start = 0;
954 unlock.l_len = 0;
956 if (fcntl(add_rem_lock_fd, F_SETLK, &unlock) == -1) {
957 (void) fprintf(stderr, gettext(ERR_UNLOCK),
958 add_rem_lock, strerror(errno));
959 } else {
960 (void) close(add_rem_lock_fd);
961 add_rem_lock_fd = -1;
966 * error adding driver; need to back out any changes to files.
967 * check flag to see which files need entries removed
968 * entry removal based on driver name
970 void
971 remove_entry(
972 int c_flag,
973 char *driver_name)
976 if (c_flag & CLEAN_NAM_MAJ) {
977 if (delete_entry(name_to_major, driver_name, " ",
978 NULL) == ERROR) {
979 (void) fprintf(stderr, gettext(ERR_NO_CLEAN),
980 name_to_major, driver_name);
984 if (c_flag & CLEAN_DRV_ALIAS) {
985 if (delete_entry(driver_aliases, driver_name, " ",
986 NULL) == ERROR) {
987 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
988 driver_name, driver_aliases);
992 if (c_flag & CLEAN_DRV_CLASSES) {
993 if (delete_entry(driver_classes, driver_name, "\t", NULL) ==
994 ERROR) {
995 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
996 driver_name, driver_classes);
1000 if (c_flag & CLEAN_MINOR_PERM) {
1001 if (delete_entry(minor_perm, driver_name, ":", NULL) == ERROR) {
1002 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1003 driver_name, minor_perm);
1007 * There's no point in removing entries from files that don't
1008 * exist. Prevent error messages by checking for file existence
1009 * first.
1011 if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
1012 access(device_policy, F_OK) == 0) {
1013 if (delete_plcy_entry(device_policy, driver_name) == ERROR) {
1014 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1015 driver_name, device_policy);
1018 if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
1019 access(extra_privs, F_OK) == 0) {
1020 if (delete_entry(extra_privs, driver_name, ":", NULL) ==
1021 ERROR) {
1022 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1023 driver_name, extra_privs);
1029 check_perms_aliases(
1030 int m_flag,
1031 int i_flag)
1034 * If neither i_flag nor m_flag are specified no need to check the
1035 * files for access permissions
1037 if (!m_flag && !i_flag)
1038 return (NOERR);
1040 /* check minor_perm file : exits and is writable */
1041 if (m_flag) {
1042 if (access(minor_perm, R_OK | W_OK)) {
1043 perror(NULL);
1044 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1045 minor_perm);
1046 return (ERROR);
1050 /* check driver_aliases file : exits and is writable */
1051 if (i_flag) {
1052 if (access(driver_aliases, R_OK | W_OK)) {
1053 perror(NULL);
1054 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1055 driver_aliases);
1056 return (ERROR);
1060 return (NOERR);
1065 check_name_to_major(int mode)
1067 /* check name_to_major file : exists and is writable */
1068 if (access(name_to_major, mode)) {
1069 perror(NULL);
1070 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1071 name_to_major);
1072 return (ERROR);
1075 return (NOERR);
1080 * All this stuff is to support a server installing
1081 * drivers on diskless clients. When on the server
1082 * need to prepend the basedir
1085 build_filenames(char *basedir)
1087 int len;
1088 int driver_aliases_len;
1089 int driver_classes_len;
1090 int minor_perm_len;
1091 int name_to_major_len;
1092 int rem_name_to_major_len;
1093 int add_rem_lock_len;
1094 int devfs_root_len;
1095 int device_policy_len;
1096 int extra_privs_len;
1098 if (basedir == NULL) {
1099 driver_aliases = DRIVER_ALIAS;
1100 driver_classes = DRIVER_CLASSES;
1101 minor_perm = MINOR_PERM;
1102 name_to_major = NAM_TO_MAJ;
1103 rem_name_to_major = REM_NAM_TO_MAJ;
1104 add_rem_lock = ADD_REM_LOCK;
1105 devfs_root = DEVFS_ROOT;
1106 device_policy = DEV_POLICY;
1107 extra_privs = EXTRA_PRIVS;
1109 } else {
1110 len = strlen(basedir) + 1;
1112 driver_aliases_len = len + sizeof (DRIVER_ALIAS);
1113 driver_classes_len = len + sizeof (DRIVER_CLASSES);
1114 minor_perm_len = len + sizeof (MINOR_PERM);
1115 name_to_major_len = len + sizeof (NAM_TO_MAJ);
1116 rem_name_to_major_len = len + sizeof (REM_NAM_TO_MAJ);
1117 add_rem_lock_len = len + sizeof (ADD_REM_LOCK);
1118 devfs_root_len = len + sizeof (DEVFS_ROOT);
1119 device_policy_len = len + sizeof (DEV_POLICY);
1120 extra_privs_len = len + sizeof (EXTRA_PRIVS);
1122 driver_aliases = malloc(driver_aliases_len);
1123 driver_classes = malloc(driver_classes_len);
1124 minor_perm = malloc(minor_perm_len);
1125 name_to_major = malloc(name_to_major_len);
1126 rem_name_to_major = malloc(rem_name_to_major_len);
1127 add_rem_lock = malloc(add_rem_lock_len);
1128 devfs_root = malloc(devfs_root_len);
1129 device_policy = malloc(device_policy_len);
1130 extra_privs = malloc(extra_privs_len);
1132 if ((driver_aliases == NULL) ||
1133 (driver_classes == NULL) ||
1134 (minor_perm == NULL) ||
1135 (name_to_major == NULL) ||
1136 (rem_name_to_major == NULL) ||
1137 (add_rem_lock == NULL) ||
1138 (devfs_root == NULL) ||
1139 (device_policy == NULL) ||
1140 (extra_privs == NULL)) {
1141 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1142 return (ERROR);
1145 (void) snprintf(driver_aliases, driver_aliases_len,
1146 "%s%s", basedir, DRIVER_ALIAS);
1147 (void) snprintf(driver_classes, driver_classes_len,
1148 "%s%s", basedir, DRIVER_CLASSES);
1149 (void) snprintf(minor_perm, minor_perm_len,
1150 "%s%s", basedir, MINOR_PERM);
1151 (void) snprintf(name_to_major, name_to_major_len,
1152 "%s%s", basedir, NAM_TO_MAJ);
1153 (void) snprintf(rem_name_to_major, rem_name_to_major_len,
1154 "%s%s", basedir, REM_NAM_TO_MAJ);
1155 (void) snprintf(add_rem_lock, add_rem_lock_len,
1156 "%s%s", basedir, ADD_REM_LOCK);
1157 (void) snprintf(devfs_root, devfs_root_len,
1158 "%s%s", basedir, DEVFS_ROOT);
1159 (void) snprintf(device_policy, device_policy_len,
1160 "%s%s", basedir, DEV_POLICY);
1161 (void) snprintf(extra_privs, extra_privs_len,
1162 "%s%s", basedir, EXTRA_PRIVS);
1165 return (NOERR);
1168 static int
1169 exec_command(char *path, char *cmdline[MAX_CMD_LINE])
1171 pid_t pid;
1172 uint_t stat_loc;
1173 int waitstat;
1174 int exit_status;
1176 /* child */
1177 if ((pid = fork()) == 0) {
1178 (void) execv(path, cmdline);
1179 perror(NULL);
1180 return (ERROR);
1181 } else if (pid == -1) {
1182 /* fork failed */
1183 perror(NULL);
1184 (void) fprintf(stderr, gettext(ERR_FORK_FAIL), cmdline);
1185 return (ERROR);
1186 } else {
1187 /* parent */
1188 do {
1189 waitstat = waitpid(pid, (int *)&stat_loc, 0);
1191 } while ((!WIFEXITED(stat_loc) &&
1192 !WIFSIGNALED(stat_loc)) || (waitstat == 0));
1194 exit_status = WEXITSTATUS(stat_loc);
1196 return (exit_status);
1201 * Exec devfsadm to perform driver config/unconfig operation,
1202 * adding or removing aliases.
1204 static int
1205 exec_devfsadm(
1206 boolean_t config,
1207 char *driver_name,
1208 major_t major_num,
1209 char *aliases,
1210 char *classes,
1211 int config_flags)
1213 int n = 0;
1214 char *cmdline[MAX_CMD_LINE];
1215 char maj_num[128];
1216 char *previous;
1217 char *current;
1218 int len;
1219 int rv;
1221 /* build command line */
1222 cmdline[n++] = DRVCONFIG;
1223 if (config == B_FALSE) {
1224 cmdline[n++] = "-u"; /* unconfigure */
1225 if (config_flags & CONFIG_DRV_FORCE)
1226 cmdline[n++] = "-f"; /* force if currently in use */
1228 if (config_flags & CONFIG_DRV_VERBOSE) {
1229 cmdline[n++] = "-v";
1231 cmdline[n++] = "-b";
1232 if (classes) {
1233 cmdline[n++] = "-c";
1234 cmdline[n++] = classes;
1236 cmdline[n++] = "-i";
1237 cmdline[n++] = driver_name;
1238 cmdline[n++] = "-m";
1239 (void) snprintf(maj_num, sizeof (maj_num), "%lu", major_num);
1240 cmdline[n++] = maj_num;
1241 if (config_flags & CONFIG_DRV_UPDATE_ONLY)
1242 cmdline[n++] = "-x";
1244 if (aliases != NULL) {
1245 len = strlen(aliases);
1246 previous = aliases;
1247 do {
1248 cmdline[n++] = "-a";
1249 cmdline[n] = calloc(len + 1, 1);
1250 if (cmdline[n] == NULL) {
1251 (void) fprintf(stderr,
1252 gettext(ERR_NO_MEM));
1253 return (ERROR);
1255 current = get_entry(previous,
1256 cmdline[n++], ' ', 0);
1257 previous = current;
1259 } while (*current != '\0');
1262 cmdline[n] = (char *)0;
1264 rv = exec_command(DRVCONFIG_PATH, cmdline);
1265 if (rv == NOERR)
1266 return (NOERR);
1267 return (ERROR);
1271 unconfig_driver(
1272 char *driver_name,
1273 major_t major_num,
1274 char *aliases,
1275 int config_flags)
1277 return (exec_devfsadm(B_FALSE, driver_name, major_num,
1278 aliases, NULL, config_flags));
1282 * check that major_num doesn't exceed maximum on this machine
1283 * do this here to support add_drv on server for diskless clients
1286 config_driver(
1287 char *driver_name,
1288 major_t major_num,
1289 char *aliases,
1290 char *classes,
1291 int cleanup_flag,
1292 int config_flags)
1294 int max_dev;
1295 int rv;
1297 if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
1298 perror(NULL);
1299 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
1300 return (ERROR);
1303 if (major_num >= max_dev) {
1304 (void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS),
1305 major_num, max_dev);
1306 return (ERROR);
1309 /* bind major number and driver name */
1310 rv = exec_devfsadm(B_TRUE, driver_name, major_num,
1311 aliases, classes, config_flags);
1313 if (rv == NOERR)
1314 return (NOERR);
1315 perror(NULL);
1316 remove_entry(cleanup_flag, driver_name);
1317 return (ERROR);
1320 void
1321 load_driver(char *driver_name, int verbose_flag)
1323 int n = 0;
1324 char *cmdline[MAX_CMD_LINE];
1325 int exec_status;
1327 /* build command line */
1328 cmdline[n++] = DEVFSADM;
1329 if (verbose_flag) {
1330 cmdline[n++] = "-v";
1332 cmdline[n++] = "-i";
1333 cmdline[n++] = driver_name;
1334 cmdline[n] = (char *)0;
1336 exec_status = exec_command(DEVFSADM_PATH, cmdline);
1338 if (exec_status != NOERR) {
1339 /* no clean : name and major number are bound */
1340 (void) fprintf(stderr, gettext(ERR_CONFIG), driver_name);
1344 void
1345 get_modid(char *driver_name, int *mod)
1347 struct modinfo modinfo;
1349 modinfo.mi_id = -1;
1350 modinfo.mi_info = MI_INFO_ALL;
1351 do {
1353 * If we are at the end of the list of loaded modules
1354 * then set *mod = -1 and return
1356 if (modctl(MODINFO, 0, &modinfo) < 0) {
1357 *mod = -1;
1358 return;
1361 *mod = modinfo.mi_id;
1362 } while (strcmp(driver_name, modinfo.mi_name) != 0);
1366 create_reconfig(char *basedir)
1368 char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1];
1369 FILE *reconfig_fp;
1371 if (basedir != NULL) {
1372 (void) strcpy(reconfig_file, basedir);
1373 (void) strcat(reconfig_file, RECONFIGURE);
1374 } else {
1375 (void) strcpy(reconfig_file, RECONFIGURE);
1377 if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL)
1378 return (ERROR);
1380 (void) fclose(reconfig_fp);
1381 return (NOERR);
1386 * update_minor_entry:
1387 * open file
1388 * for each entry in list
1389 * where list entries are separated by <list_separator>
1390 * modify entry : driver_name <entry_separator> entry
1391 * close file
1393 * return error/noerr
1396 update_minor_entry(char *driver_name, char *perm_list)
1398 FILE *fp;
1399 FILE *newfp;
1400 int match = 0;
1401 char line[MAX_DBFILE_ENTRY];
1402 char drv[FILENAME_MAX + 1];
1403 char minor[FILENAME_MAX + 1];
1404 char perm[OPT_LEN + 1];
1405 char own[OPT_LEN + 1];
1406 char grp[OPT_LEN + 1];
1407 int status = NOERR, i;
1408 char newfile[MAXPATHLEN];
1409 char *cp, *dup, *drv_minor;
1410 struct group *sysgrp;
1411 int newfd;
1413 if ((fp = fopen(minor_perm, "r")) == NULL) {
1414 perror(NULL);
1415 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1416 minor_perm);
1418 return (ERROR);
1421 /* Build filename for temporary file */
1422 (void) snprintf(newfile, sizeof (newfile), "%s%s", minor_perm, ".hold");
1425 * Set gid so we preserve group attribute. Ideally we wouldn't
1426 * assume a gid of "sys" but we can't undo the damage on already
1427 * installed systems unless we force the issue.
1429 if ((sysgrp = getgrnam("sys")) != NULL) {
1430 (void) setgid(sysgrp->gr_gid);
1433 if ((newfd = open(newfile, O_WRONLY | O_CREAT | O_EXCL, 0644)) < 0) {
1434 if (errno == EEXIST) {
1435 (void) fprintf(stderr, gettext(ERR_FILE_EXISTS),
1436 newfile);
1437 return (ERROR);
1438 } else {
1439 (void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
1440 newfile);
1441 return (ERROR);
1445 if ((newfp = fdopen(newfd, "w")) == NULL) {
1446 perror(NULL);
1447 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1448 newfile);
1449 (void) close(newfd);
1450 return (ERROR);
1453 if (sscanf(perm_list,
1454 "%" VAL2STR(FILENAME_MAX) "s" /* minor */
1455 "%" VAL2STR(OPT_LEN) "s" /* perm */
1456 "%" VAL2STR(OPT_LEN) "s" /* own */
1457 "%" VAL2STR(OPT_LEN) "s", /* grp */
1458 minor, perm, own, grp) != 4) {
1459 status = ERROR;
1462 while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
1463 /* copy the whole line into dup */
1464 if ((dup = strdup(line)) == NULL) {
1465 perror(NULL);
1466 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1467 status = ERROR;
1468 break;
1470 /* cut off comments starting with '#' */
1471 if ((cp = strchr(dup, '#')) != NULL)
1472 *cp = '\0';
1473 /* ignore comment or blank lines */
1474 if (is_blank(dup)) {
1475 if (fputs(line, newfp) == EOF) {
1476 (void) fprintf(stderr, gettext(ERR_UPDATE),
1477 minor_perm);
1478 status = ERROR;
1480 free(dup);
1481 continue;
1484 /* get the driver name */
1485 if (sscanf(dup, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
1486 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1487 minor_perm, line);
1488 status = ERROR;
1489 free(dup);
1490 break;
1494 * get the minor name; place the NULL character at the
1495 * end of the driver name, then make the drv_minor
1496 * point to the first character of the minor name.
1497 * the line missing ':' must be treated as a broken one.
1499 i = strcspn(drv, ":");
1500 if (i == strlen(drv)) {
1501 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1502 minor_perm, line);
1503 status = ERROR;
1504 free(dup);
1505 break;
1507 drv[i] = '\0';
1508 drv_minor = &drv[strlen(drv) + 1];
1511 * compare both of the driver name and the minor name.
1512 * then the new line should be written to the file if
1513 * both of them match
1515 if ((strcmp(drv, driver_name) == 0) &&
1516 (strcmp(minor, drv_minor) == 0)) {
1517 /* if it has a comment, keep it */
1518 if (cp != NULL) {
1519 cp++; /* skip a terminator */
1520 (void) snprintf(line, sizeof (line),
1521 "%s:%s %s %s %s #%s\n",
1522 drv, minor, perm, own, grp, cp);
1523 } else {
1524 (void) snprintf(line, sizeof (line),
1525 "%s:%s %s %s %s\n",
1526 drv, minor, perm, own, grp);
1528 match = 1;
1530 free(dup);
1532 /* update the file */
1533 if ((fputs(line, newfp)) == EOF) {
1534 (void) fprintf(stderr, gettext(ERR_UPDATE),
1535 minor_perm);
1536 status = ERROR;
1540 if (!match) {
1541 (void) bzero(line, sizeof (&line[0]));
1542 (void) snprintf(line, sizeof (line),
1543 "%s:%s %s %s %s\n",
1544 driver_name, minor, perm, own, grp);
1546 /* add the new entry */
1547 if ((fputs(line, newfp)) == EOF) {
1548 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1549 status = ERROR;
1553 (void) fclose(fp);
1555 if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
1556 status = ERROR;
1558 (void) fclose(newfp);
1561 * if error, leave original file, delete new file
1562 * if noerr, replace original file with new file
1564 if (status == NOERR) {
1565 if (rename(newfile, minor_perm) == -1) {
1566 perror(NULL);
1567 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1568 (void) unlink(newfile);
1569 return (ERROR);
1571 } else {
1573 * since there's an error, leave file alone; remove
1574 * new file
1576 if (unlink(newfile) == -1) {
1577 (void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
1579 return (ERROR);
1582 return (NOERR);
1588 * list_entry:
1589 * open file
1590 * read thru file, listing all entries if first entry = driver_name
1591 * close
1593 void
1594 list_entry(
1595 char *oldfile,
1596 char *driver_name,
1597 char *marker)
1599 FILE *fp;
1600 int i;
1601 char line[MAX_DBFILE_ENTRY], *cp;
1602 char drv[FILENAME_MAX + 1];
1604 if ((fp = fopen(oldfile, "r")) == NULL) {
1605 perror(NULL);
1606 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
1608 return;
1611 while (fgets(line, sizeof (line), fp) != NULL) {
1612 /* cut off comments starting with '#' */
1613 if ((cp = strchr(line, '#')) != NULL)
1614 *cp = '\0';
1615 /* ignore comment or blank lines */
1616 if (is_blank(line))
1617 continue;
1618 /* sanity-check */
1619 if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
1620 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1621 oldfile, line);
1624 for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
1625 drv[i] = '\0';
1628 if (strcmp(driver_name, drv) == 0) {
1629 (void) fprintf(stdout, "%s", line);
1633 (void) fclose(fp);
1636 static boolean_t
1637 is_token(char *tok)
1640 * Check the token here. According to IEEE1275 Open Firmware Boot
1641 * Standard, the name is composed of 1 to 31 letters,
1642 * digits and punctuation characters from the set ",._+-", and
1643 * uppercase and lowercase characters are considered distinct.
1644 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
1645 * However, since either the definition of driver or aliase names is
1646 * not known well, only '#' is avoided explicitly. (the kernel lexical
1647 * analyzer treats it as a start of a comment)
1649 for (/* nothing */; *tok != '\0'; tok++)
1650 if (*tok == '#' || iscntrl(*tok))
1651 return (B_FALSE);
1653 return (B_TRUE);
1657 * check each entry in perm_list for:
1658 * 4 arguments
1659 * permission arg is in valid range
1660 * permlist entries separated by comma
1661 * return ERROR/NOERR
1664 check_perm_opts(char *perm_list)
1666 char *current_head;
1667 char *previous_head;
1668 char *one_entry;
1669 int len, scan_stat;
1670 char minor[FILENAME_MAX + 1];
1671 char perm[OPT_LEN + 1];
1672 char own[OPT_LEN + 1];
1673 char grp[OPT_LEN + 1];
1674 char dumb[OPT_LEN + 1];
1675 int status = NOERR;
1676 int intperm;
1678 if ((len = strlen(perm_list)) == 0)
1679 return (ERROR);
1681 if ((one_entry = calloc(len + 1, 1)) == NULL) {
1682 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1683 return (ERROR);
1686 previous_head = perm_list;
1687 current_head = perm_list;
1689 while (*current_head != '\0') {
1690 bzero(one_entry, len + 1);
1691 current_head = get_perm_entry(previous_head, one_entry);
1693 previous_head = current_head;
1694 scan_stat = sscanf(one_entry,
1695 "%" VAL2STR(FILENAME_MAX) "s" /* minor */
1696 "%" VAL2STR(OPT_LEN) "s" /* perm */
1697 "%" VAL2STR(OPT_LEN) "s" /* own */
1698 "%" VAL2STR(OPT_LEN) "s" /* grp */
1699 "%" VAL2STR(OPT_LEN) "s", /* dumb */
1700 minor, perm, own, grp, dumb);
1702 if (scan_stat < 4) {
1703 (void) fprintf(stderr, gettext(ERR_MIS_TOK),
1704 "-m", one_entry);
1705 status = ERROR;
1707 if (scan_stat > 4) {
1708 (void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS),
1709 "-m", one_entry);
1710 status = ERROR;
1713 intperm = atoi(perm);
1714 if (intperm < 0000 || intperm > 4777) {
1715 (void) fprintf(stderr, gettext(ERR_BAD_MODE), perm);
1716 status = ERROR;
1720 free(one_entry);
1721 return (status);
1726 * check each alias :
1727 * alias list members separated by white space
1728 * cannot exist as driver name in /etc/name_to_major
1729 * cannot exist as driver or alias name in /etc/driver_aliases
1732 aliases_unique(char *aliases)
1734 char *current_head;
1735 char *previous_head;
1736 char *one_entry;
1737 int len;
1738 int is_unique;
1739 int err;
1741 len = strlen(aliases);
1743 one_entry = calloc(len + 1, 1);
1744 if (one_entry == NULL) {
1745 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1746 return (ERROR);
1749 previous_head = aliases;
1751 do {
1752 bzero(one_entry, len+1);
1753 current_head = get_entry(previous_head, one_entry, ' ', 1);
1754 previous_head = current_head;
1756 if ((unique_driver_name(one_entry, name_to_major,
1757 &is_unique)) == ERROR)
1758 goto err_out;
1760 if (is_unique != UNIQUE) {
1761 (void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ),
1762 one_entry);
1763 goto err_out;
1766 if ((err = unique_drv_alias(one_entry)) != UNIQUE) {
1767 if (err == NOT_UNIQUE) {
1768 (void) fprintf(stderr,
1769 gettext(ERR_ALIAS_IN_USE), one_entry);
1771 goto err_out;
1774 if (!is_token(one_entry)) {
1775 (void) fprintf(stderr, gettext(ERR_BAD_TOK),
1776 "-i", one_entry);
1777 goto err_out;
1780 } while (*current_head != '\0');
1782 free(one_entry);
1783 return (NOERR);
1785 err_out:
1786 free(one_entry);
1787 return (ERROR);
1791 * verify each alias :
1792 * alias list members separated by white space and quoted
1793 * exist as alias name in /etc/driver_aliases
1796 aliases_exist(char *aliases)
1798 char *current_head;
1799 char *previous_head;
1800 char *one_entry;
1801 int len;
1803 len = strlen(aliases);
1805 one_entry = calloc(len + 1, 1);
1806 if (one_entry == NULL) {
1807 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1808 return (ERROR);
1811 previous_head = aliases;
1813 do {
1814 bzero(one_entry, len+1);
1815 current_head = get_entry(previous_head, one_entry, ' ', 1);
1816 previous_head = current_head;
1818 if (unique_drv_alias(one_entry) != NOT_UNIQUE)
1819 goto err_out;
1821 if (!is_token(one_entry)) {
1822 (void) fprintf(stderr, gettext(ERR_BAD_TOK),
1823 "-i", one_entry);
1824 goto err_out;
1827 } while (*current_head != '\0');
1829 free(one_entry);
1830 return (NOERR);
1832 err_out:
1833 free(one_entry);
1834 return (ERROR);
1839 * check each alias :
1840 * if path-oriented alias, path exists
1843 aliases_paths_exist(char *aliases)
1845 char *current_head;
1846 char *previous_head;
1847 char *one_entry;
1848 int i, len;
1849 char path[MAXPATHLEN];
1850 struct stat buf;
1852 len = strlen(aliases);
1854 one_entry = calloc(len + 1, 1);
1855 if (one_entry == NULL) {
1856 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1857 return (ERROR);
1860 previous_head = aliases;
1862 do {
1863 for (i = 0; i <= len; i++)
1864 one_entry[i] = 0;
1866 current_head = get_entry(previous_head, one_entry, ' ', 1);
1867 previous_head = current_head;
1869 /* if the alias is a path, ensure that the path exists */
1870 if (*one_entry != '/')
1871 continue;
1872 (void) snprintf(path, sizeof (path), "/devices/%s", one_entry);
1873 if (stat(path, &buf) == 0)
1874 continue;
1876 /* no device at specified path-oriented alias path */
1877 (void) fprintf(stderr, gettext(ERR_PATH_ORIENTED_ALIAS),
1878 one_entry);
1879 free(one_entry);
1880 return (ERROR);
1882 } while (*current_head != '\0');
1884 free(one_entry);
1886 return (NOERR);
1891 update_driver_aliases(
1892 char *driver_name,
1893 char *aliases)
1895 /* make call to update the aliases file */
1896 return (append_to_file(driver_name, aliases, driver_aliases,
1897 ' ', " ", 1));
1902 * Return:
1903 * ERROR in case of memory or read error
1904 * UNIQUE if there is no existing match to the supplied alias
1905 * NOT_UNIQUE if there is a match
1906 * An error message is emitted in the case of ERROR,
1907 * up to the caller otherwise.
1910 unique_drv_alias(char *drv_alias)
1912 FILE *fp;
1913 char drv[FILENAME_MAX + 1];
1914 char line[MAX_N2M_ALIAS_LINE + 1], *cp;
1915 char alias[FILENAME_MAX + 1];
1916 char *a;
1917 int status = UNIQUE;
1919 fp = fopen(driver_aliases, "r");
1921 if (fp != NULL) {
1922 while ((fgets(line, sizeof (line), fp) != 0) &&
1923 status == UNIQUE) {
1924 /* cut off comments starting with '#' */
1925 if ((cp = strchr(line, '#')) != NULL)
1926 *cp = '\0';
1927 /* ignore comment or blank lines */
1928 if (is_blank(line))
1929 continue;
1930 /* sanity-check */
1931 if (sscanf(line,
1932 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
1933 "%" VAL2STR(FILENAME_MAX) "s", /* alias */
1934 drv, alias) != 2)
1935 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1936 driver_aliases, line);
1938 /* unquote for compare */
1939 if ((*alias == '"') &&
1940 (*(alias + strlen(alias) - 1) == '"')) {
1941 a = &alias[1];
1942 alias[strlen(alias) - 1] = '\0';
1943 } else
1944 a = alias;
1946 if ((strcmp(drv_alias, drv) == 0) ||
1947 (strcmp(drv_alias, a) == 0)) {
1948 status = NOT_UNIQUE;
1949 break;
1952 (void) fclose(fp);
1953 return (status);
1954 } else {
1955 perror(NULL);
1956 (void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
1957 return (ERROR);
1963 * search for driver_name in first field of file file_name
1964 * searching name_to_major and driver_aliases: name separated
1965 * from the remainder of the line by white space.
1968 unique_driver_name(char *driver_name, char *file_name,
1969 int *is_unique)
1971 int ret, err;
1973 if ((ret = get_major_no(driver_name, file_name)) == ERROR) {
1974 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1975 file_name);
1976 } else {
1977 /* check alias file for name collision */
1978 if ((err = unique_drv_alias(driver_name)) != UNIQUE) {
1979 if (err == NOT_UNIQUE) {
1980 (void) fprintf(stderr,
1981 gettext(ERR_ALIAS_IN_USE),
1982 driver_name);
1984 ret = ERROR;
1985 } else {
1986 if (ret != UNIQUE)
1987 *is_unique = NOT_UNIQUE;
1988 else
1989 *is_unique = ret;
1990 ret = NOERR;
1993 return (ret);
1997 * returns:
1998 * SUCCESS - not an existing driver alias
1999 * NOT_UNIQUE - matching driver alias exists
2000 * ERROR - an error occurred
2003 check_duplicate_driver_alias(char *driver_name, char *drv_alias)
2005 FILE *fp;
2006 char drv[FILENAME_MAX + 1];
2007 char line[MAX_N2M_ALIAS_LINE + 1], *cp;
2008 char alias[FILENAME_MAX + 1];
2009 char *a;
2010 int status = SUCCESS;
2012 if ((fp = fopen(driver_aliases, "r")) == NULL) {
2013 perror(NULL);
2014 (void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
2015 return (ERROR);
2018 while (fgets(line, sizeof (line), fp) != 0) {
2019 /* cut off comments starting with '#' */
2020 if ((cp = strchr(line, '#')) != NULL)
2021 *cp = '\0';
2022 /* ignore comment or blank lines */
2023 if (is_blank(line))
2024 continue;
2025 /* sanity-check */
2026 if (sscanf(line,
2027 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
2028 "%" VAL2STR(FILENAME_MAX) "s", /* alias */
2029 drv, alias) != 2)
2030 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
2031 driver_aliases, line);
2033 /* unquote for compare */
2034 if ((*alias == '"') &&
2035 (*(alias + strlen(alias) - 1) == '"')) {
2036 a = &alias[1];
2037 alias[strlen(alias) - 1] = '\0';
2038 } else
2039 a = alias;
2041 if ((strcmp(drv_alias, a) == 0) &&
2042 (strcmp(drv, driver_name) == 0)) {
2043 status = NOT_UNIQUE;
2046 if ((strcmp(drv_alias, drv) == 0) ||
2047 ((strcmp(drv_alias, a) == 0) &&
2048 (strcmp(drv, driver_name) != 0))) {
2049 (void) fprintf(stderr,
2050 gettext(ERR_ALIAS_IN_USE),
2051 drv_alias);
2052 status = ERROR;
2053 goto done;
2057 done:
2058 (void) fclose(fp);
2059 return (status);
2063 trim_duplicate_aliases(char *driver_name, char *aliases, char **aliases2p)
2065 char *current_head;
2066 char *previous_head;
2067 char *one_entry;
2068 char *aliases2;
2069 int rv, len;
2070 int n = 0;
2072 *aliases2p = NULL;
2073 len = strlen(aliases) + 1;
2075 one_entry = calloc(len, 1);
2076 aliases2 = calloc(len, 1);
2077 if (one_entry == NULL || aliases2 == NULL) {
2078 (void) fprintf(stderr, gettext(ERR_NO_MEM));
2079 return (ERROR);
2082 previous_head = aliases;
2084 do {
2085 (void) bzero(one_entry, len);
2086 current_head = get_entry(previous_head, one_entry, ' ', 1);
2087 previous_head = current_head;
2089 rv = check_duplicate_driver_alias(driver_name, one_entry);
2090 switch (rv) {
2091 case SUCCESS:
2092 /* not an existing driver alias: add it */
2093 if (n > 0) {
2094 if (strlcat(aliases2, " ", len) >= len)
2095 goto err;
2097 if (strlcat(aliases2, one_entry, len) >= len)
2098 goto err;
2099 n++;
2100 break;
2101 case NOT_UNIQUE:
2102 /* matching driver alias exists: do not add it */
2103 break;
2104 case ERROR:
2105 /* error reading the alias file */
2106 goto err;
2107 default:
2108 goto err;
2111 if (!is_token(one_entry)) {
2112 (void) fprintf(stderr, gettext(ERR_BAD_TOK),
2113 "-i", one_entry);
2114 goto err;
2116 } while (*current_head != '\0');
2119 * If all the aliases listed are already
2120 * present we actually have none to do.
2122 if (n == 0) {
2123 free(aliases2);
2124 } else {
2125 *aliases2p = aliases2;
2127 free(one_entry);
2128 return (NOERR);
2130 err:
2131 free(aliases2);
2132 free(one_entry);
2133 return (ERROR);
2137 check_space_within_quote(char *str)
2139 register int i;
2140 register int len;
2141 int quoted = 0;
2143 len = strlen(str);
2144 for (i = 0; i < len; i++, str++) {
2145 if (*str == '"') {
2146 if (quoted == 0)
2147 quoted++;
2148 else
2149 quoted--;
2150 } else if (*str == ' ' && quoted)
2151 return (ERROR);
2154 return (0);
2159 * get major number
2160 * write driver_name major_num to name_to_major file
2161 * major_num returned in major_num
2162 * return success/failure
2165 update_name_to_major(char *driver_name, major_t *major_num, int server)
2167 char major[MAX_STR_MAJOR + 1];
2168 struct stat buf;
2169 char *num_list;
2170 char drv_majnum_str[MAX_STR_MAJOR + 1];
2171 int new_maj = -1;
2172 int i, tmp = 0, is_unique, have_rem_n2m = 0;
2173 int max_dev = 0;
2176 * if driver_name already in rem_name_to_major
2177 * delete entry from rem_nam_to_major
2178 * put entry into name_to_major
2181 if (stat(rem_name_to_major, &buf) == 0) {
2182 have_rem_n2m = 1;
2185 if (have_rem_n2m) {
2186 if ((is_unique = get_major_no(driver_name, rem_name_to_major))
2187 == ERROR)
2188 return (ERROR);
2191 * found a match in rem_name_to_major
2193 if (is_unique != UNIQUE) {
2194 char scratch[FILENAME_MAX];
2197 * If there is a match in /etc/rem_name_to_major then
2198 * be paranoid: is that major number already in
2199 * /etc/name_to_major (potentially under another name)?
2201 if (get_driver_name(is_unique, name_to_major,
2202 scratch) != UNIQUE) {
2204 * nuke the rem_name_to_major entry-- it
2205 * isn't helpful.
2207 (void) delete_entry(rem_name_to_major,
2208 driver_name, " ", NULL);
2209 } else {
2210 (void) snprintf(major, sizeof (major),
2211 "%d", is_unique);
2213 if (append_to_file(driver_name, major,
2214 name_to_major, ' ', " ", 0) == ERROR) {
2215 (void) fprintf(stderr,
2216 gettext(ERR_NO_UPDATE),
2217 name_to_major);
2218 return (ERROR);
2221 if (delete_entry(rem_name_to_major,
2222 driver_name, " ", NULL) == ERROR) {
2223 (void) fprintf(stderr,
2224 gettext(ERR_DEL_ENTRY), driver_name,
2225 rem_name_to_major);
2226 return (ERROR);
2229 /* found matching entry : no errors */
2230 *major_num = is_unique;
2231 return (NOERR);
2237 * Bugid: 1264079
2238 * In a server case (with -b option), we can't use modctl() to find
2239 * the maximum major number, we need to dig thru client's
2240 * /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
2242 * if (server)
2243 * get maximum major number thru (rem_)name_to_major file on client
2244 * else
2245 * get maximum major number allowable on current system using modctl
2247 if (server) {
2248 max_dev = 0;
2249 tmp = 0;
2251 max_dev = get_max_major(name_to_major);
2253 /* If rem_name_to_major exists, we need to check it too */
2254 if (have_rem_n2m) {
2255 tmp = get_max_major(rem_name_to_major);
2258 * If name_to_major is missing, we can get max_dev from
2259 * /etc/rem_name_to_major. If both missing, bail out!
2261 if ((max_dev == ERROR) && (tmp == ERROR)) {
2262 (void) fprintf(stderr,
2263 gettext(ERR_CANT_ACCESS_FILE),
2264 name_to_major);
2265 return (ERROR);
2268 /* guard against bigger maj_num in rem_name_to_major */
2269 if (tmp > max_dev)
2270 max_dev = tmp;
2271 } else {
2273 * If we can't get major from name_to_major file
2274 * and there is no /etc/rem_name_to_major file,
2275 * then we don't have a max_dev, bail out quick!
2277 if (max_dev == ERROR)
2278 return (ERROR);
2282 * In case there is no more slack in current name_to_major
2283 * table, provide at least 1 extra entry so the add_drv can
2284 * succeed. Since only one add_drv process is allowed at one
2285 * time, and hence max_dev will be re-calculated each time
2286 * add_drv is ran, we don't need to worry about adding more
2287 * than 1 extra slot for max_dev.
2289 max_dev++;
2291 } else {
2292 if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
2293 perror(NULL);
2294 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
2295 return (ERROR);
2300 * max_dev is really how many slots the kernel has allocated for
2301 * devices... [0 , maxdev-1], not the largest available device num.
2303 if ((num_list = calloc(max_dev, 1)) == NULL) {
2304 (void) fprintf(stderr, gettext(ERR_NO_MEM));
2305 return (ERROR);
2309 * Populate the num_list array
2311 if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) {
2312 return (ERROR);
2314 if (have_rem_n2m) {
2315 if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0)
2316 return (ERROR);
2320 * Find the first free major number.
2322 * Note that we begin searching from 1, as drivers have developer the
2323 * erroneous assumption that a dev_t of 0 is invalid, and since we no
2324 * longer include system devices in the base files, a major number of
2325 * 0 may now be given to arbitrary devices.
2327 for (i = 1; i < max_dev; i++) {
2328 if (num_list[i] != 1) {
2329 new_maj = i;
2330 break;
2334 if (new_maj == -1) {
2335 (void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR));
2336 return (ERROR);
2339 (void) snprintf(drv_majnum_str, sizeof (drv_majnum_str),
2340 "%d", new_maj);
2341 if (do_the_update(driver_name, drv_majnum_str) == ERROR) {
2342 return (ERROR);
2345 *major_num = new_maj;
2346 return (NOERR);
2351 fill_n2m_array(char *filename, char **array, int *nelems)
2353 FILE *fp;
2354 char line[MAX_N2M_ALIAS_LINE + 1], *cp;
2355 char drv[FILENAME_MAX + 1];
2356 u_longlong_t dnum;
2357 major_t drv_majnum;
2360 * Read through the file, marking each major number found
2361 * order is not relevant
2363 if ((fp = fopen(filename, "r")) == NULL) {
2364 perror(NULL);
2365 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename);
2366 return (ERROR);
2369 while (fgets(line, sizeof (line), fp) != 0) {
2370 /* cut off comments starting with '#' */
2371 if ((cp = strchr(line, '#')) != NULL)
2372 *cp = '\0';
2373 /* ignore comment or blank lines */
2374 if (is_blank(line))
2375 continue;
2376 /* sanity-check */
2377 if (sscanf(line,
2378 "%" VAL2STR(FILENAME_MAX) "s %llu", drv, &dnum) != 2) {
2379 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
2380 filename, line);
2381 (void) fclose(fp);
2382 return (ERROR);
2385 if (dnum > L_MAXMAJ32) {
2386 (void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv,
2387 dnum, filename, L_MAXMAJ32);
2388 continue;
2391 * cast down to a major_t; we can be sure this is safe because
2392 * of the above range-check.
2394 drv_majnum = (major_t)dnum;
2396 if (drv_majnum >= *nelems) {
2398 * Allocate some more space, up to drv_majnum + 1 so
2399 * we can accomodate 0 through drv_majnum.
2401 * Note that in the failure case, we leak all of the
2402 * old contents of array. It's ok, since we just
2403 * wind up exiting immediately anyway.
2405 *nelems = drv_majnum + 1;
2406 *array = realloc(*array, *nelems);
2407 if (*array == NULL) {
2408 (void) fprintf(stderr, gettext(ERR_NO_MEM));
2409 return (ERROR);
2412 (*array)[drv_majnum] = 1;
2415 (void) fclose(fp);
2416 return (0);
2421 do_the_update(char *driver_name, char *major_number)
2423 return (append_to_file(driver_name, major_number, name_to_major,
2424 ' ', " ", 0));
2428 * is_blank() returns 1 (true) if a line specified is composed of
2429 * whitespace characters only. otherwise, it returns 0 (false).
2431 * Note. the argument (line) must be null-terminated.
2433 static int
2434 is_blank(char *line)
2436 for (/* nothing */; *line != '\0'; line++)
2437 if (!isspace(*line))
2438 return (0);
2439 return (1);