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