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