1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 #ifdef lint 28 #define _REENTRANT /* for localtime_r */ 29 #endif 30 31 #include <stdio.h> 32 #include <ctype.h> 33 #include <stdlib.h> 34 #include <sys/types.h> 35 #include <strings.h> 36 #include <stdarg.h> 37 #include <sys/stat.h> 38 #include <fcntl.h> 39 #include <errno.h> 40 #include <unistd.h> 41 #include <stropts.h> 42 #include <time.h> 43 #include <sys/param.h> 44 #include <sys/vfstab.h> 45 #include <dirent.h> 46 #ifdef __sparc 47 #include <sys/scsi/adapters/scsi_vhci.h> 48 #include <sys/sunmdi.h> 49 #endif /* __sparc */ 50 #include "libdevinfo.h" 51 #include "device_info.h" 52 #include <regex.h> 53 54 #define isnewline(ch) ((ch) == '\n' || (ch) == '\r' || (ch) == '\f') 55 #define isnamechar(ch) (isalpha(ch) || isdigit(ch) || (ch) == '_' ||\ 56 (ch) == '-') 57 #define MAX_TOKEN_SIZE 1024 58 #define BUFSIZE 1024 59 #define STRVAL(s) ((s) ? (s) : "NULL") 60 61 #define SCSI_VHCI_CONF "/kernel/drv/scsi_vhci.conf" 62 #define QLC_CONF "/kernel/drv/qlc.conf" 63 #define FP_CONF "/kernel/drv/fp.conf" 64 #define DRIVER_CLASSES "/etc/driver_classes" 65 #define FP_AT "fp@" 66 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl" 67 #define SLASH_DEVICES "/devices" 68 #define SLASH_DEVICES_SLASH "/devices/" 69 #define SLASH_FP_AT "/fp@" 70 #define SLASH_SCSI_VHCI "/scsi_vhci" 71 #define META_DEV "/dev/md/dsk/" 72 #define SLASH_DEV_SLASH "/dev/" 73 74 /* 75 * Macros to produce a quoted string containing the value of a 76 * preprocessor macro. For example, if SIZE is defined to be 256, 77 * VAL2STR(SIZE) is "256". This is used to construct format 78 * strings for scanf-family functions below. 79 */ 80 #define QUOTE(x) #x 81 #define VAL2STR(x) QUOTE(x) 82 83 typedef enum { 84 CLIENT_TYPE_UNKNOWN, 85 CLIENT_TYPE_PHCI, 86 CLIENT_TYPE_VHCI 87 } client_type_t; 88 89 typedef enum { 90 T_EQUALS, 91 T_AMPERSAND, 92 T_BIT_OR, 93 T_STAR, 94 T_POUND, 95 T_COLON, 96 T_SEMICOLON, 97 T_COMMA, 98 T_SLASH, 99 T_WHITE_SPACE, 100 T_NEWLINE, 101 T_EOF, 102 T_STRING, 103 T_HEXVAL, 104 T_DECVAL, 105 T_NAME 106 } token_t; 107 108 typedef enum { 109 begin, parent, drvname, drvclass, prop, 110 parent_equals, name_equals, drvclass_equals, 111 parent_equals_string, name_equals_string, 112 drvclass_equals_string, 113 prop_equals, prop_equals_string, prop_equals_integer, 114 prop_equals_string_comma, prop_equals_integer_comma 115 } conf_state_t; 116 117 /* structure to hold entries with mpxio-disable property in driver.conf file */ 118 struct conf_entry { 119 char *name; 120 char *parent; 121 char *class; 122 char *unit_address; 123 int port; 124 int mpxio_disable; 125 struct conf_entry *next; 126 }; 127 128 struct conf_file { 129 char *filename; 130 FILE *fp; 131 int linenum; 132 }; 133 134 static char *tok_err = "Unexpected token '%s'\n"; 135 136 137 /* #define DEBUG */ 138 139 #ifdef DEBUG 140 141 int devfsmap_debug = 0; 142 /* /var/run is not mounted at install time. Therefore use /tmp */ 143 char *devfsmap_logfile = "/tmp/devfsmap.log"; 144 static FILE *logfp; 145 #define logdmsg(args) log_debug_msg args 146 static void vlog_debug_msg(char *, va_list); 147 static void log_debug_msg(char *, ...); 148 #ifdef __sparc 149 static void log_confent_list(char *, struct conf_entry *, int); 150 static void log_pathlist(char **); 151 #endif /* __sparc */ 152 153 #else /* DEBUG */ 154 #define logdmsg(args) /* nothing */ 155 #endif /* DEBUG */ 156 157 158 /* 159 * Leave NEWLINE as the next character. 160 */ 161 static void 162 find_eol(FILE *fp) 163 { 164 int ch; 165 166 while ((ch = getc(fp)) != EOF) { 167 if (isnewline(ch)) { 168 (void) ungetc(ch, fp); 169 break; 170 } 171 } 172 } 173 174 /* ignore parsing errors */ 175 /*ARGSUSED*/ 176 static void 177 file_err(struct conf_file *filep, char *fmt, ...) 178 { 179 #ifdef DEBUG 180 va_list ap; 181 182 va_start(ap, fmt); 183 log_debug_msg("WARNING: %s line # %d: ", 184 filep->filename, filep->linenum); 185 vlog_debug_msg(fmt, ap); 186 va_end(ap); 187 #endif /* DEBUG */ 188 } 189 190 /* return the next token from the given driver.conf file, or -1 on error */ 191 static token_t 192 lex(struct conf_file *filep, char *val, size_t size) 193 { 194 char *cp; 195 int ch, oval, badquote; 196 size_t remain; 197 token_t token; 198 FILE *fp = filep->fp; 199 200 if (size < 2) 201 return (-1); 202 203 cp = val; 204 while ((ch = getc(fp)) == ' ' || ch == '\t') 205 ; 206 207 remain = size - 1; 208 *cp++ = (char)ch; 209 switch (ch) { 210 case '=': 211 token = T_EQUALS; 212 break; 213 case '&': 214 token = T_AMPERSAND; 215 break; 216 case '|': 217 token = T_BIT_OR; 218 break; 219 case '*': 220 token = T_STAR; 221 break; 222 case '#': 223 token = T_POUND; 224 break; 225 case ':': 226 token = T_COLON; 227 break; 228 case ';': 229 token = T_SEMICOLON; 230 break; 231 case ',': 232 token = T_COMMA; 233 break; 234 case '/': 235 token = T_SLASH; 236 break; 237 case ' ': 238 case '\t': 239 case '\f': 240 while ((ch = getc(fp)) == ' ' || 241 ch == '\t' || ch == '\f') { 242 if (--remain == 0) { 243 *cp = '\0'; 244 return (-1); 245 } 246 *cp++ = (char)ch; 247 } 248 (void) ungetc(ch, fp); 249 token = T_WHITE_SPACE; 250 break; 251 case '\n': 252 case '\r': 253 token = T_NEWLINE; 254 break; 255 case '"': 256 remain++; 257 cp--; 258 badquote = 0; 259 while (!badquote && (ch = getc(fp)) != '"') { 260 switch (ch) { 261 case '\n': 262 case EOF: 263 file_err(filep, "Missing \"\n"); 264 remain = size - 1; 265 cp = val; 266 *cp++ = '\n'; 267 badquote = 1; 268 /* since we consumed the newline/EOF */ 269 (void) ungetc(ch, fp); 270 break; 271 272 case '\\': 273 if (--remain == 0) { 274 *cp = '\0'; 275 return (-1); 276 } 277 ch = (char)getc(fp); 278 if (!isdigit(ch)) { 279 /* escape the character */ 280 *cp++ = (char)ch; 281 break; 282 } 283 oval = 0; 284 while (ch >= '0' && ch <= '7') { 285 ch -= '0'; 286 oval = (oval << 3) + ch; 287 ch = (char)getc(fp); 288 } 289 (void) ungetc(ch, fp); 290 /* check for character overflow? */ 291 if (oval > 127) { 292 file_err(filep, 293 "Character " 294 "overflow detected.\n"); 295 } 296 *cp++ = (char)oval; 297 break; 298 default: 299 if (--remain == 0) { 300 *cp = '\0'; 301 return (-1); 302 } 303 *cp++ = (char)ch; 304 break; 305 } 306 } 307 token = T_STRING; 308 break; 309 310 case EOF: 311 token = T_EOF; 312 break; 313 314 default: 315 /* 316 * detect a lone '-' (including at the end of a line), and 317 * identify it as a 'name' 318 */ 319 if (ch == '-') { 320 if (--remain == 0) { 321 *cp = '\0'; 322 return (-1); 323 } 324 *cp++ = (char)(ch = getc(fp)); 325 if (ch == ' ' || ch == '\t' || ch == '\n') { 326 (void) ungetc(ch, fp); 327 remain++; 328 cp--; 329 token = T_NAME; 330 break; 331 } 332 } else if (ch == '~' || ch == '-') { 333 if (--remain == 0) { 334 *cp = '\0'; 335 return (-1); 336 } 337 *cp++ = (char)(ch = getc(fp)); 338 } 339 340 341 if (isdigit(ch)) { 342 if (ch == '0') { 343 if ((ch = getc(fp)) == 'x') { 344 if (--remain == 0) { 345 *cp = '\0'; 346 return (-1); 347 } 348 *cp++ = (char)ch; 349 ch = getc(fp); 350 while (isxdigit(ch)) { 351 if (--remain == 0) { 352 *cp = '\0'; 353 return (-1); 354 } 355 *cp++ = (char)ch; 356 ch = getc(fp); 357 } 358 (void) ungetc(ch, fp); 359 token = T_HEXVAL; 360 } else { 361 goto digit; 362 } 363 } else { 364 ch = getc(fp); 365 digit: 366 while (isdigit(ch)) { 367 if (--remain == 0) { 368 *cp = '\0'; 369 return (-1); 370 } 371 *cp++ = (char)ch; 372 ch = getc(fp); 373 } 374 (void) ungetc(ch, fp); 375 token = T_DECVAL; 376 } 377 } else if (isalpha(ch) || ch == '\\') { 378 if (ch != '\\') { 379 ch = getc(fp); 380 } else { 381 /* 382 * if the character was a backslash, 383 * back up so we can overwrite it with 384 * the next (i.e. escaped) character. 385 */ 386 remain++; 387 cp--; 388 } 389 while (isnamechar(ch) || ch == '\\') { 390 if (ch == '\\') 391 ch = getc(fp); 392 if (--remain == 0) { 393 *cp = '\0'; 394 return (-1); 395 } 396 *cp++ = (char)ch; 397 ch = getc(fp); 398 } 399 (void) ungetc(ch, fp); 400 token = T_NAME; 401 } else { 402 return (-1); 403 } 404 break; 405 } 406 407 *cp = '\0'; 408 409 return (token); 410 } 411 412 #ifdef __sparc 413 414 static void 415 free_confent(struct conf_entry *confent) 416 { 417 if (confent->name) 418 free(confent->name); 419 if (confent->parent) 420 free(confent->parent); 421 if (confent->class) 422 free(confent->class); 423 if (confent->unit_address) 424 free(confent->unit_address); 425 free(confent); 426 } 427 428 static void 429 free_confent_list(struct conf_entry *confent_list) 430 { 431 struct conf_entry *confent, *next; 432 433 for (confent = confent_list; confent != NULL; confent = next) { 434 next = confent->next; 435 free_confent(confent); 436 } 437 } 438 439 /* 440 * Parse the next entry from the driver.conf file and return in the form of 441 * a pointer to the conf_entry. 442 */ 443 static struct conf_entry * 444 parse_conf_entry(struct conf_file *filep, char *tokbuf, size_t linesize) 445 { 446 char *prop_name, *string; 447 token_t token; 448 struct conf_entry *confent; 449 conf_state_t state; 450 int failed = 1; 451 452 if ((confent = calloc(1, sizeof (*confent))) == NULL) 453 return (NULL); 454 455 confent->port = -1; 456 confent->mpxio_disable = -1; 457 458 state = begin; 459 token = T_NAME; 460 prop_name = NULL; 461 string = NULL; 462 do { 463 switch (token) { 464 case T_NAME: 465 switch (state) { 466 case prop_equals_string: 467 case prop_equals_integer: 468 case begin: 469 state = prop; 470 if ((prop_name = strdup(tokbuf)) == NULL) 471 goto bad; 472 break; 473 default: 474 file_err(filep, tok_err, tokbuf); 475 } 476 break; 477 case T_EQUALS: 478 switch (state) { 479 case prop: 480 state = prop_equals; 481 break; 482 default: 483 file_err(filep, tok_err, tokbuf); 484 } 485 break; 486 case T_STRING: 487 switch (state) { 488 case prop_equals: 489 if ((string = strdup(tokbuf)) == NULL) 490 goto bad; 491 492 state = begin; 493 if (strcmp(prop_name, "PARENT") == 0 || 494 strcmp(prop_name, "parent") == 0) { 495 if (confent->parent) { 496 file_err(filep, 497 "'parent' property already specified\n"); 498 goto bad; 499 } 500 confent->parent = string; 501 } else if (strcmp(prop_name, "NAME") == 0 || 502 strcmp(prop_name, "name") == 0) { 503 if (confent->name) { 504 file_err(filep, 505 "'name' property already specified\n"); 506 goto bad; 507 } 508 confent->name = string; 509 } else if (strcmp(prop_name, "CLASS") == 0 || 510 strcmp(prop_name, "class") == 0) { 511 if (confent->class) { 512 file_err(filep, 513 "'class' property already specified\n"); 514 goto bad; 515 } 516 confent->class = string; 517 } else if (strcmp(prop_name, "unit-address") 518 == 0) { 519 if (confent->unit_address) { 520 file_err(filep, 521 "'unit-address' property already specified\n"); 522 goto bad; 523 } 524 confent->unit_address = string; 525 } else if (strcmp(prop_name, "mpxio-disable") 526 == 0) { 527 if (confent->mpxio_disable != -1) { 528 file_err(filep, 529 "'mpxio-disable' property already specified\n"); 530 goto bad; 531 } 532 if (strcmp(string, "yes") == 0) 533 confent->mpxio_disable = 1; 534 else if (strcmp(string, "no") == 0) 535 confent->mpxio_disable = 0; 536 else { 537 file_err(filep, 538 "'mpxio-disable' property setting is invalid. " 539 "The value must be either \"yes\" or \"no\"\n"); 540 goto bad; 541 } 542 free(string); 543 } else { 544 free(string); 545 state = prop_equals_string; 546 } 547 string = NULL; 548 free(prop_name); 549 prop_name = NULL; 550 break; 551 552 case prop_equals_string_comma: 553 state = prop_equals_string; 554 break; 555 default: 556 file_err(filep, tok_err, tokbuf); 557 } 558 break; 559 case T_HEXVAL: 560 case T_DECVAL: 561 switch (state) { 562 case prop_equals: 563 if (strcmp(prop_name, "port") == 0) { 564 if (confent->port != -1) { 565 file_err(filep, 566 "'port' property already specified\n"); 567 goto bad; 568 } 569 confent->port = 570 (int)strtol(tokbuf, NULL, 0); 571 state = begin; 572 } else 573 state = prop_equals_integer; 574 free(prop_name); 575 prop_name = NULL; 576 break; 577 578 case prop_equals_integer_comma: 579 state = prop_equals_integer; 580 break; 581 default: 582 file_err(filep, tok_err, tokbuf); 583 } 584 break; 585 case T_COMMA: 586 switch (state) { 587 case prop_equals_string: 588 state = prop_equals_string_comma; 589 break; 590 case prop_equals_integer: 591 state = prop_equals_integer_comma; 592 break; 593 default: 594 file_err(filep, tok_err, tokbuf); 595 } 596 break; 597 case T_NEWLINE: 598 filep->linenum++; 599 break; 600 case T_POUND: 601 find_eol(filep->fp); 602 break; 603 case T_EOF: 604 file_err(filep, "Unexpected EOF\n"); 605 goto bad; 606 default: 607 file_err(filep, tok_err, tokbuf); 608 goto bad; 609 } 610 } while ((token = lex(filep, tokbuf, linesize)) != T_SEMICOLON); 611 612 failed = 0; 613 614 bad: 615 if (prop_name) 616 free(prop_name); 617 if (string) 618 free(string); 619 if (failed == 1) { 620 free_confent(confent); 621 return (NULL); 622 } 623 return (confent); 624 } 625 626 /* 627 * Parse all entries with mpxio-disable property in the given driver.conf 628 * file. 629 * 630 * fname driver.conf file name 631 * confent_list on return *confent_list will contain the list of 632 * driver.conf file entries with mpxio-disable property. 633 * mpxio_disable on return *mpxio_disable is set to the setting of the 634 * driver global mpxio-dissable property as follows. 635 * 0 if driver mpxio-disable="no" 636 * 1 if driver mpxio-disable="yes" 637 * -1 if driver mpxio-disable property isn't specified. 638 */ 639 static void 640 parse_conf_file(char *fname, struct conf_entry **confent_list, 641 int *mpxio_disable) 642 { 643 struct conf_entry *confent, *tail = NULL; 644 token_t token; 645 struct conf_file file; 646 char tokval[MAX_TOKEN_SIZE]; 647 648 *confent_list = NULL; 649 *mpxio_disable = -1; 650 if ((file.fp = fopen(fname, "r")) == NULL) 651 return; 652 653 file.filename = fname; 654 file.linenum = 1; 655 656 while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) { 657 switch (token) { 658 case T_POUND: 659 /* 660 * Skip comments. 661 */ 662 find_eol(file.fp); 663 break; 664 case T_NAME: 665 if ((confent = parse_conf_entry(&file, tokval, 666 MAX_TOKEN_SIZE)) == NULL) 667 break; 668 /* 669 * No name indicates global property. 670 * Make sure parent and class not NULL. 671 */ 672 if (confent->name == NULL) { 673 if (confent->parent || 674 confent->class) { 675 file_err(&file, 676 "missing name attribute\n"); 677 } else if (confent->mpxio_disable != -1) { 678 if (*mpxio_disable == -1) 679 *mpxio_disable = 680 confent->mpxio_disable; 681 else 682 file_err(&file, 683 "'mpxio-disable' property already specified\n"); 684 } 685 free_confent(confent); 686 break; 687 } 688 689 /* 690 * This is a node spec, either parent or class 691 * must be specified. 692 */ 693 if (confent->parent == NULL && confent->class == NULL) { 694 file_err(&file, 695 "missing parent or class attribute\n"); 696 free_confent(confent); 697 break; 698 } 699 700 /* only need entries with mpxio_disable property */ 701 if (confent->mpxio_disable == -1) { 702 free_confent(confent); 703 break; 704 } 705 706 if (tail) 707 tail->next = confent; 708 else 709 *confent_list = confent; 710 tail = confent; 711 break; 712 713 case T_NEWLINE: 714 file.linenum++; 715 break; 716 default: 717 break; 718 } 719 } 720 721 (void) fclose(file.fp); 722 } 723 724 /* 725 * Return the driver class of the given driver_name. 726 * The memory for the driver class is allocated by this function and the 727 * caller must free it. 728 */ 729 static char * 730 get_driver_class(char *rootdir, char *driver_name) 731 { 732 FILE *fp; 733 char buf[BUFSIZE]; 734 char driver[BUFSIZE]; 735 char class_name[BUFSIZE]; 736 737 logdmsg(("get_driver_class: rootdir = %s, driver name = %s\n", 738 rootdir, driver_name)); 739 740 (void) snprintf(buf, sizeof (buf), "%s%s", rootdir, DRIVER_CLASSES); 741 742 if ((fp = fopen(buf, "r")) == NULL) { 743 logdmsg(("get_driver_class: failed to open %s: %s\n", 744 buf, strerror(errno))); 745 return (NULL); 746 } 747 748 while (fgets(buf, sizeof (buf), fp) != NULL) { 749 /* LINTED - unbounded string specifier */ 750 if ((sscanf(buf, "%s %s", driver, class_name) == 2) && 751 driver[0] != '#' && strcmp(driver, driver_name) == 0) { 752 logdmsg(("get_driver_class: driver class = %s\n", 753 class_name)); 754 (void) fclose(fp); 755 return (strdup(class_name)); 756 } 757 } 758 759 (void) fclose(fp); 760 return (NULL); 761 } 762 763 static int 764 lookup_in_confent_list(struct conf_entry *confent_list, 765 int match_class, char *parent, char *unit_addr, int port) 766 { 767 struct conf_entry *confent; 768 char *par; 769 770 logdmsg(("lookup_in_confent_list: %s = \"%s\", unit_addr = \"%s\", " 771 "port = %d\n", (match_class) ? "class" : "parent", parent, 772 STRVAL(unit_addr), port)); 773 774 for (confent = confent_list; confent != NULL; confent = confent->next) { 775 par = (match_class) ? confent->class : confent->parent; 776 if (unit_addr) { 777 if (confent->unit_address != NULL && 778 strcmp(confent->unit_address, unit_addr) == 0 && 779 par != NULL && strcmp(par, parent) == 0) 780 return (confent->mpxio_disable); 781 } else { 782 if (confent->port == port && 783 par != NULL && strcmp(par, parent) == 0) 784 return (confent->mpxio_disable); 785 } 786 } 787 return (-1); 788 } 789 790 /* 791 * lookup mpxio-disabled property setting for the given path in the given 792 * driver.conf file. Match the entries from most specific to least specific. 793 * 794 * conf_file the path name of either fp.conf, qlc.conf or scsi_vhci.conf 795 * path /devices node path without the /devices prefix. 796 * If the conf_file is fp.conf, path must be a fp node path 797 * if the conf_file is qlc.conf, path must be a qlc node path. 798 * if the conf_file is scsi_vhci.conf, path must be NULL. 799 * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0 800 * /pci@8,600000/SUNW,qlc@4 801 * 802 * returns: 803 * 0 if mpxio-disable="no" 804 * 1 if mpxio-disable="yes" 805 * -1 if mpxio-disable property isn't specified. 806 */ 807 static int 808 lookup_in_conf_file(char *rootdir, char *conf_file, char *path) 809 { 810 struct conf_entry *confent_list = NULL; 811 int mpxio_disable; 812 di_node_t par_node = DI_NODE_NIL; 813 char *node_name = NULL, *node_addr = NULL; 814 char *unit_addr = NULL; 815 int port = -1; 816 char *par_node_name = NULL, *par_node_addr = NULL; 817 char *par_binding_name = NULL, *par_driver_name = NULL; 818 char *par_driver_class = NULL, *par_node_name_addr; 819 int rv = -1; 820 char buf[MAXPATHLEN]; 821 822 logdmsg(("lookup_in_conf_file: rootdir = \"%s\", conf_file = \"%s\", " 823 "path = \"%s\"\n", rootdir, conf_file, STRVAL(path))); 824 825 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, conf_file); 826 parse_conf_file(buf, &confent_list, &mpxio_disable); 827 #ifdef DEBUG 828 log_confent_list(buf, confent_list, mpxio_disable); 829 #endif 830 831 /* if path is NULL, return driver global mpxio-disable setting */ 832 if (path == NULL) { 833 rv = mpxio_disable; 834 goto done; 835 } 836 837 if ((node_name = strrchr(path, '/')) == NULL) 838 goto done; 839 840 *node_name = '\0'; 841 node_name++; 842 843 if ((node_addr = strchr(node_name, '@')) == NULL) 844 goto done; 845 846 *node_addr = '\0'; 847 node_addr++; 848 849 if (strcmp(node_name, "fp") == 0) { 850 /* get port number; encoded in the node addr as a hex number */ 851 port = (int)strtol(node_addr, NULL, 16); 852 } else 853 unit_addr = node_addr; 854 855 /* 856 * Match from most specific to least specific; 857 * first, start the lookup based on full path. 858 */ 859 if ((rv = lookup_in_confent_list(confent_list, 0, path, 860 unit_addr, port)) != -1) 861 goto done; 862 863 /* lookup nodename@address */ 864 if ((par_node_name_addr = strrchr(path, '/')) != NULL) { 865 par_node_name_addr++; 866 if ((rv = lookup_in_confent_list(confent_list, 0, 867 par_node_name_addr, unit_addr, port)) != -1) 868 goto done; 869 } 870 871 /* di_init() doesn't work when 0 is passed in flags */ 872 par_node = di_init(path, DINFOMINOR); 873 if (par_node != DI_NODE_NIL) { 874 par_node_name = di_node_name(par_node); 875 par_node_addr = di_bus_addr(par_node); 876 par_binding_name = di_binding_name(par_node); 877 par_driver_name = di_driver_name(par_node); 878 } 879 880 logdmsg(("par_node_name = %s\n", STRVAL(par_node_name))); 881 logdmsg(("par_node_addr = %s\n", STRVAL(par_node_addr))); 882 logdmsg(("par_binding_name = %s\n", STRVAL(par_binding_name))); 883 logdmsg(("par_driver_name = %s\n", STRVAL(par_driver_name))); 884 885 /* lookup bindingname@address */ 886 if (par_binding_name != NULL && par_binding_name != par_node_name && 887 par_node_addr != NULL) { 888 (void) snprintf(buf, sizeof (buf), "%s@%s", par_binding_name, 889 par_node_addr); 890 if ((rv = lookup_in_confent_list(confent_list, 0, 891 buf, unit_addr, port)) != -1) 892 goto done; 893 } 894 895 /* lookup binding name */ 896 if (par_binding_name != NULL) { 897 if ((rv = lookup_in_confent_list(confent_list, 0, 898 par_binding_name, unit_addr, port)) != -1) 899 goto done; 900 } 901 902 if (par_driver_name != NULL) { 903 /* lookup driver name */ 904 if ((rv = lookup_in_confent_list(confent_list, 0, 905 par_driver_name, unit_addr, port)) != -1) 906 goto done; 907 908 /* finally, lookup class name */ 909 par_driver_class = get_driver_class(rootdir, par_driver_name); 910 if (par_driver_class != NULL) { 911 if ((rv = lookup_in_confent_list(confent_list, 1, 912 par_driver_class, unit_addr, port)) != -1) 913 goto done; 914 } 915 } 916 917 /* 918 * no match so far; 919 * use the driver global mpxio-disable setting if exists. 920 */ 921 rv = mpxio_disable; 922 923 done: 924 if (node_name != NULL) 925 *(node_name - 1) = '/'; 926 if (node_addr != NULL) 927 *(node_addr - 1) = '@'; 928 if (par_driver_class != NULL) 929 free(par_driver_class); 930 if (confent_list != NULL) 931 free_confent_list(confent_list); 932 if (par_node != DI_NODE_NIL) 933 di_fini(par_node); 934 935 return (rv); 936 } 937 938 /* 939 * Given client_name return whether it is a phci or vhci based name. 940 * client_name is /devices name of a client without the /devices prefix. 941 * 942 * client_name Return value 943 * .../fp@xxx/ssd@yyy CLIENT_TYPE_PHCI 944 * .../scsi_vhci/ssd@yyy CLIENT_TYPE_VHCI 945 * other CLIENT_TYPE_UNKNOWN 946 */ 947 static client_type_t 948 client_name_type(char *client_name) 949 { 950 client_type_t client_type; 951 char *p1, *p2; 952 953 logdmsg(("client_name_type: client_name = %s\n", client_name)); 954 955 if (strncmp(client_name, SLASH_SCSI_VHCI, 956 sizeof (SLASH_SCSI_VHCI) - 1) == 0) 957 return (CLIENT_TYPE_VHCI); 958 959 if (*client_name != '/') 960 return (CLIENT_TYPE_UNKNOWN); 961 962 if ((p1 = strrchr(client_name, '/')) == NULL) 963 return (CLIENT_TYPE_UNKNOWN); 964 965 *p1 = '\0'; 966 967 if ((p2 = strrchr(client_name, '/')) != NULL && 968 strncmp(p2, SLASH_FP_AT, sizeof (SLASH_FP_AT) - 1) == 0) 969 client_type = CLIENT_TYPE_PHCI; 970 else 971 client_type = CLIENT_TYPE_UNKNOWN; 972 973 *p1 = '/'; 974 return (client_type); 975 } 976 977 /* 978 * Compare controller name portion of dev1 and dev2. 979 * 980 * rootdir root directory of the target environment 981 * dev1 can be either a /dev link or /devices name in the target 982 * environemnt 983 * dev2 /devices name of a device without the /devices prefix 984 * 985 * Returns: 986 * 0 if controller names match 987 * 1 if controller names don't match 988 * -1 an error occurred. 989 */ 990 static int 991 compare_controller(char *rootdir, char *dev1, char *dev2) 992 { 993 int linksize; 994 char *p1, *p; 995 char physdev1[MAXPATHLEN]; 996 char buf[MAXPATHLEN]; 997 998 logdmsg(("compare_controller: rootdir = %s, dev1 = %s, dev2 = %s\n", 999 rootdir, dev1, dev2)); 1000 1001 if (strncmp(dev1, SLASH_DEV_SLASH, sizeof (SLASH_DEV_SLASH) - 1) 1002 == 0) { 1003 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, dev1); 1004 if ((linksize = readlink(buf, physdev1, MAXPATHLEN)) > 0 && 1005 linksize < (MAXPATHLEN - 1)) { 1006 physdev1[linksize] = '\0'; 1007 logdmsg(("compare_controller: physdev1 = %s\n", 1008 physdev1)); 1009 } else 1010 return (-1); 1011 } else 1012 (void) strlcpy(physdev1, dev1, MAXPATHLEN); 1013 1014 if ((p1 = strstr(physdev1, SLASH_DEVICES)) == NULL) 1015 return (-1); 1016 1017 p1 += sizeof (SLASH_DEVICES) - 1; 1018 /* strip the device portion */ 1019 if ((p = strrchr(p1, '/')) == NULL) 1020 return (-1); 1021 *p = '\0'; 1022 1023 if ((p = strrchr(dev2, '/')) == NULL) 1024 return (-1); 1025 *p = '\0'; 1026 1027 logdmsg(("compare_controller: path1 = %s, path2 = %s\n", 1028 p1, dev2)); 1029 if (strcmp(p1, dev2) == 0) { 1030 *p = '/'; 1031 return (0); 1032 } else { 1033 *p = '/'; 1034 return (1); 1035 } 1036 } 1037 1038 /* 1039 * Check if the specified device path is on the root controller. 1040 * 1041 * rootdir root directory of the target environment 1042 * path /devices name of a device without the /devices prefix 1043 * 1044 * Returns 1045 * 1 if the path is on the root controller 1046 * 0 if the path is not on the root controller 1047 * -1 if an error occurs 1048 */ 1049 static int 1050 is_root_controller(char *rootdir, char *path) 1051 { 1052 FILE *fp; 1053 char *tmpfile; 1054 int rv = -1; 1055 struct vfstab vfsent; 1056 char buf[MAXPATHLEN]; 1057 char ctd[MAXNAMELEN + 1]; 1058 1059 logdmsg(("is_root_controller: rootdir = %s, path = %s\n", rootdir, 1060 path)); 1061 1062 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, VFSTAB); 1063 1064 if ((fp = fopen(buf, "r")) == NULL) { 1065 logdmsg(("is_root_controller: failed to open %s: %s\n", 1066 buf, strerror(errno))); 1067 return (-1); 1068 } 1069 1070 if (getvfsfile(fp, &vfsent, "/") != 0) { 1071 logdmsg(("is_root_controller: getvfsfile: failed to read " 1072 "vfstab entry for mount point \"/\": %s\n", 1073 strerror(errno))); 1074 (void) fclose(fp); 1075 return (-1); 1076 } 1077 (void) fclose(fp); 1078 1079 /* check if the root is an svm metadisk */ 1080 if (strncmp(vfsent.vfs_special, META_DEV, sizeof (META_DEV) - 1) != 0) { 1081 if (compare_controller(rootdir, vfsent.vfs_special, path) == 0) 1082 return (1); 1083 else 1084 return (0); 1085 } 1086 1087 /* Don't use /var/run as it is not mounted in miniroot */ 1088 if ((tmpfile = tempnam("/tmp", "diirc")) == NULL) { 1089 logdmsg(("is_root_controller: tempnam: failed: %s\n", 1090 strerror(errno))); 1091 return (-1); 1092 } 1093 1094 /* get metadisk components using metastat command */ 1095 (void) snprintf(buf, MAXPATHLEN, 1096 "/usr/sbin/metastat -p %s 2>/dev/null | " 1097 "/usr/bin/grep ' 1 1 ' | " 1098 "/usr/bin/sed -e 's/^.* 1 1 //' | " 1099 "/usr/bin/cut -f1 -d ' ' > %s", 1100 vfsent.vfs_special + sizeof (META_DEV) - 1, tmpfile); 1101 1102 logdmsg(("is_root_controller: command = %s\n", buf)); 1103 fp = NULL; 1104 if (system(buf) == 0 && (fp = fopen(tmpfile, "r")) != NULL) { 1105 while (fscanf(fp, "%" VAL2STR(MAXNAMELEN) "s", ctd) == 1) { 1106 (void) snprintf(buf, MAXPATHLEN, "/dev/dsk/%s", ctd); 1107 if (compare_controller(rootdir, buf, path) == 0) { 1108 rv = 1; 1109 goto out; 1110 } 1111 } 1112 rv = 0; 1113 } 1114 1115 out: 1116 if (fp) 1117 (void) fclose(fp); 1118 (void) unlink(tmpfile); 1119 free(tmpfile); 1120 return (rv); 1121 } 1122 1123 static int 1124 file_exists(char *rootdir, char *path) 1125 { 1126 struct stat