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