1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <sys/sysmacros.h>
31 #include <libintl.h>
32 #include <wait.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <sys/buf.h>
39 #include <sys/stat.h>
40 #include <grp.h>
41 #include "addrem.h"
42 #include "errmsg.h"
43 #include "plcysubr.h"
44
45 /*
46 * Macros to produce a quoted string containing the value of a
47 * preprocessor macro. For example, if SIZE is defined to be 256,
48 * VAL2STR(SIZE) is "256". This is used to construct format
49 * strings for scanf-family functions below.
50 * Note: For format string use, the argument to VAL2STR() must
51 * be a numeric constant that is one less than the size of the
52 * corresponding data buffer.
53 */
54 #define VAL2STR_QUOTE(x) #x
55 #define VAL2STR(x) VAL2STR_QUOTE(x)
56
57 /*
58 * Convenience macro to determine if a character is a quote
59 */
60 #define isquote(c) (((c) == '"') || ((c) == '\''))
61
62
63 static char *add_rem_lock; /* lock file */
64 static char *tmphold; /* temporary file for updating */
65 static int add_rem_lock_fd = -1;
66
67 static int get_cached_n_to_m_file(char *filename, char ***cache);
68 static int get_name_to_major_entry(int *major_no, char *driver_name,
69 char *file_name);
70
71 static int is_blank(char *);
72
73 /*ARGSUSED*/
74 void
log_minorperm_error(minorperm_err_t err,int key)75 log_minorperm_error(minorperm_err_t err, int key)
76 {
77 switch (err) {
78 case MP_FOPEN_ERR:
79 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
80 MINOR_PERM_FILE);
81 break;
82 case MP_FCLOSE_ERR:
83 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
84 MINOR_PERM_FILE);
85 (void) fprintf(stderr, gettext(ERR_NO_MEM));
86 break;
87 case MP_IGNORING_LINE_ERR:
88 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
89 MINOR_PERM_FILE);
90 break;
91 case MP_ALLOC_ERR:
92 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
93 MINOR_PERM_FILE);
94 (void) fprintf(stderr, gettext(ERR_NO_MEM));
95 break;
96 case MP_NVLIST_ERR:
97 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
98 MINOR_PERM_FILE);
99 (void) fprintf(stderr, gettext(ERR_NO_MEM));
100 break;
101 case MP_CANT_FIND_USER_ERR:
102 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
103 MINOR_PERM_FILE);
104 break;
105 case MP_CANT_FIND_GROUP_ERR:
106 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
107 MINOR_PERM_FILE);
108 break;
109 }
110 }
111
112 /*
113 * open file
114 * for each entry in list
115 * where list entries are separated by <list_separator>
116 * append entry : driver_name <entry_separator> entry
117 * close file
118 * return error/noerr
119 */
120 int
append_to_file(char * driver_name,char * entry_list,char * filename,char list_separator,char * entry_separator,int quoted)121 append_to_file(
122 char *driver_name,
123 char *entry_list,
124 char *filename,
125 char list_separator,
126 char *entry_separator,
127 int quoted)
128 {
129 int len, line_len;
130 int fpint;
131 char *current_head, *previous_head;
132 char *line, *one_entry;
133 FILE *fp;
134
135 if ((fp = fopen(filename, "a")) == NULL) {
136 perror(NULL);
137 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
138 filename);
139 return (ERROR);
140 }
141
142 len = strlen(entry_list);
143
144 one_entry = calloc(len + 1, 1);
145 if (one_entry == NULL) {
146 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
147 (void) fprintf(stderr, gettext(ERR_NO_MEM));
148 (void) fclose(fp);
149 return (ERROR);
150 }
151
152 previous_head = entry_list;
153
154 line_len = strlen(driver_name) + len + 4;
155 if (quoted)
156 line_len += 2;
157
158 line = calloc(line_len, 1);
159 if (line == NULL) {
160 (void) fprintf(stderr, gettext(ERR_NO_MEM));
161 (void) fclose(fp);
162 err_exit();
163 }
164
165 /*
166 * get one entry at a time from list and append to <filename> file
167 */
168
169 do {
170 bzero(one_entry, len + 1);
171 bzero(line, line_len);
172
173 current_head = get_entry(previous_head, one_entry,
174 list_separator, quoted);
175 previous_head = current_head;
176
177 (void) snprintf(line, line_len,
178 quoted ? "%s%s\"%s\"\n" : "%s%s%s\n",
179 driver_name, entry_separator, one_entry);
180
181 if ((fputs(line, fp)) == EOF) {
182 perror(NULL);
183 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
184 filename);
185 }
186
187 } while (*current_head != '\0');
188
189
190 (void) fflush(fp);
191
192 fpint = fileno(fp);
193 (void) fsync(fpint);
194
195 (void) fclose(fp);
196
197 free(one_entry);
198 free(line);
199
200 return (NOERR);
201 }
202
203 /*
204 * open file
205 * for each entry in list
206 * where list entries are separated by <list_separator>
207 * append entry : driver_name <entry_separator> entry
208 * close file
209 * return error/noerr
210 */
211 int
append_to_minor_perm(char * driver_name,char * entry_list,char * filename)212 append_to_minor_perm(
213 char *driver_name,
214 char *entry_list,
215 char *filename)
216 {
217 int len, line_len;
218 int fpint;
219 char *current_head, *previous_head;
220 char *line, *one_entry;
221 FILE *fp;
222
223 if ((fp = fopen(filename, "a")) == NULL) {
224 perror(NULL);
225 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
226 filename);
227 return (ERROR);
228 }
229
230 len = strlen(entry_list);
231
232 one_entry = calloc(len + 1, 1);
233 if (one_entry == NULL) {
234 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename);
235 (void) fprintf(stderr, gettext(ERR_NO_MEM));
236 (void) fclose(fp);
237 return (ERROR);
238 }
239
240 previous_head = entry_list;
241
242 line_len = strlen(driver_name) + len + 4;
243 line = calloc(line_len, 1);
244 if (line == NULL) {
245 (void) fprintf(stderr, gettext(ERR_NO_MEM));
246 (void) fclose(fp);
247 err_exit();
248 }
249
250 /*
251 * get one entry at a time from list and append to <filename> file
252 */
253 do {
254 bzero(one_entry, len + 1);
255 bzero(line, line_len);
256
257 current_head = get_perm_entry(previous_head, one_entry);
258 previous_head = current_head;
259
260 (void) snprintf(line, line_len, "%s:%s\n",
261 driver_name, one_entry);
262
263 if ((fputs(line, fp)) == EOF) {
264 perror(NULL);
265 (void) fprintf(stderr, gettext(ERR_NO_UPDATE),
266 filename);
267 }
268
269 } while (*current_head != '\0');
270
271
272 (void) fflush(fp);
273
274 fpint = fileno(fp);
275 (void) fsync(fpint);
276
277 (void) fclose(fp);
278
279 free(one_entry);
280 free(line);
281
282 return (NOERR);
283 }
284
285 /*
286 * Require exact match to delete a driver alias/permission entry.
287 * Note line argument does not remain unchanged. Return 1 if matched.
288 */
289 static int
match_entry(char * line,char * match)290 match_entry(char *line, char *match)
291 {
292 char *token, *p;
293 int n;
294
295 /* skip any leading white space */
296 while (*line && isspace(*line))
297 line++;
298 /*
299 * Find separator for driver name, either space or colon
300 * minor_perm: <driver>:<perm>
301 * driver_aliases: <driver> <alias>
302 * extra_privs: <driver>:<priv>
303 */
304 if ((token = strpbrk(line, " :\t")) == NULL)
305 return (0);
306 token++;
307 /* skip leading white space and quotes */
308 while (*token && (isspace(*token) || isquote(*token)))
309 token++;
310 /* strip trailing newline, white space and quotes */
311 n = strlen(token);
312 p = token + n-1;
313 while (n > 0 && (*p == '\n' || isspace(*p) || isquote(*p))) {
314 *p-- = 0;
315 n--;
316 }
317 if (n == 0)
318 return (0);
319 return (strcmp(token, match) == 0);
320 }
321
322 /*
323 * open file
324 * read thru file, deleting all entries if first
325 * entry = driver_name
326 * close
327 * if error, leave original file intact with message
328 * assumption : drvconfig has been modified to work with clone
329 * entries in /etc/minor_perm as driver:mummble NOT
330 * clone:driver mummble
331 * this implementation will NOT find clone entries
332 * clone:driver mummble
333 * match:
334 * delete just the matching entry
335 *
336 */
337 int
delete_entry(char * oldfile,char * driver_name,char * marker,char * match)338 delete_entry(
339 char *oldfile,
340 char *driver_name,
341 char *marker,
342 char *match)
343 {
344 int rv, i;
345 int status = NOERR;
346 int drvr_found = 0;
347 boolean_t nomatch = B_TRUE;
348 char *newfile, *tptr, *cp;
349 char line[MAX_DBFILE_ENTRY];
350 char drv[FILENAME_MAX + 1];
351 FILE *fp, *newfp;
352 struct group *sysgrp;
353 char *copy; /* same size as line */
354 char *match2 = NULL; /* match with quotes cleaned up */
355
356 /*
357 * if match is specified, sanity check it and clean it
358 * up by removing surrounding quotes as we require
359 * an exact match.
360 */
361 if (match) {
362 cp = match;
363 while (*cp && (isspace(*cp)))
364 cp++;
365 i = strlen(cp);
366 if (i > 0) {
367 if ((match2 = strdup(cp)) == NULL) {
368 perror(NULL);
369 (void) fprintf(stderr, gettext(ERR_NO_MEM));
370 return (ERROR);
371 }
372 i = strlen(match2) - 1;
373 while (i >= 0 && (isspace(match2[i]))) {
374 match2[i] = 0;
375 i--;
376 }
377 }
378 if (match2 == NULL || (strlen(match2) == 0)) {
379 (void) fprintf(stderr,
380 gettext(ERR_INT_UPDATE), oldfile);
381 return (ERROR);
382 }
383 }
384
385 if ((fp = fopen(oldfile, "r")) == NULL) {
386 perror(NULL);
387 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
388 return (ERROR);
389 }
390
391 /* Space for defensive copy of input line */
392 copy = calloc(sizeof (line), 1);
393
394 /* Build filename for temporary file */
395 tptr = calloc(strlen(oldfile) + strlen(XEND) + 1, 1);
396 if (tptr == NULL || copy == NULL) {
397 perror(NULL);
398 (void) fprintf(stderr, gettext(ERR_NO_MEM));
399 return (ERROR);
400 }
401
402 (void) strcpy(tptr, oldfile);
403 (void) strcat(tptr, XEND);
404
405 /*
406 * Set gid so we preserve group attribute. Ideally we wouldn't
407 * assume a gid of "sys" but we can't undo the damage on already
408 * installed systems unless we force the issue.
409 */
410 if ((sysgrp = getgrnam("sys")) != NULL) {
411 (void) setgid(sysgrp->gr_gid);
412 }
413
414 newfile = mktemp(tptr);
415
416 if ((newfp = fopen(newfile, "w")) == NULL) {
417 perror(NULL);
418 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
419 newfile);
420 return (ERROR);
421 }
422
423 while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
424 /* copy the whole line */
425 if (strlcpy(copy, line, sizeof (line)) >= sizeof (line)) {
426 (void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
427 status = ERROR;
428 break;
429 }
430 /* cut off comments starting with '#' */
431 if ((cp = strchr(copy, '#')) != NULL)
432 *cp = '\0';
433 /* ignore comment or blank lines */
434 if (is_blank(copy)) {
435 if (fputs(line, newfp) == EOF) {
436 (void) fprintf(stderr, gettext(ERR_UPDATE),
437 oldfile);
438 status = ERROR;
439 }
440 continue;
441 }
442
443 /* get the driver name */
444 if (sscanf(copy, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
445 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
446 oldfile, line);
447 status = ERROR;
448 break;
449 }
450
451 for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
452 drv[i] = '\0';
453 }
454
455 if (strcmp(driver_name, drv) != 0) {
456 if ((fputs(line, newfp)) == EOF) {
457 (void) fprintf(stderr, gettext(ERR_UPDATE),
458 oldfile);
459 status = ERROR;
460 }
461 } else {
462 drvr_found++;
463 if (match2) { /* Just delete one entry */
464 /* for now delete just minor_perm and aliases */
465 if ((strcmp(oldfile, minor_perm) == 0) ||
466 (strcmp(oldfile, extra_privs) == 0) ||
467 (strcmp(oldfile, driver_aliases) == 0)) {
468
469 /* make defensive copy */
470 if (strlcpy(copy, line, sizeof (line))
471 >= sizeof (line)) {
472 (void) fprintf(stderr,
473 gettext(ERR_UPDATE),
474 oldfile);
475 status = ERROR;
476 break;
477 }
478 if (match_entry(copy, match2)) {
479 nomatch = B_FALSE;
480 } else {
481 if ((fputs(line, newfp)) ==
482 EOF) {
483 (void) fprintf(stderr,
484 gettext(ERR_UPDATE),
485 oldfile);
486 status = ERROR;
487 }
488 if (nomatch != B_FALSE)
489 nomatch = B_TRUE;
490 }
491 }
492 }
493
494 } /* end of else */
495 } /* end of while */
496
497 (void) fclose(fp);
498 free(tptr);
499 free(copy);
500 if (match2)
501 free(match2);
502
503 /* Make sure that the file is on disk */
504 if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
505 status = ERROR;
506 else
507 rv = NOERR;
508
509 (void) fclose(newfp);
510
511 /* no matching driver found */
512 rv = NOERR;
513 if (!drvr_found ||
514 (nomatch == B_TRUE)) {
515 rv = NONE_FOUND;
516 }
517
518 /*
519 * if error, leave original file, delete new file
520 * if noerr, replace original file with new file
521 */
522
523 if (status == NOERR) {
524 if (rename(oldfile, tmphold) == -1) {
525 perror(NULL);
526 (void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
527 (void) unlink(newfile);
528 return (ERROR);
529 } else if (rename(newfile, oldfile) == -1) {
530 perror(NULL);
531 (void) fprintf(stderr, gettext(ERR_UPDATE), oldfile);
532 (void) unlink(oldfile);
533 (void) unlink(newfile);
534 if (link(tmphold, oldfile) == -1) {
535 perror(NULL);
536 (void) fprintf(stderr, gettext(ERR_BAD_LINK),
537 oldfile, tmphold);
538 }
539 return (ERROR);
540 }
541 (void) unlink(tmphold);
542 } else {
543 /*
544 * since there's an error, leave file alone; remove
545 * new file
546 */
547 if (unlink(newfile) == -1) {
548 (void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
549 }
550 return (ERROR);
551 }
552
553 return (rv);
554 }
555
556
557 /*
558 * wrapper for call to get_name_to_major_entry(): given driver name,
559 * retrieve major number.
560 */
561 int
get_major_no(char * driver_name,char * file_name)562 get_major_no(char *driver_name, char *file_name)
563 {
564 int major = UNIQUE;
565
566 if (get_name_to_major_entry(&major, driver_name, file_name) == ERROR)
567 return (ERROR);
568 else
569 return (major);
570 }
571
572 /*
573 * wrapper for call to get_name_to_major_entry(): given major number,
574 * retrieve driver name.
575 */
576 int
get_driver_name(int major,char * file_name,char * buf)577 get_driver_name(int major, char *file_name, char *buf)
578 {
579 if (major < 0)
580 return (ERROR);
581 return (get_name_to_major_entry(&major, buf, file_name));
582 }
583
584
585 /*
586 * return pointer to cached name_to_major file - reads file into
587 * cache if this has not already been done. Since there may be
588 * requests for multiple name_to_major files (rem_name_to_major,
589 * name_to_major), this routine keeps a list of cached files.
590 */
591 static int
get_cached_n_to_m_file(char * filename,char *** cache)592 get_cached_n_to_m_file(char *filename, char ***cache)
593 {
594 struct n_to_m_cache {
595 char *file;
596 char **cached_file;
597 int size;
598 struct n_to_m_cache *next;
599 };
600 static struct n_to_m_cache *head = NULL;
601 struct n_to_m_cache *ptr;
602 FILE *fp;
603 char drv[FILENAME_MAX + 1];
604 char entry[FILENAME_MAX + 1];
605 char line[MAX_N2M_ALIAS_LINE], *cp;
606 int maj;
607 int size = 0;
608
609
610 /*
611 * see if the file is already cached - either
612 * rem_name_to_major or name_to_major
613 */
614 ptr = head;
615 while (ptr != NULL) {
616 if (strcmp(ptr->file, filename) == 0)
617 break;
618 ptr = ptr->next;
619 }
620
621 if (ptr == NULL) { /* we need to cache the contents */
622 if ((fp = fopen(filename, "r")) == NULL) {
623 perror(NULL);
624 (void) fprintf(stderr, gettext(ERR_CANT_OPEN),
625 filename);
626 return (ERROR);
627 }
628
629 while (fgets(line, sizeof (line), fp) != NULL) {
630 /* cut off comments starting with '#' */
631 if ((cp = strchr(line, '#')) != NULL)
632 *cp = '\0';
633 /* ignore comment or blank lines */
634 if (is_blank(line))
635 continue;
636 /* sanity-check */
637 if (sscanf(line,
638 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
639 "%" VAL2STR(FILENAME_MAX) "s", /* entry */
640 drv, entry) != 2) {
641 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
642 filename, line);
643 continue;
644 }
645 maj = atoi(entry);
646 if (maj > size)
647 size = maj;
648 }
649
650 /* allocate struct to cache the file */
651 ptr = (struct n_to_m_cache *)calloc(1,
652 sizeof (struct n_to_m_cache));
653 if (ptr == NULL) {
654 (void) fprintf(stderr, gettext(ERR_NO_MEM));
655 return (ERROR);
656 }
657 ptr->size = size + 1;
658 /* allocate space to cache contents of file */
659 ptr->cached_file = (char **)calloc(ptr->size, sizeof (char *));
660 if (ptr->cached_file == NULL) {
661 free(ptr);
662 (void) fprintf(stderr, gettext(ERR_NO_MEM));
663 return (ERROR);
664 }
665
666 rewind(fp);
667
668 /*
669 * now fill the cache
670 * the cache is an array of char pointers indexed by major
671 * number
672 */
673 while (fgets(line, sizeof (line), fp) != NULL) {
674 /* cut off comments starting with '#' */
675 if ((cp = strchr(line, '#')) != NULL)
676 *cp = '\0';
677 /* ignore comment or blank lines */
678 if (is_blank(line))
679 continue;
680 /* sanity-check */
681 if (sscanf(line,
682 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
683 "%" VAL2STR(FILENAME_MAX) "s", /* entry */
684 drv, entry) != 2) {
685 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
686 filename, line);
687 continue;
688 }
689 maj = atoi(entry);
690 if ((ptr->cached_file[maj] = strdup(drv)) == NULL) {
691 (void) fprintf(stderr, gettext(ERR_NO_MEM));
692 free(ptr->cached_file);
693 free(ptr);
694 return (ERROR);
695 }
696 (void) strcpy(ptr->cached_file[maj], drv);
697 }
698 (void) fclose(fp);
699 /* link the cache struct into the list of cached files */
700 ptr->file = strdup(filename);
701 if (ptr->file == NULL) {
702 for (maj = 0; maj <= ptr->size; maj++)
703 free(ptr->cached_file[maj]);
704 free(ptr->cached_file);
705 free(ptr);
706 (void) fprintf(stderr, gettext(ERR_NO_MEM));
707 return (ERROR);
708 }
709 ptr->next = head;
710 head = ptr;
711 }
712 /* return value pointer to contents of file */
713 *cache = ptr->cached_file;
714
715 /* return size */
716 return (ptr->size);
717 }
718
719
720 /*
721 * Using get_cached_n_to_m_file(), retrieve maximum major number
722 * found in the specificed file (name_to_major/rem_name_to_major).
723 *
724 * The return value is actually the size of the internal cache including 0.
725 */
726 int
get_max_major(char * file_name)727 get_max_major(char *file_name)
728 {
729 char **n_to_m_cache = NULL;
730
731 return (get_cached_n_to_m_file(file_name, &n_to_m_cache));
732 }
733
734
735 /*
736 * searching name_to_major: if major_no == UNIQUE then the caller wants to
737 * use the driver name as the key. Otherwise, the caller wants to use
738 * the major number as a key.
739 *
740 * This routine caches the contents of the name_to_major file on
741 * first call. And it could be generalized to deal with other
742 * config files if necessary.
743 */
744 static int
get_name_to_major_entry(int * major_no,char * driver_name,char * file_name)745 get_name_to_major_entry(int *major_no, char *driver_name, char *file_name)
746 {
747 int maj;
748 char **n_to_m_cache = NULL;
749 int size = 0;
750
751 int ret = NOT_UNIQUE;
752
753 /*
754 * read the file in - we cache it in case caller wants to
755 * do multiple lookups
756 */
757 size = get_cached_n_to_m_file(file_name, &n_to_m_cache);
758
759 if (size == ERROR)
760 return (ERROR);
761
762 /* search with driver name as key */
763 if (*major_no == UNIQUE) {
764 for (maj = 0; maj < size; maj++) {
765 if ((n_to_m_cache[maj] != NULL) &&
766 (strcmp(driver_name, n_to_m_cache[maj]) == 0)) {
767 *major_no = maj;
768 break;
769 }
770 }
771 if (maj >= size)
772 ret = UNIQUE;
773 /* search with major number as key */
774 } else {
775 /*
776 * Bugid 1254588, drvconfig dump core after loading driver
777 * with major number bigger than entries defined in
778 * /etc/name_to_major.
779 */
780 if (*major_no >= size)
781 return (UNIQUE);
782
783 if (n_to_m_cache[*major_no] != NULL) {
784 (void) strcpy(driver_name, n_to_m_cache[*major_no]);
785 } else
786 ret = UNIQUE;
787 }
788 return (ret);
789 }
790
791 /*
792 * Given pointer to begining of member 'n' in a space (or separator)
793 * separated list, return pointer to member 'n+1', and establish member 'n'
794 * in *current_entry. If unquote, then we skip a leading quote and treat
795 * the trailing quote as a separator (and skip).
796 */
797 char *
get_entry(char * prev_member,char * current_entry,char separator,int unquote)798 get_entry(
799 char *prev_member,
800 char *current_entry,
801 char separator,
802 int unquote)
803 {
804 char *ptr;
805 int quoted = 0;
806
807 ptr = prev_member;
808
809 /* skip white space */
810 while (isspace(*ptr))
811 ptr++;
812
813 /* if unquote skip leading quote */
814 if (unquote && *ptr == '"') {
815 quoted++;
816 ptr++;
817 }
818
819 /* read thru the current entry looking for end, separator, or unquote */
820 while (*ptr &&
821 (*ptr != separator) && (!isspace(*ptr)) &&
822 (!quoted || (*ptr != '"'))) {
823 *current_entry++ = *ptr++;
824 }
825 *current_entry = '\0';
826
827 if (separator && (*ptr == separator))
828 ptr++; /* skip over separator */
829 if (quoted && (*ptr == '"'))
830 ptr++; /* skip over trailing quote */
831
832 /* skip white space */
833 while (isspace(*ptr))
834 ptr++;
835
836 return (ptr);
837 }
838
839 /*
840 * A parser specific to the add_drv "-m permission" syntax:
841 *
842 * -m '<minor-name> <permissions> <owner> <group>', ...
843 *
844 * One entry is parsed starting at prev_member and returned
845 * in the string pointed at by current_entry. A pointer
846 * to the entry following is returned.
847 */
848 char *
get_perm_entry(char * prev_member,char * current_entry)849 get_perm_entry(
850 char *prev_member,
851 char *current_entry)
852 {
853 char *ptr;
854 int nfields = 0;
855 int maxfields = 4; /* fields in a permissions format */
856
857 ptr = prev_member;
858 while (isspace(*ptr))
859 ptr++;
860
861 while (*ptr) {
862 /* comma allowed in minor name token only */
863 if (*ptr == ',' && nfields > 0) {
864 break;
865 } else if (isspace(*ptr)) {
866 *current_entry++ = *ptr++;
867 while (isspace(*ptr))
868 ptr++;
869 if (++nfields == maxfields)
870 break;
871 } else
872 *current_entry++ = *ptr++;
873 }
874 *current_entry = '\0';
875
876 while (isspace(*ptr))
877 ptr++;
878 if (*ptr == ',') {
879 ptr++; /* skip over optional trailing comma */
880 }
881 while (isspace(*ptr))
882 ptr++;
883
884 return (ptr);
885 }
886
887 void
enter_lock(void)888 enter_lock(void)
889 {
890 struct flock lock;
891
892 /*
893 * Attempt to create the lock file. Open the file itself,
894 * and not a symlink to some other file.
895 */
896 add_rem_lock_fd = open(add_rem_lock,
897 O_CREAT|O_RDWR|O_NOFOLLOW|O_NOLINKS, S_IRUSR|S_IWUSR);
898 if (add_rem_lock_fd < 0) {
899 (void) fprintf(stderr, gettext(ERR_CREAT_LOCK),
900 add_rem_lock, strerror(errno));
901 exit(1);
902 }
903
904 lock.l_type = F_WRLCK;
905 lock.l_whence = SEEK_SET;
906 lock.l_start = 0;
907 lock.l_len = 0;
908
909 /* Try for the lock but don't wait. */
910 if (fcntl(add_rem_lock_fd, F_SETLK, &lock) == -1) {
911 if (errno == EACCES || errno == EAGAIN) {
912 (void) fprintf(stderr, gettext(ERR_PROG_IN_USE));
913 } else {
914 (void) fprintf(stderr, gettext(ERR_LOCK),
915 add_rem_lock, strerror(errno));
916 }
917 exit(1);
918 }
919 }
920
921 void
err_exit(void)922 err_exit(void)
923 {
924 /* release memory allocated for moddir */
925 cleanup_moddir();
926 /* remove add_drv/rem_drv lock */
927 exit_unlock();
928 exit(1);
929 }
930
931 void
cleanup_moddir(void)932 cleanup_moddir(void)
933 {
934 struct drvmod_dir *walk_ptr;
935 struct drvmod_dir *free_ptr = moddir;
936
937 while (free_ptr != NULL) {
938 walk_ptr = free_ptr->next;
939 free(free_ptr);
940 free_ptr = walk_ptr;
941 }
942 }
943
944 void
exit_unlock(void)945 exit_unlock(void)
946 {
947 struct flock unlock;
948
949 if (add_rem_lock_fd < 0)
950 return;
951
952 unlock.l_type = F_UNLCK;
953 unlock.l_whence = SEEK_SET;
954 unlock.l_start = 0;
955 unlock.l_len = 0;
956
957 if (fcntl(add_rem_lock_fd, F_SETLK, &unlock) == -1) {
958 (void) fprintf(stderr, gettext(ERR_UNLOCK),
959 add_rem_lock, strerror(errno));
960 } else {
961 (void) close(add_rem_lock_fd);
962 add_rem_lock_fd = -1;
963 }
964 }
965
966 /*
967 * error adding driver; need to back out any changes to files.
968 * check flag to see which files need entries removed
969 * entry removal based on driver name
970 */
971 void
remove_entry(int c_flag,char * driver_name)972 remove_entry(
973 int c_flag,
974 char *driver_name)
975 {
976
977 if (c_flag & CLEAN_NAM_MAJ) {
978 if (delete_entry(name_to_major, driver_name, " ",
979 NULL) == ERROR) {
980 (void) fprintf(stderr, gettext(ERR_NO_CLEAN),
981 name_to_major, driver_name);
982 }
983 }
984
985 if (c_flag & CLEAN_DRV_ALIAS) {
986 if (delete_entry(driver_aliases, driver_name, " ",
987 NULL) == ERROR) {
988 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
989 driver_name, driver_aliases);
990 }
991 }
992
993 if (c_flag & CLEAN_DRV_CLASSES) {
994 if (delete_entry(driver_classes, driver_name, "\t", NULL) ==
995 ERROR) {
996 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
997 driver_name, driver_classes);
998 }
999 }
1000
1001 if (c_flag & CLEAN_MINOR_PERM) {
1002 if (delete_entry(minor_perm, driver_name, ":", NULL) == ERROR) {
1003 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1004 driver_name, minor_perm);
1005 }
1006 }
1007 /*
1008 * There's no point in removing entries from files that don't
1009 * exist. Prevent error messages by checking for file existence
1010 * first.
1011 */
1012 if ((c_flag & CLEAN_DEV_POLICY) != 0 &&
1013 access(device_policy, F_OK) == 0) {
1014 if (delete_plcy_entry(device_policy, driver_name) == ERROR) {
1015 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1016 driver_name, device_policy);
1017 }
1018 }
1019 if ((c_flag & CLEAN_DRV_PRIV) != 0 &&
1020 access(extra_privs, F_OK) == 0) {
1021 if (delete_entry(extra_privs, driver_name, ":", NULL) ==
1022 ERROR) {
1023 (void) fprintf(stderr, gettext(ERR_DEL_ENTRY),
1024 driver_name, extra_privs);
1025 }
1026 }
1027 }
1028
1029 int
check_perms_aliases(int m_flag,int i_flag)1030 check_perms_aliases(
1031 int m_flag,
1032 int i_flag)
1033 {
1034 /*
1035 * If neither i_flag nor m_flag are specified no need to check the
1036 * files for access permissions
1037 */
1038 if (!m_flag && !i_flag)
1039 return (NOERR);
1040
1041 /* check minor_perm file : exits and is writable */
1042 if (m_flag) {
1043 if (access(minor_perm, R_OK | W_OK)) {
1044 perror(NULL);
1045 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1046 minor_perm);
1047 return (ERROR);
1048 }
1049 }
1050
1051 /* check driver_aliases file : exits and is writable */
1052 if (i_flag) {
1053 if (access(driver_aliases, R_OK | W_OK)) {
1054 perror(NULL);
1055 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1056 driver_aliases);
1057 return (ERROR);
1058 }
1059 }
1060
1061 return (NOERR);
1062 }
1063
1064
1065 int
check_name_to_major(int mode)1066 check_name_to_major(int mode)
1067 {
1068 /* check name_to_major file : exists and is writable */
1069 if (access(name_to_major, mode)) {
1070 perror(NULL);
1071 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1072 name_to_major);
1073 return (ERROR);
1074 }
1075
1076 return (NOERR);
1077 }
1078
1079
1080 /*
1081 * All this stuff is to support a server installing
1082 * drivers on diskless clients. When on the server
1083 * need to prepend the basedir
1084 */
1085 int
build_filenames(char * basedir)1086 build_filenames(char *basedir)
1087 {
1088 int len;
1089 int driver_aliases_len;
1090 int driver_classes_len;
1091 int minor_perm_len;
1092 int name_to_major_len;
1093 int rem_name_to_major_len;
1094 int add_rem_lock_len;
1095 int tmphold_len;
1096 int devfs_root_len;
1097 int device_policy_len;
1098 int extra_privs_len;
1099
1100 if (basedir == NULL) {
1101 driver_aliases = DRIVER_ALIAS;
1102 driver_classes = DRIVER_CLASSES;
1103 minor_perm = MINOR_PERM;
1104 name_to_major = NAM_TO_MAJ;
1105 rem_name_to_major = REM_NAM_TO_MAJ;
1106 add_rem_lock = ADD_REM_LOCK;
1107 tmphold = TMPHOLD;
1108 devfs_root = DEVFS_ROOT;
1109 device_policy = DEV_POLICY;
1110 extra_privs = EXTRA_PRIVS;
1111
1112 } else {
1113 len = strlen(basedir) + 1;
1114
1115 driver_aliases_len = len + sizeof (DRIVER_ALIAS);
1116 driver_classes_len = len + sizeof (DRIVER_CLASSES);
1117 minor_perm_len = len + sizeof (MINOR_PERM);
1118 name_to_major_len = len + sizeof (NAM_TO_MAJ);
1119 rem_name_to_major_len = len + sizeof (REM_NAM_TO_MAJ);
1120 add_rem_lock_len = len + sizeof (ADD_REM_LOCK);
1121 tmphold_len = len + sizeof (TMPHOLD);
1122 devfs_root_len = len + sizeof (DEVFS_ROOT);
1123 device_policy_len = len + sizeof (DEV_POLICY);
1124 extra_privs_len = len + sizeof (EXTRA_PRIVS);
1125
1126 driver_aliases = malloc(driver_aliases_len);
1127 driver_classes = malloc(driver_classes_len);
1128 minor_perm = malloc(minor_perm_len);
1129 name_to_major = malloc(name_to_major_len);
1130 rem_name_to_major = malloc(rem_name_to_major_len);
1131 add_rem_lock = malloc(add_rem_lock_len);
1132 tmphold = malloc(tmphold_len);
1133 devfs_root = malloc(devfs_root_len);
1134 device_policy = malloc(device_policy_len);
1135 extra_privs = malloc(extra_privs_len);
1136
1137 if ((driver_aliases == NULL) ||
1138 (driver_classes == NULL) ||
1139 (minor_perm == NULL) ||
1140 (name_to_major == NULL) ||
1141 (rem_name_to_major == NULL) ||
1142 (add_rem_lock == NULL) ||
1143 (tmphold == NULL) ||
1144 (devfs_root == NULL) ||
1145 (device_policy == NULL) ||
1146 (extra_privs == NULL)) {
1147 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1148 return (ERROR);
1149 }
1150
1151 (void) snprintf(driver_aliases, driver_aliases_len,
1152 "%s%s", basedir, DRIVER_ALIAS);
1153 (void) snprintf(driver_classes, driver_classes_len,
1154 "%s%s", basedir, DRIVER_CLASSES);
1155 (void) snprintf(minor_perm, minor_perm_len,
1156 "%s%s", basedir, MINOR_PERM);
1157 (void) snprintf(name_to_major, name_to_major_len,
1158 "%s%s", basedir, NAM_TO_MAJ);
1159 (void) snprintf(rem_name_to_major, rem_name_to_major_len,
1160 "%s%s", basedir, REM_NAM_TO_MAJ);
1161 (void) snprintf(add_rem_lock, add_rem_lock_len,
1162 "%s%s", basedir, ADD_REM_LOCK);
1163 (void) snprintf(tmphold, tmphold_len,
1164 "%s%s", basedir, TMPHOLD);
1165 (void) snprintf(devfs_root, devfs_root_len,
1166 "%s%s", basedir, DEVFS_ROOT);
1167 (void) snprintf(device_policy, device_policy_len,
1168 "%s%s", basedir, DEV_POLICY);
1169 (void) snprintf(extra_privs, extra_privs_len,
1170 "%s%s", basedir, EXTRA_PRIVS);
1171 }
1172
1173 return (NOERR);
1174 }
1175
1176 static int
exec_command(char * path,char * cmdline[MAX_CMD_LINE])1177 exec_command(char *path, char *cmdline[MAX_CMD_LINE])
1178 {
1179 pid_t pid;
1180 uint_t stat_loc;
1181 int waitstat;
1182 int exit_status;
1183
1184 /* child */
1185 if ((pid = fork()) == 0) {
1186 (void) execv(path, cmdline);
1187 perror(NULL);
1188 return (ERROR);
1189 } else if (pid == -1) {
1190 /* fork failed */
1191 perror(NULL);
1192 (void) fprintf(stderr, gettext(ERR_FORK_FAIL), cmdline);
1193 return (ERROR);
1194 } else {
1195 /* parent */
1196 do {
1197 waitstat = waitpid(pid, (int *)&stat_loc, 0);
1198
1199 } while ((!WIFEXITED(stat_loc) &&
1200 !WIFSIGNALED(stat_loc)) || (waitstat == 0));
1201
1202 exit_status = WEXITSTATUS(stat_loc);
1203
1204 return (exit_status);
1205 }
1206 }
1207
1208 /*
1209 * Exec devfsadm to perform driver config/unconfig operation,
1210 * adding or removing aliases.
1211 */
1212 static int
exec_devfsadm(boolean_t config,char * driver_name,major_t major_num,char * aliases,char * classes,int config_flags)1213 exec_devfsadm(
1214 boolean_t config,
1215 char *driver_name,
1216 major_t major_num,
1217 char *aliases,
1218 char *classes,
1219 int config_flags)
1220 {
1221 int n = 0;
1222 char *cmdline[MAX_CMD_LINE];
1223 char maj_num[128];
1224 char *previous;
1225 char *current;
1226 int len;
1227 int rv;
1228
1229 /* build command line */
1230 cmdline[n++] = DRVCONFIG;
1231 if (config == B_FALSE) {
1232 cmdline[n++] = "-u"; /* unconfigure */
1233 if (config_flags & CONFIG_DRV_FORCE)
1234 cmdline[n++] = "-f"; /* force if currently in use */
1235 }
1236 if (config_flags & CONFIG_DRV_VERBOSE) {
1237 cmdline[n++] = "-v";
1238 }
1239 cmdline[n++] = "-b";
1240 if (classes) {
1241 cmdline[n++] = "-c";
1242 cmdline[n++] = classes;
1243 }
1244 cmdline[n++] = "-i";
1245 cmdline[n++] = driver_name;
1246 cmdline[n++] = "-m";
1247 (void) snprintf(maj_num, sizeof (maj_num), "%lu", major_num);
1248 cmdline[n++] = maj_num;
1249 if (config_flags & CONFIG_DRV_UPDATE_ONLY)
1250 cmdline[n++] = "-x";
1251
1252 if (aliases != NULL) {
1253 len = strlen(aliases);
1254 previous = aliases;
1255 do {
1256 cmdline[n++] = "-a";
1257 cmdline[n] = calloc(len + 1, 1);
1258 if (cmdline[n] == NULL) {
1259 (void) fprintf(stderr,
1260 gettext(ERR_NO_MEM));
1261 return (ERROR);
1262 }
1263 current = get_entry(previous,
1264 cmdline[n++], ' ', 0);
1265 previous = current;
1266
1267 } while (*current != '\0');
1268
1269 }
1270 cmdline[n] = (char *)0;
1271
1272 rv = exec_command(DRVCONFIG_PATH, cmdline);
1273 if (rv == NOERR)
1274 return (NOERR);
1275 return (ERROR);
1276 }
1277
1278 int
unconfig_driver(char * driver_name,major_t major_num,char * aliases,int config_flags)1279 unconfig_driver(
1280 char *driver_name,
1281 major_t major_num,
1282 char *aliases,
1283 int config_flags)
1284 {
1285 return (exec_devfsadm(B_FALSE, driver_name, major_num,
1286 aliases, NULL, config_flags));
1287 }
1288
1289 /*
1290 * check that major_num doesn't exceed maximum on this machine
1291 * do this here to support add_drv on server for diskless clients
1292 */
1293 int
config_driver(char * driver_name,major_t major_num,char * aliases,char * classes,int cleanup_flag,int config_flags)1294 config_driver(
1295 char *driver_name,
1296 major_t major_num,
1297 char *aliases,
1298 char *classes,
1299 int cleanup_flag,
1300 int config_flags)
1301 {
1302 int max_dev;
1303 int rv;
1304
1305 if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
1306 perror(NULL);
1307 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
1308 return (ERROR);
1309 }
1310
1311 if (major_num >= max_dev) {
1312 (void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS),
1313 major_num, max_dev);
1314 return (ERROR);
1315 }
1316
1317 /* bind major number and driver name */
1318 rv = exec_devfsadm(B_TRUE, driver_name, major_num,
1319 aliases, classes, config_flags);
1320
1321 if (rv == NOERR)
1322 return (NOERR);
1323 perror(NULL);
1324 remove_entry(cleanup_flag, driver_name);
1325 return (ERROR);
1326 }
1327
1328 void
load_driver(char * driver_name,int verbose_flag)1329 load_driver(char *driver_name, int verbose_flag)
1330 {
1331 int n = 0;
1332 char *cmdline[MAX_CMD_LINE];
1333 int exec_status;
1334
1335 /* build command line */
1336 cmdline[n++] = DEVFSADM;
1337 if (verbose_flag) {
1338 cmdline[n++] = "-v";
1339 }
1340 cmdline[n++] = "-i";
1341 cmdline[n++] = driver_name;
1342 cmdline[n] = (char *)0;
1343
1344 exec_status = exec_command(DEVFSADM_PATH, cmdline);
1345
1346 if (exec_status != NOERR) {
1347 /* no clean : name and major number are bound */
1348 (void) fprintf(stderr, gettext(ERR_CONFIG), driver_name);
1349 }
1350 }
1351
1352 void
get_modid(char * driver_name,int * mod)1353 get_modid(char *driver_name, int *mod)
1354 {
1355 struct modinfo modinfo;
1356
1357 modinfo.mi_id = -1;
1358 modinfo.mi_info = MI_INFO_ALL;
1359 do {
1360 /*
1361 * If we are at the end of the list of loaded modules
1362 * then set *mod = -1 and return
1363 */
1364 if (modctl(MODINFO, 0, &modinfo) < 0) {
1365 *mod = -1;
1366 return;
1367 }
1368
1369 *mod = modinfo.mi_id;
1370 } while (strcmp(driver_name, modinfo.mi_name) != 0);
1371 }
1372
1373 int
create_reconfig(char * basedir)1374 create_reconfig(char *basedir)
1375 {
1376 char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1];
1377 FILE *reconfig_fp;
1378
1379 if (basedir != NULL) {
1380 (void) strcpy(reconfig_file, basedir);
1381 (void) strcat(reconfig_file, RECONFIGURE);
1382 } else {
1383 (void) strcpy(reconfig_file, RECONFIGURE);
1384 }
1385 if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL)
1386 return (ERROR);
1387
1388 (void) fclose(reconfig_fp);
1389 return (NOERR);
1390 }
1391
1392
1393 /*
1394 * update_minor_entry:
1395 * open file
1396 * for each entry in list
1397 * where list entries are separated by <list_separator>
1398 * modify entry : driver_name <entry_separator> entry
1399 * close file
1400 *
1401 * return error/noerr
1402 */
1403 int
update_minor_entry(char * driver_name,char * perm_list)1404 update_minor_entry(char *driver_name, char *perm_list)
1405 {
1406 FILE *fp;
1407 FILE *newfp;
1408 int match = 0;
1409 char line[MAX_DBFILE_ENTRY];
1410 char drv[FILENAME_MAX + 1];
1411 char minor[FILENAME_MAX + 1];
1412 char perm[OPT_LEN + 1];
1413 char own[OPT_LEN + 1];
1414 char grp[OPT_LEN + 1];
1415 int status = NOERR, i;
1416 char *newfile, *tptr;
1417 char *cp, *dup, *drv_minor;
1418 struct group *sysgrp;
1419
1420 if ((fp = fopen(minor_perm, "r")) == NULL) {
1421 perror(NULL);
1422 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1423 minor_perm);
1424
1425 return (ERROR);
1426 }
1427
1428 /*
1429 * Build filename for temporary file
1430 */
1431 if ((tptr = calloc(strlen(minor_perm) + strlen(XEND) + 1, 1)) == NULL) {
1432 perror(NULL);
1433 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1434 }
1435 (void) strcpy(tptr, minor_perm);
1436 (void) strcat(tptr, XEND);
1437
1438 /*
1439 * Set gid so we preserve group attribute. Ideally we wouldn't
1440 * assume a gid of "sys" but we can't undo the damage on already
1441 * installed systems unless we force the issue.
1442 */
1443 if ((sysgrp = getgrnam("sys")) != NULL) {
1444 (void) setgid(sysgrp->gr_gid);
1445 }
1446
1447 newfile = mktemp(tptr);
1448 if ((newfp = fopen(newfile, "w")) == NULL) {
1449 perror(NULL);
1450 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1451 newfile);
1452 return (ERROR);
1453 }
1454
1455 if (sscanf(perm_list,
1456 "%" VAL2STR(FILENAME_MAX) "s" /* minor */
1457 "%" VAL2STR(OPT_LEN) "s" /* perm */
1458 "%" VAL2STR(OPT_LEN) "s" /* own */
1459 "%" VAL2STR(OPT_LEN) "s", /* grp */
1460 minor, perm, own, grp) != 4) {
1461 status = ERROR;
1462 }
1463
1464 while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) {
1465 /* copy the whole line into dup */
1466 if ((dup = strdup(line)) == NULL) {
1467 perror(NULL);
1468 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1469 status = ERROR;
1470 break;
1471 }
1472 /* cut off comments starting with '#' */
1473 if ((cp = strchr(dup, '#')) != NULL)
1474 *cp = '\0';
1475 /* ignore comment or blank lines */
1476 if (is_blank(dup)) {
1477 if (fputs(line, newfp) == EOF) {
1478 (void) fprintf(stderr, gettext(ERR_UPDATE),
1479 minor_perm);
1480 status = ERROR;
1481 }
1482 free(dup);
1483 continue;
1484 }
1485
1486 /* get the driver name */
1487 if (sscanf(dup, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
1488 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1489 minor_perm, line);
1490 status = ERROR;
1491 free(dup);
1492 break;
1493 }
1494
1495 /*
1496 * get the minor name; place the NULL character at the
1497 * end of the driver name, then make the drv_minor
1498 * point to the first character of the minor name.
1499 * the line missing ':' must be treated as a broken one.
1500 */
1501 i = strcspn(drv, ":");
1502 if (i == strlen(drv)) {
1503 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1504 minor_perm, line);
1505 status = ERROR;
1506 free(dup);
1507 break;
1508 }
1509 drv[i] = '\0';
1510 drv_minor = &drv[strlen(drv) + 1];
1511
1512 /*
1513 * compare both of the driver name and the minor name.
1514 * then the new line should be written to the file if
1515 * both of them match
1516 */
1517 if ((strcmp(drv, driver_name) == 0) &&
1518 (strcmp(minor, drv_minor) == 0)) {
1519 /* if it has a comment, keep it */
1520 if (cp != NULL) {
1521 cp++; /* skip a terminator */
1522 (void) snprintf(line, sizeof (line),
1523 "%s:%s %s %s %s #%s\n",
1524 drv, minor, perm, own, grp, cp);
1525 } else {
1526 (void) snprintf(line, sizeof (line),
1527 "%s:%s %s %s %s\n",
1528 drv, minor, perm, own, grp);
1529 }
1530 match = 1;
1531 }
1532 free(dup);
1533
1534 /* update the file */
1535 if ((fputs(line, newfp)) == EOF) {
1536 (void) fprintf(stderr, gettext(ERR_UPDATE),
1537 minor_perm);
1538 status = ERROR;
1539 }
1540 }
1541
1542 if (!match) {
1543 (void) bzero(line, sizeof (&line[0]));
1544 (void) snprintf(line, sizeof (line),
1545 "%s:%s %s %s %s\n",
1546 driver_name, minor, perm, own, grp);
1547
1548 /* add the new entry */
1549 if ((fputs(line, newfp)) == EOF) {
1550 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1551 status = ERROR;
1552 }
1553 }
1554
1555 (void) fclose(fp);
1556
1557 if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0)
1558 status = ERROR;
1559
1560 (void) fclose(newfp);
1561
1562 /*
1563 * if error, leave original file, delete new file
1564 * if noerr, replace original file with new file
1565 */
1566 if (status == NOERR) {
1567 if (rename(minor_perm, tmphold) == -1) {
1568 perror(NULL);
1569 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1570 (void) unlink(newfile);
1571 return (ERROR);
1572 } else if (rename(newfile, minor_perm) == -1) {
1573 perror(NULL);
1574 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm);
1575 (void) unlink(minor_perm);
1576 (void) unlink(newfile);
1577 if (link(tmphold, minor_perm) == -1) {
1578 perror(NULL);
1579 (void) fprintf(stderr, gettext(ERR_BAD_LINK),
1580 minor_perm, tmphold);
1581 }
1582 return (ERROR);
1583 }
1584 (void) unlink(tmphold);
1585 } else {
1586 /*
1587 * since there's an error, leave file alone; remove
1588 * new file
1589 */
1590 if (unlink(newfile) == -1) {
1591 (void) fprintf(stderr, gettext(ERR_CANT_RM), newfile);
1592 }
1593 return (ERROR);
1594 }
1595
1596 return (NOERR);
1597
1598 }
1599
1600
1601 /*
1602 * list_entry:
1603 * open file
1604 * read thru file, listing all entries if first entry = driver_name
1605 * close
1606 */
1607 void
list_entry(char * oldfile,char * driver_name,char * marker)1608 list_entry(
1609 char *oldfile,
1610 char *driver_name,
1611 char *marker)
1612 {
1613 FILE *fp;
1614 int i;
1615 char line[MAX_DBFILE_ENTRY], *cp;
1616 char drv[FILENAME_MAX + 1];
1617
1618 if ((fp = fopen(oldfile, "r")) == NULL) {
1619 perror(NULL);
1620 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile);
1621
1622 return;
1623 }
1624
1625 while (fgets(line, sizeof (line), fp) != NULL) {
1626 /* cut off comments starting with '#' */
1627 if ((cp = strchr(line, '#')) != NULL)
1628 *cp = '\0';
1629 /* ignore comment or blank lines */
1630 if (is_blank(line))
1631 continue;
1632 /* sanity-check */
1633 if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s", drv) != 1) {
1634 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1635 oldfile, line);
1636 }
1637
1638 for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) {
1639 drv[i] = '\0';
1640 }
1641
1642 if (strcmp(driver_name, drv) == 0) {
1643 (void) fprintf(stdout, "%s", line);
1644 }
1645 }
1646
1647 (void) fclose(fp);
1648 }
1649
1650 static boolean_t
is_token(char * tok)1651 is_token(char *tok)
1652 {
1653 /*
1654 * Check the token here. According to IEEE1275 Open Firmware Boot
1655 * Standard, the name is composed of 1 to 31 letters,
1656 * digits and punctuation characters from the set ",._+-", and
1657 * uppercase and lowercase characters are considered distinct.
1658 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31)
1659 * However, since either the definition of driver or aliase names is
1660 * not known well, only '#' is avoided explicitly. (the kernel lexical
1661 * analyzer treats it as a start of a comment)
1662 */
1663 for (/* nothing */; *tok != '\0'; tok++)
1664 if (*tok == '#' || iscntrl(*tok))
1665 return (B_FALSE);
1666
1667 return (B_TRUE);
1668 }
1669
1670 /*
1671 * check each entry in perm_list for:
1672 * 4 arguments
1673 * permission arg is in valid range
1674 * permlist entries separated by comma
1675 * return ERROR/NOERR
1676 */
1677 int
check_perm_opts(char * perm_list)1678 check_perm_opts(char *perm_list)
1679 {
1680 char *current_head;
1681 char *previous_head;
1682 char *one_entry;
1683 int len, scan_stat;
1684 char minor[FILENAME_MAX + 1];
1685 char perm[OPT_LEN + 1];
1686 char own[OPT_LEN + 1];
1687 char grp[OPT_LEN + 1];
1688 char dumb[OPT_LEN + 1];
1689 int status = NOERR;
1690 int intperm;
1691
1692 if ((len = strlen(perm_list)) == 0)
1693 return (ERROR);
1694
1695 if ((one_entry = calloc(len + 1, 1)) == NULL) {
1696 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1697 return (ERROR);
1698 }
1699
1700 previous_head = perm_list;
1701 current_head = perm_list;
1702
1703 while (*current_head != '\0') {
1704 bzero(one_entry, len + 1);
1705 current_head = get_perm_entry(previous_head, one_entry);
1706
1707 previous_head = current_head;
1708 scan_stat = sscanf(one_entry,
1709 "%" VAL2STR(FILENAME_MAX) "s" /* minor */
1710 "%" VAL2STR(OPT_LEN) "s" /* perm */
1711 "%" VAL2STR(OPT_LEN) "s" /* own */
1712 "%" VAL2STR(OPT_LEN) "s" /* grp */
1713 "%" VAL2STR(OPT_LEN) "s", /* dumb */
1714 minor, perm, own, grp, dumb);
1715
1716 if (scan_stat < 4) {
1717 (void) fprintf(stderr, gettext(ERR_MIS_TOK),
1718 "-m", one_entry);
1719 status = ERROR;
1720 }
1721 if (scan_stat > 4) {
1722 (void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS),
1723 "-m", one_entry);
1724 status = ERROR;
1725 }
1726
1727 intperm = atoi(perm);
1728 if (intperm < 0000 || intperm > 4777) {
1729 (void) fprintf(stderr, gettext(ERR_BAD_MODE), perm);
1730 status = ERROR;
1731 }
1732 }
1733
1734 free(one_entry);
1735 return (status);
1736 }
1737
1738
1739 /*
1740 * check each alias :
1741 * alias list members separated by white space
1742 * cannot exist as driver name in /etc/name_to_major
1743 * cannot exist as driver or alias name in /etc/driver_aliases
1744 */
1745 int
aliases_unique(char * aliases)1746 aliases_unique(char *aliases)
1747 {
1748 char *current_head;
1749 char *previous_head;
1750 char *one_entry;
1751 int len;
1752 int is_unique;
1753 int err;
1754
1755 len = strlen(aliases);
1756
1757 one_entry = calloc(len + 1, 1);
1758 if (one_entry == NULL) {
1759 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1760 return (ERROR);
1761 }
1762
1763 previous_head = aliases;
1764
1765 do {
1766 bzero(one_entry, len+1);
1767 current_head = get_entry(previous_head, one_entry, ' ', 1);
1768 previous_head = current_head;
1769
1770 if ((unique_driver_name(one_entry, name_to_major,
1771 &is_unique)) == ERROR)
1772 goto err_out;
1773
1774 if (is_unique != UNIQUE) {
1775 (void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ),
1776 one_entry);
1777 goto err_out;
1778 }
1779
1780 if ((err = unique_drv_alias(one_entry)) != UNIQUE) {
1781 if (err == NOT_UNIQUE) {
1782 (void) fprintf(stderr,
1783 gettext(ERR_ALIAS_IN_USE), one_entry);
1784 }
1785 goto err_out;
1786 }
1787
1788 if (!is_token(one_entry)) {
1789 (void) fprintf(stderr, gettext(ERR_BAD_TOK),
1790 "-i", one_entry);
1791 goto err_out;
1792 }
1793
1794 } while (*current_head != '\0');
1795
1796 free(one_entry);
1797 return (NOERR);
1798
1799 err_out:
1800 free(one_entry);
1801 return (ERROR);
1802 }
1803
1804 /*
1805 * verify each alias :
1806 * alias list members separated by white space and quoted
1807 * exist as alias name in /etc/driver_aliases
1808 */
1809 int
aliases_exist(char * aliases)1810 aliases_exist(char *aliases)
1811 {
1812 char *current_head;
1813 char *previous_head;
1814 char *one_entry;
1815 int len;
1816
1817 len = strlen(aliases);
1818
1819 one_entry = calloc(len + 1, 1);
1820 if (one_entry == NULL) {
1821 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1822 return (ERROR);
1823 }
1824
1825 previous_head = aliases;
1826
1827 do {
1828 bzero(one_entry, len+1);
1829 current_head = get_entry(previous_head, one_entry, ' ', 1);
1830 previous_head = current_head;
1831
1832 if (unique_drv_alias(one_entry) != NOT_UNIQUE)
1833 goto err_out;
1834
1835 if (!is_token(one_entry)) {
1836 (void) fprintf(stderr, gettext(ERR_BAD_TOK),
1837 "-i", one_entry);
1838 goto err_out;
1839 }
1840
1841 } while (*current_head != '\0');
1842
1843 free(one_entry);
1844 return (NOERR);
1845
1846 err_out:
1847 free(one_entry);
1848 return (ERROR);
1849 }
1850
1851
1852 /*
1853 * check each alias :
1854 * if path-oriented alias, path exists
1855 */
1856 int
aliases_paths_exist(char * aliases)1857 aliases_paths_exist(char *aliases)
1858 {
1859 char *current_head;
1860 char *previous_head;
1861 char *one_entry;
1862 int i, len;
1863 char path[MAXPATHLEN];
1864 struct stat buf;
1865
1866 len = strlen(aliases);
1867
1868 one_entry = calloc(len + 1, 1);
1869 if (one_entry == NULL) {
1870 (void) fprintf(stderr, gettext(ERR_NO_MEM));
1871 return (ERROR);
1872 }
1873
1874 previous_head = aliases;
1875
1876 do {
1877 for (i = 0; i <= len; i++)
1878 one_entry[i] = 0;
1879
1880 current_head = get_entry(previous_head, one_entry, ' ', 1);
1881 previous_head = current_head;
1882
1883 /* if the alias is a path, ensure that the path exists */
1884 if (*one_entry != '/')
1885 continue;
1886 (void) snprintf(path, sizeof (path), "/devices/%s", one_entry);
1887 if (stat(path, &buf) == 0)
1888 continue;
1889
1890 /* no device at specified path-oriented alias path */
1891 (void) fprintf(stderr, gettext(ERR_PATH_ORIENTED_ALIAS),
1892 one_entry);
1893 free(one_entry);
1894 return (ERROR);
1895
1896 } while (*current_head != '\0');
1897
1898 free(one_entry);
1899
1900 return (NOERR);
1901 }
1902
1903
1904 int
update_driver_aliases(char * driver_name,char * aliases)1905 update_driver_aliases(
1906 char *driver_name,
1907 char *aliases)
1908 {
1909 /* make call to update the aliases file */
1910 return (append_to_file(driver_name, aliases, driver_aliases,
1911 ' ', " ", 1));
1912 }
1913
1914
1915 /*
1916 * Return:
1917 * ERROR in case of memory or read error
1918 * UNIQUE if there is no existing match to the supplied alias
1919 * NOT_UNIQUE if there is a match
1920 * An error message is emitted in the case of ERROR,
1921 * up to the caller otherwise.
1922 */
1923 int
unique_drv_alias(char * drv_alias)1924 unique_drv_alias(char *drv_alias)
1925 {
1926 FILE *fp;
1927 char drv[FILENAME_MAX + 1];
1928 char line[MAX_N2M_ALIAS_LINE + 1], *cp;
1929 char alias[FILENAME_MAX + 1];
1930 char *a;
1931 int status = UNIQUE;
1932
1933 fp = fopen(driver_aliases, "r");
1934
1935 if (fp != NULL) {
1936 while ((fgets(line, sizeof (line), fp) != 0) &&
1937 status == UNIQUE) {
1938 /* cut off comments starting with '#' */
1939 if ((cp = strchr(line, '#')) != NULL)
1940 *cp = '\0';
1941 /* ignore comment or blank lines */
1942 if (is_blank(line))
1943 continue;
1944 /* sanity-check */
1945 if (sscanf(line,
1946 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
1947 "%" VAL2STR(FILENAME_MAX) "s", /* alias */
1948 drv, alias) != 2)
1949 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
1950 driver_aliases, line);
1951
1952 /* unquote for compare */
1953 if ((*alias == '"') &&
1954 (*(alias + strlen(alias) - 1) == '"')) {
1955 a = &alias[1];
1956 alias[strlen(alias) - 1] = '\0';
1957 } else
1958 a = alias;
1959
1960 if ((strcmp(drv_alias, drv) == 0) ||
1961 (strcmp(drv_alias, a) == 0)) {
1962 status = NOT_UNIQUE;
1963 break;
1964 }
1965 }
1966 (void) fclose(fp);
1967 return (status);
1968 } else {
1969 perror(NULL);
1970 (void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
1971 return (ERROR);
1972 }
1973 }
1974
1975
1976 /*
1977 * search for driver_name in first field of file file_name
1978 * searching name_to_major and driver_aliases: name separated
1979 * from the remainder of the line by white space.
1980 */
1981 int
unique_driver_name(char * driver_name,char * file_name,int * is_unique)1982 unique_driver_name(char *driver_name, char *file_name,
1983 int *is_unique)
1984 {
1985 int ret, err;
1986
1987 if ((ret = get_major_no(driver_name, file_name)) == ERROR) {
1988 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE),
1989 file_name);
1990 } else {
1991 /* check alias file for name collision */
1992 if ((err = unique_drv_alias(driver_name)) != UNIQUE) {
1993 if (err == NOT_UNIQUE) {
1994 (void) fprintf(stderr,
1995 gettext(ERR_ALIAS_IN_USE),
1996 driver_name);
1997 }
1998 ret = ERROR;
1999 } else {
2000 if (ret != UNIQUE)
2001 *is_unique = NOT_UNIQUE;
2002 else
2003 *is_unique = ret;
2004 ret = NOERR;
2005 }
2006 }
2007 return (ret);
2008 }
2009
2010 /*
2011 * returns:
2012 * SUCCESS - not an existing driver alias
2013 * NOT_UNIQUE - matching driver alias exists
2014 * ERROR - an error occurred
2015 */
2016 int
check_duplicate_driver_alias(char * driver_name,char * drv_alias)2017 check_duplicate_driver_alias(char *driver_name, char *drv_alias)
2018 {
2019 FILE *fp;
2020 char drv[FILENAME_MAX + 1];
2021 char line[MAX_N2M_ALIAS_LINE + 1], *cp;
2022 char alias[FILENAME_MAX + 1];
2023 char *a;
2024 int status = SUCCESS;
2025
2026 if ((fp = fopen(driver_aliases, "r")) == NULL) {
2027 perror(NULL);
2028 (void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases);
2029 return (ERROR);
2030 }
2031
2032 while (fgets(line, sizeof (line), fp) != 0) {
2033 /* cut off comments starting with '#' */
2034 if ((cp = strchr(line, '#')) != NULL)
2035 *cp = '\0';
2036 /* ignore comment or blank lines */
2037 if (is_blank(line))
2038 continue;
2039 /* sanity-check */
2040 if (sscanf(line,
2041 "%" VAL2STR(FILENAME_MAX) "s" /* drv */
2042 "%" VAL2STR(FILENAME_MAX) "s", /* alias */
2043 drv, alias) != 2)
2044 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
2045 driver_aliases, line);
2046
2047 /* unquote for compare */
2048 if ((*alias == '"') &&
2049 (*(alias + strlen(alias) - 1) == '"')) {
2050 a = &alias[1];
2051 alias[strlen(alias) - 1] = '\0';
2052 } else
2053 a = alias;
2054
2055 if ((strcmp(drv_alias, a) == 0) &&
2056 (strcmp(drv, driver_name) == 0)) {
2057 status = NOT_UNIQUE;
2058 }
2059
2060 if ((strcmp(drv_alias, drv) == 0) ||
2061 ((strcmp(drv_alias, a) == 0) &&
2062 (strcmp(drv, driver_name) != 0))) {
2063 (void) fprintf(stderr,
2064 gettext(ERR_ALIAS_IN_USE),
2065 drv_alias);
2066 status = ERROR;
2067 goto done;
2068 }
2069 }
2070
2071 done:
2072 (void) fclose(fp);
2073 return (status);
2074 }
2075
2076 int
trim_duplicate_aliases(char * driver_name,char * aliases,char ** aliases2p)2077 trim_duplicate_aliases(char *driver_name, char *aliases, char **aliases2p)
2078 {
2079 char *current_head;
2080 char *previous_head;
2081 char *one_entry;
2082 char *aliases2;
2083 int rv, len;
2084 int n = 0;
2085
2086 *aliases2p = NULL;
2087 len = strlen(aliases) + 1;
2088
2089 one_entry = calloc(len, 1);
2090 aliases2 = calloc(len, 1);
2091 if (one_entry == NULL || aliases2 == NULL) {
2092 (void) fprintf(stderr, gettext(ERR_NO_MEM));
2093 return (ERROR);
2094 }
2095
2096 previous_head = aliases;
2097
2098 do {
2099 (void) bzero(one_entry, len);
2100 current_head = get_entry(previous_head, one_entry, ' ', 1);
2101 previous_head = current_head;
2102
2103 rv = check_duplicate_driver_alias(driver_name, one_entry);
2104 switch (rv) {
2105 case SUCCESS:
2106 /* not an existing driver alias: add it */
2107 if (n > 0) {
2108 if (strlcat(aliases2, " ", len) >= len)
2109 goto err;
2110 }
2111 if (strlcat(aliases2, one_entry, len) >= len)
2112 goto err;
2113 n++;
2114 break;
2115 case NOT_UNIQUE:
2116 /* matching driver alias exists: do not add it */
2117 break;
2118 case ERROR:
2119 /* error reading the alias file */
2120 goto err;
2121 default:
2122 goto err;
2123 }
2124
2125 if (!is_token(one_entry)) {
2126 (void) fprintf(stderr, gettext(ERR_BAD_TOK),
2127 "-i", one_entry);
2128 goto err;
2129 }
2130 } while (*current_head != '\0');
2131
2132 /*
2133 * If all the aliases listed are already
2134 * present we actually have none to do.
2135 */
2136 if (n == 0) {
2137 free(aliases2);
2138 } else {
2139 *aliases2p = aliases2;
2140 }
2141 free(one_entry);
2142 return (NOERR);
2143
2144 err:
2145 free(aliases2);
2146 free(one_entry);
2147 return (ERROR);
2148 }
2149
2150 int
check_space_within_quote(char * str)2151 check_space_within_quote(char *str)
2152 {
2153 register int i;
2154 register int len;
2155 int quoted = 0;
2156
2157 len = strlen(str);
2158 for (i = 0; i < len; i++, str++) {
2159 if (*str == '"') {
2160 if (quoted == 0)
2161 quoted++;
2162 else
2163 quoted--;
2164 } else if (*str == ' ' && quoted)
2165 return (ERROR);
2166 }
2167
2168 return (0);
2169 }
2170
2171
2172 /*
2173 * get major number
2174 * write driver_name major_num to name_to_major file
2175 * major_num returned in major_num
2176 * return success/failure
2177 */
2178 int
update_name_to_major(char * driver_name,major_t * major_num,int server)2179 update_name_to_major(char *driver_name, major_t *major_num, int server)
2180 {
2181 char major[MAX_STR_MAJOR + 1];
2182 struct stat buf;
2183 char *num_list;
2184 char drv_majnum_str[MAX_STR_MAJOR + 1];
2185 int new_maj = -1;
2186 int i, tmp = 0, is_unique, have_rem_n2m = 0;
2187 int max_dev = 0;
2188
2189 /*
2190 * if driver_name already in rem_name_to_major
2191 * delete entry from rem_nam_to_major
2192 * put entry into name_to_major
2193 */
2194
2195 if (stat(rem_name_to_major, &buf) == 0) {
2196 have_rem_n2m = 1;
2197 }
2198
2199 if (have_rem_n2m) {
2200 if ((is_unique = get_major_no(driver_name, rem_name_to_major))
2201 == ERROR)
2202 return (ERROR);
2203
2204 /*
2205 * found a match in rem_name_to_major
2206 */
2207 if (is_unique != UNIQUE) {
2208 char scratch[FILENAME_MAX];
2209
2210 /*
2211 * If there is a match in /etc/rem_name_to_major then
2212 * be paranoid: is that major number already in
2213 * /etc/name_to_major (potentially under another name)?
2214 */
2215 if (get_driver_name(is_unique, name_to_major,
2216 scratch) != UNIQUE) {
2217 /*
2218 * nuke the rem_name_to_major entry-- it
2219 * isn't helpful.
2220 */
2221 (void) delete_entry(rem_name_to_major,
2222 driver_name, " ", NULL);
2223 } else {
2224 (void) snprintf(major, sizeof (major),
2225 "%d", is_unique);
2226
2227 if (append_to_file(driver_name, major,
2228 name_to_major, ' ', " ", 0) == ERROR) {
2229 (void) fprintf(stderr,
2230 gettext(ERR_NO_UPDATE),
2231 name_to_major);
2232 return (ERROR);
2233 }
2234
2235 if (delete_entry(rem_name_to_major,
2236 driver_name, " ", NULL) == ERROR) {
2237 (void) fprintf(stderr,
2238 gettext(ERR_DEL_ENTRY), driver_name,
2239 rem_name_to_major);
2240 return (ERROR);
2241 }
2242
2243 /* found matching entry : no errors */
2244 *major_num = is_unique;
2245 return (NOERR);
2246 }
2247 }
2248 }
2249
2250 /*
2251 * Bugid: 1264079
2252 * In a server case (with -b option), we can't use modctl() to find
2253 * the maximum major number, we need to dig thru client's
2254 * /etc/name_to_major and /etc/rem_name_to_major for the max_dev.
2255 *
2256 * if (server)
2257 * get maximum major number thru (rem_)name_to_major file on client
2258 * else
2259 * get maximum major number allowable on current system using modctl
2260 */
2261 if (server) {
2262 max_dev = 0;
2263 tmp = 0;
2264
2265 max_dev = get_max_major(name_to_major);
2266
2267 /* If rem_name_to_major exists, we need to check it too */
2268 if (have_rem_n2m) {
2269 tmp = get_max_major(rem_name_to_major);
2270
2271 /*
2272 * If name_to_major is missing, we can get max_dev from
2273 * /etc/rem_name_to_major. If both missing, bail out!
2274 */
2275 if ((max_dev == ERROR) && (tmp == ERROR)) {
2276 (void) fprintf(stderr,
2277 gettext(ERR_CANT_ACCESS_FILE),
2278 name_to_major);
2279 return (ERROR);
2280 }
2281
2282 /* guard against bigger maj_num in rem_name_to_major */
2283 if (tmp > max_dev)
2284 max_dev = tmp;
2285 } else {
2286 /*
2287 * If we can't get major from name_to_major file
2288 * and there is no /etc/rem_name_to_major file,
2289 * then we don't have a max_dev, bail out quick!
2290 */
2291 if (max_dev == ERROR)
2292 return (ERROR);
2293 }
2294
2295 /*
2296 * In case there is no more slack in current name_to_major
2297 * table, provide at least 1 extra entry so the add_drv can
2298 * succeed. Since only one add_drv process is allowed at one
2299 * time, and hence max_dev will be re-calculated each time
2300 * add_drv is ran, we don't need to worry about adding more
2301 * than 1 extra slot for max_dev.
2302 */
2303 max_dev++;
2304
2305 } else {
2306 if (modctl(MODRESERVED, NULL, &max_dev) < 0) {
2307 perror(NULL);
2308 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR));
2309 return (ERROR);
2310 }
2311 }
2312
2313 /*
2314 * max_dev is really how many slots the kernel has allocated for
2315 * devices... [0 , maxdev-1], not the largest available device num.
2316 */
2317 if ((num_list = calloc(max_dev, 1)) == NULL) {
2318 (void) fprintf(stderr, gettext(ERR_NO_MEM));
2319 return (ERROR);
2320 }
2321
2322 /*
2323 * Populate the num_list array
2324 */
2325 if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) {
2326 return (ERROR);
2327 }
2328 if (have_rem_n2m) {
2329 if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0)
2330 return (ERROR);
2331 }
2332
2333 /* find first free major number */
2334 for (i = 0; i < max_dev; i++) {
2335 if (num_list[i] != 1) {
2336 new_maj = i;
2337 break;
2338 }
2339 }
2340
2341 if (new_maj == -1) {
2342 (void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR));
2343 return (ERROR);
2344 }
2345
2346 (void) snprintf(drv_majnum_str, sizeof (drv_majnum_str),
2347 "%d", new_maj);
2348 if (do_the_update(driver_name, drv_majnum_str) == ERROR) {
2349 return (ERROR);
2350 }
2351
2352 *major_num = new_maj;
2353 return (NOERR);
2354 }
2355
2356
2357 int
fill_n2m_array(char * filename,char ** array,int * nelems)2358 fill_n2m_array(char *filename, char **array, int *nelems)
2359 {
2360 FILE *fp;
2361 char line[MAX_N2M_ALIAS_LINE + 1], *cp;
2362 char drv[FILENAME_MAX + 1];
2363 u_longlong_t dnum;
2364 major_t drv_majnum;
2365
2366 /*
2367 * Read through the file, marking each major number found
2368 * order is not relevant
2369 */
2370 if ((fp = fopen(filename, "r")) == NULL) {
2371 perror(NULL);
2372 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename);
2373 return (ERROR);
2374 }
2375
2376 while (fgets(line, sizeof (line), fp) != 0) {
2377 /* cut off comments starting with '#' */
2378 if ((cp = strchr(line, '#')) != NULL)
2379 *cp = '\0';
2380 /* ignore comment or blank lines */
2381 if (is_blank(line))
2382 continue;
2383 /* sanity-check */
2384 if (sscanf(line,
2385 "%" VAL2STR(FILENAME_MAX) "s %llu", drv, &dnum) != 2) {
2386 (void) fprintf(stderr, gettext(ERR_BAD_LINE),
2387 filename, line);
2388 (void) fclose(fp);
2389 return (ERROR);
2390 }
2391
2392 if (dnum > L_MAXMAJ32) {
2393 (void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv,
2394 dnum, filename, L_MAXMAJ32);
2395 continue;
2396 }
2397 /*
2398 * cast down to a major_t; we can be sure this is safe because
2399 * of the above range-check.
2400 */
2401 drv_majnum = (major_t)dnum;
2402
2403 if (drv_majnum >= *nelems) {
2404 /*
2405 * Allocate some more space, up to drv_majnum + 1 so
2406 * we can accomodate 0 through drv_majnum.
2407 *
2408 * Note that in the failure case, we leak all of the
2409 * old contents of array. It's ok, since we just
2410 * wind up exiting immediately anyway.
2411 */
2412 *nelems = drv_majnum + 1;
2413 *array = realloc(*array, *nelems);
2414 if (*array == NULL) {
2415 (void) fprintf(stderr, gettext(ERR_NO_MEM));
2416 return (ERROR);
2417 }
2418 }
2419 (*array)[drv_majnum] = 1;
2420 }
2421
2422 (void) fclose(fp);
2423 return (0);
2424 }
2425
2426
2427 int
do_the_update(char * driver_name,char * major_number)2428 do_the_update(char *driver_name, char *major_number)
2429 {
2430 return (append_to_file(driver_name, major_number, name_to_major,
2431 ' ', " ", 0));
2432 }
2433
2434 /*
2435 * is_blank() returns 1 (true) if a line specified is composed of
2436 * whitespace characters only. otherwise, it returns 0 (false).
2437 *
2438 * Note. the argument (line) must be null-terminated.
2439 */
2440 static int
is_blank(char * line)2441 is_blank(char *line)
2442 {
2443 for (/* nothing */; *line != '\0'; line++)
2444 if (!isspace(*line))
2445 return (0);
2446 return (1);
2447 }
2448