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
)
513 /* Make sure that the file is on disk */
514 if (fflush(newfp
) != 0 || fsync(fileno(newfp
)) != 0)
519 (void) fclose(newfp
);
521 /* no matching driver found */
524 (nomatch
== B_TRUE
)) {
529 * if error, leave original file, delete new file
530 * if noerr, replace original file with new file
533 if (status
== NOERR
) {
534 if (rename(newfile
, oldfile
) == -1) {
536 (void) fprintf(stderr
, gettext(ERR_UPDATE
), oldfile
);
537 (void) unlink(newfile
);
542 * since there's an error, leave file alone; remove
545 if (unlink(newfile
) == -1) {
546 (void) fprintf(stderr
, gettext(ERR_CANT_RM
), newfile
);
556 * wrapper for call to get_name_to_major_entry(): given driver name,
557 * retrieve major number.
560 get_major_no(char *driver_name
, char *file_name
)
564 if (get_name_to_major_entry(&major
, driver_name
, file_name
) == ERROR
)
571 * wrapper for call to get_name_to_major_entry(): given major number,
572 * retrieve driver name.
575 get_driver_name(int major
, char *file_name
, char *buf
)
579 return (get_name_to_major_entry(&major
, buf
, file_name
));
584 * return pointer to cached name_to_major file - reads file into
585 * cache if this has not already been done. Since there may be
586 * requests for multiple name_to_major files (rem_name_to_major,
587 * name_to_major), this routine keeps a list of cached files.
590 get_cached_n_to_m_file(char *filename
, char ***cache
)
592 struct n_to_m_cache
{
596 struct n_to_m_cache
*next
;
598 static struct n_to_m_cache
*head
= NULL
;
599 struct n_to_m_cache
*ptr
;
601 char drv
[FILENAME_MAX
+ 1];
602 char entry
[FILENAME_MAX
+ 1];
603 char line
[MAX_N2M_ALIAS_LINE
], *cp
;
609 * see if the file is already cached - either
610 * rem_name_to_major or name_to_major
613 while (ptr
!= NULL
) {
614 if (strcmp(ptr
->file
, filename
) == 0)
619 if (ptr
== NULL
) { /* we need to cache the contents */
620 if ((fp
= fopen(filename
, "r")) == NULL
) {
622 (void) fprintf(stderr
, gettext(ERR_CANT_OPEN
),
627 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
628 /* cut off comments starting with '#' */
629 if ((cp
= strchr(line
, '#')) != NULL
)
631 /* ignore comment or blank lines */
636 "%" VAL2STR(FILENAME_MAX
) "s" /* drv */
637 "%" VAL2STR(FILENAME_MAX
) "s", /* entry */
639 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
648 /* allocate struct to cache the file */
649 ptr
= (struct n_to_m_cache
*)calloc(1,
650 sizeof (struct n_to_m_cache
));
652 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
655 ptr
->size
= size
+ 1;
656 /* allocate space to cache contents of file */
657 ptr
->cached_file
= (char **)calloc(ptr
->size
, sizeof (char *));
658 if (ptr
->cached_file
== NULL
) {
660 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
668 * the cache is an array of char pointers indexed by major
671 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
672 /* cut off comments starting with '#' */
673 if ((cp
= strchr(line
, '#')) != NULL
)
675 /* ignore comment or blank lines */
680 "%" VAL2STR(FILENAME_MAX
) "s" /* drv */
681 "%" VAL2STR(FILENAME_MAX
) "s", /* entry */
683 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
688 if ((ptr
->cached_file
[maj
] = strdup(drv
)) == NULL
) {
689 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
690 free(ptr
->cached_file
);
694 (void) strcpy(ptr
->cached_file
[maj
], drv
);
697 /* link the cache struct into the list of cached files */
698 ptr
->file
= strdup(filename
);
699 if (ptr
->file
== NULL
) {
700 for (maj
= 0; maj
<= ptr
->size
; maj
++)
701 free(ptr
->cached_file
[maj
]);
702 free(ptr
->cached_file
);
704 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
710 /* return value pointer to contents of file */
711 *cache
= ptr
->cached_file
;
719 * Using get_cached_n_to_m_file(), retrieve maximum major number
720 * found in the specificed file (name_to_major/rem_name_to_major).
722 * The return value is actually the size of the internal cache including 0.
725 get_max_major(char *file_name
)
727 char **n_to_m_cache
= NULL
;
729 return (get_cached_n_to_m_file(file_name
, &n_to_m_cache
));
734 * searching name_to_major: if major_no == UNIQUE then the caller wants to
735 * use the driver name as the key. Otherwise, the caller wants to use
736 * the major number as a key.
738 * This routine caches the contents of the name_to_major file on
739 * first call. And it could be generalized to deal with other
740 * config files if necessary.
743 get_name_to_major_entry(int *major_no
, char *driver_name
, char *file_name
)
746 char **n_to_m_cache
= NULL
;
749 int ret
= NOT_UNIQUE
;
752 * read the file in - we cache it in case caller wants to
753 * do multiple lookups
755 size
= get_cached_n_to_m_file(file_name
, &n_to_m_cache
);
760 /* search with driver name as key */
761 if (*major_no
== UNIQUE
) {
762 for (maj
= 0; maj
< size
; maj
++) {
763 if ((n_to_m_cache
[maj
] != NULL
) &&
764 (strcmp(driver_name
, n_to_m_cache
[maj
]) == 0)) {
771 /* search with major number as key */
774 * Bugid 1254588, drvconfig dump core after loading driver
775 * with major number bigger than entries defined in
776 * /etc/name_to_major.
778 if (*major_no
>= size
)
781 if (n_to_m_cache
[*major_no
] != NULL
) {
782 (void) strcpy(driver_name
, n_to_m_cache
[*major_no
]);
790 * Given pointer to begining of member 'n' in a space (or separator)
791 * separated list, return pointer to member 'n+1', and establish member 'n'
792 * in *current_entry. If unquote, then we skip a leading quote and treat
793 * the trailing quote as a separator (and skip).
807 /* skip white space */
808 while (isspace(*ptr
))
811 /* if unquote skip leading quote */
812 if (unquote
&& *ptr
== '"') {
817 /* read thru the current entry looking for end, separator, or unquote */
819 (*ptr
!= separator
) && (!isspace(*ptr
)) &&
820 (!quoted
|| (*ptr
!= '"'))) {
821 *current_entry
++ = *ptr
++;
823 *current_entry
= '\0';
825 if (separator
&& (*ptr
== separator
))
826 ptr
++; /* skip over separator */
827 if (quoted
&& (*ptr
== '"'))
828 ptr
++; /* skip over trailing quote */
830 /* skip white space */
831 while (isspace(*ptr
))
838 * A parser specific to the add_drv "-m permission" syntax:
840 * -m '<minor-name> <permissions> <owner> <group>', ...
842 * One entry is parsed starting at prev_member and returned
843 * in the string pointed at by current_entry. A pointer
844 * to the entry following is returned.
853 int maxfields
= 4; /* fields in a permissions format */
856 while (isspace(*ptr
))
860 /* comma allowed in minor name token only */
861 if (*ptr
== ',' && nfields
> 0) {
863 } else if (isspace(*ptr
)) {
864 *current_entry
++ = *ptr
++;
865 while (isspace(*ptr
))
867 if (++nfields
== maxfields
)
870 *current_entry
++ = *ptr
++;
872 *current_entry
= '\0';
874 while (isspace(*ptr
))
877 ptr
++; /* skip over optional trailing comma */
879 while (isspace(*ptr
))
891 * Attempt to create the lock file. Open the file itself,
892 * and not a symlink to some other file.
894 add_rem_lock_fd
= open(add_rem_lock
,
895 O_CREAT
|O_RDWR
|O_NOFOLLOW
|O_NOLINKS
, S_IRUSR
|S_IWUSR
);
896 if (add_rem_lock_fd
< 0) {
897 (void) fprintf(stderr
, gettext(ERR_CREAT_LOCK
),
898 add_rem_lock
, strerror(errno
));
902 lock
.l_type
= F_WRLCK
;
903 lock
.l_whence
= SEEK_SET
;
907 /* Try for the lock but don't wait. */
908 if (fcntl(add_rem_lock_fd
, F_SETLK
, &lock
) == -1) {
909 if (errno
== EACCES
|| errno
== EAGAIN
) {
910 (void) fprintf(stderr
, gettext(ERR_PROG_IN_USE
));
912 (void) fprintf(stderr
, gettext(ERR_LOCK
),
913 add_rem_lock
, strerror(errno
));
922 /* release memory allocated for moddir */
924 /* remove add_drv/rem_drv lock */
932 struct drvmod_dir
*walk_ptr
;
933 struct drvmod_dir
*free_ptr
= moddir
;
935 while (free_ptr
!= NULL
) {
936 walk_ptr
= free_ptr
->next
;
947 if (add_rem_lock_fd
< 0)
950 unlock
.l_type
= F_UNLCK
;
951 unlock
.l_whence
= SEEK_SET
;
955 if (fcntl(add_rem_lock_fd
, F_SETLK
, &unlock
) == -1) {
956 (void) fprintf(stderr
, gettext(ERR_UNLOCK
),
957 add_rem_lock
, strerror(errno
));
959 (void) close(add_rem_lock_fd
);
960 add_rem_lock_fd
= -1;
965 * error adding driver; need to back out any changes to files.
966 * check flag to see which files need entries removed
967 * entry removal based on driver name
975 if (c_flag
& CLEAN_NAM_MAJ
) {
976 if (delete_entry(name_to_major
, driver_name
, " ",
978 (void) fprintf(stderr
, gettext(ERR_NO_CLEAN
),
979 name_to_major
, driver_name
);
983 if (c_flag
& CLEAN_DRV_ALIAS
) {
984 if (delete_entry(driver_aliases
, driver_name
, " ",
986 (void) fprintf(stderr
, gettext(ERR_DEL_ENTRY
),
987 driver_name
, driver_aliases
);
991 if (c_flag
& CLEAN_DRV_CLASSES
) {
992 if (delete_entry(driver_classes
, driver_name
, "\t", NULL
) ==
994 (void) fprintf(stderr
, gettext(ERR_DEL_ENTRY
),
995 driver_name
, driver_classes
);
999 if (c_flag
& CLEAN_MINOR_PERM
) {
1000 if (delete_entry(minor_perm
, driver_name
, ":", NULL
) == ERROR
) {
1001 (void) fprintf(stderr
, gettext(ERR_DEL_ENTRY
),
1002 driver_name
, minor_perm
);
1006 * There's no point in removing entries from files that don't
1007 * exist. Prevent error messages by checking for file existence
1010 if ((c_flag
& CLEAN_DEV_POLICY
) != 0 &&
1011 access(device_policy
, F_OK
) == 0) {
1012 if (delete_plcy_entry(device_policy
, driver_name
) == ERROR
) {
1013 (void) fprintf(stderr
, gettext(ERR_DEL_ENTRY
),
1014 driver_name
, device_policy
);
1017 if ((c_flag
& CLEAN_DRV_PRIV
) != 0 &&
1018 access(extra_privs
, F_OK
) == 0) {
1019 if (delete_entry(extra_privs
, driver_name
, ":", NULL
) ==
1021 (void) fprintf(stderr
, gettext(ERR_DEL_ENTRY
),
1022 driver_name
, extra_privs
);
1028 check_perms_aliases(
1033 * If neither i_flag nor m_flag are specified no need to check the
1034 * files for access permissions
1036 if (!m_flag
&& !i_flag
)
1039 /* check minor_perm file : exits and is writable */
1041 if (access(minor_perm
, R_OK
| W_OK
)) {
1043 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
1049 /* check driver_aliases file : exits and is writable */
1051 if (access(driver_aliases
, R_OK
| W_OK
)) {
1053 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
1064 check_name_to_major(int mode
)
1066 /* check name_to_major file : exists and is writable */
1067 if (access(name_to_major
, mode
)) {
1069 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
1079 * All this stuff is to support a server installing
1080 * drivers on diskless clients. When on the server
1081 * need to prepend the basedir
1084 build_filenames(char *basedir
)
1087 int driver_aliases_len
;
1088 int driver_classes_len
;
1090 int name_to_major_len
;
1091 int rem_name_to_major_len
;
1092 int add_rem_lock_len
;
1094 int device_policy_len
;
1095 int extra_privs_len
;
1097 if (basedir
== NULL
) {
1098 driver_aliases
= DRIVER_ALIAS
;
1099 driver_classes
= DRIVER_CLASSES
;
1100 minor_perm
= MINOR_PERM
;
1101 name_to_major
= NAM_TO_MAJ
;
1102 rem_name_to_major
= REM_NAM_TO_MAJ
;
1103 add_rem_lock
= ADD_REM_LOCK
;
1104 devfs_root
= DEVFS_ROOT
;
1105 device_policy
= DEV_POLICY
;
1106 extra_privs
= EXTRA_PRIVS
;
1109 len
= strlen(basedir
) + 1;
1111 driver_aliases_len
= len
+ sizeof (DRIVER_ALIAS
);
1112 driver_classes_len
= len
+ sizeof (DRIVER_CLASSES
);
1113 minor_perm_len
= len
+ sizeof (MINOR_PERM
);
1114 name_to_major_len
= len
+ sizeof (NAM_TO_MAJ
);
1115 rem_name_to_major_len
= len
+ sizeof (REM_NAM_TO_MAJ
);
1116 add_rem_lock_len
= len
+ sizeof (ADD_REM_LOCK
);
1117 devfs_root_len
= len
+ sizeof (DEVFS_ROOT
);
1118 device_policy_len
= len
+ sizeof (DEV_POLICY
);
1119 extra_privs_len
= len
+ sizeof (EXTRA_PRIVS
);
1121 driver_aliases
= malloc(driver_aliases_len
);
1122 driver_classes
= malloc(driver_classes_len
);
1123 minor_perm
= malloc(minor_perm_len
);
1124 name_to_major
= malloc(name_to_major_len
);
1125 rem_name_to_major
= malloc(rem_name_to_major_len
);
1126 add_rem_lock
= malloc(add_rem_lock_len
);
1127 devfs_root
= malloc(devfs_root_len
);
1128 device_policy
= malloc(device_policy_len
);
1129 extra_privs
= malloc(extra_privs_len
);
1131 if ((driver_aliases
== NULL
) ||
1132 (driver_classes
== NULL
) ||
1133 (minor_perm
== NULL
) ||
1134 (name_to_major
== NULL
) ||
1135 (rem_name_to_major
== NULL
) ||
1136 (add_rem_lock
== NULL
) ||
1137 (devfs_root
== NULL
) ||
1138 (device_policy
== NULL
) ||
1139 (extra_privs
== NULL
)) {
1140 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1144 (void) snprintf(driver_aliases
, driver_aliases_len
,
1145 "%s%s", basedir
, DRIVER_ALIAS
);
1146 (void) snprintf(driver_classes
, driver_classes_len
,
1147 "%s%s", basedir
, DRIVER_CLASSES
);
1148 (void) snprintf(minor_perm
, minor_perm_len
,
1149 "%s%s", basedir
, MINOR_PERM
);
1150 (void) snprintf(name_to_major
, name_to_major_len
,
1151 "%s%s", basedir
, NAM_TO_MAJ
);
1152 (void) snprintf(rem_name_to_major
, rem_name_to_major_len
,
1153 "%s%s", basedir
, REM_NAM_TO_MAJ
);
1154 (void) snprintf(add_rem_lock
, add_rem_lock_len
,
1155 "%s%s", basedir
, ADD_REM_LOCK
);
1156 (void) snprintf(devfs_root
, devfs_root_len
,
1157 "%s%s", basedir
, DEVFS_ROOT
);
1158 (void) snprintf(device_policy
, device_policy_len
,
1159 "%s%s", basedir
, DEV_POLICY
);
1160 (void) snprintf(extra_privs
, extra_privs_len
,
1161 "%s%s", basedir
, EXTRA_PRIVS
);
1168 exec_command(char *path
, char *cmdline
[MAX_CMD_LINE
])
1176 if ((pid
= fork()) == 0) {
1177 (void) execv(path
, cmdline
);
1180 } else if (pid
== -1) {
1183 (void) fprintf(stderr
, gettext(ERR_FORK_FAIL
), cmdline
);
1188 waitstat
= waitpid(pid
, (int *)&stat_loc
, 0);
1190 } while ((!WIFEXITED(stat_loc
) &&
1191 !WIFSIGNALED(stat_loc
)) || (waitstat
== 0));
1193 exit_status
= WEXITSTATUS(stat_loc
);
1195 return (exit_status
);
1200 * Exec devfsadm to perform driver config/unconfig operation,
1201 * adding or removing aliases.
1213 char *cmdline
[MAX_CMD_LINE
];
1220 /* build command line */
1221 cmdline
[n
++] = DRVCONFIG
;
1222 if (config
== B_FALSE
) {
1223 cmdline
[n
++] = "-u"; /* unconfigure */
1224 if (config_flags
& CONFIG_DRV_FORCE
)
1225 cmdline
[n
++] = "-f"; /* force if currently in use */
1227 if (config_flags
& CONFIG_DRV_VERBOSE
) {
1228 cmdline
[n
++] = "-v";
1230 cmdline
[n
++] = "-b";
1232 cmdline
[n
++] = "-c";
1233 cmdline
[n
++] = classes
;
1235 cmdline
[n
++] = "-i";
1236 cmdline
[n
++] = driver_name
;
1237 cmdline
[n
++] = "-m";
1238 (void) snprintf(maj_num
, sizeof (maj_num
), "%lu", major_num
);
1239 cmdline
[n
++] = maj_num
;
1240 if (config_flags
& CONFIG_DRV_UPDATE_ONLY
)
1241 cmdline
[n
++] = "-x";
1243 if (aliases
!= NULL
) {
1244 len
= strlen(aliases
);
1247 cmdline
[n
++] = "-a";
1248 cmdline
[n
] = calloc(len
+ 1, 1);
1249 if (cmdline
[n
] == NULL
) {
1250 (void) fprintf(stderr
,
1251 gettext(ERR_NO_MEM
));
1254 current
= get_entry(previous
,
1255 cmdline
[n
++], ' ', 0);
1258 } while (*current
!= '\0');
1263 rv
= exec_command(DRVCONFIG_PATH
, cmdline
);
1276 return (exec_devfsadm(B_FALSE
, driver_name
, major_num
,
1277 aliases
, NULL
, config_flags
));
1281 * check that major_num doesn't exceed maximum on this machine
1282 * do this here to support add_drv on server for diskless clients
1296 if (modctl(MODRESERVED
, NULL
, &max_dev
) < 0) {
1298 (void) fprintf(stderr
, gettext(ERR_MAX_MAJOR
));
1302 if (major_num
>= max_dev
) {
1303 (void) fprintf(stderr
, gettext(ERR_MAX_EXCEEDS
),
1304 major_num
, max_dev
);
1308 /* bind major number and driver name */
1309 rv
= exec_devfsadm(B_TRUE
, driver_name
, major_num
,
1310 aliases
, classes
, config_flags
);
1315 remove_entry(cleanup_flag
, driver_name
);
1320 load_driver(char *driver_name
, int verbose_flag
)
1323 char *cmdline
[MAX_CMD_LINE
];
1326 /* build command line */
1327 cmdline
[n
++] = DEVFSADM
;
1329 cmdline
[n
++] = "-v";
1331 cmdline
[n
++] = "-i";
1332 cmdline
[n
++] = driver_name
;
1335 exec_status
= exec_command(DEVFSADM_PATH
, cmdline
);
1337 if (exec_status
!= NOERR
) {
1338 /* no clean : name and major number are bound */
1339 (void) fprintf(stderr
, gettext(ERR_CONFIG
), driver_name
);
1344 get_modid(char *driver_name
, int *mod
)
1346 struct modinfo modinfo
;
1349 modinfo
.mi_info
= MI_INFO_ALL
;
1352 * If we are at the end of the list of loaded modules
1353 * then set *mod = -1 and return
1355 if (modctl(MODINFO
, 0, &modinfo
) < 0) {
1360 *mod
= modinfo
.mi_id
;
1361 } while (strcmp(driver_name
, modinfo
.mi_name
) != 0);
1365 create_reconfig(char *basedir
)
1367 char reconfig_file
[MAXPATHLEN
+ FILENAME_MAX
+ 1];
1370 if (basedir
!= NULL
) {
1371 (void) strcpy(reconfig_file
, basedir
);
1372 (void) strcat(reconfig_file
, RECONFIGURE
);
1374 (void) strcpy(reconfig_file
, RECONFIGURE
);
1376 if ((reconfig_fp
= fopen(reconfig_file
, "a")) == NULL
)
1379 (void) fclose(reconfig_fp
);
1385 * update_minor_entry:
1387 * for each entry in list
1388 * where list entries are separated by <list_separator>
1389 * modify entry : driver_name <entry_separator> entry
1392 * return error/noerr
1395 update_minor_entry(char *driver_name
, char *perm_list
)
1400 char line
[MAX_DBFILE_ENTRY
];
1401 char drv
[FILENAME_MAX
+ 1];
1402 char minor
[FILENAME_MAX
+ 1];
1403 char perm
[OPT_LEN
+ 1];
1404 char own
[OPT_LEN
+ 1];
1405 char grp
[OPT_LEN
+ 1];
1406 int status
= NOERR
, i
;
1407 char newfile
[MAXPATHLEN
];
1408 char *cp
, *dup
, *drv_minor
;
1409 struct group
*sysgrp
;
1412 if ((fp
= fopen(minor_perm
, "r")) == NULL
) {
1414 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
1420 /* Build filename for temporary file */
1421 (void) snprintf(newfile
, sizeof (newfile
), "%s%s", minor_perm
, ".hold");
1424 * Set gid so we preserve group attribute. Ideally we wouldn't
1425 * assume a gid of "sys" but we can't undo the damage on already
1426 * installed systems unless we force the issue.
1428 if ((sysgrp
= getgrnam("sys")) != NULL
) {
1429 (void) setgid(sysgrp
->gr_gid
);
1432 if ((newfd
= open(newfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0644)) < 0) {
1433 if (errno
== EEXIST
) {
1434 (void) fprintf(stderr
, gettext(ERR_FILE_EXISTS
),
1438 (void) fprintf(stderr
, gettext(ERR_CREAT_LOCK
),
1444 if ((newfp
= fdopen(newfd
, "w")) == NULL
) {
1446 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
1448 (void) close(newfd
);
1452 if (sscanf(perm_list
,
1453 "%" VAL2STR(FILENAME_MAX
) "s" /* minor */
1454 "%" VAL2STR(OPT_LEN
) "s" /* perm */
1455 "%" VAL2STR(OPT_LEN
) "s" /* own */
1456 "%" VAL2STR(OPT_LEN
) "s", /* grp */
1457 minor
, perm
, own
, grp
) != 4) {
1461 while ((fgets(line
, sizeof (line
), fp
) != NULL
) && status
== NOERR
) {
1462 /* copy the whole line into dup */
1463 if ((dup
= strdup(line
)) == NULL
) {
1465 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1469 /* cut off comments starting with '#' */
1470 if ((cp
= strchr(dup
, '#')) != NULL
)
1472 /* ignore comment or blank lines */
1473 if (is_blank(dup
)) {
1474 if (fputs(line
, newfp
) == EOF
) {
1475 (void) fprintf(stderr
, gettext(ERR_UPDATE
),
1483 /* get the driver name */
1484 if (sscanf(dup
, "%" VAL2STR(FILENAME_MAX
) "s", drv
) != 1) {
1485 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
1493 * get the minor name; place the NULL character at the
1494 * end of the driver name, then make the drv_minor
1495 * point to the first character of the minor name.
1496 * the line missing ':' must be treated as a broken one.
1498 i
= strcspn(drv
, ":");
1499 if (i
== strlen(drv
)) {
1500 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
1507 drv_minor
= &drv
[strlen(drv
) + 1];
1510 * compare both of the driver name and the minor name.
1511 * then the new line should be written to the file if
1512 * both of them match
1514 if ((strcmp(drv
, driver_name
) == 0) &&
1515 (strcmp(minor
, drv_minor
) == 0)) {
1516 /* if it has a comment, keep it */
1518 cp
++; /* skip a terminator */
1519 (void) snprintf(line
, sizeof (line
),
1520 "%s:%s %s %s %s #%s\n",
1521 drv
, minor
, perm
, own
, grp
, cp
);
1523 (void) snprintf(line
, sizeof (line
),
1525 drv
, minor
, perm
, own
, grp
);
1531 /* update the file */
1532 if ((fputs(line
, newfp
)) == EOF
) {
1533 (void) fprintf(stderr
, gettext(ERR_UPDATE
),
1540 (void) bzero(line
, sizeof (&line
[0]));
1541 (void) snprintf(line
, sizeof (line
),
1543 driver_name
, minor
, perm
, own
, grp
);
1545 /* add the new entry */
1546 if ((fputs(line
, newfp
)) == EOF
) {
1547 (void) fprintf(stderr
, gettext(ERR_UPDATE
), minor_perm
);
1554 if (fflush(newfp
) != 0 || fsync(fileno(newfp
)) != 0)
1557 (void) fclose(newfp
);
1560 * if error, leave original file, delete new file
1561 * if noerr, replace original file with new file
1563 if (status
== NOERR
) {
1564 if (rename(newfile
, minor_perm
) == -1) {
1566 (void) fprintf(stderr
, gettext(ERR_UPDATE
), minor_perm
);
1567 (void) unlink(newfile
);
1572 * since there's an error, leave file alone; remove
1575 if (unlink(newfile
) == -1) {
1576 (void) fprintf(stderr
, gettext(ERR_CANT_RM
), newfile
);
1589 * read thru file, listing all entries if first entry = driver_name
1600 char line
[MAX_DBFILE_ENTRY
], *cp
;
1601 char drv
[FILENAME_MAX
+ 1];
1603 if ((fp
= fopen(oldfile
, "r")) == NULL
) {
1605 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
), oldfile
);
1610 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
1611 /* cut off comments starting with '#' */
1612 if ((cp
= strchr(line
, '#')) != NULL
)
1614 /* ignore comment or blank lines */
1618 if (sscanf(line
, "%" VAL2STR(FILENAME_MAX
) "s", drv
) != 1) {
1619 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
1623 for (i
= strcspn(drv
, marker
); i
< FILENAME_MAX
; i
++) {
1627 if (strcmp(driver_name
, drv
) == 0) {
1628 (void) fprintf(stdout
, "%s", line
);
1639 * Check the token here. According to IEEE1275 Open Firmware Boot
1640 * Standard, the name is composed of 1 to 31 letters,
1641 * digits and punctuation characters from the set ",._+-", and
1642 * uppercase and lowercase characters are considered distinct.
1643 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
1644 * However, since either the definition of driver or aliase names is
1645 * not known well, only '#' is avoided explicitly. (the kernel lexical
1646 * analyzer treats it as a start of a comment)
1648 for (/* nothing */; *tok
!= '\0'; tok
++)
1649 if (*tok
== '#' || iscntrl(*tok
))
1656 * check each entry in perm_list for:
1658 * permission arg is in valid range
1659 * permlist entries separated by comma
1660 * return ERROR/NOERR
1663 check_perm_opts(char *perm_list
)
1666 char *previous_head
;
1669 char minor
[FILENAME_MAX
+ 1];
1670 char perm
[OPT_LEN
+ 1];
1671 char own
[OPT_LEN
+ 1];
1672 char grp
[OPT_LEN
+ 1];
1673 char dumb
[OPT_LEN
+ 1];
1677 if ((len
= strlen(perm_list
)) == 0)
1680 if ((one_entry
= calloc(len
+ 1, 1)) == NULL
) {
1681 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1685 previous_head
= perm_list
;
1686 current_head
= perm_list
;
1688 while (*current_head
!= '\0') {
1689 bzero(one_entry
, len
+ 1);
1690 current_head
= get_perm_entry(previous_head
, one_entry
);
1692 previous_head
= current_head
;
1693 scan_stat
= sscanf(one_entry
,
1694 "%" VAL2STR(FILENAME_MAX
) "s" /* minor */
1695 "%" VAL2STR(OPT_LEN
) "s" /* perm */
1696 "%" VAL2STR(OPT_LEN
) "s" /* own */
1697 "%" VAL2STR(OPT_LEN
) "s" /* grp */
1698 "%" VAL2STR(OPT_LEN
) "s", /* dumb */
1699 minor
, perm
, own
, grp
, dumb
);
1701 if (scan_stat
< 4) {
1702 (void) fprintf(stderr
, gettext(ERR_MIS_TOK
),
1706 if (scan_stat
> 4) {
1707 (void) fprintf(stderr
, gettext(ERR_TOO_MANY_ARGS
),
1712 intperm
= atoi(perm
);
1713 if (intperm
< 0000 || intperm
> 4777) {
1714 (void) fprintf(stderr
, gettext(ERR_BAD_MODE
), perm
);
1725 * check each alias :
1726 * alias list members separated by white space
1727 * cannot exist as driver name in /etc/name_to_major
1728 * cannot exist as driver or alias name in /etc/driver_aliases
1731 aliases_unique(char *aliases
)
1734 char *previous_head
;
1740 len
= strlen(aliases
);
1742 one_entry
= calloc(len
+ 1, 1);
1743 if (one_entry
== NULL
) {
1744 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1748 previous_head
= aliases
;
1751 bzero(one_entry
, len
+1);
1752 current_head
= get_entry(previous_head
, one_entry
, ' ', 1);
1753 previous_head
= current_head
;
1755 if ((unique_driver_name(one_entry
, name_to_major
,
1756 &is_unique
)) == ERROR
)
1759 if (is_unique
!= UNIQUE
) {
1760 (void) fprintf(stderr
, gettext(ERR_ALIAS_IN_NAM_MAJ
),
1765 if ((err
= unique_drv_alias(one_entry
)) != UNIQUE
) {
1766 if (err
== NOT_UNIQUE
) {
1767 (void) fprintf(stderr
,
1768 gettext(ERR_ALIAS_IN_USE
), one_entry
);
1773 if (!is_token(one_entry
)) {
1774 (void) fprintf(stderr
, gettext(ERR_BAD_TOK
),
1779 } while (*current_head
!= '\0');
1790 * verify each alias :
1791 * alias list members separated by white space and quoted
1792 * exist as alias name in /etc/driver_aliases
1795 aliases_exist(char *aliases
)
1798 char *previous_head
;
1802 len
= strlen(aliases
);
1804 one_entry
= calloc(len
+ 1, 1);
1805 if (one_entry
== NULL
) {
1806 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1810 previous_head
= aliases
;
1813 bzero(one_entry
, len
+1);
1814 current_head
= get_entry(previous_head
, one_entry
, ' ', 1);
1815 previous_head
= current_head
;
1817 if (unique_drv_alias(one_entry
) != NOT_UNIQUE
)
1820 if (!is_token(one_entry
)) {
1821 (void) fprintf(stderr
, gettext(ERR_BAD_TOK
),
1826 } while (*current_head
!= '\0');
1838 * check each alias :
1839 * if path-oriented alias, path exists
1842 aliases_paths_exist(char *aliases
)
1845 char *previous_head
;
1848 char path
[MAXPATHLEN
];
1851 len
= strlen(aliases
);
1853 one_entry
= calloc(len
+ 1, 1);
1854 if (one_entry
== NULL
) {
1855 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
1859 previous_head
= aliases
;
1862 for (i
= 0; i
<= len
; i
++)
1865 current_head
= get_entry(previous_head
, one_entry
, ' ', 1);
1866 previous_head
= current_head
;
1868 /* if the alias is a path, ensure that the path exists */
1869 if (*one_entry
!= '/')
1871 (void) snprintf(path
, sizeof (path
), "/devices/%s", one_entry
);
1872 if (stat(path
, &buf
) == 0)
1875 /* no device at specified path-oriented alias path */
1876 (void) fprintf(stderr
, gettext(ERR_PATH_ORIENTED_ALIAS
),
1881 } while (*current_head
!= '\0');
1890 update_driver_aliases(
1894 /* make call to update the aliases file */
1895 return (append_to_file(driver_name
, aliases
, driver_aliases
,
1902 * ERROR in case of memory or read error
1903 * UNIQUE if there is no existing match to the supplied alias
1904 * NOT_UNIQUE if there is a match
1905 * An error message is emitted in the case of ERROR,
1906 * up to the caller otherwise.
1909 unique_drv_alias(char *drv_alias
)
1912 char drv
[FILENAME_MAX
+ 1];
1913 char line
[MAX_N2M_ALIAS_LINE
+ 1], *cp
;
1914 char alias
[FILENAME_MAX
+ 1];
1916 int status
= UNIQUE
;
1918 fp
= fopen(driver_aliases
, "r");
1921 while ((fgets(line
, sizeof (line
), fp
) != 0) &&
1923 /* cut off comments starting with '#' */
1924 if ((cp
= strchr(line
, '#')) != NULL
)
1926 /* ignore comment or blank lines */
1931 "%" VAL2STR(FILENAME_MAX
) "s" /* drv */
1932 "%" VAL2STR(FILENAME_MAX
) "s", /* alias */
1934 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
1935 driver_aliases
, line
);
1937 /* unquote for compare */
1938 if ((*alias
== '"') &&
1939 (*(alias
+ strlen(alias
) - 1) == '"')) {
1941 alias
[strlen(alias
) - 1] = '\0';
1945 if ((strcmp(drv_alias
, drv
) == 0) ||
1946 (strcmp(drv_alias
, a
) == 0)) {
1947 status
= NOT_UNIQUE
;
1955 (void) fprintf(stderr
, gettext(ERR_CANT_OPEN
), driver_aliases
);
1962 * search for driver_name in first field of file file_name
1963 * searching name_to_major and driver_aliases: name separated
1964 * from the remainder of the line by white space.
1967 unique_driver_name(char *driver_name
, char *file_name
,
1972 if ((ret
= get_major_no(driver_name
, file_name
)) == ERROR
) {
1973 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
),
1976 /* check alias file for name collision */
1977 if ((err
= unique_drv_alias(driver_name
)) != UNIQUE
) {
1978 if (err
== NOT_UNIQUE
) {
1979 (void) fprintf(stderr
,
1980 gettext(ERR_ALIAS_IN_USE
),
1986 *is_unique
= NOT_UNIQUE
;
1997 * SUCCESS - not an existing driver alias
1998 * NOT_UNIQUE - matching driver alias exists
1999 * ERROR - an error occurred
2002 check_duplicate_driver_alias(char *driver_name
, char *drv_alias
)
2005 char drv
[FILENAME_MAX
+ 1];
2006 char line
[MAX_N2M_ALIAS_LINE
+ 1], *cp
;
2007 char alias
[FILENAME_MAX
+ 1];
2009 int status
= SUCCESS
;
2011 if ((fp
= fopen(driver_aliases
, "r")) == NULL
) {
2013 (void) fprintf(stderr
, gettext(ERR_CANT_OPEN
), driver_aliases
);
2017 while (fgets(line
, sizeof (line
), fp
) != 0) {
2018 /* cut off comments starting with '#' */
2019 if ((cp
= strchr(line
, '#')) != NULL
)
2021 /* ignore comment or blank lines */
2026 "%" VAL2STR(FILENAME_MAX
) "s" /* drv */
2027 "%" VAL2STR(FILENAME_MAX
) "s", /* alias */
2029 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
2030 driver_aliases
, line
);
2032 /* unquote for compare */
2033 if ((*alias
== '"') &&
2034 (*(alias
+ strlen(alias
) - 1) == '"')) {
2036 alias
[strlen(alias
) - 1] = '\0';
2040 if ((strcmp(drv_alias
, a
) == 0) &&
2041 (strcmp(drv
, driver_name
) == 0)) {
2042 status
= NOT_UNIQUE
;
2045 if ((strcmp(drv_alias
, drv
) == 0) ||
2046 ((strcmp(drv_alias
, a
) == 0) &&
2047 (strcmp(drv
, driver_name
) != 0))) {
2048 (void) fprintf(stderr
,
2049 gettext(ERR_ALIAS_IN_USE
),
2062 trim_duplicate_aliases(char *driver_name
, char *aliases
, char **aliases2p
)
2065 char *previous_head
;
2072 len
= strlen(aliases
) + 1;
2074 one_entry
= calloc(len
, 1);
2075 aliases2
= calloc(len
, 1);
2076 if (one_entry
== NULL
|| aliases2
== NULL
) {
2077 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
2081 previous_head
= aliases
;
2084 (void) bzero(one_entry
, len
);
2085 current_head
= get_entry(previous_head
, one_entry
, ' ', 1);
2086 previous_head
= current_head
;
2088 rv
= check_duplicate_driver_alias(driver_name
, one_entry
);
2091 /* not an existing driver alias: add it */
2093 if (strlcat(aliases2
, " ", len
) >= len
)
2096 if (strlcat(aliases2
, one_entry
, len
) >= len
)
2101 /* matching driver alias exists: do not add it */
2104 /* error reading the alias file */
2110 if (!is_token(one_entry
)) {
2111 (void) fprintf(stderr
, gettext(ERR_BAD_TOK
),
2115 } while (*current_head
!= '\0');
2118 * If all the aliases listed are already
2119 * present we actually have none to do.
2124 *aliases2p
= aliases2
;
2136 check_space_within_quote(char *str
)
2143 for (i
= 0; i
< len
; i
++, str
++) {
2149 } else if (*str
== ' ' && quoted
)
2159 * write driver_name major_num to name_to_major file
2160 * major_num returned in major_num
2161 * return success/failure
2164 update_name_to_major(char *driver_name
, major_t
*major_num
, int server
)
2166 char major
[MAX_STR_MAJOR
+ 1];
2169 char drv_majnum_str
[MAX_STR_MAJOR
+ 1];
2171 int i
, tmp
= 0, is_unique
, have_rem_n2m
= 0;
2175 * if driver_name already in rem_name_to_major
2176 * delete entry from rem_nam_to_major
2177 * put entry into name_to_major
2180 if (stat(rem_name_to_major
, &buf
) == 0) {
2185 if ((is_unique
= get_major_no(driver_name
, rem_name_to_major
))
2190 * found a match in rem_name_to_major
2192 if (is_unique
!= UNIQUE
) {
2193 char scratch
[FILENAME_MAX
];
2196 * If there is a match in /etc/rem_name_to_major then
2197 * be paranoid: is that major number already in
2198 * /etc/name_to_major (potentially under another name)?
2200 if (get_driver_name(is_unique
, name_to_major
,
2201 scratch
) != UNIQUE
) {
2203 * nuke the rem_name_to_major entry-- it
2206 (void) delete_entry(rem_name_to_major
,
2207 driver_name
, " ", NULL
);
2209 (void) snprintf(major
, sizeof (major
),
2212 if (append_to_file(driver_name
, major
,
2213 name_to_major
, ' ', " ", 0) == ERROR
) {
2214 (void) fprintf(stderr
,
2215 gettext(ERR_NO_UPDATE
),
2220 if (delete_entry(rem_name_to_major
,
2221 driver_name
, " ", NULL
) == ERROR
) {
2222 (void) fprintf(stderr
,
2223 gettext(ERR_DEL_ENTRY
), driver_name
,
2228 /* found matching entry : no errors */
2229 *major_num
= is_unique
;
2237 * In a server case (with -b option), we can't use modctl() to find
2238 * the maximum major number, we need to dig thru client's
2239 * /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
2242 * get maximum major number thru (rem_)name_to_major file on client
2244 * get maximum major number allowable on current system using modctl
2250 max_dev
= get_max_major(name_to_major
);
2252 /* If rem_name_to_major exists, we need to check it too */
2254 tmp
= get_max_major(rem_name_to_major
);
2257 * If name_to_major is missing, we can get max_dev from
2258 * /etc/rem_name_to_major. If both missing, bail out!
2260 if ((max_dev
== ERROR
) && (tmp
== ERROR
)) {
2261 (void) fprintf(stderr
,
2262 gettext(ERR_CANT_ACCESS_FILE
),
2267 /* guard against bigger maj_num in rem_name_to_major */
2272 * If we can't get major from name_to_major file
2273 * and there is no /etc/rem_name_to_major file,
2274 * then we don't have a max_dev, bail out quick!
2276 if (max_dev
== ERROR
)
2281 * In case there is no more slack in current name_to_major
2282 * table, provide at least 1 extra entry so the add_drv can
2283 * succeed. Since only one add_drv process is allowed at one
2284 * time, and hence max_dev will be re-calculated each time
2285 * add_drv is ran, we don't need to worry about adding more
2286 * than 1 extra slot for max_dev.
2291 if (modctl(MODRESERVED
, NULL
, &max_dev
) < 0) {
2293 (void) fprintf(stderr
, gettext(ERR_MAX_MAJOR
));
2299 * max_dev is really how many slots the kernel has allocated for
2300 * devices... [0 , maxdev-1], not the largest available device num.
2302 if ((num_list
= calloc(max_dev
, 1)) == NULL
) {
2303 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
2308 * Populate the num_list array
2310 if (fill_n2m_array(name_to_major
, &num_list
, &max_dev
) != 0) {
2314 if (fill_n2m_array(rem_name_to_major
, &num_list
, &max_dev
) != 0)
2319 * Find the first free major number.
2321 * Note that we begin searching from 1, as drivers have developer the
2322 * erroneous assumption that a dev_t of 0 is invalid, and since we no
2323 * longer include system devices in the base files, a major number of
2324 * 0 may now be given to arbitrary devices.
2326 for (i
= 1; i
< max_dev
; i
++) {
2327 if (num_list
[i
] != 1) {
2333 if (new_maj
== -1) {
2334 (void) fprintf(stderr
, gettext(ERR_NO_FREE_MAJOR
));
2338 (void) snprintf(drv_majnum_str
, sizeof (drv_majnum_str
),
2340 if (do_the_update(driver_name
, drv_majnum_str
) == ERROR
) {
2344 *major_num
= new_maj
;
2350 fill_n2m_array(char *filename
, char **array
, int *nelems
)
2353 char line
[MAX_N2M_ALIAS_LINE
+ 1], *cp
;
2354 char drv
[FILENAME_MAX
+ 1];
2359 * Read through the file, marking each major number found
2360 * order is not relevant
2362 if ((fp
= fopen(filename
, "r")) == NULL
) {
2364 (void) fprintf(stderr
, gettext(ERR_CANT_ACCESS_FILE
), filename
);
2368 while (fgets(line
, sizeof (line
), fp
) != 0) {
2369 /* cut off comments starting with '#' */
2370 if ((cp
= strchr(line
, '#')) != NULL
)
2372 /* ignore comment or blank lines */
2377 "%" VAL2STR(FILENAME_MAX
) "s %llu", drv
, &dnum
) != 2) {
2378 (void) fprintf(stderr
, gettext(ERR_BAD_LINE
),
2384 if (dnum
> L_MAXMAJ32
) {
2385 (void) fprintf(stderr
, gettext(ERR_MAJ_TOOBIG
), drv
,
2386 dnum
, filename
, L_MAXMAJ32
);
2390 * cast down to a major_t; we can be sure this is safe because
2391 * of the above range-check.
2393 drv_majnum
= (major_t
)dnum
;
2395 if (drv_majnum
>= *nelems
) {
2397 * Allocate some more space, up to drv_majnum + 1 so
2398 * we can accomodate 0 through drv_majnum.
2400 * Note that in the failure case, we leak all of the
2401 * old contents of array. It's ok, since we just
2402 * wind up exiting immediately anyway.
2404 *nelems
= drv_majnum
+ 1;
2405 *array
= realloc(*array
, *nelems
);
2406 if (*array
== NULL
) {
2407 (void) fprintf(stderr
, gettext(ERR_NO_MEM
));
2411 (*array
)[drv_majnum
] = 1;
2420 do_the_update(char *driver_name
, char *major_number
)
2422 return (append_to_file(driver_name
, major_number
, name_to_major
,
2427 * is_blank() returns 1 (true) if a line specified is composed of
2428 * whitespace characters only. otherwise, it returns 0 (false).
2430 * Note. the argument (line) must be null-terminated.
2433 is_blank(char *line
)
2435 for (/* nothing */; *line
!= '\0'; line
++)
2436 if (!isspace(*line
))