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