1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 #ifdef lint 28 #define _REENTRANT /* for localtime_r */ 29 #endif 30 31 #include <stdio.h> 32 #include <ctype.h> 33 #include <stdlib.h> 34 #include <sys/types.h> 35 #include <strings.h> 36 #include <stdarg.h> 37 #include <sys/stat.h> 38 #include <fcntl.h> 39 #include <errno.h> 40 #include <unistd.h> 41 #include <stropts.h> 42 #include <time.h> 43 #include <sys/param.h> 44 #include <sys/vfstab.h> 45 #include <dirent.h> 46 #ifdef __sparc 47 #include <sys/scsi/adapters/scsi_vhci.h> 48 #include <sys/sunmdi.h> 49 #endif /* __sparc */ 50 #include "libdevinfo.h" 51 #include "device_info.h" 52 #include <regex.h> 53 54 #define isnewline(ch) ((ch) == '\n' || (ch) == '\r' || (ch) == '\f') 55 #define isnamechar(ch) (isalpha(ch) || isdigit(ch) || (ch) == '_' ||\ 56 (ch) == '-') 57 #define MAX_TOKEN_SIZE 1024 58 #define BUFSIZE 1024 59 #define STRVAL(s) ((s) ? (s) : "NULL") 60 61 #define SCSI_VHCI_CONF "/kernel/drv/scsi_vhci.conf" 62 #define QLC_CONF "/kernel/drv/qlc.conf" 63 #define FP_CONF "/kernel/drv/fp.conf" 64 #define DRIVER_CLASSES "/etc/driver_classes" 65 #define FP_AT "fp@" 66 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl" 67 #define SLASH_DEVICES "/devices" 68 #define SLASH_DEVICES_SLASH "/devices/" 69 #define SLASH_FP_AT "/fp@" 70 #define SLASH_SCSI_VHCI "/scsi_vhci" 71 #define META_DEV "/dev/md/dsk/" 72 #define SLASH_DEV_SLASH "/dev/" 73 74 /* 75 * Macros to produce a quoted string containing the value of a 76 * preprocessor macro. For example, if SIZE is defined to be 256, 77 * VAL2STR(SIZE) is "256". This is used to construct format 78 * strings for scanf-family functions below. 79 */ 80 #define QUOTE(x) #x 81 #define VAL2STR(x) QUOTE(x) 82 83 typedef enum { 84 CLIENT_TYPE_UNKNOWN, 85 CLIENT_TYPE_PHCI, 86 CLIENT_TYPE_VHCI 87 } client_type_t; 88 89 typedef enum { 90 T_EQUALS, 91 T_AMPERSAND, 92 T_BIT_OR, 93 T_STAR, 94 T_POUND, 95 T_COLON, 96 T_SEMICOLON, 97 T_COMMA, 98 T_SLASH, 99 T_WHITE_SPACE, 100 T_NEWLINE, 101 T_EOF, 102 T_STRING, 103 T_HEXVAL, 104 T_DECVAL, 105 T_NAME 106 } token_t; 107 108 typedef enum { 109 begin, parent, drvname, drvclass, prop, 110 parent_equals, name_equals, drvclass_equals, 111 parent_equals_string, name_equals_string, 112 drvclass_equals_string, 113 prop_equals, prop_equals_string, prop_equals_integer, 114 prop_equals_string_comma, prop_equals_integer_comma 115 } conf_state_t; 116 117 /* structure to hold entries with mpxio-disable property in driver.conf file */ 118 struct conf_entry { 119 char *name; 120 char *parent; 121 char *class; 122 char *unit_address; 123 int port; 124 int mpxio_disable; 125 struct conf_entry *next; 126 }; 127 128 struct conf_file { 129 char *filename; 130 FILE *fp; 131 int linenum; 132 }; 133 134 static char *tok_err = "Unexpected token '%s'\n"; 135 136 137 /* #define DEBUG */ 138 139 #ifdef DEBUG 140 141 int devfsmap_debug = 0; 142 /* /var/run is not mounted at install time. Therefore use /tmp */ 143 char *devfsmap_logfile = "/tmp/devfsmap.log"; 144 static FILE *logfp; 145 #define logdmsg(args) log_debug_msg args 146 static void vlog_debug_msg(char *, va_list); 147 static void log_debug_msg(char *, ...); 148 #ifdef __sparc 149 static void log_confent_list(char *, struct conf_entry *, int); 150 static void log_pathlist(char **); 151 #endif /* __sparc */ 152 153 #else /* DEBUG */ 154 #define logdmsg(args) /* nothing */ 155 #endif /* DEBUG */ 156 157 158 /* 159 * Leave NEWLINE as the next character. 160 */ 161 static void 162 find_eol(FILE *fp) 163 { 164 int ch; 165 166 while ((ch = getc(fp)) != EOF) { 167 if (isnewline(ch)) { 168 (void) ungetc(ch, fp); 169 break; 170 } 171 } 172 } 173 174 /* ignore parsing errors */ 175 /*ARGSUSED*/ 176 static void 177 file_err(struct conf_file *filep, char *fmt, ...) 178 { 179 #ifdef DEBUG 180 va_list ap; 181 182 va_start(ap, fmt); 183 log_debug_msg("WARNING: %s line # %d: ", 184 filep->filename, filep->linenum); 185 vlog_debug_msg(fmt, ap); 186 va_end(ap); 187 #endif /* DEBUG */ 188 } 189 190 /* return the next token from the given driver.conf file, or -1 on error */ 191 static token_t 192 lex(struct conf_file *filep, char *val, size_t size) 193 { 194 char *cp; 195 int ch, oval, badquote; 196 size_t remain; 197 token_t token; 198 FILE *fp = filep->fp; 199 200 if (size < 2) 201 return (-1); 202 203 cp = val; 204 while ((ch = getc(fp)) == ' ' || ch == '\t') 205 ; 206 207 remain = size - 1; 208 *cp++ = (char)ch; 209 switch (ch) { 210 case '=': 211 token = T_EQUALS; 212 break; 213 case '&': 214 token = T_AMPERSAND; 215 break; 216 case '|': 217 token = T_BIT_OR; 218 break; 219 case '*': 220 token = T_STAR; 221 break; 222 case '#': 223 token = T_POUND; 224 break; 225 case ':': 226 token = T_COLON; 227 break; 228 case ';': 229 token = T_SEMICOLON; 230 break; 231 case ',': 232 token = T_COMMA; 233 break; 234 case '/': 235 token = T_SLASH; 236 break; 237 case ' ': 238 case '\t': 239 case '\f': 240 while ((ch = getc(fp)) == ' ' || 241 ch == '\t' || ch == '\f') { 242 if (--remain == 0) { 243 *cp = '\0'; 244 return (-1); 245 } 246 *cp++ = (char)ch; 247 } 248 (void) ungetc(ch, fp); 249 token = T_WHITE_SPACE; 250 break; 251 case '\n': 252 case '\r': 253 token = T_NEWLINE; 254 break; 255 case '"': 256 remain++; 257 cp--; 258 badquote = 0; 259 while (!badquote && (ch = getc(fp)) != '"') { 260 switch (ch) { 261 case '\n': 262 case EOF: 263 file_err(filep, "Missing \"\n"); 264 remain = size - 1; 265 cp = val; 266 *cp++ = '\n'; 267 badquote = 1; 268 /* since we consumed the newline/EOF */ 269 (void) ungetc(ch, fp); 270 break; 271 272 case '\\': 273 if (--remain == 0) { 274 *cp = '\0'; 275 return (-1); 276 } 277 ch = (char)getc(fp); 278 if (!isdigit(ch)) { 279 /* escape the character */ 280 *cp++ = (char)ch; 281 break; 282 } 283 oval = 0; 284 while (ch >= '0' && ch <= '7') { 285 ch -= '0'; 286 oval = (oval << 3) + ch; 287 ch = (char)getc(fp); 288 } 289 (void) ungetc(ch, fp); 290 /* check for character overflow? */ 291 if (oval > 127) { 292 file_err(filep, 293 "Character " 294 "overflow detected.\n"); 295 } 296 *cp++ = (char)oval; 297 break; 298 default: 299 if (--remain == 0) { 300 *cp = '\0'; 301 return (-1); 302 } 303 *cp++ = (char)ch; 304 break; 305 } 306 } 307 token = T_STRING; 308 break; 309 310 case EOF: 311 token = T_EOF; 312 break; 313 314 default: 315 /* 316 * detect a lone '-' (including at the end of a line), and 317 * identify it as a 'name' 318 */ 319 if (ch == '-') { 320 if (--remain == 0) { 321 *cp = '\0'; 322 return (-1); 323 } 324 *cp++ = (char)(ch = getc(fp)); 325 if (ch == ' ' || ch == '\t' || ch == '\n') { 326 (void) ungetc(ch, fp); 327 remain++; 328 cp--; 329 token = T_NAME; 330 break; 331 } 332 } else if (ch == '~' || ch == '-') { 333 if (--remain == 0) { 334 *cp = '\0'; 335 return (-1); 336 } 337 *cp++ = (char)(ch = getc(fp)); 338 } 339 340 341 if (isdigit(ch)) { 342 if (ch == '0') { 343 if ((ch = getc(fp)) == 'x') { 344 if (--remain == 0) { 345 *cp = '\0'; 346 return (-1); 347 } 348 *cp++ = (char)ch; 349 ch = getc(fp); 350 while (isxdigit(ch)) { 351 if (--remain == 0) { 352 *cp = '\0'; 353 return (-1); 354 } 355 *cp++ = (char)ch; 356 ch = getc(fp); 357 } 358 (void) ungetc(ch, fp); 359 token = T_HEXVAL; 360 } else { 361 goto digit; 362 } 363 } else { 364 ch = getc(fp); 365 digit: 366 while (isdigit(ch)) { 367 if (--remain == 0) { 368 *cp = '\0'; 369 return (-1); 370 } 371 *cp++ = (char)ch; 372 ch = getc(fp); 373 } 374 (void) ungetc(ch, fp); 375 token = T_DECVAL; 376 } 377 } else if (isalpha(ch) || ch == '\\') { 378 if (ch != '\\') { 379 ch = getc(fp); 380 } else { 381 /* 382 * if the character was a backslash, 383 * back up so we can overwrite it with 384 * the next (i.e. escaped) character. 385 */ 386 remain++; 387 cp--; 388 } 389 while (isnamechar(ch) || ch == '\\') { 390 if (ch == '\\') 391 ch = getc(fp); 392 if (--remain == 0) { 393 *cp = '\0'; 394 return (-1); 395 } 396 *cp++ = (char)ch; 397 ch = getc(fp); 398 } 399 (void) ungetc(ch, fp); 400 token = T_NAME; 401 } else { 402 return (-1); 403 } 404 break; 405 } 406 407 *cp = '\0'; 408 409 return (token); 410 } 411 412 #ifdef __sparc 413 414 static void 415 free_confent(struct conf_entry *confent) 416 { 417 if (confent->name) 418 free(confent->name); 419 if (confent->parent) 420 free(confent->parent); 421 if (confent->class) 422 free(confent->class); 423 if (confent->unit_address) 424 free(confent->unit_address); 425 free(confent); 426 } 427 428 static void 429 free_confent_list(struct conf_entry *confent_list) 430 { 431 struct conf_entry *confent, *next; 432 433 for (confent = confent_list; confent != NULL; confent = next) { 434 next = confent->next; 435 free_confent(confent); 436 } 437 } 438 439 /* 440 * Parse the next entry from the driver.conf file and return in the form of 441 * a pointer to the conf_entry. 442 */ 443 static struct conf_entry * 444 parse_conf_entry(struct conf_file *filep, char *tokbuf, size_t linesize) 445 { 446 char *prop_name, *string; 447 token_t token; 448 struct conf_entry *confent; 449 conf_state_t state; 450 int failed = 1; 451 452 if ((confent = calloc(1, sizeof (*confent))) == NULL) 453 return (NULL); 454 455 confent->port = -1; 456 confent->mpxio_disable = -1; 457 458 state = begin; 459 token = T_NAME; 460 prop_name = NULL; 461 string = NULL; 462 do { 463 switch (token) { 464 case T_NAME: 465 switch (state) { 466 case prop_equals_string: 467 case prop_equals_integer: 468 case begin: 469 state = prop; 470 if ((prop_name = strdup(tokbuf)) == NULL) 471 goto bad; 472 break; 473 default: 474 file_err(filep, tok_err, tokbuf); 475 } 476 break; 477 case T_EQUALS: 478 switch (state) { 479 case prop: 480 state = prop_equals; 481 break; 482 default: 483 file_err(filep, tok_err, tokbuf); 484 } 485 break; 486 case T_STRING: 487 switch (state) { 488 case prop_equals: 489 if ((string = strdup(tokbuf)) == NULL) 490 goto bad; 491 492 state = begin; 493 if (strcmp(prop_name, "PARENT") == 0 || 494 strcmp(prop_name, "parent") == 0) { 495 if (confent->parent) { 496 file_err(filep, 497 "'parent' property already specified\n"); 498 goto bad; 499 } 500 confent->parent = string; 501 } else if (strcmp(prop_name, "NAME") == 0 || 502 strcmp(prop_name, "name") == 0) { 503 if (confent->name) { 504 file_err(filep, 505 "'name' property already specified\n"); 506 goto bad; 507 } 508 confent->name = string; 509 } else if (strcmp(prop_name, "CLASS") == 0 || 510 strcmp(prop_name, "class") == 0) { 511 if (confent->class) { 512 file_err(filep, 513 "'class' property already specified\n"); 514 goto bad; 515 } 516 confent->class = string; 517 } else if (strcmp(prop_name, "unit-address") 518 == 0) { 519 if (confent->unit_address) { 520 file_err(filep, 521 "'unit-address' property already specified\n"); 522 goto bad; 523 } 524 confent->unit_address = string; 525 } else if (strcmp(prop_name, "mpxio-disable") 526 == 0) { 527 if (confent->mpxio_disable != -1) { 528 file_err(filep, 529 "'mpxio-disable' property already specified\n"); 530 goto bad; 531 } 532 if (strcmp(string, "yes") == 0) 533 confent->mpxio_disable = 1; 534 else if (strcmp(string, "no") == 0) 535 confent->mpxio_disable = 0; 536 else { 537 file_err(filep, 538 "'mpxio-disable' property setting is invalid. " 539 "The value must be either \"yes\" or \"no\"\n"); 540 goto bad; 541 } 542 free(string); 543 } else { 544 free(string); 545 state = prop_equals_string; 546 } 547 string = NULL; 548 free(prop_name); 549 prop_name = NULL; 550 break; 551 552 case prop_equals_string_comma: 553 state = prop_equals_string; 554 break; 555 default: 556 file_err(filep, tok_err, tokbuf); 557 } 558 break; 559 case T_HEXVAL: 560 case T_DECVAL: 561 switch (state) { 562 case prop_equals: 563 if (strcmp(prop_name, "port") == 0) { 564 if (confent->port != -1) { 565 file_err(filep, 566 "'port' property already specified\n"); 567 goto bad; 568 } 569 confent->port = 570 (int)strtol(tokbuf, NULL, 0); 571 state = begin; 572 } else 573 state = prop_equals_integer; 574 free(prop_name); 575 prop_name = NULL; 576 break; 577 578 case prop_equals_integer_comma: 579 state = prop_equals_integer; 580 break; 581 default: 582 file_err(filep, tok_err, tokbuf); 583 } 584 break; 585 case T_COMMA: 586 switch (state) { 587 case prop_equals_string: 588 state = prop_equals_string_comma; 589 break; 590 case prop_equals_integer: 591 state = prop_equals_integer_comma; 592 break; 593 default: 594 file_err(filep, tok_err, tokbuf); 595 } 596 break; 597 case T_NEWLINE: 598 filep->linenum++; 599 break; 600 case T_POUND: 601 find_eol(filep->fp); 602 break; 603 case T_EOF: 604 file_err(filep, "Unexpected EOF\n"); 605 goto bad; 606 default: 607 file_err(filep, tok_err, tokbuf); 608 goto bad; 609 } 610 } while ((token = lex(filep, tokbuf, linesize)) != T_SEMICOLON); 611 612 failed = 0; 613 614 bad: 615 if (prop_name) 616 free(prop_name); 617 if (string) 618 free(string); 619 if (failed == 1) { 620 free_confent(confent); 621 return (NULL); 622 } 623 return (confent); 624 } 625 626 /* 627 * Parse all entries with mpxio-disable property in the given driver.conf 628 * file. 629 * 630 * fname driver.conf file name 631 * confent_list on return *confent_list will contain the list of 632 * driver.conf file entries with mpxio-disable property. 633 * mpxio_disable on return *mpxio_disable is set to the setting of the 634 * driver global mpxio-dissable property as follows. 635 * 0 if driver mpxio-disable="no" 636 * 1 if driver mpxio-disable="yes" 637 * -1 if driver mpxio-disable property isn't specified. 638 */ 639 static void 640 parse_conf_file(char *fname, struct conf_entry **confent_list, 641 int *mpxio_disable) 642 { 643 struct conf_entry *confent, *tail = NULL; 644 token_t token; 645 struct conf_file file; 646 char tokval[MAX_TOKEN_SIZE]; 647 648 *confent_list = NULL; 649 *mpxio_disable = -1; 650 if ((file.fp = fopen(fname, "r")) == NULL) 651 return; 652 653 file.filename = fname; 654 file.linenum = 1; 655 656 while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) { 657 switch (token) { 658 case T_POUND: 659 /* 660 * Skip comments. 661 */ 662 find_eol(file.fp); 663 break; 664 case T_NAME: 665 if ((confent = parse_conf_entry(&file, tokval, 666 MAX_TOKEN_SIZE)) == NULL) 667 break; 668 /* 669 * No name indicates global property. 670 * Make sure parent and class not NULL. 671 */ 672 if (confent->name == NULL) { 673 if (confent->parent || 674 confent->class) { 675 file_err(&file, 676 "missing name attribute\n"); 677 } else if (confent->mpxio_disable != -1) { 678 if (*mpxio_disable == -1) 679 *mpxio_disable = 680 confent->mpxio_disable; 681 else 682 file_err(&file, 683 "'mpxio-disable' property already specified\n"); 684 } 685 free_confent(confent); 686 break; 687 } 688 689 /* 690 * This is a node spec, either parent or class 691 * must be specified. 692 */ 693 if (confent->parent == NULL && confent->class == NULL) { 694 file_err(&file, 695 "missing parent or class attribute\n"); 696 free_confent(confent); 697 break; 698 } 699 700 /* only need entries with mpxio_disable property */ 701 if (confent->mpxio_disable == -1) { 702 free_confent(confent); 703 break; 704 } 705 706 if (tail) 707 tail->next = confent; 708 else 709 *confent_list = confent; 710 tail = confent; 711 break; 712 713 case T_NEWLINE: 714 file.linenum++; 715 break; 716 default: 717 break; 718 } 719 } 720 721 (void) fclose(file.fp); 722 } 723 724 /* 725 * Return the driver class of the given driver_name. 726 * The memory for the driver class is allocated by this function and the 727 * caller must free it. 728 */ 729 static char * 730 get_driver_class(char *rootdir, char *driver_name) 731 { 732 FILE *fp; 733 char buf[BUFSIZE]; 734 char driver[BUFSIZE]; 735 char class_name[BUFSIZE]; 736 737 logdmsg(("get_driver_class: rootdir = %s, driver name = %s\n", 738 rootdir, driver_name)); 739 740 (void) snprintf(buf, sizeof (buf), "%s%s", rootdir, DRIVER_CLASSES); 741 742 if ((fp = fopen(buf, "r")) == NULL) { 743 logdmsg(("get_driver_class: failed to open %s: %s\n", 744 buf, strerror(errno))); 745 return (NULL); 746 } 747 748 while (fgets(buf, sizeof (buf), fp) != NULL) { 749 /* LINTED - unbounded string specifier */ 750 if ((sscanf(buf, "%s %s", driver, class_name) == 2) && 751 driver[0] != '#' && strcmp(driver, driver_name) == 0) { 752 logdmsg(("get_driver_class: driver class = %s\n", 753 class_name)); 754 (void) fclose(fp); 755 return (strdup(class_name)); 756 } 757 } 758 759 (void) fclose(fp); 760 return (NULL); 761 } 762 763 static int 764 lookup_in_confent_list(struct conf_entry *confent_list, 765 int match_class, char *parent, char *unit_addr, int port) 766 { 767 struct conf_entry *confent; 768 char *par; 769 770 logdmsg(("lookup_in_confent_list: %s = \"%s\", unit_addr = \"%s\", " 771 "port = %d\n", (match_class) ? "class" : "parent", parent, 772 STRVAL(unit_addr), port)); 773 774 for (confent = confent_list; confent != NULL; confent = confent->next) { 775 par = (match_class) ? confent->class : confent->parent; 776 if (unit_addr) { 777 if (confent->unit_address != NULL && 778 strcmp(confent->unit_address, unit_addr) == 0 && 779 par != NULL && strcmp(par, parent) == 0) 780 return (confent->mpxio_disable); 781 } else { 782 if (confent->port == port && 783 par != NULL && strcmp(par, parent) == 0) 784 return (confent->mpxio_disable); 785 } 786 } 787 return (-1); 788 } 789 790 /* 791 * lookup mpxio-disabled property setting for the given path in the given 792 * driver.conf file. Match the entries from most specific to least specific. 793 * 794 * conf_file the path name of either fp.conf, qlc.conf or scsi_vhci.conf 795 * path /devices node path without the /devices prefix. 796 * If the conf_file is fp.conf, path must be a fp node path 797 * if the conf_file is qlc.conf, path must be a qlc node path. 798 * if the conf_file is scsi_vhci.conf, path must be NULL. 799 * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0 800 * /pci@8,600000/SUNW,qlc@4 801 * 802 * returns: 803 * 0 if mpxio-disable="no" 804 * 1 if mpxio-disable="yes" 805 * -1 if mpxio-disable property isn't specified. 806 */ 807 static int 808 lookup_in_conf_file(char *rootdir, char *conf_file, char *path) 809 { 810 struct conf_entry *confent_list = NULL; 811 int mpxio_disable; 812 di_node_t par_node = DI_NODE_NIL; 813 char *node_name = NULL, *node_addr = NULL; 814 char *unit_addr = NULL; 815 int port = -1; 816 char *par_node_name = NULL, *par_node_addr = NULL; 817 char *par_binding_name = NULL, *par_driver_name = NULL; 818 char *par_driver_class = NULL, *par_node_name_addr; 819 int rv = -1; 820 char buf[MAXPATHLEN]; 821 822 logdmsg(("lookup_in_conf_file: rootdir = \"%s\", conf_file = \"%s\", " 823 "path = \"%s\"\n", rootdir, conf_file, STRVAL(path))); 824 825 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, conf_file); 826 parse_conf_file(buf, &confent_list, &mpxio_disable); 827 #ifdef DEBUG 828 log_confent_list(buf, confent_list, mpxio_disable); 829 #endif 830 831 /* if path is NULL, return driver global mpxio-disable setting */ 832 if (path == NULL) { 833 rv = mpxio_disable; 834 goto done; 835 } 836 837 if ((node_name = strrchr(path, '/')) == NULL) 838 goto done; 839 840 *node_name = '\0'; 841 node_name++; 842 843 if ((node_addr = strchr(node_name, '@')) == NULL) 844 goto done; 845 846 *node_addr = '\0'; 847 node_addr++; 848 849 if (strcmp(node_name, "fp") == 0) { 850 /* get port number; encoded in the node addr as a hex number */ 851 port = (int)strtol(node_addr, NULL, 16); 852 } else 853 unit_addr = node_addr; 854 855 /* 856 * Match from most specific to least specific; 857 * first, start the lookup based on full path. 858 */ 859 if ((rv = lookup_in_confent_list(confent_list, 0, path, 860 unit_addr, port)) != -1) 861 goto done; 862 863 /* lookup nodename@address */ 864 if ((par_node_name_addr = strrchr(path, '/')) != NULL) { 865 par_node_name_addr++; 866 if ((rv = lookup_in_confent_list(confent_list, 0, 867 par_node_name_addr, unit_addr, port)) != -1) 868 goto done; 869 } 870 871 /* di_init() doesn't work when 0 is passed in flags */ 872 par_node = di_init(path, DINFOMINOR); 873 if (par_node != DI_NODE_NIL) { 874 par_node_name = di_node_name(par_node); 875 par_node_addr = di_bus_addr(par_node); 876 par_binding_name = di_binding_name(par_node); 877 par_driver_name = di_driver_name(par_node); 878 } 879 880 logdmsg(("par_node_name = %s\n", STRVAL(par_node_name))); 881 logdmsg(("par_node_addr = %s\n", STRVAL(par_node_addr))); 882 logdmsg(("par_binding_name = %s\n", STRVAL(par_binding_name))); 883 logdmsg(("par_driver_name = %s\n", STRVAL(par_driver_name))); 884 885 /* lookup bindingname@address */ 886 if (par_binding_name != NULL && par_binding_name != par_node_name && 887 par_node_addr != NULL) { 888 (void) snprintf(buf, sizeof (buf), "%s@%s", par_binding_name, 889 par_node_addr); 890 if ((rv = lookup_in_confent_list(confent_list, 0, 891 buf, unit_addr, port)) != -1) 892 goto done; 893 } 894 895 /* lookup binding name */ 896 if (par_binding_name != NULL) { 897 if ((rv = lookup_in_confent_list(confent_list, 0, 898 par_binding_name, unit_addr, port)) != -1) 899 goto done; 900 } 901 902 if (par_driver_name != NULL) { 903 /* lookup driver name */ 904 if ((rv = lookup_in_confent_list(confent_list, 0, 905 par_driver_name, unit_addr, port)) != -1) 906 goto done; 907 908 /* finally, lookup class name */ 909 par_driver_class = get_driver_class(rootdir, par_driver_name); 910 if (par_driver_class != NULL) { 911 if ((rv = lookup_in_confent_list(confent_list, 1, 912 par_driver_class, unit_addr, port)) != -1) 913 goto done; 914 } 915 } 916 917 /* 918 * no match so far; 919 * use the driver global mpxio-disable setting if exists. 920 */ 921 rv = mpxio_disable; 922 923 done: 924 if (node_name != NULL) 925 *(node_name - 1) = '/'; 926 if (node_addr != NULL) 927 *(node_addr - 1) = '@'; 928 if (par_driver_class != NULL) 929 free(par_driver_class); 930 if (confent_list != NULL) 931 free_confent_list(confent_list); 932 if (par_node != DI_NODE_NIL) 933 di_fini(par_node); 934 935 return (rv); 936 } 937 938 /* 939 * Given client_name return whether it is a phci or vhci based name. 940 * client_name is /devices name of a client without the /devices prefix. 941 * 942 * client_name Return value 943 * .../fp@xxx/ssd@yyy CLIENT_TYPE_PHCI 944 * .../scsi_vhci/ssd@yyy CLIENT_TYPE_VHCI 945 * other CLIENT_TYPE_UNKNOWN 946 */ 947 static client_type_t 948 client_name_type(char *client_name) 949 { 950 client_type_t client_type; 951 char *p1, *p2; 952 953 logdmsg(("client_name_type: client_name = %s\n", client_name)); 954 955 if (strncmp(client_name, SLASH_SCSI_VHCI, 956 sizeof (SLASH_SCSI_VHCI) - 1) == 0) 957 return (CLIENT_TYPE_VHCI); 958 959 if (*client_name != '/') 960 return (CLIENT_TYPE_UNKNOWN); 961 962 if ((p1 = strrchr(client_name, '/')) == NULL) 963 return (CLIENT_TYPE_UNKNOWN); 964 965 *p1 = '\0'; 966 967 if ((p2 = strrchr(client_name, '/')) != NULL && 968 strncmp(p2, SLASH_FP_AT, sizeof (SLASH_FP_AT) - 1) == 0) 969 client_type = CLIENT_TYPE_PHCI; 970 else 971 client_type = CLIENT_TYPE_UNKNOWN; 972 973 *p1 = '/'; 974 return (client_type); 975 } 976 977 /* 978 * Compare controller name portion of dev1 and dev2. 979 * 980 * rootdir root directory of the target environment 981 * dev1 can be either a /dev link or /devices name in the target 982 * environemnt 983 * dev2 /devices name of a device without the /devices prefix 984 * 985 * Returns: 986 * 0 if controller names match 987 * 1 if controller names don't match 988 * -1 an error occurred. 989 */ 990 static int 991 compare_controller(char *rootdir, char *dev1, char *dev2) 992 { 993 int linksize; 994 char *p1, *p; 995 char physdev1[MAXPATHLEN]; 996 char buf[MAXPATHLEN]; 997 998 logdmsg(("compare_controller: rootdir = %s, dev1 = %s, dev2 = %s\n", 999 rootdir, dev1, dev2)); 1000 1001 if (strncmp(dev1, SLASH_DEV_SLASH, sizeof (SLASH_DEV_SLASH) - 1) 1002 == 0) { 1003 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, dev1); 1004 if ((linksize = readlink(buf, physdev1, MAXPATHLEN)) > 0 && 1005 linksize < (MAXPATHLEN - 1)) { 1006 physdev1[linksize] = '\0'; 1007 logdmsg(("compare_controller: physdev1 = %s\n", 1008 physdev1)); 1009 } else 1010 return (-1); 1011 } else 1012 (void) strlcpy(physdev1, dev1, MAXPATHLEN); 1013 1014 if ((p1 = strstr(physdev1, SLASH_DEVICES)) == NULL) 1015 return (-1); 1016 1017 p1 += sizeof (SLASH_DEVICES) - 1; 1018 /* strip the device portion */ 1019 if ((p = strrchr(p1, '/')) == NULL) 1020 return (-1); 1021 *p = '\0'; 1022 1023 if ((p = strrchr(dev2, '/')) == NULL) 1024 return (-1); 1025 *p = '\0'; 1026 1027 logdmsg(("compare_controller: path1 = %s, path2 = %s\n", 1028 p1, dev2)); 1029 if (strcmp(p1, dev2) == 0) { 1030 *p = '/'; 1031 return (0); 1032 } else { 1033 *p = '/'; 1034 return (1); 1035 } 1036 } 1037 1038 /* 1039 * Check if the specified device path is on the root controller. 1040 * 1041 * rootdir root directory of the target environment 1042 * path /devices name of a device without the /devices prefix 1043 * 1044 * Returns 1045 * 1 if the path is on the root controller 1046 * 0 if the path is not on the root controller 1047 * -1 if an error occurs 1048 */ 1049 static int 1050 is_root_controller(char *rootdir, char *path) 1051 { 1052 FILE *fp; 1053 char *tmpfile; 1054 int rv = -1; 1055 struct vfstab vfsent; 1056 char buf[MAXPATHLEN]; 1057 char ctd[MAXNAMELEN + 1]; 1058 1059 logdmsg(("is_root_controller: rootdir = %s, path = %s\n", rootdir, 1060 path)); 1061 1062 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, VFSTAB); 1063 1064 if ((fp = fopen(buf, "r")) == NULL) { 1065 logdmsg(("is_root_controller: failed to open %s: %s\n", 1066 buf, strerror(errno))); 1067 return (-1); 1068 } 1069 1070 if (getvfsfile(fp, &vfsent, "/") != 0) { 1071 logdmsg(("is_root_controller: getvfsfile: failed to read " 1072 "vfstab entry for mount point \"/\": %s\n", 1073 strerror(errno))); 1074 (void) fclose(fp); 1075 return (-1); 1076 } 1077 (void) fclose(fp); 1078 1079 /* check if the root is an svm metadisk */ 1080 if (strncmp(vfsent.vfs_special, META_DEV, sizeof (META_DEV) - 1) != 0) { 1081 if (compare_controller(rootdir, vfsent.vfs_special, path) == 0) 1082 return (1); 1083 else 1084 return (0); 1085 } 1086 1087 /* Don't use /var/run as it is not mounted in miniroot */ 1088 if ((tmpfile = tempnam("/tmp", "diirc")) == NULL) { 1089 logdmsg(("is_root_controller: tempnam: failed: %s\n", 1090 strerror(errno))); 1091 return (-1); 1092 } 1093 1094 /* get metadisk components using metastat command */ 1095 (void) snprintf(buf, MAXPATHLEN, 1096 "/usr/sbin/metastat -p %s 2>/dev/null | " 1097 "/usr/bin/grep ' 1 1 ' | " 1098 "/usr/bin/sed -e 's/^.* 1 1 //' | " 1099 "/usr/bin/cut -f1 -d ' ' > %s", 1100 vfsent.vfs_special + sizeof (META_DEV) - 1, tmpfile); 1101 1102 logdmsg(("is_root_controller: command = %s\n", buf)); 1103 fp = NULL; 1104 if (system(buf) == 0 && (fp = fopen(tmpfile, "r")) != NULL) { 1105 while (fscanf(fp, "%" VAL2STR(MAXNAMELEN) "s", ctd) == 1) { 1106 (void) snprintf(buf, MAXPATHLEN, "/dev/dsk/%s", ctd); 1107 if (compare_controller(rootdir, buf, path) == 0) { 1108 rv = 1; 1109 goto out; 1110 } 1111 } 1112 rv = 0; 1113 } 1114 1115 out: 1116 if (fp) 1117 (void) fclose(fp); 1118 (void) unlink(tmpfile); 1119 free(tmpfile); 1120 return (rv); 1121 } 1122 1123 static int 1124 file_exists(char *rootdir, char *path) 1125 { 1126 struct stat stbuf; 1127 char fullpath[MAXPATHLEN]; 1128 int x; 1129 1130 (void) snprintf(fullpath, MAXPATHLEN, "%s%s", rootdir, path); 1131 1132 x = stat(fullpath, &stbuf); 1133 logdmsg(("file_exists: %s: %s\n", fullpath, (x == 0) ? "yes" : "no")); 1134 if (x == 0) 1135 return (1); 1136 else 1137 return (0); 1138 } 1139 1140 /* 1141 * Check if mpxio is enabled or disabled on the specified device path. 1142 * Looks through the .conf files to determine the mpxio setting. 1143 * 1144 * rootdir root directory of the target environment 1145 * path /devices name of a device without the /devices prefix and 1146 * minor name component. 1147 * 1148 * Returns 1149 * 1 if mpxio is disabled 1150 * 0 if mpxio is enabled 1151 * -1 if an error occurs 1152 */ 1153 static int 1154 is_mpxio_disabled(char *rootdir, char *path) 1155 { 1156 int mpxio_disable; 1157 char *p; 1158 int check_root_controller; 1159 1160 logdmsg(("is_mpxio_disabled: rootdir = %s, path = %s\n", 1161 rootdir, path)); 1162 1163 if (file_exists(rootdir, SCSI_VHCI_CONF) == 0) { 1164 /* 1165 * scsi_vhci.conf doesn't exist: 1166 * if upgrading from a pre solaris 9 release. or 1167 * if this function is called during fresh or flash install 1168 * prior to installing scsi_vhci.conf file. 1169 */ 1170 if (file_exists(rootdir, "/kernel/drv")) 1171 /* upgrading from pre solaris 9 */ 1172 return (1); 1173 else 1174 /* fresh or flash install */ 1175 return (0); 1176 } 1177 1178 mpxio_disable = lookup_in_conf_file(rootdir, SCSI_VHCI_CONF, NULL); 1179 1180 /* 1181 * scsi_vhci.conf contains mpxio-disable property only in s9 and 1182 * s8+sfkpatch. This property is no longer present from s10 onwards. 1183 */ 1184 if (mpxio_disable == 1) { 1185 /* upgrading from s8 or s9 with mpxio globally disabled */ 1186 return (1); 1187 } else if (mpxio_disable == 0) { 1188 /* upgrading from s8 or s9 with mpxio globally enabled */ 1189 check_root_controller = 1; 1190 } else { 1191 /* 1192 * We are looking at the s10 version of the file. This is 1193 * the case if this function is called after installing the 1194 * new scsi_vhci.conf file. 1195 */ 1196 check_root_controller = 0; 1197 } 1198 1199 if ((mpxio_disable = lookup_in_conf_file(rootdir, FP_CONF, path)) 1200 != -1) 1201 return (mpxio_disable); 1202 1203 if ((p = strrchr(path, '/')) == NULL) 1204 return (-1); 1205 1206 *p = '\0'; 1207 if ((mpxio_disable = lookup_in_conf_file(rootdir, QLC_CONF, path)) 1208 != -1) { 1209 *p = '/'; 1210 return (mpxio_disable); 1211 } 1212 *p = '/'; 1213 1214 /* 1215 * mpxio-disable setting is not found in the .conf files. 1216 * The default is to enable mpxio, except if the path is on the root 1217 * controller. 1218 * 1219 * In s8 and s9 mpxio is not supported on the root controller. 1220 * NWS supplies a patch to enable root controller support in s8 and s9. 1221 * If the system had the patch installed, the fp.conf file would have 1222 * explicit "mpxio-disable=no" for the root controller. So we would 1223 * have found the mpxio-disable setting when we looked up this property 1224 * in the fp.conf file. 1225 */ 1226 if (check_root_controller) { 1227 mpxio_disable = is_root_controller(rootdir, path); 1228 logdmsg(("is_mpxio_disabled: is_root_controller returned %d\n", 1229 mpxio_disable)); 1230 } else 1231 mpxio_disable = 0; 1232 1233 return (mpxio_disable); 1234 } 1235 1236 static int 1237 vhci_ctl(sv_iocdata_t *iocp, int cmd) 1238 { 1239 int fd, rv; 1240 1241 if ((fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) 1242 return (-1); 1243 rv = ioctl(fd, cmd, iocp); 1244 (void) close(fd); 1245 return (rv); 1246 } 1247 1248 /* 1249 * Convert a phci client name to vhci client name. 1250 * 1251 * phci_name phci client /devices name without the /devices prefix and 1252 * minor name component. 1253 * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 1254 * 1255 * Returns on success, vhci client name is returned. The memory for 1256 * the vhci name is allocated by this function and the caller 1257 * must free it. 1258 * on failure, NULL is returned. 1259 */ 1260 static char * 1261 phci_to_vhci(char *phci_name) 1262 { 1263 sv_iocdata_t ioc; 1264 char *slash, *addr, *retp; 1265 char vhci_name_buf[MAXPATHLEN]; 1266 char phci_name_buf[MAXPATHLEN]; 1267 char addr_buf[MAXNAMELEN]; 1268 1269 logdmsg(("phci_to_vhci: pchi_name = %s\n", phci_name)); 1270 (void) strlcpy(phci_name_buf, phci_name, MAXPATHLEN); 1271 1272 if ((slash = strrchr(phci_name_buf, '/')) == NULL || 1273 (addr = strchr(slash, '@')) == NULL) 1274 return (NULL); 1275 1276 *slash = '\0'; 1277 addr++; 1278 (void) strlcpy(addr_buf, addr, MAXNAMELEN); 1279 1280 bzero(&ioc, sizeof (sv_iocdata_t)); 1281 ioc.client = vhci_name_buf; 1282 ioc.phci = phci_name_buf; 1283 ioc.addr = addr_buf; 1284 if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_NAME) != 0) { 1285 logdmsg(("phci_to_vhci: vhci_ctl failed: %s\n", 1286 strerror(errno))); 1287 return (NULL); 1288 } 1289 1290 retp = strdup(vhci_name_buf); 1291 logdmsg(("phci_to_vhci: vhci name = %s\n", STRVAL(retp))); 1292 return (retp); 1293 } 1294 1295 static int 1296 add_to_phci_list(char **phci_list, sv_path_info_t *pi, int npaths, int state, 1297 char *node_name) 1298 { 1299 int rv = 0; 1300 char name[MAXPATHLEN]; 1301 1302 while (npaths--) { 1303 if (state == pi->ret_state) { 1304 (void) snprintf(name, MAXPATHLEN, "%s/%s@%s", 1305 pi->device.ret_phci, node_name, pi->ret_addr); 1306 if ((*phci_list = strdup(name)) == NULL) 1307 return (-1); 1308 phci_list++; 1309 rv++; 1310 } 1311 pi++; 1312 } 1313 1314 return (rv); 1315 } 1316 1317 static void 1318 free_pathlist(char **pathlist) 1319 { 1320 char **p; 1321 1322 if (pathlist != NULL) { 1323 for (p = pathlist; *p != NULL; p++) 1324 free(*p); 1325 free(pathlist); 1326 } 1327 } 1328 1329 1330 /* 1331 * Convert a vhci client name to phci client names. 1332 * 1333 * vhci_name vhci client /devices name without the /devices prefix and 1334 * minor name component. 1335 * num_paths On return, *num_paths is set to the number paths in the 1336 * returned path list. 1337 * 1338 * Returns NULL terminated path list containing phci client paths is 1339 * returned on success. The memory for the path list is 1340 * allocated by this function and the caller must free it by 1341 * calling free_pathlist(). 1342 * NULL is returned on failure. 1343 */ 1344 static char ** 1345 vhci_to_phci(char *vhci_name, int *num_paths) 1346 { 1347 sv_iocdata_t ioc; 1348 uint_t npaths; 1349 int n; 1350 char **phci_list = NULL; 1351 char *node_name, *at; 1352 char vhci_name_buf[MAXPATHLEN]; 1353 1354 logdmsg(("vhci_to_phci: vchi_name = %s\n", vhci_name)); 1355 1356 *num_paths = 0; 1357 (void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN); 1358 1359 /* first get the number paths */ 1360 bzero(&ioc, sizeof (sv_iocdata_t)); 1361 ioc.client = vhci_name_buf; 1362 ioc.ret_elem = &npaths; 1363 if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 || 1364 npaths == 0) { 1365 logdmsg(("vhci_to_phci: vhci_ctl failed to get npaths: %s\n", 1366 strerror(errno))); 1367 return (NULL); 1368 } 1369 1370 /* now allocate memory for the path information and get all paths */ 1371 bzero(&ioc, sizeof (sv_iocdata_t)); 1372 ioc.client = vhci_name_buf; 1373 ioc.buf_elem = npaths; 1374 ioc.ret_elem = &npaths; 1375 if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths, 1376 sizeof (sv_path_info_t))) == NULL) 1377 return (NULL); 1378 if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 || 1379 npaths == 0) { 1380 logdmsg(("vhci_to_phci: vhci_ctl failed: %s\n", 1381 strerror(errno))); 1382 goto out; 1383 } 1384 1385 if (ioc.buf_elem < npaths) 1386 npaths = ioc.buf_elem; 1387 1388 if ((node_name = strrchr(vhci_name_buf, '/')) == NULL || 1389 (at = strchr(node_name, '@')) == NULL) 1390 goto out; 1391 1392 node_name++; 1393 *at = '\0'; 1394 1395 /* allocate one more (than npaths) for the terminating NULL pointer */ 1396 if ((phci_list = calloc(npaths + 1, sizeof (char *))) == NULL) 1397 goto out; 1398 1399 /* 1400 * add only online paths as non-online paths may not be accessible 1401 * in the target environment. 1402 */ 1403 if ((n = add_to_phci_list(phci_list, ioc.ret_buf, npaths, 1404 MDI_PATHINFO_STATE_ONLINE, node_name)) <= 0) 1405 goto out; 1406 1407 free(ioc.ret_buf); 1408 *num_paths = n; 1409 1410 #ifdef DEBUG 1411 logdmsg(("vhci_to_phci: phci list:\n")); 1412 log_pathlist(phci_list); 1413 #endif 1414 return (phci_list); 1415 1416 out: 1417 free(ioc.ret_buf); 1418 if (phci_list) 1419 free_pathlist(phci_list); 1420 return (NULL); 1421 } 1422 1423 /* 1424 * build list of paths accessible from the target environment 1425 */ 1426 static int 1427 build_pathlist(char *rootdir, char *vhcipath, char **pathlist, int npaths) 1428 { 1429 int mpxio_disabled; 1430 int i, j; 1431 char *vpath = NULL; 1432 1433 for (i = 0; i < npaths; i++) { 1434 mpxio_disabled = is_mpxio_disabled(rootdir, pathlist[i]); 1435 logdmsg(("build_pathlist: mpxio_disabled = %d " 1436 "on path %s\n", mpxio_disabled, pathlist[i])); 1437 if (mpxio_disabled == -1) 1438 return (-1); 1439 if (mpxio_disabled == 0) { 1440 /* 1441 * mpxio is enabled on this phci path. 1442 * So use vhci path instead of phci path. 1443 */ 1444 if (vpath == NULL) { 1445 if ((vpath = strdup(vhcipath)) == NULL) 1446 return (-1); 1447 free(pathlist[i]); 1448 /* keep vhci path at beginning of the list */ 1449 for (j = i; j > 0; j--) 1450 pathlist[j] = pathlist[j - 1]; 1451 pathlist[0] = vpath; 1452 } else { 1453 free(pathlist[i]); 1454 npaths--; 1455 for (j = i; j < npaths; j++) 1456 pathlist[j] = pathlist[j + 1]; 1457 pathlist[npaths] = NULL; 1458 /* compensate for i++ in the for loop */ 1459 i--; 1460 } 1461 } 1462 } 1463 1464 #ifdef DEBUG 1465 logdmsg(("build_pathlist: returning npaths = %d, pathlist:\n", npaths)); 1466 log_pathlist(pathlist); 1467 #endif 1468 return (npaths); 1469 } 1470 1471 /* 1472 * Check if the specified device is refenced in the vfstab file. 1473 * Return 1 if referenced, 0 if not. 1474 * 1475 * rootdir root directory of the target environment 1476 * nodepath /devices path of a device in the target environment without 1477 * the /devices prefix and minor component. 1478 */ 1479 static int 1480 is_dev_in_vfstab(char *rootdir, char *nodepath) 1481 { 1482 FILE *fp; 1483 int linksize; 1484 struct vfstab vfsent; 1485 char *abspath, *minor; 1486 char physpath[MAXPATHLEN]; 1487 char buf[MAXPATHLEN]; 1488 1489 logdmsg(("is_dev_in_vfstab: rootdir = %s, nodepath = %s\n", 1490 rootdir, nodepath)); 1491 1492 (void) snprintf(buf, sizeof (buf), "%s%s", rootdir, VFSTAB); 1493 1494 if ((fp = fopen(buf, "r")) == NULL) 1495 return (0); 1496 1497 /* 1498 * read device specials from vfstab and compare names at physical 1499 * node path level. 1500 */ 1501 while (getvfsent(fp, &vfsent) == 0) { 1502 if (strncmp(vfsent.vfs_special, SLASH_DEV_SLASH, 1503 sizeof (SLASH_DEV_SLASH) - 1) == 0) { 1504 (void) snprintf(buf, MAXPATHLEN, "%s%s", 1505 rootdir, vfsent.vfs_special); 1506 if ((linksize = readlink(buf, physpath, 1507 MAXPATHLEN)) > 0 && linksize < (MAXPATHLEN - 1)) { 1508 physpath[linksize] = '\0'; 1509 if ((abspath = strstr(physpath, 1510 SLASH_DEVICES_SLASH)) == NULL) 1511 continue; 1512 } else 1513 continue; 1514 } else if (strncmp(vfsent.vfs_special, SLASH_DEVICES_SLASH, 1515 sizeof (SLASH_DEVICES_SLASH) - 1) == 0) { 1516 (void) strlcpy(physpath, vfsent.vfs_special, 1517 MAXPATHLEN); 1518 abspath = physpath; 1519 } else 1520 continue; 1521 1522 /* point to / after /devices */ 1523 abspath += sizeof (SLASH_DEVICES_SLASH) - 2; 1524 /* strip minor component */ 1525 if ((minor = strrchr(abspath, ':')) != NULL) 1526 *minor = '\0'; 1527 1528 if (strcmp(nodepath, abspath) == 0) { 1529 (void) fclose(fp); 1530 logdmsg(("is_dev_in_vfstab: returning 1\n")); 1531 return (1); 1532 } 1533 } 1534 1535 (void) fclose(fp); 1536 return (0); 1537 } 1538 1539 #endif /* __sparc */ 1540 1541 static int 1542 devlink_callback(di_devlink_t devlink, void *argp) 1543 { 1544 const char *link; 1545 1546 if ((link = di_devlink_path(devlink)) != NULL) 1547 (void) strlcpy((char *)argp, link, MAXPATHLEN); 1548 1549 return (DI_WALK_CONTINUE); 1550 } 1551 1552 /* 1553 * Get the /dev name in the install environment corresponding to physpath. 1554 * 1555 * physpath /devices path in the install environment without the /devices 1556 * prefix. 1557 * buf caller supplied buffer where the /dev name is placed on return 1558 * bufsz length of the buffer 1559 * 1560 * Returns strlen of the /dev name on success, -1 on failure. 1561 */ 1562 static int 1563 get_install_devlink(char *physpath, char *buf, size_t bufsz) 1564 { 1565 di_devlink_handle_t devlink_hdl; 1566 char devname[MAXPATHLEN]; 1567 1568 logdmsg(("get_install_devlink: physpath = %s\n", physpath)); 1569 1570 if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) { 1571 logdmsg(("get_install_devlink: di_devlink_init() failed: %s\n", 1572 strerror(errno))); 1573 return (-1); 1574 } 1575 1576 devname[0] = '\0'; 1577 if (di_devlink_walk(devlink_hdl, NULL, physpath, DI_PRIMARY_LINK, 1578 devname, devlink_callback) != 0 || devname[0] == '\0') { 1579 logdmsg(("get_install_devlink: di_devlink_walk failed: %s\n", 1580 strerror(errno))); 1581 (void) di_devlink_fini(&devlink_hdl); 1582 return (-1); 1583 } 1584 1585 (void) di_devlink_fini(&devlink_hdl); 1586 1587 logdmsg(("get_install_devlink: devlink = %s\n", devname)); 1588 return (strlcpy(buf, devname, bufsz)); 1589 } 1590 1591 /* 1592 * Get the /dev name in the target environment corresponding to physpath. 1593 * 1594 * rootdir root directory of the target environment 1595 * physpath /devices path in the target environment without the /devices 1596 * prefix. 1597 * buf caller supplied buffer where the /dev name is placed on return 1598 * bufsz length of the buffer 1599 * 1600 * Returns strlen of the /dev name on success, -1 on failure. 1601 */ 1602 static int 1603 get_target_devlink(char *rootdir, char *physpath, char *buf, size_t bufsz) 1604 { 1605 char *p; 1606 int linksize; 1607 DIR *dirp; 1608 struct dirent *direntry; 1609 char dirpath[MAXPATHLEN]; 1610 char devname[MAXPATHLEN]; 1611 char physdev[MAXPATHLEN]; 1612 1613 logdmsg(("get_target_devlink: rootdir = %s, physpath = %s\n", 1614 rootdir, physpath)); 1615 1616 if ((p = strrchr(physpath, '/')) == NULL) 1617 return (-1); 1618 1619 if (strstr(p, ",raw") != NULL) { 1620 (void) snprintf(dirpath, MAXPATHLEN, "%s/dev/rdsk", rootdir); 1621 } else { 1622 (void) snprintf(dirpath, MAXPATHLEN, "%s/dev/dsk", rootdir); 1623 } 1624 1625 if ((dirp = opendir(dirpath)) == NULL) 1626 return (-1); 1627 1628 while ((direntry = readdir(dirp)) != NULL) { 1629 if (strcmp(direntry->d_name, ".") == 0 || 1630 strcmp(direntry->d_name, "..") == 0) 1631 continue; 1632 1633 (void) snprintf(devname, MAXPATHLEN, "%s/%s", 1634 dirpath, direntry->d_name); 1635 1636 if ((linksize = readlink(devname, physdev, MAXPATHLEN)) > 0 && 1637 linksize < (MAXPATHLEN - 1)) { 1638 physdev[linksize] = '\0'; 1639 if ((p = strstr(physdev, SLASH_DEVICES_SLASH)) != 1640 NULL && strcmp(p + sizeof (SLASH_DEVICES) - 1, 1641 physpath) == 0) { 1642 (void) closedir(dirp); 1643 logdmsg(("get_target_devlink: devlink = %s\n", 1644 devname + strlen(rootdir))); 1645 return (strlcpy(buf, devname + strlen(rootdir), 1646 bufsz)); 1647 } 1648 } 1649 } 1650 1651 (void) closedir(dirp); 1652 return (-1); 1653 } 1654 1655 /* 1656 * Convert device name to physpath. 1657 * 1658 * rootdir root directory 1659 * devname a /dev name or /devices name under rootdir 1660 * physpath caller supplied buffer where the /devices path will be placed 1661 * on return (without the /devices prefix). 1662 * physpathlen length of the physpath buffer 1663 * 1664 * Returns 0 on success, -1 on failure. 1665 */ 1666 static int 1667 devname2physpath(char *rootdir, char *devname, char *physpath, int physpathlen) 1668 { 1669 int linksize; 1670 char *p; 1671 char devlink[MAXPATHLEN]; 1672 char tmpphyspath[MAXPATHLEN]; 1673 1674 logdmsg(("devname2physpath: rootdir = %s, devname = %s\n", 1675 rootdir, devname)); 1676 1677 if (strncmp(devname, SLASH_DEVICES_SLASH, 1678 sizeof (SLASH_DEVICES_SLASH) - 1) != 0) { 1679 if (*rootdir == '\0') 1680 linksize = readlink(devname, tmpphyspath, MAXPATHLEN); 1681 else { 1682 (void) snprintf(devlink, MAXPATHLEN, "%s%s", 1683 rootdir, devname); 1684 linksize = readlink(devlink, tmpphyspath, MAXPATHLEN); 1685 } 1686 if (linksize > 0 && linksize < (MAXPATHLEN - 1)) { 1687 tmpphyspath[linksize] = '\0'; 1688 if ((p = strstr(tmpphyspath, SLASH_DEVICES_SLASH)) 1689 == NULL) 1690 return (-1); 1691 } else 1692 return (-1); 1693 } else 1694 p = devname; 1695 1696 (void) strlcpy(physpath, p + sizeof (SLASH_DEVICES) - 1, physpathlen); 1697 logdmsg(("devname2physpath: physpath = %s\n", physpath)); 1698 return (0); 1699 } 1700 1701 /* 1702 * Map a device name (devname) from the target environment to the 1703 * install environment. 1704 * 1705 * rootdir root directory of the target environment 1706 * devname /dev or /devices name under the target environment 1707 * buf caller supplied buffer where the mapped /dev name is placed 1708 * on return 1709 * bufsz length of the buffer 1710 * 1711 * Returns strlen of the mapped /dev name on success, -1 on failure. 1712 */ 1713 int 1714 devfs_target2install(const char *rootdir, const char *devname, char *buf, 1715 size_t bufsz) 1716 { 1717 char physpath[MAXPATHLEN]; 1718 1719 logdmsg(("devfs_target2install: rootdir = %s, devname = %s\n", 1720 STRVAL(rootdir), STRVAL(devname))); 1721 1722 if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0) 1723 return (-1); 1724 1725 if (strcmp(rootdir, "/") == 0) 1726 rootdir = ""; 1727 1728 if (devname2physpath((char *)rootdir, (char *)devname, physpath, 1729 MAXPATHLEN) != 0) 1730 return (-1); 1731 1732 #ifdef __sparc 1733 if (client_name_type(physpath) == CLIENT_TYPE_PHCI) { 1734 char *mapped_node_path, *minor; 1735 char minorbuf[MAXNAMELEN]; 1736 1737 /* strip minor component if present */ 1738 if ((minor = strrchr(physpath, ':')) != NULL) { 1739 *minor = '\0'; 1740 minor++; 1741 (void) strlcpy(minorbuf, minor, MAXNAMELEN); 1742 } 1743 if ((mapped_node_path = phci_to_vhci(physpath)) != NULL) { 1744 if (minor) 1745 (void) snprintf(physpath, MAXPATHLEN, 1746 "%s:%s", mapped_node_path, minorbuf); 1747 else 1748 (void) strlcpy(physpath, mapped_node_path, 1749 MAXPATHLEN); 1750 free(mapped_node_path); 1751 logdmsg(("devfs_target2install: mapped physpath: %s\n", 1752 physpath)); 1753 1754 } else if (minor) 1755 *(minor - 1) = ':'; 1756 } 1757 #endif /* __sparc */ 1758 1759 return (get_install_devlink(physpath, buf, bufsz)); 1760 } 1761 1762 /* 1763 * Map a device name (devname) from the install environment to the target 1764 * environment. 1765 * 1766 * rootdir root directory of the target environment 1767 * devname /dev or /devices name under the install environment 1768 * buf caller supplied buffer where the mapped /dev name is placed 1769 * on return 1770 * bufsz length of the buffer 1771 * 1772 * Returns strlen of the mapped /dev name on success, -1 on failure. 1773 */ 1774 int 1775 devfs_install2target(const char *rootdir, const char *devname, char *buf, 1776 size_t bufsz) 1777 { 1778 char physpath[MAXPATHLEN]; 1779 1780 logdmsg(("devfs_install2target: rootdir = %s, devname = %s\n", 1781 STRVAL(rootdir), STRVAL(devname))); 1782 1783 if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0) 1784 return (-1); 1785 1786 if (strcmp(rootdir, "/") == 0) 1787 rootdir = ""; 1788 1789 if (devname2physpath("", (char *)devname, physpath, MAXPATHLEN) != 0) 1790 return (-1); 1791 1792 #ifdef __sparc 1793 if (client_name_type(physpath) == CLIENT_TYPE_VHCI) { 1794 char **pathlist; 1795 int npaths, i, j; 1796 char *minor; 1797 char minorbuf[MAXNAMELEN]; 1798 1799 /* strip minor component if present */ 1800 if ((minor = strrchr(physpath, ':')) != NULL) { 1801 *minor = '\0'; 1802 minor++; 1803 (void) strlcpy(minorbuf, minor, MAXNAMELEN); 1804 } 1805 1806 if ((pathlist = vhci_to_phci(physpath, &npaths)) == NULL) 1807 return (-1); 1808 1809 if ((npaths = build_pathlist((char *)rootdir, physpath, 1810 pathlist, npaths)) <= 0) { 1811 free_pathlist(pathlist); 1812 return (-1); 1813 } 1814 1815 /* 1816 * in case of more than one path, try to use the path 1817 * referenced in the vfstab file, otherwise use the first path. 1818 */ 1819 j = 0; 1820 if (npaths > 1) { 1821 for (i = 0; i < npaths; i++) { 1822 if (is_dev_in_vfstab((char *)rootdir, 1823 pathlist[i])) { 1824 j = i; 1825 break; 1826 } 1827 } 1828 } 1829 1830 if (minor) 1831 (void) snprintf(physpath, MAXPATHLEN, 1832 "%s:%s", pathlist[j], minorbuf); 1833 else 1834 (void) strlcpy(physpath, pathlist[j], MAXPATHLEN); 1835 free_pathlist(pathlist); 1836 } 1837 #endif /* __sparc */ 1838 1839 return (get_target_devlink((char *)rootdir, physpath, buf, bufsz)); 1840 } 1841 1842 /* 1843 * A parser for /etc/path_to_inst. 1844 * The user-supplied callback is called once for each entry in the file. 1845 * Returns 0 on success, ENOMEM/ENOENT/EINVAL on error. 1846 * Callback may return DI_WALK_TERMINATE to terminate the walk, 1847 * otherwise DI_WALK_CONTINUE. 1848 */ 1849 int 1850 devfs_parse_binding_file(const char *binding_file, 1851 int (*callback)(void *, const char *, int, 1852 const char *), void *cb_arg) 1853 { 1854 token_t token; 1855 struct conf_file file; 1856 char tokval[MAX_TOKEN_SIZE]; 1857 enum { STATE_RESET, STATE_DEVPATH, STATE_INSTVAL } state; 1858 char *devpath; 1859 char *bindname; 1860 int instval = 0; 1861 int rv; 1862 1863 if ((devpath = calloc(1, MAXPATHLEN)) == NULL) 1864 return (ENOMEM); 1865 if ((bindname = calloc(1, MAX_TOKEN_SIZE)) == NULL) { 1866 free(devpath); 1867 return (ENOMEM); 1868 } 1869 1870 if ((file.fp = fopen(binding_file, "r")) == NULL) { 1871 free(devpath); 1872 free(bindname); 1873 return (errno); 1874 } 1875 1876 file.filename = (char *)binding_file; 1877 file.linenum = 1; 1878 1879 state = STATE_RESET; 1880 while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) { 1881 switch (token) { 1882 case T_POUND: 1883 /* 1884 * Skip comments. 1885 */ 1886 find_eol(file.fp); 1887 break; 1888 case T_NAME: 1889 case T_STRING: 1890 switch (state) { 1891 case STATE_RESET: 1892 if (strlcpy(devpath, tokval, 1893 MAXPATHLEN) >= MAXPATHLEN) 1894 goto err; 1895 state = STATE_DEVPATH; 1896 break; 1897 case STATE_INSTVAL: 1898 if (strlcpy(bindname, tokval, 1899 MAX_TOKEN_SIZE) >= MAX_TOKEN_SIZE) 1900 goto err; 1901 rv = callback(cb_arg, 1902 devpath, instval, bindname); 1903 if (rv == DI_WALK_TERMINATE) 1904 goto done; 1905 if (rv != DI_WALK_CONTINUE) 1906 goto err; 1907 state = STATE_RESET; 1908 break; 1909 default: 1910 file_err(&file, tok_err, tokval); 1911 state = STATE_RESET; 1912 break; 1913 } 1914 break; 1915 case T_DECVAL: 1916 case T_HEXVAL: 1917 switch (state) { 1918 case STATE_DEVPATH: 1919 instval = (int)strtol(tokval, NULL, 0); 1920 state = STATE_INSTVAL; 1921 break; 1922 default: 1923 file_err(&file, tok_err, tokval); 1924 state = STATE_RESET; 1925 break; 1926 } 1927 break; 1928 case T_NEWLINE: 1929 file.linenum++; 1930 state = STATE_RESET; 1931 break; 1932 default: 1933 file_err(&file, tok_err, tokval); 1934 state = STATE_RESET; 1935 break; 1936 } 1937 } 1938 1939 done: 1940 (void) fclose(file.fp); 1941 free(devpath); 1942 free(bindname); 1943 return (0); 1944 1945 err: 1946 (void) fclose(file.fp); 1947 free(devpath); 1948 free(bindname); 1949 return (EINVAL); 1950 } 1951 1952 /* 1953 * Walk the minor nodes of all children below the specified device 1954 * by calling the provided callback with the path to each minor. 1955 */ 1956 static int 1957 devfs_walk_children_minors(const char *device_path, struct stat *st, 1958 int (*callback)(void *, const char *), void *cb_arg, int *terminate) 1959 { 1960 DIR *dir; 1961 struct dirent *dp; 1962 char *minor_path = NULL; 1963 int need_close = 0; 1964 int rv; 1965 1966 if ((minor_path = calloc(1, MAXPATHLEN)) == NULL) 1967 return (ENOMEM); 1968 1969 if ((dir = opendir(device_path)) == NULL) { 1970 rv = ENOENT; 1971 goto err; 1972 } 1973 need_close = 1; 1974 1975 while ((dp = readdir(dir)) != NULL) { 1976 if ((strcmp(dp->d_name, ".") == 0) || 1977 (strcmp(dp->d_name, "..") == 0)) 1978 continue; 1979 (void) snprintf(minor_path, MAXPATHLEN, 1980 "%s/%s", device_path, dp->d_name); 1981 if (stat(minor_path, st) == -1) 1982 continue; 1983 if (S_ISDIR(st->st_mode)) { 1984 rv = devfs_walk_children_minors( 1985 (const char *)minor_path, st, 1986 callback, cb_arg, terminate); 1987 if (rv != 0) 1988 goto err; 1989 if (*terminate) 1990 break; 1991 } else { 1992 rv = callback(cb_arg, minor_path); 1993 if (rv == DI_WALK_TERMINATE) { 1994 *terminate = 1; 1995 break; 1996 } 1997 if (rv != DI_WALK_CONTINUE) { 1998 rv = EINVAL; 1999 goto err; 2000 } 2001 } 2002 } 2003 2004 rv = 0; 2005 err: 2006 if (need_close) 2007 (void) closedir(dir); 2008 if (minor_path) 2009 free(minor_path); 2010 return (rv); 2011 } 2012 2013 /* 2014 * Return the path to each minor node for a device by 2015 * calling the provided callback. 2016 */ 2017 static int 2018 devfs_walk_device_minors(const char *device_path, struct stat *st, 2019 int (*callback)(void *, const char *), void *cb_arg, int *terminate) 2020 { 2021 char *minor_path; 2022 char *devpath; 2023 char *expr; 2024 regex_t regex; 2025 int need_regfree = 0; 2026 int need_close = 0; 2027 DIR *dir; 2028 struct dirent *dp; 2029 int rv; 2030 char *p; 2031 2032 minor_path = calloc(1, MAXPATHLEN); 2033 devpath = calloc(1, MAXPATHLEN); 2034 expr = calloc(1, MAXNAMELEN); 2035 if (devpath == NULL || expr == NULL || minor_path == NULL) { 2036 rv = ENOMEM; 2037 goto err; 2038 } 2039 2040 rv = EINVAL; 2041 if (strlcpy(devpath, device_path, MAXPATHLEN) >= MAXPATHLEN) 2042 goto err; 2043 if ((p = strrchr(devpath, '/')) == NULL) 2044 goto err; 2045 *p++ = 0; 2046 if (strlen(p) == 0) 2047 goto err; 2048 if (snprintf(expr, MAXNAMELEN, "%s:.*", p) >= MAXNAMELEN) 2049 goto err; 2050 if (regcomp(®ex, expr, REG_EXTENDED) != 0) 2051 goto err; 2052 need_regfree = 1; 2053 2054 if ((dir = opendir(devpath)) == NULL) { 2055 rv = ENOENT; 2056 goto err; 2057 } 2058 need_close = 1; 2059 2060 while ((dp = readdir(dir)) != NULL) { 2061 if ((strcmp(dp->d_name, ".") == 0) || 2062 (strcmp(dp->d_name, "..") == 0)) 2063 continue; 2064 (void) snprintf(minor_path, MAXPATHLEN, 2065 "%s/%s", devpath, dp->d_name); 2066 if (stat(minor_path, st) == -1) 2067 continue; 2068 if ((S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) && 2069 regexec(®ex, dp->d_name, 0, NULL, 0) == 0) { 2070 rv = callback(cb_arg, minor_path); 2071 if (rv == DI_WALK_TERMINATE) { 2072 *terminate = 1; 2073 break; 2074 } 2075 if (rv != DI_WALK_CONTINUE) { 2076 rv = EINVAL; 2077 goto err; 2078 } 2079 } 2080 } 2081 2082 rv = 0; 2083 err: 2084 if (need_close) 2085 (void) closedir(dir); 2086 if (need_regfree) 2087 regfree(®ex); 2088 if (devpath) 2089 free(devpath); 2090 if (minor_path) 2091 free(minor_path); 2092 if (expr) 2093 free(expr); 2094 return (rv); 2095 } 2096 2097 /* 2098 * Perform a walk of all minor nodes for the specified device, 2099 * and minor nodes below the device. 2100 */ 2101 int 2102 devfs_walk_minor_nodes(const char *device_path, 2103 int (*callback)(void *, const char *), void *cb_arg) 2104 { 2105 struct stat stbuf; 2106 int rv; 2107 int terminate = 0; 2108 2109 rv = devfs_walk_device_minors(device_path, 2110 &stbuf, callback, cb_arg, &terminate); 2111 if (rv == 0 && terminate == 0) { 2112 rv = devfs_walk_children_minors(device_path, 2113 &stbuf, callback, cb_arg, &terminate); 2114 } 2115 return (rv); 2116 } 2117 2118 #ifdef DEBUG 2119 2120 static void 2121 vlog_debug_msg(char *fmt, va_list ap) 2122 { 2123 time_t clock; 2124 struct tm t; 2125 2126 if (!devfsmap_debug) 2127 return; 2128 2129 if (logfp == NULL) { 2130 if (*devfsmap_logfile != '\0') { 2131 logfp = fopen(devfsmap_logfile, "a"); 2132 if (logfp) 2133 (void) fprintf(logfp, "\nNew Log:\n"); 2134 } 2135 2136 if (logfp == NULL) 2137 logfp = stdout; 2138 } 2139 2140 clock = time(NULL); 2141 (void) localtime_r(&clock, &t); 2142 (void) fprintf(logfp, "%02d:%02d:%02d ", t.tm_hour, t.tm_min, 2143 t.tm_sec); 2144 (void) vfprintf(logfp, fmt, ap); 2145 (void) fflush(logfp); 2146 } 2147 2148 static void 2149 log_debug_msg(char *fmt, ...) 2150 { 2151 va_list ap; 2152 2153 va_start(ap, fmt); 2154 vlog_debug_msg(fmt, ap); 2155 va_end(ap); 2156 } 2157 2158 #ifdef __sparc 2159 2160 static char * 2161 mpxio_disable_string(int mpxio_disable) 2162 { 2163 if (mpxio_disable == 0) 2164 return ("no"); 2165 else if (mpxio_disable == 1) 2166 return ("yes"); 2167 else 2168 return ("not specified"); 2169 } 2170 2171 static void 2172 log_confent_list(char *filename, struct conf_entry *confent_list, 2173 int global_mpxio_disable) 2174 { 2175 struct conf_entry *confent; 2176 2177 log_debug_msg("log_confent_list: filename = %s:\n", filename); 2178 if (global_mpxio_disable != -1) 2179 log_debug_msg("\tdriver global mpxio_disable = \"%s\"\n\n", 2180 mpxio_disable_string(global_mpxio_disable)); 2181 2182 for (confent = confent_list; confent != NULL; confent = confent->next) { 2183 if (confent->name) 2184 log_debug_msg("\tname = %s\n", confent->name); 2185 if (confent->parent) 2186 log_debug_msg("\tparent = %s\n", confent->parent); 2187 if (confent->class) 2188 log_debug_msg("\tclass = %s\n", confent->class); 2189 if (confent->unit_address) 2190 log_debug_msg("\tunit_address = %s\n", 2191 confent->unit_address); 2192 if (confent->port != -1) 2193 log_debug_msg("\tport = %d\n", confent->port); 2194 log_debug_msg("\tmpxio_disable = \"%s\"\n\n", 2195 mpxio_disable_string(confent->mpxio_disable)); 2196 } 2197 } 2198 2199 static void 2200 log_pathlist(char **pathlist) 2201 { 2202 char **p; 2203 2204 for (p = pathlist; *p != NULL; p++) 2205 log_debug_msg("\t%s\n", *p); 2206 } 2207 2208 #endif /* __sparc */ 2209 2210 #endif /* DEBUG */ 2211