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]
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.
34 #include <sys/sysmacros.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
,
74 static int is_blank(char *);
78 log_minorperm_error(minorperm_err_t err
, int key
)
82 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
86 (void) fprintf(stderr
, gettext(ERR_NO_UPDATE
),
88 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
90 case MP_IGNORING_LINE_ERR
:
91 (void) fprintf(stderr
, gettext(ERR_NO_UPDATE
),
95 (void) fprintf(stderr
, gettext(ERR_NO_UPDATE
),
97 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
100 (void) fprintf(stderr
, gettext(ERR_NO_UPDATE
),
102 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
104 case MP_CANT_FIND_USER_ERR
:
105 (void) fprintf(stderr
, gettext(ERR_NO_UPDATE
),
108 case MP_CANT_FIND_GROUP_ERR
:
109 (void) fprintf(stderr
, gettext(ERR_NO_UPDATE
),
117 * for each entry in list
118 * where list entries are separated by <list_separator>
119 * append entry : driver_name <entry_separator> entry
129 char *entry_separator
,
134 char *current_head
, *previous_head
;
135 char *line
, *one_entry
;
138 if ((fp
= fopen(filename
, "a")) == NULL
) {
140 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
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
));
155 previous_head
= entry_list
;
157 line_len
= strlen(driver_name
) + len
+ 4;
161 line
= calloc(line_len
, 1);
163 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
169 * get one entry at a time from list and append to <filename> file
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
) {
186 (void) fprintf(stderr
, gettext(ERR_NO_UPDATE
),
190 } while (*current_head
!= '\0');
208 * for each entry in list
209 * where list entries are separated by <list_separator>
210 * append entry : driver_name <entry_separator> entry
215 append_to_minor_perm(
222 char *current_head
, *previous_head
;
223 char *line
, *one_entry
;
226 if ((fp
= fopen(filename
, "a")) == NULL
) {
228 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
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
));
243 previous_head
= entry_list
;
245 line_len
= strlen(driver_name
) + len
+ 4;
246 line
= calloc(line_len
, 1);
248 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
254 * get one entry at a time from list and append to <filename> file
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
) {
268 (void) fprintf(stderr
, gettext(ERR_NO_UPDATE
),
272 } while (*current_head
!= '\0');
289 * Require exact match to delete a driver alias/permission entry.
290 * Note line argument does not remain unchanged. Return 1 if matched.
293 match_entry(char *line
, char *match
)
298 /* skip any leading white space */
299 while (*line
&& isspace(*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
)
310 /* skip leading white space and quotes */
311 while (*token
&& (isspace(*token
) || isquote(*token
)))
313 /* strip trailing newline, white space and quotes */
316 while (n
> 0 && (*p
== '\n' || isspace(*p
) || isquote(*p
))) {
322 return (strcmp(token
, match
) == 0);
327 * read thru file, deleting all entries if first
328 * entry = driver_name
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
337 * delete just the matching entry
350 boolean_t nomatch
= B_TRUE
;
351 char newfile
[MAXPATHLEN
];
353 char line
[MAX_DBFILE_ENTRY
];
354 char drv
[FILENAME_MAX
+ 1];
356 struct group
*sysgrp
;
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
368 while (*cp
&& (isspace(*cp
)))
372 if ((match2
= strdup(cp
)) == NULL
) {
374 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
377 i
= strlen(match2
) - 1;
378 while (i
>= 0 && (isspace(match2
[i
]))) {
383 if (match2
== NULL
|| (strlen(match2
) == 0)) {
384 (void) fprintf(stderr
,
385 gettext(ERR_INT_UPDATE
), oldfile
);
390 if ((fp
= fopen(oldfile
, "r")) == NULL
) {
392 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
), oldfile
);
396 /* Space for defensive copy of input line */
397 if ((copy
= calloc(sizeof (line
), 1)) == NULL
) {
399 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
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
),
421 (void) fprintf(stderr
, gettext(ERR_CREAT_LOCK
),
427 if ((newfp
= fdopen(newfd
, "w")) == NULL
) {
429 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
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
);
442 /* cut off comments starting with '#' */
443 if ((cp
= strchr(copy
, '#')) != NULL
)
445 /* ignore comment or blank lines */
446 if (is_blank(copy
)) {
447 if (fputs(line
, newfp
) == EOF
) {
448 (void) fprintf(stderr
, gettext(ERR_UPDATE
),
455 /* get the driver name */
456 if (sscanf(copy
, "%" VAL2STR(FILENAME_MAX
) "s", drv
) != 1) {
457 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
463 for (i
= strcspn(drv
, marker
); i
< FILENAME_MAX
; i
++) {
467 if (strcmp(driver_name
, drv
) != 0) {
468 if ((fputs(line
, newfp
)) == EOF
) {
469 (void) fprintf(stderr
, gettext(ERR_UPDATE
),
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
))
484 (void) fprintf(stderr
,
490 if (match_entry(copy
, match2
)) {
493 if ((fputs(line
, newfp
)) ==
495 (void) fprintf(stderr
,
500 if (nomatch
!= B_FALSE
)
514 /* Make sure that the file is on disk */
515 if (fflush(newfp
) != 0 || fsync(fileno(newfp
)) != 0)
520 (void) fclose(newfp
);
522 /* no matching driver found */
525 (nomatch
== B_TRUE
)) {
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) {
537 (void) fprintf(stderr
, gettext(ERR_UPDATE
), oldfile
);
538 (void) unlink(newfile
);
543 * since there's an error, leave file alone; remove
546 if (unlink(newfile
) == -1) {
547 (void) fprintf(stderr
, gettext(ERR_CANT_RM
), newfile
);
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
)
565 if (get_name_to_major_entry(&major
, driver_name
, file_name
) == ERROR
)
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
)
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.
591 get_cached_n_to_m_file(char *filename
, char ***cache
)
593 struct n_to_m_cache
{
597 struct n_to_m_cache
*next
;
599 static struct n_to_m_cache
*head
= NULL
;
600 struct n_to_m_cache
*ptr
;
602 char drv
[FILENAME_MAX
+ 1];
603 char entry
[FILENAME_MAX
+ 1];
604 char line
[MAX_N2M_ALIAS_LINE
], *cp
;
610 * see if the file is already cached - either
611 * rem_name_to_major or name_to_major
614 while (ptr
!= NULL
) {
615 if (strcmp(ptr
->file
, filename
) == 0)
620 if (ptr
== NULL
) { /* we need to cache the contents */
621 if ((fp
= fopen(filename
, "r")) == NULL
) {
623 (void) fprintf(stderr
, gettext(ERR_CANT_OPEN
),
628 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
629 /* cut off comments starting with '#' */
630 if ((cp
= strchr(line
, '#')) != NULL
)
632 /* ignore comment or blank lines */
637 "%" VAL2STR(FILENAME_MAX
) "s" /* drv */
638 "%" VAL2STR(FILENAME_MAX
) "s", /* entry */
640 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
649 /* allocate struct to cache the file */
650 ptr
= (struct n_to_m_cache
*)calloc(1,
651 sizeof (struct n_to_m_cache
));
653 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
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
) {
661 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
669 * the cache is an array of char pointers indexed by major
672 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
673 /* cut off comments starting with '#' */
674 if ((cp
= strchr(line
, '#')) != NULL
)
676 /* ignore comment or blank lines */
681 "%" VAL2STR(FILENAME_MAX
) "s" /* drv */
682 "%" VAL2STR(FILENAME_MAX
) "s", /* entry */
684 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
689 if ((ptr
->cached_file
[maj
] = strdup(drv
)) == NULL
) {
690 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
691 free(ptr
->cached_file
);
695 (void) strcpy(ptr
->cached_file
[maj
], drv
);
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
);
705 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
711 /* return value pointer to contents of file */
712 *cache
= ptr
->cached_file
;
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.
744 get_name_to_major_entry(int *major_no
, char *driver_name
, char *file_name
)
747 char **n_to_m_cache
= NULL
;
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
);
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)) {
772 /* search with major number as key */
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
)
782 if (n_to_m_cache
[*major_no
] != NULL
) {
783 (void) strcpy(driver_name
, n_to_m_cache
[*major_no
]);
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).
808 /* skip white space */
809 while (isspace(*ptr
))
812 /* if unquote skip leading quote */
813 if (unquote
&& *ptr
== '"') {
818 /* read thru the current entry looking for end, separator, or unquote */
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
))
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.
854 int maxfields
= 4; /* fields in a permissions format */
857 while (isspace(*ptr
))
861 /* comma allowed in minor name token only */
862 if (*ptr
== ',' && nfields
> 0) {
864 } else if (isspace(*ptr
)) {
865 *current_entry
++ = *ptr
++;
866 while (isspace(*ptr
))
868 if (++nfields
== maxfields
)
871 *current_entry
++ = *ptr
++;
873 *current_entry
= '\0';
875 while (isspace(*ptr
))
878 ptr
++; /* skip over optional trailing comma */
880 while (isspace(*ptr
))
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
));
903 lock
.l_type
= F_WRLCK
;
904 lock
.l_whence
= SEEK_SET
;
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
));
913 (void) fprintf(stderr
, gettext(ERR_LOCK
),
914 add_rem_lock
, strerror(errno
));
923 /* release memory allocated for moddir */
925 /* remove add_drv/rem_drv lock */
933 struct drvmod_dir
*walk_ptr
;
934 struct drvmod_dir
*free_ptr
= moddir
;
936 while (free_ptr
!= NULL
) {
937 walk_ptr
= free_ptr
->next
;
948 if (add_rem_lock_fd
< 0)
951 unlock
.l_type
= F_UNLCK
;
952 unlock
.l_whence
= SEEK_SET
;
956 if (fcntl(add_rem_lock_fd
, F_SETLK
, &unlock
) == -1) {
957 (void) fprintf(stderr
, gettext(ERR_UNLOCK
),
958 add_rem_lock
, strerror(errno
));
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
976 if (c_flag
& CLEAN_NAM_MAJ
) {
977 if (delete_entry(name_to_major
, driver_name
, " ",
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
, " ",
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
) ==
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
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
) ==
1022 (void) fprintf(stderr
, gettext(ERR_DEL_ENTRY
),
1023 driver_name
, extra_privs
);
1029 check_perms_aliases(
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
)
1040 /* check minor_perm file : exits and is writable */
1042 if (access(minor_perm
, R_OK
| W_OK
)) {
1044 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
1050 /* check driver_aliases file : exits and is writable */
1052 if (access(driver_aliases
, R_OK
| W_OK
)) {
1054 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
1065 check_name_to_major(int mode
)
1067 /* check name_to_major file : exists and is writable */
1068 if (access(name_to_major
, mode
)) {
1070 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
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
)
1088 int driver_aliases_len
;
1089 int driver_classes_len
;
1091 int name_to_major_len
;
1092 int rem_name_to_major_len
;
1093 int add_rem_lock_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
;
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
));
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
);
1169 exec_command(char *path
, char *cmdline
[MAX_CMD_LINE
])
1177 if ((pid
= fork()) == 0) {
1178 (void) execv(path
, cmdline
);
1181 } else if (pid
== -1) {
1184 (void) fprintf(stderr
, gettext(ERR_FORK_FAIL
), cmdline
);
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.
1214 char *cmdline
[MAX_CMD_LINE
];
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";
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
);
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
));
1255 current
= get_entry(previous
,
1256 cmdline
[n
++], ' ', 0);
1259 } while (*current
!= '\0');
1262 cmdline
[n
] = (char *)0;
1264 rv
= exec_command(DRVCONFIG_PATH
, cmdline
);
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
1297 if (modctl(MODRESERVED
, NULL
, &max_dev
) < 0) {
1299 (void) fprintf(stderr
, gettext(ERR_MAX_MAJOR
));
1303 if (major_num
>= max_dev
) {
1304 (void) fprintf(stderr
, gettext(ERR_MAX_EXCEEDS
),
1305 major_num
, max_dev
);
1309 /* bind major number and driver name */
1310 rv
= exec_devfsadm(B_TRUE
, driver_name
, major_num
,
1311 aliases
, classes
, config_flags
);
1316 remove_entry(cleanup_flag
, driver_name
);
1321 load_driver(char *driver_name
, int verbose_flag
)
1324 char *cmdline
[MAX_CMD_LINE
];
1327 /* build command line */
1328 cmdline
[n
++] = DEVFSADM
;
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
);
1345 get_modid(char *driver_name
, int *mod
)
1347 struct modinfo modinfo
;
1350 modinfo
.mi_info
= MI_INFO_ALL
;
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) {
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];
1371 if (basedir
!= NULL
) {
1372 (void) strcpy(reconfig_file
, basedir
);
1373 (void) strcat(reconfig_file
, RECONFIGURE
);
1375 (void) strcpy(reconfig_file
, RECONFIGURE
);
1377 if ((reconfig_fp
= fopen(reconfig_file
, "a")) == NULL
)
1380 (void) fclose(reconfig_fp
);
1386 * update_minor_entry:
1388 * for each entry in list
1389 * where list entries are separated by <list_separator>
1390 * modify entry : driver_name <entry_separator> entry
1393 * return error/noerr
1396 update_minor_entry(char *driver_name
, char *perm_list
)
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
;
1413 if ((fp
= fopen(minor_perm
, "r")) == NULL
) {
1415 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
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
),
1439 (void) fprintf(stderr
, gettext(ERR_CREAT_LOCK
),
1445 if ((newfp
= fdopen(newfd
, "w")) == NULL
) {
1447 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
1449 (void) close(newfd
);
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) {
1462 while ((fgets(line
, sizeof (line
), fp
) != NULL
) && status
== NOERR
) {
1463 /* copy the whole line into dup */
1464 if ((dup
= strdup(line
)) == NULL
) {
1466 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1470 /* cut off comments starting with '#' */
1471 if ((cp
= strchr(dup
, '#')) != NULL
)
1473 /* ignore comment or blank lines */
1474 if (is_blank(dup
)) {
1475 if (fputs(line
, newfp
) == EOF
) {
1476 (void) fprintf(stderr
, gettext(ERR_UPDATE
),
1484 /* get the driver name */
1485 if (sscanf(dup
, "%" VAL2STR(FILENAME_MAX
) "s", drv
) != 1) {
1486 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
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
),
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 */
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
);
1524 (void) snprintf(line
, sizeof (line
),
1526 drv
, minor
, perm
, own
, grp
);
1532 /* update the file */
1533 if ((fputs(line
, newfp
)) == EOF
) {
1534 (void) fprintf(stderr
, gettext(ERR_UPDATE
),
1541 (void) bzero(line
, sizeof (&line
[0]));
1542 (void) snprintf(line
, sizeof (line
),
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
);
1555 if (fflush(newfp
) != 0 || fsync(fileno(newfp
)) != 0)
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) {
1567 (void) fprintf(stderr
, gettext(ERR_UPDATE
), minor_perm
);
1568 (void) unlink(newfile
);
1573 * since there's an error, leave file alone; remove
1576 if (unlink(newfile
) == -1) {
1577 (void) fprintf(stderr
, gettext(ERR_CANT_RM
), newfile
);
1590 * read thru file, listing all entries if first entry = driver_name
1601 char line
[MAX_DBFILE_ENTRY
], *cp
;
1602 char drv
[FILENAME_MAX
+ 1];
1604 if ((fp
= fopen(oldfile
, "r")) == NULL
) {
1606 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
), oldfile
);
1611 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
1612 /* cut off comments starting with '#' */
1613 if ((cp
= strchr(line
, '#')) != NULL
)
1615 /* ignore comment or blank lines */
1619 if (sscanf(line
, "%" VAL2STR(FILENAME_MAX
) "s", drv
) != 1) {
1620 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
1624 for (i
= strcspn(drv
, marker
); i
< FILENAME_MAX
; i
++) {
1628 if (strcmp(driver_name
, drv
) == 0) {
1629 (void) fprintf(stdout
, "%s", line
);
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
))
1657 * check each entry in perm_list for:
1659 * permission arg is in valid range
1660 * permlist entries separated by comma
1661 * return ERROR/NOERR
1664 check_perm_opts(char *perm_list
)
1667 char *previous_head
;
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];
1678 if ((len
= strlen(perm_list
)) == 0)
1681 if ((one_entry
= calloc(len
+ 1, 1)) == NULL
) {
1682 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
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
),
1707 if (scan_stat
> 4) {
1708 (void) fprintf(stderr
, gettext(ERR_TOO_MANY_ARGS
),
1713 intperm
= atoi(perm
);
1714 if (intperm
< 0000 || intperm
> 4777) {
1715 (void) fprintf(stderr
, gettext(ERR_BAD_MODE
), perm
);
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
)
1735 char *previous_head
;
1741 len
= strlen(aliases
);
1743 one_entry
= calloc(len
+ 1, 1);
1744 if (one_entry
== NULL
) {
1745 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1749 previous_head
= aliases
;
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
)
1760 if (is_unique
!= UNIQUE
) {
1761 (void) fprintf(stderr
, gettext(ERR_ALIAS_IN_NAM_MAJ
),
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
);
1774 if (!is_token(one_entry
)) {
1775 (void) fprintf(stderr
, gettext(ERR_BAD_TOK
),
1780 } while (*current_head
!= '\0');
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
)
1799 char *previous_head
;
1803 len
= strlen(aliases
);
1805 one_entry
= calloc(len
+ 1, 1);
1806 if (one_entry
== NULL
) {
1807 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1811 previous_head
= aliases
;
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
)
1821 if (!is_token(one_entry
)) {
1822 (void) fprintf(stderr
, gettext(ERR_BAD_TOK
),
1827 } while (*current_head
!= '\0');
1839 * check each alias :
1840 * if path-oriented alias, path exists
1843 aliases_paths_exist(char *aliases
)
1846 char *previous_head
;
1849 char path
[MAXPATHLEN
];
1852 len
= strlen(aliases
);
1854 one_entry
= calloc(len
+ 1, 1);
1855 if (one_entry
== NULL
) {
1856 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1860 previous_head
= aliases
;
1863 for (i
= 0; i
<= len
; i
++)
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
!= '/')
1872 (void) snprintf(path
, sizeof (path
), "/devices/%s", one_entry
);
1873 if (stat(path
, &buf
) == 0)
1876 /* no device at specified path-oriented alias path */
1877 (void) fprintf(stderr
, gettext(ERR_PATH_ORIENTED_ALIAS
),
1882 } while (*current_head
!= '\0');
1891 update_driver_aliases(
1895 /* make call to update the aliases file */
1896 return (append_to_file(driver_name
, aliases
, driver_aliases
,
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
)
1913 char drv
[FILENAME_MAX
+ 1];
1914 char line
[MAX_N2M_ALIAS_LINE
+ 1], *cp
;
1915 char alias
[FILENAME_MAX
+ 1];
1917 int status
= UNIQUE
;
1919 fp
= fopen(driver_aliases
, "r");
1922 while ((fgets(line
, sizeof (line
), fp
) != 0) &&
1924 /* cut off comments starting with '#' */
1925 if ((cp
= strchr(line
, '#')) != NULL
)
1927 /* ignore comment or blank lines */
1932 "%" VAL2STR(FILENAME_MAX
) "s" /* drv */
1933 "%" VAL2STR(FILENAME_MAX
) "s", /* alias */
1935 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
1936 driver_aliases
, line
);
1938 /* unquote for compare */
1939 if ((*alias
== '"') &&
1940 (*(alias
+ strlen(alias
) - 1) == '"')) {
1942 alias
[strlen(alias
) - 1] = '\0';
1946 if ((strcmp(drv_alias
, drv
) == 0) ||
1947 (strcmp(drv_alias
, a
) == 0)) {
1948 status
= NOT_UNIQUE
;
1956 (void) fprintf(stderr
, gettext(ERR_CANT_OPEN
), driver_aliases
);
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
,
1973 if ((ret
= get_major_no(driver_name
, file_name
)) == ERROR
) {
1974 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
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
),
1987 *is_unique
= NOT_UNIQUE
;
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
)
2006 char drv
[FILENAME_MAX
+ 1];
2007 char line
[MAX_N2M_ALIAS_LINE
+ 1], *cp
;
2008 char alias
[FILENAME_MAX
+ 1];
2010 int status
= SUCCESS
;
2012 if ((fp
= fopen(driver_aliases
, "r")) == NULL
) {
2014 (void) fprintf(stderr
, gettext(ERR_CANT_OPEN
), driver_aliases
);
2018 while (fgets(line
, sizeof (line
), fp
) != 0) {
2019 /* cut off comments starting with '#' */
2020 if ((cp
= strchr(line
, '#')) != NULL
)
2022 /* ignore comment or blank lines */
2027 "%" VAL2STR(FILENAME_MAX
) "s" /* drv */
2028 "%" VAL2STR(FILENAME_MAX
) "s", /* alias */
2030 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
2031 driver_aliases
, line
);
2033 /* unquote for compare */
2034 if ((*alias
== '"') &&
2035 (*(alias
+ strlen(alias
) - 1) == '"')) {
2037 alias
[strlen(alias
) - 1] = '\0';
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
),
2063 trim_duplicate_aliases(char *driver_name
, char *aliases
, char **aliases2p
)
2066 char *previous_head
;
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
));
2082 previous_head
= aliases
;
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
);
2092 /* not an existing driver alias: add it */
2094 if (strlcat(aliases2
, " ", len
) >= len
)
2097 if (strlcat(aliases2
, one_entry
, len
) >= len
)
2102 /* matching driver alias exists: do not add it */
2105 /* error reading the alias file */
2111 if (!is_token(one_entry
)) {
2112 (void) fprintf(stderr
, gettext(ERR_BAD_TOK
),
2116 } while (*current_head
!= '\0');
2119 * If all the aliases listed are already
2120 * present we actually have none to do.
2125 *aliases2p
= aliases2
;
2137 check_space_within_quote(char *str
)
2144 for (i
= 0; i
< len
; i
++, str
++) {
2150 } else if (*str
== ' ' && quoted
)
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];
2170 char drv_majnum_str
[MAX_STR_MAJOR
+ 1];
2172 int i
, tmp
= 0, is_unique
, have_rem_n2m
= 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) {
2186 if ((is_unique
= get_major_no(driver_name
, rem_name_to_major
))
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
2207 (void) delete_entry(rem_name_to_major
,
2208 driver_name
, " ", NULL
);
2210 (void) snprintf(major
, sizeof (major
),
2213 if (append_to_file(driver_name
, major
,
2214 name_to_major
, ' ', " ", 0) == ERROR
) {
2215 (void) fprintf(stderr
,
2216 gettext(ERR_NO_UPDATE
),
2221 if (delete_entry(rem_name_to_major
,
2222 driver_name
, " ", NULL
) == ERROR
) {
2223 (void) fprintf(stderr
,
2224 gettext(ERR_DEL_ENTRY
), driver_name
,
2229 /* found matching entry : no errors */
2230 *major_num
= is_unique
;
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.
2243 * get maximum major number thru (rem_)name_to_major file on client
2245 * get maximum major number allowable on current system using modctl
2251 max_dev
= get_max_major(name_to_major
);
2253 /* If rem_name_to_major exists, we need to check it too */
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
),
2268 /* guard against bigger maj_num in rem_name_to_major */
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
)
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.
2292 if (modctl(MODRESERVED
, NULL
, &max_dev
) < 0) {
2294 (void) fprintf(stderr
, gettext(ERR_MAX_MAJOR
));
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
));
2309 * Populate the num_list array
2311 if (fill_n2m_array(name_to_major
, &num_list
, &max_dev
) != 0) {
2315 if (fill_n2m_array(rem_name_to_major
, &num_list
, &max_dev
) != 0)
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) {
2334 if (new_maj
== -1) {
2335 (void) fprintf(stderr
, gettext(ERR_NO_FREE_MAJOR
));
2339 (void) snprintf(drv_majnum_str
, sizeof (drv_majnum_str
),
2341 if (do_the_update(driver_name
, drv_majnum_str
) == ERROR
) {
2345 *major_num
= new_maj
;
2351 fill_n2m_array(char *filename
, char **array
, int *nelems
)
2354 char line
[MAX_N2M_ALIAS_LINE
+ 1], *cp
;
2355 char drv
[FILENAME_MAX
+ 1];
2360 * Read through the file, marking each major number found
2361 * order is not relevant
2363 if ((fp
= fopen(filename
, "r")) == NULL
) {
2365 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
), filename
);
2369 while (fgets(line
, sizeof (line
), fp
) != 0) {
2370 /* cut off comments starting with '#' */
2371 if ((cp
= strchr(line
, '#')) != NULL
)
2373 /* ignore comment or blank lines */
2378 "%" VAL2STR(FILENAME_MAX
) "s %llu", drv
, &dnum
) != 2) {
2379 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
2385 if (dnum
> L_MAXMAJ32
) {
2386 (void) fprintf(stderr
, gettext(ERR_MAJ_TOOBIG
), drv
,
2387 dnum
, filename
, L_MAXMAJ32
);
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
));
2412 (*array
)[drv_majnum
] = 1;
2421 do_the_update(char *driver_name
, char *major_number
)
2423 return (append_to_file(driver_name
, major_number
, name_to_major
,
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.
2434 is_blank(char *line
)
2436 for (/* nothing */; *line
!= '\0'; line
++)
2437 if (!isspace(*line
))