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