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