1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <ctype.h> 29 #include <unistd.h> 30 #include <sys/sysmacros.h> 31 #include <libintl.h> 32 #include <wait.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <signal.h> 38 #include <sys/buf.h> 39 #include <sys/stat.h> 40 #include <grp.h> 41 #include "addrem.h" 42 #include "errmsg.h" 43 #include "plcysubr.h" 44 45 static char *add_rem_lock; /* lock file */ 46 static char *tmphold; /* temperary file for updating */ 47 static int add_rem_lock_fd = -1; 48 49 static int get_cached_n_to_m_file(char *filename, char ***cache); 50 static int get_name_to_major_entry(int *major_no, char *driver_name, 51 char *file_name); 52 53 static int is_blank(char *); 54 55 /*ARGSUSED*/ 56 void 57 log_minorperm_error(minorperm_err_t err, int key) 58 { 59 switch (err) { 60 case MP_FOPEN_ERR: 61 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), 62 MINOR_PERM_FILE); 63 break; 64 case MP_FCLOSE_ERR: 65 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), 66 MINOR_PERM_FILE); 67 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 68 break; 69 case MP_IGNORING_LINE_ERR: 70 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), 71 MINOR_PERM_FILE); 72 break; 73 case MP_ALLOC_ERR: 74 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), 75 MINOR_PERM_FILE); 76 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 77 break; 78 case MP_NVLIST_ERR: 79 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), 80 MINOR_PERM_FILE); 81 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 82 break; 83 case MP_CANT_FIND_USER_ERR: 84 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), 85 MINOR_PERM_FILE); 86 break; 87 case MP_CANT_FIND_GROUP_ERR: 88 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), 89 MINOR_PERM_FILE); 90 break; 91 } 92 } 93 94 /* 95 * open file 96 * for each entry in list 97 * where list entries are separated by <list_separator> 98 * append entry : driver_name <entry_separator> entry 99 * close file 100 * return error/noerr 101 */ 102 int 103 append_to_file( 104 char *driver_name, 105 char *entry_list, 106 char *filename, 107 char list_separator, 108 char *entry_separator, 109 int quoted) 110 { 111 int i, len; 112 int fpint; 113 char *current_head, *previous_head; 114 char *line, *one_entry; 115 FILE *fp; 116 117 if ((fp = fopen(filename, "a")) == NULL) { 118 perror(NULL); 119 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), 120 filename); 121 return (ERROR); 122 } 123 124 len = strlen(entry_list); 125 126 one_entry = calloc(len + 1, 1); 127 if (one_entry == NULL) { 128 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), filename); 129 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 130 (void) fclose(fp); 131 return (ERROR); 132 } 133 134 previous_head = entry_list; 135 136 line = calloc(strlen(driver_name) + len + 4, 1); 137 if (line == NULL) { 138 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 139 (void) fclose(fp); 140 err_exit(); 141 } 142 143 /* 144 * get one entry at a time from list and append to <filename> file 145 */ 146 147 do { 148 149 for (i = 0; i <= len; i++) 150 one_entry[i] = 0; 151 152 for (i = 0; i <= (int)strlen(line); i++) 153 line[i] = 0; 154 155 current_head = get_entry(previous_head, one_entry, 156 list_separator, quoted); 157 previous_head = current_head; 158 159 (void) strcpy(line, driver_name); 160 (void) strcat(line, entry_separator); 161 if (quoted) 162 (void) strcat(line, "\""); 163 (void) strcat(line, one_entry); 164 if (quoted) 165 (void) strcat(line, "\""); 166 (void) strcat(line, "\n"); 167 168 if ((fputs(line, fp)) == EOF) { 169 perror(NULL); 170 (void) fprintf(stderr, gettext(ERR_NO_UPDATE), 171 filename); 172 } 173 174 } while (*current_head != '\0'); 175 176 177 (void) fflush(fp); 178 179 fpint = fileno(fp); 180 (void) fsync(fpint); 181 182 (void) fclose(fp); 183 184 free(one_entry); 185 free(line); 186 187 return (NOERR); 188 } 189 190 /* 191 * Require exact match to delete a driver alias/permission entry. 192 * Note line argument does not remain unchanged. Return 1 if matched. 193 */ 194 static int 195 match_entry(char *line, char *match) 196 { 197 char *token, *p; 198 int n; 199 200 /* skip any leading white space */ 201 while (*line && ((*line == ' ') || (*line == '\t'))) 202 line++; 203 /* 204 * Find separator for driver name, either space or colon 205 * minor_perm: <driver>:<perm> 206 * driver_aliases: <driver> <alias> 207 * extra_privs: <driver>:<priv> 208 */ 209 if ((token = strpbrk(line, " :\t")) == NULL) 210 return (0); 211 token++; 212 /* skip leading white space and quotes */ 213 while (*token && (*token == ' ' || *token == '\t' || 214 *token == '"' || *token == '\'')) 215 token++; 216 /* strip trailing newline, white space and quotes */ 217 n = strlen(token); 218 p = token + n-1; 219 while (n > 0 && (*p == '\n' || *p == ' ' || *p == '\t' || 220 *p == '"' || *p == '\'')) { 221 *p-- = 0; 222 n--; 223 } 224 if (n == 0) 225 return (0); 226 return (strcmp(token, match) == 0); 227 } 228 229 /* 230 * open file 231 * read thru file, deleting all entries if first 232 * entry = driver_name 233 * close 234 * if error, leave original file intact with message 235 * assumption : drvconfig has been modified to work with clone 236 * entries in /etc/minor_perm as driver:mummble NOT 237 * clone:driver mummble 238 * this implementation will NOT find clone entries 239 * clone:driver mummble 240 * match: 241 * delete just the matching entry 242 * 243 */ 244 int 245 delete_entry( 246 char *oldfile, 247 char *driver_name, 248 char *marker, 249 char *match) 250 { 251 int rv, i; 252 int status = NOERR; 253 int drvr_found = 0; 254 boolean_t nomatch = B_TRUE; 255 char *newfile, *tptr, *cp; 256 char line[MAX_DBFILE_ENTRY], drv[FILENAME_MAX + 1]; 257 FILE *fp, *newfp; 258 struct group *sysgrp; 259 char *copy; /* same size as line */ 260 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 * Exec devfsadm to perform driver config/unconfig operation, 1009 * adding or removing aliases. 1010 */ 1011 static int 1012 exec_devfsadm( 1013 boolean_t config, 1014 char *driver_name, 1015 major_t major_num, 1016 char *aliases, 1017 char *classes, 1018 int verbose_flag, 1019 int force_flag) 1020 { 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 int rv; 1029 1030 /* build command line */ 1031 cmdline[n++] = DRVCONFIG; 1032 if (config == B_FALSE) { 1033 cmdline[n++] = "-u"; /* unconfigure */ 1034 if (force_flag) 1035 cmdline[n++] = "-f"; /* force if currently in use */ 1036 } 1037 if (verbose_flag) { 1038 cmdline[n++] = "-v"; 1039 } 1040 cmdline[n++] = "-b"; 1041 if (classes) { 1042 cmdline[n++] = "-c"; 1043 cmdline[n++] = classes; 1044 } 1045 cmdline[n++] = "-i"; 1046 cmdline[n++] = driver_name; 1047 cmdline[n++] = "-m"; 1048 (void) sprintf(maj_num, "%lu", major_num); 1049 cmdline[n++] = maj_num; 1050 1051 if (aliases != NULL) { 1052 len = strlen(aliases); 1053 previous = aliases; 1054 do { 1055 cmdline[n++] = "-a"; 1056 cmdline[n] = calloc(len + 1, 1); 1057 if (cmdline[n] == NULL) { 1058 (void) fprintf(stderr, 1059 gettext(ERR_NO_MEM)); 1060 return (ERROR); 1061 } 1062 current = get_entry(previous, 1063 cmdline[n++], ' ', 0); 1064 previous = current; 1065 1066 } while (*current != '\0'); 1067 1068 } 1069 cmdline[n] = (char *)0; 1070 1071 rv = exec_command(DRVCONFIG_PATH, cmdline); 1072 if (rv == NOERR) 1073 return (NOERR); 1074 return (ERROR); 1075 } 1076 1077 int 1078 unconfig_driver( 1079 char *driver_name, 1080 major_t major_num, 1081 char *aliases, 1082 int verbose_flag, 1083 int force_flag) 1084 { 1085 return (exec_devfsadm(B_FALSE, driver_name, major_num, 1086 aliases, NULL, verbose_flag, force_flag)); 1087 } 1088 1089 /* 1090 * check that major_num doesn't exceed maximum on this machine 1091 * do this here to support add_drv on server for diskless clients 1092 */ 1093 int 1094 config_driver( 1095 char *driver_name, 1096 major_t major_num, 1097 char *aliases, 1098 char *classes, 1099 int cleanup_flag, 1100 int verbose_flag) 1101 { 1102 int max_dev; 1103 int rv; 1104 1105 if (modctl(MODRESERVED, NULL, &max_dev) < 0) { 1106 perror(NULL); 1107 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR)); 1108 return (ERROR); 1109 } 1110 1111 if (major_num >= max_dev) { 1112 (void) fprintf(stderr, gettext(ERR_MAX_EXCEEDS), 1113 major_num, max_dev); 1114 return (ERROR); 1115 } 1116 1117 /* bind major number and driver name */ 1118 rv = exec_devfsadm(B_TRUE, driver_name, major_num, 1119 aliases, classes, verbose_flag, 0); 1120 1121 if (rv == NOERR) 1122 return (NOERR); 1123 perror(NULL); 1124 remove_entry(cleanup_flag, driver_name); 1125 return (ERROR); 1126 } 1127 1128 void 1129 load_driver(char *driver_name, int verbose_flag) 1130 { 1131 int n = 0; 1132 char *cmdline[MAX_CMD_LINE]; 1133 int exec_status; 1134 1135 /* build command line */ 1136 cmdline[n++] = DEVFSADM; 1137 if (verbose_flag) { 1138 cmdline[n++] = "-v"; 1139 } 1140 cmdline[n++] = "-i"; 1141 cmdline[n++] = driver_name; 1142 cmdline[n] = (char *)0; 1143 1144 exec_status = exec_command(DEVFSADM_PATH, cmdline); 1145 1146 if (exec_status != NOERR) { 1147 /* no clean : name and major number are bound */ 1148 (void) fprintf(stderr, gettext(ERR_CONFIG), driver_name); 1149 } 1150 } 1151 1152 void 1153 get_modid(char *driver_name, int *mod) 1154 { 1155 struct modinfo modinfo; 1156 1157 modinfo.mi_id = -1; 1158 modinfo.mi_info = MI_INFO_ALL; 1159 do { 1160 /* 1161 * If we are at the end of the list of loaded modules 1162 * then set *mod = -1 and return 1163 */ 1164 if (modctl(MODINFO, 0, &modinfo) < 0) { 1165 *mod = -1; 1166 return; 1167 } 1168 1169 *mod = modinfo.mi_id; 1170 } while (strcmp(driver_name, modinfo.mi_name) != 0); 1171 } 1172 1173 int 1174 create_reconfig(char *basedir) 1175 { 1176 char reconfig_file[MAXPATHLEN + FILENAME_MAX + 1]; 1177 FILE *reconfig_fp; 1178 1179 if (basedir != NULL) { 1180 (void) strcpy(reconfig_file, basedir); 1181 (void) strcat(reconfig_file, RECONFIGURE); 1182 } else { 1183 (void) strcpy(reconfig_file, RECONFIGURE); 1184 } 1185 if ((reconfig_fp = fopen(reconfig_file, "a")) == NULL) 1186 return (ERROR); 1187 1188 (void) fclose(reconfig_fp); 1189 return (NOERR); 1190 } 1191 1192 1193 /* 1194 * update_minor_entry: 1195 * open file 1196 * for each entry in list 1197 * where list entries are separated by <list_separator> 1198 * modify entry : driver_name <entry_separator> entry 1199 * close file 1200 * 1201 * return error/noerr 1202 */ 1203 int 1204 update_minor_entry(char *driver_name, char *perm_list) 1205 { 1206 FILE *fp; 1207 FILE *newfp; 1208 struct group *sysgrp; 1209 int match = 0; 1210 char line[MAX_DBFILE_ENTRY], *cp, *dup; 1211 char drv[FILENAME_MAX + 1], *drv_minor; 1212 char minor[FILENAME_MAX + 1], perm[OPT_LEN + 1]; 1213 char own[OPT_LEN + 1], grp[OPT_LEN + 1]; 1214 int status = NOERR, i; 1215 char *newfile, *tptr; 1216 1217 if ((fp = fopen(minor_perm, "r")) == NULL) { 1218 perror(NULL); 1219 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), 1220 minor_perm); 1221 1222 return (ERROR); 1223 } 1224 1225 /* 1226 * Build filename for temporary file 1227 */ 1228 if ((tptr = calloc(strlen(minor_perm) + strlen(XEND) + 1, 1)) == NULL) { 1229 perror(NULL); 1230 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 1231 } 1232 (void) strcpy(tptr, minor_perm); 1233 (void) strcat(tptr, XEND); 1234 1235 /* 1236 * Set gid so we preserve group attribute. Ideally we wouldn't 1237 * assume a gid of "sys" but we can't undo the damage on already 1238 * installed systems unless we force the issue. 1239 */ 1240 if ((sysgrp = getgrnam("sys")) != NULL) { 1241 (void) setgid(sysgrp->gr_gid); 1242 } 1243 1244 newfile = mktemp(tptr); 1245 if ((newfp = fopen(newfile, "w")) == NULL) { 1246 perror(NULL); 1247 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), 1248 newfile); 1249 return (ERROR); 1250 } 1251 1252 if (sscanf(perm_list, "%s%s%s%s", minor, perm, own, grp) != 4) { 1253 status = ERROR; 1254 } 1255 1256 while ((fgets(line, sizeof (line), fp) != NULL) && status == NOERR) { 1257 /* copy the whole line into dup */ 1258 if ((dup = strdup(line)) == NULL) { 1259 perror(NULL); 1260 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 1261 status = ERROR; 1262 break; 1263 } 1264 /* cut off comments starting with '#' */ 1265 if ((cp = strchr(dup, '#')) != NULL) 1266 *cp = '\0'; 1267 /* ignore comment or blank lines */ 1268 if (is_blank(dup)) { 1269 if (fputs(line, newfp) == EOF) { 1270 (void) fprintf(stderr, gettext(ERR_UPDATE), 1271 minor_perm); 1272 status = ERROR; 1273 } 1274 free(dup); 1275 continue; 1276 } 1277 1278 /* get the driver name */ 1279 if (sscanf(dup, "%s", drv) != 1) { 1280 (void) fprintf(stderr, gettext(ERR_BAD_LINE), 1281 minor_perm, line); 1282 status = ERROR; 1283 free(dup); 1284 break; 1285 } 1286 1287 /* 1288 * get the minor name; place the NULL character at the 1289 * end of the driver name, then make the drv_minor 1290 * point to the first character of the minor name. 1291 * the line missing ':' must be treated as a broken one. 1292 */ 1293 i = strcspn(drv, ":"); 1294 if (i == strlen(drv)) { 1295 (void) fprintf(stderr, gettext(ERR_BAD_LINE), 1296 minor_perm, line); 1297 status = ERROR; 1298 free(dup); 1299 break; 1300 } 1301 drv[i] = '\0'; 1302 drv_minor = &drv[strlen(drv) + 1]; 1303 1304 /* 1305 * compare both of the driver name and the minor name. 1306 * then the new line should be written to the file if 1307 * both of them match 1308 */ 1309 if ((strcmp(drv, driver_name) == 0) && 1310 (strcmp(minor, drv_minor) == 0)) { 1311 /* if it has a comment, keep it */ 1312 if (cp != NULL) { 1313 cp++; /* skip a terminator */ 1314 (void) sprintf(line, "%s:%s %s %s %s #%s\n", 1315 drv, minor, perm, own, grp, cp); 1316 } else { 1317 (void) sprintf(line, "%s:%s %s %s %s\n", 1318 drv, minor, perm, own, grp); 1319 } 1320 match = 1; 1321 } 1322 free(dup); 1323 1324 /* update the file */ 1325 if ((fputs(line, newfp)) == EOF) { 1326 (void) fprintf(stderr, gettext(ERR_UPDATE), 1327 minor_perm); 1328 status = ERROR; 1329 } 1330 } 1331 1332 if (!match) { 1333 (void) bzero(line, sizeof (&line[0])); 1334 (void) sprintf(line, "%s:%s %s %s %s\n", 1335 driver_name, minor, perm, own, grp); 1336 1337 /* add the new entry */ 1338 if ((fputs(line, newfp)) == EOF) { 1339 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm); 1340 status = ERROR; 1341 } 1342 } 1343 1344 (void) fclose(fp); 1345 1346 if (fflush(newfp) != 0 || fsync(fileno(newfp)) != 0) 1347 status = ERROR; 1348 1349 (void) fclose(newfp); 1350 1351 /* 1352 * if error, leave original file, delete new file 1353 * if noerr, replace original file with new file 1354 */ 1355 if (status == NOERR) { 1356 if (rename(minor_perm, tmphold) == -1) { 1357 perror(NULL); 1358 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm); 1359 (void) unlink(newfile); 1360 return (ERROR); 1361 } else if (rename(newfile, minor_perm) == -1) { 1362 perror(NULL); 1363 (void) fprintf(stderr, gettext(ERR_UPDATE), minor_perm); 1364 (void) unlink(minor_perm); 1365 (void) unlink(newfile); 1366 if (link(tmphold, minor_perm) == -1) { 1367 perror(NULL); 1368 (void) fprintf(stderr, gettext(ERR_BAD_LINK), 1369 minor_perm, tmphold); 1370 } 1371 return (ERROR); 1372 } 1373 (void) unlink(tmphold); 1374 } else { 1375 /* 1376 * since there's an error, leave file alone; remove 1377 * new file 1378 */ 1379 if (unlink(newfile) == -1) { 1380 (void) fprintf(stderr, gettext(ERR_CANT_RM), newfile); 1381 } 1382 return (ERROR); 1383 } 1384 1385 return (NOERR); 1386 1387 } 1388 1389 1390 /* 1391 * list_entry: 1392 * open file 1393 * read thru file, listing all entries if first entry = driver_name 1394 * close 1395 */ 1396 void 1397 list_entry( 1398 char *oldfile, 1399 char *driver_name, 1400 char *marker) 1401 { 1402 FILE *fp; 1403 int i; 1404 char line[MAX_DBFILE_ENTRY], *cp; 1405 char drv[FILENAME_MAX + 1]; 1406 1407 if ((fp = fopen(oldfile, "r")) == NULL) { 1408 perror(NULL); 1409 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), oldfile); 1410 1411 return; 1412 } 1413 1414 while (fgets(line, sizeof (line), fp) != NULL) { 1415 /* cut off comments starting with '#' */ 1416 if ((cp = strchr(line, '#')) != NULL) 1417 *cp = '\0'; 1418 /* ignore comment or blank lines */ 1419 if (is_blank(line)) 1420 continue; 1421 /* sanity-check */ 1422 if (sscanf(line, "%s", drv) != 1) { 1423 (void) fprintf(stderr, gettext(ERR_BAD_LINE), 1424 oldfile, line); 1425 } 1426 1427 for (i = strcspn(drv, marker); i < FILENAME_MAX; i++) { 1428 drv[i] = '\0'; 1429 } 1430 1431 if (strcmp(driver_name, drv) == 0) { 1432 (void) fprintf(stdout, "%s", line); 1433 } 1434 } 1435 1436 (void) fclose(fp); 1437 } 1438 1439 static boolean_t 1440 is_token(char *tok) 1441 { 1442 /* 1443 * Check the token here. According to IEEE1275 Open Firmware Boot 1444 * Standard, the name is composed of 1 to 31 letters, 1445 * digits and punctuation characters from the set ",._+-", and 1446 * uppercase and lowercase characters are considered distinct. 1447 * (ie. token := [a-zA-Z0-9,._+-]+, length(token) <= 31) 1448 * However, since either the definition of driver or aliase names is 1449 * not known well, only '#' is avoided explicitly. (the kernel lexical 1450 * analyzer treats it as a start of a comment) 1451 */ 1452 for (/* nothing */; *tok != '\0'; tok++) 1453 if (*tok == '#' || iscntrl(*tok)) 1454 return (B_FALSE); 1455 1456 return (B_TRUE); 1457 } 1458 1459 /* 1460 * check each entry in perm_list for: 1461 * 4 arguments 1462 * permission arg is in valid range 1463 * permlist entries separated by comma 1464 * return ERROR/NOERR 1465 */ 1466 int 1467 check_perm_opts(char *perm_list) 1468 { 1469 char *current_head; 1470 char *previous_head; 1471 char *one_entry; 1472 int i, len, scan_stat; 1473 char minor[FILENAME_MAX + 1]; 1474 char perm[OPT_LEN + 1]; 1475 char own[OPT_LEN + 1]; 1476 char grp[OPT_LEN + 1]; 1477 char dumb[OPT_LEN + 1]; 1478 int status = NOERR; 1479 int intperm; 1480 1481 len = strlen(perm_list); 1482 1483 if (len == 0) { 1484 return (ERROR); 1485 } 1486 1487 one_entry = calloc(len + 1, 1); 1488 if (one_entry == NULL) { 1489 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 1490 return (ERROR); 1491 } 1492 1493 previous_head = perm_list; 1494 current_head = perm_list; 1495 1496 while (*current_head != '\0') { 1497 1498 for (i = 0; i <= len; i++) 1499 one_entry[i] = 0; 1500 1501 current_head = get_entry(previous_head, one_entry, ',', 0); 1502 1503 previous_head = current_head; 1504 scan_stat = sscanf(one_entry, "%s%s%s%s%s", minor, perm, own, 1505 grp, dumb); 1506 1507 if (scan_stat < 4) { 1508 (void) fprintf(stderr, gettext(ERR_MIS_TOK), 1509 "-m", one_entry); 1510 status = ERROR; 1511 } 1512 if (scan_stat > 4) { 1513 (void) fprintf(stderr, gettext(ERR_TOO_MANY_ARGS), 1514 "-m", one_entry); 1515 status = ERROR; 1516 } 1517 1518 intperm = atoi(perm); 1519 if (intperm < 0000 || intperm > 4777) { 1520 (void) fprintf(stderr, gettext(ERR_BAD_MODE), perm); 1521 status = ERROR; 1522 } 1523 } 1524 1525 free(one_entry); 1526 return (status); 1527 } 1528 1529 1530 /* 1531 * check each alias : 1532 * alias list members separated by white space 1533 * cannot exist as driver name in /etc/name_to_major 1534 * cannot exist as driver or alias name in /etc/driver_aliases 1535 */ 1536 int 1537 aliases_unique(char *aliases) 1538 { 1539 char *current_head; 1540 char *previous_head; 1541 char *one_entry; 1542 int len; 1543 int is_unique; 1544 int err; 1545 1546 len = strlen(aliases); 1547 1548 one_entry = calloc(len + 1, 1); 1549 if (one_entry == NULL) { 1550 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 1551 return (ERROR); 1552 } 1553 1554 previous_head = aliases; 1555 1556 do { 1557 bzero(one_entry, len+1); 1558 current_head = get_entry(previous_head, one_entry, ' ', 1); 1559 previous_head = current_head; 1560 1561 if ((unique_driver_name(one_entry, name_to_major, 1562 &is_unique)) == ERROR) 1563 goto err_out; 1564 1565 if (is_unique != UNIQUE) { 1566 (void) fprintf(stderr, gettext(ERR_ALIAS_IN_NAM_MAJ), 1567 one_entry); 1568 goto err_out; 1569 } 1570 1571 if ((err = unique_drv_alias(one_entry)) != UNIQUE) { 1572 if (err == NOT_UNIQUE) { 1573 (void) fprintf(stderr, 1574 gettext(ERR_ALIAS_IN_USE), one_entry); 1575 } 1576 goto err_out; 1577 } 1578 1579 if (!is_token(one_entry)) { 1580 (void) fprintf(stderr, gettext(ERR_BAD_TOK), 1581 "-i", one_entry); 1582 goto err_out; 1583 } 1584 1585 } while (*current_head != '\0'); 1586 1587 free(one_entry); 1588 return (NOERR); 1589 1590 err_out: 1591 free(one_entry); 1592 return (ERROR); 1593 } 1594 1595 /* 1596 * verify each alias : 1597 * alias list members separated by white space and quoted 1598 * exist as alias name in /etc/driver_aliases 1599 */ 1600 int 1601 aliases_exist(char *drvname, char *aliases) 1602 { 1603 char *current_head; 1604 char *previous_head; 1605 char *one_entry; 1606 int len; 1607 int is_unique; 1608 int err; 1609 1610 len = strlen(aliases); 1611 1612 one_entry = calloc(len + 1, 1); 1613 if (one_entry == NULL) { 1614 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 1615 return (ERROR); 1616 } 1617 1618 previous_head = aliases; 1619 1620 do { 1621 bzero(one_entry, len+1); 1622 current_head = get_entry(previous_head, one_entry, ' ', 1); 1623 previous_head = current_head; 1624 1625 if ((err = unique_drv_alias(one_entry)) != NOT_UNIQUE) 1626 goto err_out; 1627 1628 if (!is_token(one_entry)) { 1629 (void) fprintf(stderr, gettext(ERR_BAD_TOK), 1630 "-i", one_entry); 1631 goto err_out; 1632 } 1633 1634 } while (*current_head != '\0'); 1635 1636 free(one_entry); 1637 return (NOERR); 1638 1639 err_out: 1640 free(one_entry); 1641 return (ERROR); 1642 } 1643 1644 1645 /* 1646 * check each alias : 1647 * if path-oriented alias, path exists 1648 */ 1649 int 1650 aliases_paths_exist(char *aliases) 1651 { 1652 char *current_head; 1653 char *previous_head; 1654 char *one_entry; 1655 int i, len; 1656 char path[MAXPATHLEN]; 1657 struct stat buf; 1658 1659 len = strlen(aliases); 1660 1661 one_entry = calloc(len + 1, 1); 1662 if (one_entry == NULL) { 1663 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 1664 return (ERROR); 1665 } 1666 1667 previous_head = aliases; 1668 1669 do { 1670 for (i = 0; i <= len; i++) 1671 one_entry[i] = 0; 1672 1673 current_head = get_entry(previous_head, one_entry, ' ', 1); 1674 previous_head = current_head; 1675 1676 /* if the alias is a path, ensure that the path exists */ 1677 if (*one_entry != '/') 1678 continue; 1679 (void) snprintf(path, sizeof (path), "/devices/%s", one_entry); 1680 if (stat(path, &buf) == 0) 1681 continue; 1682 1683 /* no device at specified path-oriented alias path */ 1684 (void) fprintf(stderr, gettext(ERR_PATH_ORIENTED_ALIAS), 1685 one_entry); 1686 free(one_entry); 1687 return (ERROR); 1688 1689 } while (*current_head != '\0'); 1690 1691 free(one_entry); 1692 1693 return (NOERR); 1694 } 1695 1696 1697 int 1698 update_driver_aliases( 1699 char *driver_name, 1700 char *aliases) 1701 { 1702 /* make call to update the aliases file */ 1703 return (append_to_file(driver_name, aliases, driver_aliases, 1704 ' ', " ", 1)); 1705 } 1706 1707 1708 /* 1709 * Return: 1710 * ERROR in case of memory or read error 1711 * UNIQUE if there is no existing match to the supplied alias 1712 * NOT_UNIQUE if there is a match 1713 * An error message is emitted in the case of ERROR, 1714 * up to the caller otherwise. 1715 */ 1716 int 1717 unique_drv_alias(char *drv_alias) 1718 { 1719 FILE *fp; 1720 char drv[FILENAME_MAX + 1]; 1721 char line[MAX_N2M_ALIAS_LINE + 1], *cp; 1722 char alias[FILENAME_MAX + 1]; 1723 char *a; 1724 int status = UNIQUE; 1725 1726 fp = fopen(driver_aliases, "r"); 1727 1728 if (fp != NULL) { 1729 while ((fgets(line, sizeof (line), fp) != 0) && 1730 status == UNIQUE) { 1731 /* cut off comments starting with '#' */ 1732 if ((cp = strchr(line, '#')) != NULL) 1733 *cp = '\0'; 1734 /* ignore comment or blank lines */ 1735 if (is_blank(line)) 1736 continue; 1737 /* sanity-check */ 1738 if (sscanf(line, "%s %s", drv, alias) != 2) 1739 (void) fprintf(stderr, gettext(ERR_BAD_LINE), 1740 driver_aliases, line); 1741 1742 /* unquote for compare */ 1743 if ((*alias == '"') && 1744 (*(alias + strlen(alias) - 1) == '"')) { 1745 a = &alias[1]; 1746 alias[strlen(alias) - 1] = '\0'; 1747 } else 1748 a = alias; 1749 1750 if ((strcmp(drv_alias, drv) == 0) || 1751 (strcmp(drv_alias, a) == 0)) { 1752 status = NOT_UNIQUE; 1753 break; 1754 } 1755 } 1756 (void) fclose(fp); 1757 return (status); 1758 } else { 1759 perror(NULL); 1760 (void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases); 1761 return (ERROR); 1762 } 1763 } 1764 1765 1766 /* 1767 * search for driver_name in first field of file file_name 1768 * searching name_to_major and driver_aliases: name separated 1769 * from the remainder of the line by white space. 1770 */ 1771 int 1772 unique_driver_name(char *driver_name, char *file_name, 1773 int *is_unique) 1774 { 1775 int ret, err; 1776 1777 if ((ret = get_major_no(driver_name, file_name)) == ERROR) { 1778 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), 1779 file_name); 1780 } else { 1781 /* check alias file for name collision */ 1782 if ((err = unique_drv_alias(driver_name)) != UNIQUE) { 1783 if (err == NOT_UNIQUE) { 1784 (void) fprintf(stderr, 1785 gettext(ERR_ALIAS_IN_USE), 1786 driver_name); 1787 } 1788 ret = ERROR; 1789 } else { 1790 if (ret != UNIQUE) 1791 *is_unique = NOT_UNIQUE; 1792 else 1793 *is_unique = ret; 1794 ret = NOERR; 1795 } 1796 } 1797 return (ret); 1798 } 1799 1800 /* 1801 * returns: 1802 * SUCCESS - not an existing driver alias 1803 * NOT_UNIQUE - matching driver alias exists 1804 * ERROR - an error occurred 1805 */ 1806 int 1807 check_duplicate_driver_alias(char *driver_name, char *drv_alias) 1808 { 1809 FILE *fp; 1810 char drv[FILENAME_MAX + 1]; 1811 char line[MAX_N2M_ALIAS_LINE + 1], *cp; 1812 char alias[FILENAME_MAX + 1]; 1813 char *a; 1814 int status = SUCCESS; 1815 1816 if ((fp = fopen(driver_aliases, "r")) == NULL) { 1817 perror(NULL); 1818 (void) fprintf(stderr, gettext(ERR_CANT_OPEN), driver_aliases); 1819 return (ERROR); 1820 } 1821 1822 while (fgets(line, sizeof (line), fp) != 0) { 1823 /* cut off comments starting with '#' */ 1824 if ((cp = strchr(line, '#')) != NULL) 1825 *cp = '\0'; 1826 /* ignore comment or blank lines */ 1827 if (is_blank(line)) 1828 continue; 1829 /* sanity-check */ 1830 if (sscanf(line, "%s %s", drv, alias) != 2) 1831 (void) fprintf(stderr, gettext(ERR_BAD_LINE), 1832 driver_aliases, line); 1833 1834 /* unquote for compare */ 1835 if ((*alias == '"') && 1836 (*(alias + strlen(alias) - 1) == '"')) { 1837 a = &alias[1]; 1838 alias[strlen(alias) - 1] = '\0'; 1839 } else 1840 a = alias; 1841 1842 if ((strcmp(drv_alias, a) == 0) && 1843 (strcmp(drv, driver_name) == 0)) { 1844 status = NOT_UNIQUE; 1845 } 1846 1847 if ((strcmp(drv_alias, drv) == 0) || 1848 ((strcmp(drv_alias, a) == 0) && 1849 (strcmp(drv, driver_name) != 0))) { 1850 (void) fprintf(stderr, 1851 gettext(ERR_ALIAS_IN_USE), 1852 drv_alias); 1853 status = ERROR; 1854 goto done; 1855 } 1856 } 1857 1858 done: 1859 (void) fclose(fp); 1860 return (status); 1861 } 1862 1863 int 1864 trim_duplicate_aliases(char *driver_name, char *aliases, char **aliases2p) 1865 { 1866 char *current_head; 1867 char *previous_head; 1868 char *one_entry; 1869 char *aliases2; 1870 int rv, len; 1871 int n = 0; 1872 1873 *aliases2p = NULL; 1874 len = strlen(aliases) + 1; 1875 1876 one_entry = calloc(len, 1); 1877 aliases2 = calloc(len, 1); 1878 if (one_entry == NULL || aliases2 == NULL) { 1879 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 1880 return (ERROR); 1881 } 1882 1883 previous_head = aliases; 1884 1885 do { 1886 (void) bzero(one_entry, len); 1887 current_head = get_entry(previous_head, one_entry, ' ', 1); 1888 previous_head = current_head; 1889 1890 rv = check_duplicate_driver_alias(driver_name, one_entry); 1891 switch (rv) { 1892 case SUCCESS: 1893 /* not an existing driver alias: add it */ 1894 if (n > 0) { 1895 if (strlcat(aliases2, " ", len) >= len) 1896 goto err; 1897 } 1898 if (strlcat(aliases2, one_entry, len) >= len) 1899 goto err; 1900 n++; 1901 break; 1902 case NOT_UNIQUE: 1903 /* matching driver alias exists: do not add it */ 1904 break; 1905 case ERROR: 1906 /* error reading the alias file */ 1907 goto err; 1908 default: 1909 goto err; 1910 } 1911 1912 if (!is_token(one_entry)) { 1913 (void) fprintf(stderr, gettext(ERR_BAD_TOK), 1914 "-i", one_entry); 1915 goto err; 1916 } 1917 } while (*current_head != '\0'); 1918 1919 /* 1920 * If all the aliases listed are already 1921 * present we actually have none to do. 1922 */ 1923 if (n == 0) { 1924 free(aliases2); 1925 } else { 1926 *aliases2p = aliases2; 1927 } 1928 free(one_entry); 1929 return (NOERR); 1930 1931 err: 1932 free(aliases2); 1933 free(one_entry); 1934 return (ERROR); 1935 } 1936 1937 int 1938 check_space_within_quote(char *str) 1939 { 1940 register int i; 1941 register int len; 1942 int quoted = 0; 1943 1944 len = strlen(str); 1945 for (i = 0; i < len; i++, str++) { 1946 if (*str == '"') { 1947 if (quoted == 0) 1948 quoted++; 1949 else 1950 quoted--; 1951 } else if (*str == ' ' && quoted) 1952 return (ERROR); 1953 } 1954 1955 return (0); 1956 } 1957 1958 1959 /* 1960 * get major number 1961 * write driver_name major_num to name_to_major file 1962 * major_num returned in major_num 1963 * return success/failure 1964 */ 1965 int 1966 update_name_to_major(char *driver_name, major_t *major_num, int server) 1967 { 1968 char major[MAX_STR_MAJOR + 1]; 1969 struct stat buf; 1970 char *num_list; 1971 char drv_majnum_str[MAX_STR_MAJOR + 1]; 1972 int new_maj = -1; 1973 int i, tmp = 0, is_unique, have_rem_n2m = 0; 1974 int max_dev = 0; 1975 1976 /* 1977 * if driver_name already in rem_name_to_major 1978 * delete entry from rem_nam_to_major 1979 * put entry into name_to_major 1980 */ 1981 1982 if (stat(rem_name_to_major, &buf) == 0) { 1983 have_rem_n2m = 1; 1984 } 1985 1986 if (have_rem_n2m) { 1987 if ((is_unique = get_major_no(driver_name, rem_name_to_major)) 1988 == ERROR) 1989 return (ERROR); 1990 1991 /* 1992 * found a match in rem_name_to_major 1993 */ 1994 if (is_unique != UNIQUE) { 1995 char scratch[FILENAME_MAX]; 1996 1997 /* 1998 * If there is a match in /etc/rem_name_to_major then 1999 * be paranoid: is that major number already in 2000 * /etc/name_to_major (potentially under another name)? 2001 */ 2002 if (get_driver_name(is_unique, name_to_major, 2003 scratch) != UNIQUE) { 2004 /* 2005 * nuke the rem_name_to_major entry-- it 2006 * isn't helpful. 2007 */ 2008 (void) delete_entry(rem_name_to_major, 2009 driver_name, " ", NULL); 2010 } else { 2011 (void) snprintf(major, sizeof (major), 2012 "%d", is_unique); 2013 2014 if (append_to_file(driver_name, major, 2015 name_to_major, ' ', " ", 0) == ERROR) { 2016 (void) fprintf(stderr, 2017 gettext(ERR_NO_UPDATE), 2018 name_to_major); 2019 return (ERROR); 2020 } 2021 2022 if (delete_entry(rem_name_to_major, 2023 driver_name, " ", NULL) == ERROR) { 2024 (void) fprintf(stderr, 2025 gettext(ERR_DEL_ENTRY), driver_name, 2026 rem_name_to_major); 2027 return (ERROR); 2028 } 2029 2030 /* found matching entry : no errors */ 2031 *major_num = is_unique; 2032 return (NOERR); 2033 } 2034 } 2035 } 2036 2037 /* 2038 * Bugid: 1264079 2039 * In a server case (with -b option), we can't use modctl() to find 2040 * the maximum major number, we need to dig thru client's 2041 * /etc/name_to_major and /etc/rem_name_to_major for the max_dev. 2042 * 2043 * if (server) 2044 * get maximum major number thru (rem_)name_to_major file on client 2045 * else 2046 * get maximum major number allowable on current system using modctl 2047 */ 2048 if (server) { 2049 max_dev = 0; 2050 tmp = 0; 2051 2052 max_dev = get_max_major(name_to_major); 2053 2054 /* If rem_name_to_major exists, we need to check it too */ 2055 if (have_rem_n2m) { 2056 tmp = get_max_major(rem_name_to_major); 2057 2058 /* 2059 * If name_to_major is missing, we can get max_dev from 2060 * /etc/rem_name_to_major. If both missing, bail out! 2061 */ 2062 if ((max_dev == ERROR) && (tmp == ERROR)) { 2063 (void) fprintf(stderr, 2064 gettext(ERR_CANT_ACCESS_FILE), 2065 name_to_major); 2066 return (ERROR); 2067 } 2068 2069 /* guard against bigger maj_num in rem_name_to_major */ 2070 if (tmp > max_dev) 2071 max_dev = tmp; 2072 } else { 2073 /* 2074 * If we can't get major from name_to_major file 2075 * and there is no /etc/rem_name_to_major file, 2076 * then we don't have a max_dev, bail out quick! 2077 */ 2078 if (max_dev == ERROR) 2079 return (ERROR); 2080 } 2081 2082 /* 2083 * In case there is no more slack in current name_to_major 2084 * table, provide at least 1 extra entry so the add_drv can 2085 * succeed. Since only one add_drv process is allowed at one 2086 * time, and hence max_dev will be re-calculated each time 2087 * add_drv is ran, we don't need to worry about adding more 2088 * than 1 extra slot for max_dev. 2089 */ 2090 max_dev++; 2091 2092 } else { 2093 if (modctl(MODRESERVED, NULL, &max_dev) < 0) { 2094 perror(NULL); 2095 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR)); 2096 return (ERROR); 2097 } 2098 } 2099 2100 /* 2101 * max_dev is really how many slots the kernel has allocated for 2102 * devices... [0 , maxdev-1], not the largest available device num. 2103 */ 2104 if ((num_list = calloc(max_dev, 1)) == NULL) { 2105 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 2106 return (ERROR); 2107 } 2108 2109 /* 2110 * Populate the num_list array 2111 */ 2112 if (fill_n2m_array(name_to_major, &num_list, &max_dev) != 0) { 2113 return (ERROR); 2114 } 2115 if (have_rem_n2m) { 2116 if (fill_n2m_array(rem_name_to_major, &num_list, &max_dev) != 0) 2117 return (ERROR); 2118 } 2119 2120 /* find first free major number */ 2121 for (i = 0; i < max_dev; i++) { 2122 if (num_list[i] != 1) { 2123 new_maj = i; 2124 break; 2125 } 2126 } 2127 2128 if (new_maj == -1) { 2129 (void) fprintf(stderr, gettext(ERR_NO_FREE_MAJOR)); 2130 return (ERROR); 2131 } 2132 2133 (void) sprintf(drv_majnum_str, "%d", new_maj); 2134 if (do_the_update(driver_name, drv_majnum_str) == ERROR) { 2135 return (ERROR); 2136 } 2137 2138 *major_num = new_maj; 2139 return (NOERR); 2140 } 2141 2142 2143 int 2144 fill_n2m_array(char *filename, char **array, int *nelems) 2145 { 2146 FILE *fp; 2147 char line[MAX_N2M_ALIAS_LINE + 1], *cp; 2148 char drv[FILENAME_MAX + 1]; 2149 u_longlong_t dnum; 2150 major_t drv_majnum; 2151 2152 /* 2153 * Read through the file, marking each major number found 2154 * order is not relevant 2155 */ 2156 if ((fp = fopen(filename, "r")) == NULL) { 2157 perror(NULL); 2158 (void) fprintf(stderr, gettext(ERR_CANT_ACCESS_FILE), filename); 2159 return (ERROR); 2160 } 2161 2162 while (fgets(line, sizeof (line), fp) != 0) { 2163 /* cut off comments starting with '#' */ 2164 if ((cp = strchr(line, '#')) != NULL) 2165 *cp = '\0'; 2166 /* ignore comment or blank lines */ 2167 if (is_blank(line)) 2168 continue; 2169 /* sanity-check */ 2170 if (sscanf(line, "%s %llu", drv, &dnum) != 2) { 2171 (void) fprintf(stderr, gettext(ERR_BAD_LINE), 2172 filename, line); 2173 (void) fclose(fp); 2174 return (ERROR); 2175 } 2176 2177 if (dnum > L_MAXMAJ32) { 2178 (void) fprintf(stderr, gettext(ERR_MAJ_TOOBIG), drv, 2179 dnum, filename, L_MAXMAJ32); 2180 continue; 2181 } 2182 /* 2183 * cast down to a major_t; we can be sure this is safe because 2184 * of the above range-check. 2185 */ 2186 drv_majnum = (major_t)dnum; 2187 2188 if (drv_majnum >= *nelems) { 2189 /* 2190 * Allocate some more space, up to drv_majnum + 1 so 2191 * we can accomodate 0 through drv_majnum. 2192 * 2193 * Note that in the failure case, we leak all of the 2194 * old contents of array. It's ok, since we just 2195 * wind up exiting immediately anyway. 2196 */ 2197 *nelems = drv_majnum + 1; 2198 *array = realloc(*array, *nelems); 2199 if (*array == NULL) { 2200 (void) fprintf(stderr, gettext(ERR_NO_MEM)); 2201 return (ERROR); 2202 } 2203 } 2204 (*array)[drv_majnum] = 1; 2205 } 2206 2207 (void) fclose(fp); 2208 return (0); 2209 } 2210 2211 2212 int 2213 do_the_update(char *driver_name, char *major_number) 2214 { 2215 return (append_to_file(driver_name, major_number, name_to_major, 2216 ' ', " ", 0)); 2217 } 2218 2219 /* 2220 * is_blank() returns 1 (true) if a line specified is composed of 2221 * whitespace characters only. otherwise, it returns 0 (false). 2222 * 2223 * Note. the argument (line) must be null-terminated. 2224 */ 2225 static int 2226 is_blank(char *line) 2227 { 2228 for (/* nothing */; *line != '\0'; line++) 2229 if (!isspace(*line)) 2230 return (0); 2231 return (1); 2232 } 2233