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