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 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <stdarg.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 #include <errno.h> 36 #include <unistd.h> 37 #include <stropts.h> 38 #include <strings.h> 39 #include <dirent.h> 40 #include <sys/param.h> 41 #include <sys/scsi/adapters/scsi_vhci.h> 42 #include <libdevinfo.h> 43 #include <libgen.h> 44 #include <dlfcn.h> 45 #include <link.h> 46 #include <locale.h> 47 #include <libintl.h> 48 #include <sys/syscall.h> 49 #include <sys/vfstab.h> 50 #include <sys/mount.h> 51 #include <devid.h> 52 #include <sys/libdevid.h> 53 54 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl" 55 #define SLASH_DEVICES "/devices/" 56 57 #ifdef sparc 58 #define DISK_NODE_NAME "ssd" 59 #define DISK_DRV_NAME "ssd" 60 #else /* sparc */ 61 #define DISK_NODE_NAME "disk" 62 #define DISK_DRV_NAME "sd" 63 #endif 64 65 #define DISK_AT_G "disk@g" 66 #define SLASH_FP_AT "/fp@" 67 #define SLASH_SCSI_VHCI "/scsi_vhci" 68 #define DEV_DSK "/dev/dsk/" 69 #define DEV_RDSK "/dev/rdsk/" 70 #define SYS_FILENAME_LEN 256 71 72 /* 73 * Save directory is the directory in which system files are saved. 74 * Save directory must be under the root filesystem, as this program is 75 * typically run before any other filesystems are mounted. 76 */ 77 #define SAVE_DIR "/etc/mpxio" 78 79 /* fcp driver publishes this property */ 80 #define NODE_WWN_PROP "node-wwn" 81 82 /* 83 * For SAS, we look for "sas-$drivername", eg sas-mpt, but 84 * we strncat the driver name later once we've parsed the 85 * args passed in from the shell. 86 */ 87 #define SASPROP "sas-" 88 89 90 typedef enum { 91 CLIENT_TYPE_UNKNOWN, 92 CLIENT_TYPE_PHCI, 93 CLIENT_TYPE_VHCI 94 } client_type_t; 95 96 struct devlink_cbarg { 97 char *devlink; 98 size_t len; 99 }; 100 101 static di_node_t devinfo_root = DI_NODE_NIL; 102 static di_devlink_handle_t devlink_hdl = NULL; 103 static int vhci_fd = -1; 104 static int patch_vfstab, cap_m_option, debug; 105 static int list_option, list_guid_mappings, list_controllernum = -1; 106 static char *mapdev = ""; 107 static char *map_vhciname = ""; 108 static char *stmsboot = "stmsboot"; 109 110 char *drvname = (char *)NULL; /* "fp" or "mpt" or ... */ 111 /* "node-wwn" if drvname=fp, or "sas-$drivername" otherwise */ 112 char *drvprop = (char *)NULL; 113 static int parent = 0; /* for "-n" usage */ 114 115 static int make_temp(char *, char *, char *, size_t); 116 static void commit_change(char *, char *, char *, int); 117 static int map_devname(char *, char *, size_t, int); 118 static int update_vfstab(char *, char *); 119 static int list_mappings(int, int); 120 static int canopen(char *); 121 static client_type_t client_by_props(char *path); 122 static void list_nodes(char *drivername); 123 124 static void logerr(char *, ...); 125 static void logdmsg(char *, ...); 126 static void *s_malloc(const size_t); 127 static char *s_strdup(const char *); 128 static void s_strlcpy(char *, const char *, size_t); 129 static int map_openable_vhciname(char *, char *, size_t); 130 /* 131 * Using an exit function not marked __NORETURN causes a warning with gcc. 132 * To suppress the warning, use __NORETURN attribute. 133 */ 134 static void clean_exit(int)__NORETURN; 135 136 /* 137 * Print usage and exit. 138 */ 139 static void 140 usage(char *argv0) 141 { 142 char *progname; 143 144 progname = strrchr(argv0, '/'); 145 if (progname != NULL) 146 progname++; 147 else 148 progname = argv0; 149 150 /* 151 * -u update /etc/vfstab 152 * -m devname 153 * if devname is phci based name and not open-able, map it to 154 * vhci based /devices name. 155 * if devname is vhci based name and not open-able, map it to 156 * phci based /devices name. 157 * -M devname 158 * same as -m except that /dev link is printed instead of 159 * /devices name. 160 * -l controller 161 * list non-STMS to STMS device name mappings for the specific 162 * controller 163 * -L list non-STMS to STMS device name mappings for all controllers 164 * -p devname 165 * if devname is vhci based name and open-able, get the first 166 * onlined phci based name without /devices prefix. 167 * Used in stmsboot to update the phci based bootpath. 168 * -D drvname 169 * if supplied, indicates that we're going to operate on 170 * devices attached to this driver 171 * -n 172 * if supplied, returns name of the node containing "fp" or 173 * "sas-$driver", appends "sd@" or "ssd@" or "disk@". Can only 174 * be used if -D drv is specified as well 175 */ 176 (void) fprintf(stderr, gettext("usage: %s -u | -m devname | " 177 "-M devname | -l controller | -L | \n" 178 "\t\t-p devname | -D { fp | mpt } | -n\n"), progname); 179 exit(2); 180 } 181 182 /* 183 * Parse command line arguments. 184 */ 185 static void 186 parse_args(int argc, char *argv[]) 187 { 188 char opt; 189 int n = 0; 190 191 if (argc == 1) { 192 usage(argv[0]); 193 /*NOTREACHED*/ 194 } 195 196 while ((opt = getopt(argc, argv, "udm:M:Ll:gp:D:n")) != EOF) { 197 switch (opt) { 198 case 'u': 199 patch_vfstab = 1; 200 n++; 201 break; 202 203 case 'd': 204 debug = 1; 205 break; 206 207 case 'm': 208 mapdev = s_strdup(optarg); 209 n++; 210 break; 211 212 case 'M': 213 mapdev = s_strdup(optarg); 214 cap_m_option = 1; 215 n++; 216 break; 217 218 case 'L': 219 list_option = 1; 220 n++; 221 break; 222 223 case 'l': 224 list_option = 1; 225 list_controllernum = (int)atol(optarg); 226 if (list_controllernum < 0) { 227 logerr(gettext("controller number %d is " 228 "invalid\n"), list_controllernum); 229 clean_exit(1); 230 } 231 n++; 232 break; 233 234 case 'g': 235 /* 236 * private option to display non-STMS device name 237 * to GUID mappings. 238 */ 239 list_guid_mappings = 1; 240 break; 241 242 case 'p': 243 /* 244 * map openable vhci based name to phci base name 245 */ 246 map_vhciname = s_strdup(optarg); 247 n++; 248 break; 249 250 case 'D': 251 /* 252 * Grab the driver name we need to look for. Each 253 * time we add support for a new SAS or FC driver 254 * to this utility, make sure that its driver name 255 * is checked here. 256 */ 257 drvname = s_malloc(sizeof (optarg) + 1); 258 drvname = s_strdup(optarg); 259 if (strcmp(drvname, "fp") == 0) { 260 drvprop = s_malloc(sizeof (NODE_WWN_PROP)); 261 (void) snprintf(drvprop, sizeof (NODE_WWN_PROP), 262 NODE_WWN_PROP); 263 } else if (strcmp(drvname, "mpt") == 0) { 264 drvprop = s_malloc(sizeof (SASPROP) + 265 sizeof (drvname) + 1); 266 (void) snprintf(drvprop, sizeof (SASPROP) + 267 sizeof (drvname), "%s%s", 268 SASPROP, drvname); 269 } else { 270 logerr(gettext("Driver %s is not supported\n"), 271 drvname); 272 clean_exit(1); 273 } 274 275 break; 276 277 case 'n': 278 ++parent; 279 n++; 280 break; 281 282 default: 283 usage(argv[0]); 284 /*NOTREACHED*/ 285 } 286 } 287 288 if (n != 1) { 289 usage(argv[0]); 290 /*NOTREACHED*/ 291 } 292 } 293 294 int 295 main(int argc, char *argv[]) 296 { 297 char save_vfstab[SYS_FILENAME_LEN], tmp_vfstab[SYS_FILENAME_LEN]; 298 int vfstab_updated; 299 300 (void) setlocale(LC_ALL, ""); 301 (void) textdomain(TEXT_DOMAIN); 302 303 if (getuid() != 0) { 304 logerr(gettext("must be super-user to run this program\n")); 305 clean_exit(1); 306 } 307 308 parse_args(argc, argv); 309 (void) umask(022); 310 311 /* 312 * NOTE: The mpxio boot-up script executes this program with the 313 * mapping (-m) option before the /usr is even mounted and when the 314 * root filesystem is still mounted read-only. 315 */ 316 if (*mapdev != '\0') { 317 char newname[MAXPATHLEN]; 318 319 if (map_devname(mapdev, newname, sizeof (newname), 320 cap_m_option) == 0) { 321 (void) printf("%s\n", newname); 322 clean_exit(0); 323 } 324 clean_exit(1); 325 } 326 if (*map_vhciname != '\0') { 327 char newname[MAXPATHLEN]; 328 329 if (map_openable_vhciname(map_vhciname, newname, 330 sizeof (newname)) == 0) { 331 (void) printf("%s\n", newname); 332 clean_exit(0); 333 } 334 clean_exit(1); 335 } 336 337 if (list_option || list_guid_mappings) { 338 if (list_mappings(list_controllernum, list_guid_mappings) == 0) 339 clean_exit(0); 340 clean_exit(1); 341 } 342 343 if (parent > 0) { 344 if (strcmp(drvname, "") == 0) { 345 usage(argv[0]); 346 clean_exit(1); 347 } else { 348 list_nodes(drvname); 349 clean_exit(0); 350 } 351 } 352 353 /* create a directory where a copy of the system files are saved */ 354 if (patch_vfstab) { 355 if (mkdirp(SAVE_DIR, 0755) != 0 && errno != EEXIST) { 356 logerr(gettext("mkdirp: failed to create %1$s: %2$s\n"), 357 SAVE_DIR, strerror(errno)); 358 clean_exit(1); 359 } 360 361 if (make_temp(VFSTAB, save_vfstab, tmp_vfstab, 362 SYS_FILENAME_LEN) != 0) 363 clean_exit(1); 364 365 /* build new vfstab without modifying the existing one */ 366 if ((vfstab_updated = update_vfstab(VFSTAB, tmp_vfstab)) 367 == -1) { 368 logerr(gettext("failed to update %s\n"), VFSTAB); 369 clean_exit(1); 370 } 371 372 commit_change(VFSTAB, save_vfstab, tmp_vfstab, vfstab_updated); 373 } 374 375 clean_exit(0); 376 /*NOTREACHED*/ 377 } 378 379 /* 380 * Make saved and temporary filenames in SAVE_DIR. 381 * 382 * ex: if the filename is /etc/vfstab then the save_filename and tmp_filename 383 * would be SAVE_DIR/vfstab and SAVE_DIR/vfstab.tmp respectively. 384 * 385 * Returns 0 on success, -1 on failure. 386 */ 387 static int 388 make_temp(char *filename, char *save_filename, char *tmp_filename, size_t len) 389 { 390 char *ptr; 391 392 if ((ptr = strrchr(filename, '/')) == NULL) { 393 logdmsg("invalid file %s\n", filename); 394 return (-1); 395 } 396 (void) snprintf(save_filename, len, "%s%s", SAVE_DIR, ptr); 397 (void) snprintf(tmp_filename, len, "%s%s.tmp", SAVE_DIR, ptr); 398 logdmsg("make_temp: %s: save = %s, temp = %s\n", filename, 399 save_filename, tmp_filename); 400 return (0); 401 } 402 403 /* 404 * Commit the changes made to the system file 405 */ 406 static void 407 commit_change(char *filename, char *save_filename, char *tmp_filename, 408 int updated) 409 { 410 int x; 411 412 if (updated) { 413 /* save the original */ 414 if ((x = rename(filename, save_filename)) != 0) { 415 logerr(gettext("rename %1$s to %2$s failed: %3$s\n"), 416 filename, save_filename, strerror(errno)); 417 } 418 419 /* now rename the new file to the actual file */ 420 if (rename(tmp_filename, filename) != 0) { 421 logerr(gettext("rename %1$s to %2$s failed: %3$s\n"), 422 tmp_filename, filename, strerror(errno)); 423 424 /* restore the original */ 425 if (x == 0 && rename(save_filename, filename) != 0) { 426 logerr( 427 gettext("rename %1$s to %2$s failed: %3$s\n" 428 "%4$s is a copy of the original %5$s file" 429 "\n"), 430 save_filename, filename, strerror(errno), 431 save_filename, filename); 432 } 433 } else 434 (void) printf(gettext("%1$s: %2$s has been updated.\n"), 435 stmsboot, filename); 436 } else { 437 /* remove the temp file */ 438 (void) unlink(tmp_filename); 439 (void) printf(gettext("%1$s: %2$s was not modified as no " 440 "changes were needed.\n"), stmsboot, filename); 441 } 442 } 443 444 /* 445 * Get the GUID of the device. 446 * 447 * physpath /devices name without the /devices prefix and minor name 448 * component. 449 * guid caller supplied buffer where the GUID will be placed on return 450 * guid_len length of the caller supplied guid buffer. 451 * no_delay_flag if set open the device with O_NDELAY 452 * node di_node corresponding to physpath if already available, 453 * otherwise pass DI_NODE_NIL. 454 * 455 * Returns 0 on success, -1 on failure. 456 */ 457 static int 458 get_guid(char *physpath, char *guid, int guid_len, int no_delay_flag, 459 di_node_t node) 460 { 461 int fd; 462 ddi_devid_t devid; 463 int rv = -1; 464 char *i_guid = NULL; 465 char physpath_raw[MAXPATHLEN]; 466 uchar_t *wwnp; 467 int i, n, snapshot_taken = 0; 468 469 logdmsg("get_guid: physpath = %s\n", physpath); 470 471 #ifdef sparc 472 (void) snprintf(physpath_raw, MAXPATHLEN, 473 "/devices%s:a,raw", physpath); 474 #else 475 (void) snprintf(physpath_raw, MAXPATHLEN, 476 "/devices%s:c,raw", physpath); 477 #endif 478 479 *guid = '\0'; 480 481 if (no_delay_flag) 482 no_delay_flag = O_NDELAY; 483 484 /* 485 * Open the raw device 486 * Without the O_DELAY flag, the open will fail on standby paths of 487 * T3 if its mp_support mode is "mpxio". 488 */ 489 if ((fd = open(physpath_raw, O_RDONLY | no_delay_flag)) == -1) { 490 logdmsg("get_guid: failed to open %s: %s\n", physpath_raw, 491 strerror(errno)); 492 return (-1); 493 } 494 495 if (devid_get(fd, &devid) == 0) { 496 i_guid = devid_to_guid(devid); 497 devid_free(devid); 498 499 if (i_guid != NULL) { 500 s_strlcpy(guid, i_guid, guid_len); 501 devid_free_guid(i_guid); 502 rv = 0; 503 goto out; 504 } else { 505 logdmsg("get_guid: devid_to_guid() failed\n"); 506 logdmsg("Unable to get a GUID for device " 507 "%s\n", physpath_raw); 508 } 509 510 } else 511 logdmsg("get_guid: devid_get() failed: %s\n", strerror(errno)); 512 513 /* 514 * Unless we're looking at an fp-attached device, we now 515 * fallback to node name as the guid as this is what the 516 * fcp driver does. A sas-attached device will have the 517 * client-guid property set. 518 */ 519 if (node == DI_NODE_NIL) { 520 if ((node = di_init(physpath, DINFOCPYALL | DINFOFORCE)) 521 == DI_NODE_NIL) { 522 logdmsg("get_guid: di_init on %s failed: %s\n", 523 physpath, strerror(errno)); 524 goto out; 525 } 526 snapshot_taken = 1; 527 } 528 529 /* non-fp fallout */ 530 if (strstr(physpath, "fp") == (char *)NULL) { 531 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 532 "client-guid", &guid) < 0) { 533 logdmsg("get_guid: non-fp-attached device, " 534 "bailing out\n"); 535 goto out; 536 } 537 } 538 539 if ((n = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, NODE_WWN_PROP, 540 &wwnp)) == -1) { 541 logdmsg("get_guid: di_prop_lookup_bytes() failed to lookup " 542 "%s: %s\n", NODE_WWN_PROP, strerror(errno)); 543 goto out; 544 } 545 546 if (guid_len >= ((n * 2) + 1)) { 547 for (i = 0; i < n; i++) { 548 (void) sprintf(guid + (i * 2), "%02x", (uint_t)(*wwnp)); 549 wwnp++; 550 } 551 rv = 0; 552 } else 553 logerr(gettext("insufficient buffer size: need %1$d " 554 "bytes, passed %2$d bytes\n"), (n * 2) + 1, guid_len); 555 556 out: 557 if (snapshot_taken) 558 di_fini(node); 559 560 (void) close(fd); 561 logdmsg("get_guid: GUID = %s\n", guid); 562 return (rv); 563 } 564 565 /* 566 * Given client_name return whether it is a phci or vhci based name. 567 * client_name is /devices name of a client without the /devices prefix. 568 * 569 * client_name Return value 570 * on sparc: 571 * .../fp@xxx/ssd@yyy CLIENT_TYPE_PHCI (fc) 572 * .../LSILogic,sas@xxx/sd@yyy CLIENT_TYPE_PHCI (sas) 573 * .../scsi_vhci/ssd@yyy CLIENT_TYPE_VHCI (fc) 574 * .../scsi_vhci/disk@yyy CLIENT_TYPE_VHCI (sas) 575 * other CLIENT_TYPE_UNKNOWN 576 * on x86: 577 * .../fp@xxx/disk@yyy CLIENT_TYPE_PHCI (fc) 578 * .../pci1000,????@xxx/sd@yyy CLIENT_TYPE_PHCI (sas) 579 * .../scsi_vhci/disk@yyy CLIENT_TYPE_VHCI 580 * other CLIENT_TYPE_UNKNOWN 581 */ 582 static client_type_t 583 client_name_type(char *client_name) 584 { 585 client_type_t client_type = CLIENT_TYPE_UNKNOWN; 586 char *p1; 587 char *client_path; 588 589 client_path = s_strdup(client_name); 590 logdmsg("client_name_type: client is %s\n", client_path); 591 592 if (*client_name != '/') 593 return (CLIENT_TYPE_UNKNOWN); 594 595 if ((p1 = strrchr(client_name, '/')) == NULL || 596 ((strncmp(p1, "/ssd@", sizeof ("/ssd@") - 1) != 0) && 597 (strncmp(p1, "/sd@", sizeof ("/sd@") - 1) != 0) && 598 (strncmp(p1, "/disk@", sizeof ("/disk@") - 1) != 0))) { 599 logdmsg("client_name_type: p1 = %s\n", p1); 600 return (CLIENT_TYPE_UNKNOWN); 601 } 602 603 *p1 = '\0'; 604 605 /* 606 * Courtesy of the if (..) block above, we know that any 607 * device path we have now is either PHCI or VHCI 608 */ 609 client_type = client_by_props(client_path); 610 611 logdmsg("client_name_type: client_type = %d\n", client_type); 612 613 *p1 = '/'; 614 return (client_type); 615 } 616 617 /* 618 * client_by_props() is called to determine what the client type 619 * is, based on properties in the device tree: 620 * 621 * drivername property type 622 * ------------------------------------- 623 * fp node-wwn CLIENT_TYPE_PHCI 624 * mpt sas-mpt CLIENT_TYPE_PHCI 625 * mpt client-guid CLIENT_TYPE_PHCI (corner case) 626 * 627 * Normally, the "client-guid" property only shows up for a node 628 * if we've enumerated that node under scsi_vhci. During testing 629 * of this function, one particular corner case was found which 630 * requires an exception handler. 631 */ 632 633 static client_type_t 634 client_by_props(char *path) { 635 636 di_node_t clientnode = DI_NODE_NIL; 637 di_node_t parentnode = DI_NODE_NIL; 638 unsigned int rval = CLIENT_TYPE_UNKNOWN; 639 uchar_t *byteprop[32]; 640 char *charprop = NULL; 641 char *physpath; 642 char *parentpath; 643 644 physpath = s_malloc(MAXPATHLEN); 645 bzero(physpath, MAXPATHLEN); 646 647 physpath = s_strdup(path); 648 649 logdmsg("client_by_props: physpath = (%s)\n", physpath); 650 651 /* easy short-circuits */ 652 if (strstr(physpath, "scsi_vhci") != (char *)NULL) { 653 logdmsg("client_by_props: found " 654 "'scsi_vhci' on path (%s)\n", physpath); 655 rval = CLIENT_TYPE_VHCI; 656 goto out; 657 } else if ((strstr(physpath, "ide") != (char *)NULL) || 658 (strstr(physpath, "storage") != (char *)NULL)) { 659 logdmsg("client_by_props: ignoring this device\n"); 660 goto out; 661 } 662 663 parentpath = s_malloc(MAXPATHLEN); 664 bzero(parentpath, MAXPATHLEN); 665 666 (void) strncpy(parentpath, physpath, strlen(physpath) - 667 strlen(strrchr(physpath, '/'))); 668 669 if ((parentnode = di_init(parentpath, DINFOCPYALL | 670 DINFOFORCE)) == DI_NODE_NIL) { 671 logdmsg("client_by_props: unable to di_init(%s)\n", 672 parentpath); 673 goto out; 674 } 675 676 if (strstr(physpath, "fp") != (char *)NULL) { 677 if (drvprop == (char *)NULL) { 678 drvprop = s_malloc(strlen(NODE_WWN_PROP) + 1); 679 } 680 logdmsg("NODE_WWN_PROP\n"); 681 (void) snprintf(drvprop, strlen(NODE_WWN_PROP) + 1, 682 NODE_WWN_PROP); 683 } else { 684 if (drvname == (char *)NULL) { 685 drvname = di_driver_name(parentnode); 686 logdmsg("client_by_props: drvname = %s\n", drvname); 687 } 688 689 if (drvprop == (char *)NULL) { 690 drvprop = s_malloc(sizeof (SASPROP) + 691 sizeof (drvname) + 1); 692 } 693 (void) snprintf(drvprop, sizeof (SASPROP) + 694 sizeof (drvname), "%s%s", SASPROP, drvname); 695 696 logdmsg("parentpath: %s\nphyspath: %s\n" 697 "length %d, strrchr: %d\n", 698 parentpath, physpath, strlen(physpath), 699 strlen(strrchr(physpath, '/'))); 700 } 701 702 logdmsg("client_by_props: searching for property '%s'\n", drvprop); 703 704 if ((clientnode = di_init(physpath, DINFOCPYALL | DINFOFORCE)) == 705 DI_NODE_NIL) { 706 logdmsg("client_by_props: unable to di_init(%s)\n", 707 physpath); 708 709 /* 710 * On x86/x64 systems, we won't be able to di_init() the 711 * node we want in the device tree, however the parent 712 * node will still have 'mpxio-disable' set, so we can 713 * check for that property and make our decision on type 714 */ 715 716 if (di_prop_lookup_strings(DDI_DEV_T_ANY, parentnode, 717 "mpxio-disable", &charprop) > -1) { 718 rval = CLIENT_TYPE_PHCI; 719 di_fini(parentnode); 720 logdmsg("client_by_props: device %s is PHCI\n", 721 physpath); 722 } 723 goto out; 724 } 725 726 if (di_prop_lookup_bytes(DDI_DEV_T_ANY, 727 clientnode, drvprop, byteprop) > -1) { 728 logdmsg("client_by_props: found prop %s on " 729 "path %s\n", drvprop, physpath); 730 rval = CLIENT_TYPE_PHCI; 731 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, 732 clientnode, "client-guid", &charprop) > -1) { 733 /* 734 * A corner case was seen during testing where 735 * scsi_vhci was loaded, but not all applicable 736 * devices were enumerated under it. That left 737 * the phci mapping along with the "client-guid" 738 * property. 739 */ 740 logdmsg("client_by_props: weird... \n"); 741 rval = CLIENT_TYPE_PHCI; 742 } else { 743 logdmsg("client_by_props: unable to find " 744 "property 'client-guid', 'mpxio-disable' " 745 "or '%s' anywhere on path (%s)\n", 746 drvprop, physpath); 747 logdmsg("client_by_props: this node is unknown\n"); 748 } 749 750 di_fini(parentnode); 751 di_fini(clientnode); 752 out: 753 free(physpath); 754 return (rval); 755 } 756 757 758 /* 759 * Map phci based client name to vhci based client name. 760 * 761 * phci_name 762 * phci based client /devices name without the /devices prefix and 763 * minor name component. 764 * ex: 765 * 766 * (FC) 767 * for sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 768 * for x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 769 * 770 * (SAS) 771 * for sparc: /pci@0,2/LSILogic,sas@1/disk@6,0 772 * for x86: /pci1000,3060@3/sd@0,0 773 * 774 * vhci_name 775 * Caller supplied buffer where vhci /devices name will be placed on 776 * return (without the /devices prefix and minor name component). 777 * ex: 778 * 779 * (FC) 780 * for sparc: /scsi_vhci/ssd@g2000002037cd9f72 781 * for x86: /scsi_vhci/disk@g2000002037cd9f72 782 * 783 * (SAS) 784 * both: /scsi_vhci/disk@g600a0b8000254d3e00000284453ed8ac 785 * 786 * vhci_name_len 787 * Length of the caller supplied vhci_name buffer. 788 * 789 * Returns 0 on success, -1 on failure. 790 */ 791 static int 792 phci_to_vhci(char *phci_name, char *vhci_name, size_t vhci_name_len) 793 { 794 sv_iocdata_t ioc; 795 char *slash, *at; 796 char vhci_name_buf[MAXPATHLEN]; 797 char phci_name_buf[MAXPATHLEN]; 798 char addr_buf[MAXNAMELEN]; 799 800 logdmsg("phci_to_vhci: client = %s\n", phci_name); 801 802 s_strlcpy(phci_name_buf, phci_name, MAXPATHLEN); 803 804 if (client_name_type(phci_name_buf) != CLIENT_TYPE_PHCI || 805 (slash = strrchr(phci_name_buf, '/')) == NULL || 806 ((strncmp(slash, "/ssd@", sizeof ("/ssd@") - 1) != 0) && 807 (strncmp(slash, "/sd@", sizeof ("/sd@") - 1) != 0) && 808 (strncmp(slash, "/disk@", sizeof ("/disk@") - 1) != 0))) { 809 logdmsg("phci_to_vhci: %s is not of CLIENT_TYPE_PHCI\n", 810 phci_name); 811 return (-1); 812 } 813 814 if (vhci_fd < 0) { 815 if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) 816 return (-1); 817 } 818 819 *slash = '\0'; 820 821 at = strchr(slash + 1, '@'); 822 s_strlcpy(addr_buf, at + 1, MAXNAMELEN); 823 824 bzero(&ioc, sizeof (sv_iocdata_t)); 825 ioc.client = vhci_name_buf; 826 ioc.phci = phci_name_buf; 827 ioc.addr = addr_buf; 828 829 if (ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_NAME, &ioc) != 0) { 830 logdmsg("SCSI_VHCI_GET_CLIENT_NAME on %s " 831 "failed: %s\n", phci_name, strerror(errno)); 832 return (-1); 833 } 834 835 s_strlcpy(vhci_name, vhci_name_buf, vhci_name_len); 836 logdmsg("phci_to_vhci: %s maps to %s\n", phci_name, vhci_name); 837 return (0); 838 } 839 840 /* 841 * Map vhci based client name to phci based client name. 842 * If the client has multiple paths, only one of the paths with which client 843 * can be accessed is returned. This function does not use SCSI_VHCI ioctls 844 * as it is called on mpxio disabled paths. 845 * 846 * vhci_name 847 * vhci based client /devices name without the /devices prefix and 848 * minor name component. 849 * ex: 850 * sparc: /scsi_vhci/ssd@g2000002037cd9f72 851 * x86: /scsi_vhci/disk@g2000002037cd9f72 852 * 853 * phci_name 854 * Caller supplied buffer where phci /devices name will be placed on 855 * return (without the /devices prefix and minor name component). 856 * ex: 857 * sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 858 * x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 859 * 860 * phci_name_len 861 * Length of the caller supplied phci_name buffer. 862 * 863 * Returns 0 on success, -1 on failure. 864 */ 865 static int 866 vhci_to_phci(char *vhci_name, char *phci_name, size_t phci_name_len) 867 { 868 di_node_t node = DI_NODE_NIL; 869 char *vhci_guid, *devfspath; 870 char phci_guid[MAXPATHLEN]; 871 char *node_name; 872 873 logdmsg("vhci_to_phci: client = %s\n", vhci_name); 874 875 if (client_name_type(vhci_name) != CLIENT_TYPE_VHCI) { 876 logdmsg("vhci_to_phci: %s is not of CLIENT_TYPE_VHCI\n", 877 vhci_name); 878 return (-1); 879 } 880 881 882 if ((vhci_guid = strrchr(vhci_name, '@')) == NULL || 883 *(++vhci_guid) != 'g') { 884 logerr(gettext("couldn't get guid from %s\n"), vhci_name); 885 return (-1); 886 } 887 888 /* point to guid */ 889 ++vhci_guid; 890 891 /* 892 * Get devinfo snapshot and walk all ssd nodes whose parent is fp. 893 * For each node get the guid and match it with vhci_guid. 894 */ 895 if (devinfo_root == DI_NODE_NIL) { 896 logdmsg("vhci_to_phci: taking devinfo snapshot\n"); 897 if ((devinfo_root = di_init("/", DINFOCPYALL | DINFOFORCE)) 898 == DI_NODE_NIL) { 899 logerr(gettext("di_init failed: %s\n"), 900 strerror(errno)); 901 return (-1); 902 } 903 logdmsg("vhci_to_phci: done taking devinfo snapshot\n"); 904 } 905 906 907 /* 908 * When we finally get a unified "sd" driver for all 909 * architectures that Solaris runs on, we can remove this 910 * first loop around for "ssd" 911 */ 912 for (node = di_drv_first_node("ssd", devinfo_root); 913 node != DI_NODE_NIL; node = di_drv_next_node(node)) { 914 915 if ((node_name = di_node_name(node)) == NULL) 916 continue; 917 918 if ((strcmp(node_name, "disk") != 0) && 919 (strcmp(node_name, "sd") != 0) && 920 (strcmp(node_name, "ssd") != 0)) 921 continue; 922 923 if (di_parent_node(node) == DI_NODE_NIL) 924 continue; 925 926 if ((devfspath = di_devfs_path(node)) == NULL) 927 continue; 928 929 /* 930 * Don't set no_delay_flag to have get_guid() fail on 931 * standby paths of T3. So we'll find the preferred paths. 932 */ 933 if (get_guid(devfspath, phci_guid, 934 sizeof (phci_guid), 0, node) != 0) 935 continue; 936 937 if (strcmp(phci_guid, vhci_guid) == 0) { 938 s_strlcpy(phci_name, devfspath, phci_name_len); 939 di_devfs_path_free(devfspath); 940 logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name, 941 phci_name); 942 return (0); 943 } 944 945 di_devfs_path_free(devfspath); 946 } 947 948 for (node = di_drv_first_node("sd", devinfo_root); 949 node != DI_NODE_NIL; node = di_drv_next_node(node)) { 950 951 if ((node_name = di_node_name(node)) == NULL) 952 continue; 953 954 if ((strcmp(node_name, "disk") != 0) && 955 (strcmp(node_name, "sd") != 0) && 956 (strcmp(node_name, "ssd") != 0)) 957 continue; 958 959 if (di_parent_node(node) == DI_NODE_NIL) 960 continue; 961 962 if ((devfspath = di_devfs_path(node)) == NULL) 963 continue; 964 965 /* 966 * Don't set no_delay_flag to have get_guid() fail on 967 * standby paths of T3. So we'll find the preferred paths. 968 */ 969 if (get_guid(devfspath, phci_guid, 970 sizeof (phci_guid), 0, node) != 0) 971 continue; 972 973 if (strcmp(phci_guid, vhci_guid) == 0) { 974 s_strlcpy(phci_name, devfspath, phci_name_len); 975 di_devfs_path_free(devfspath); 976 logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name, 977 phci_name); 978 return (0); 979 } 980 981 di_devfs_path_free(devfspath); 982 } 983 984 logdmsg("vhci_to_phci: couldn't get phci name for %s\n", vhci_name); 985 return (-1); 986 } 987 988 /* 989 * Map vhci based client name to phci based client name. 990 * If the client has multiple paths, only one of the paths with which client 991 * can be accessed is returned. 992 * This function uses SCSI_VHCI ioctls to get the phci paths 993 * 994 * vhci_name 995 * vhci based client /devices name without the /devices prefix and 996 * minor name component. 997 * ex: 998 * sparc: /scsi_vhci/ssd@g2000002037cd9f72 999 * x86: /scsi_vhci/disk@g2000002037cd9f72 1000 * 1001 * phci_name 1002 * Caller supplied buffer where phci /devices name will be placed on 1003 * return (without the /devices prefix and minor name component). 1004 * ex: 1005 * sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 1006 * x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 1007 * 1008 * phci_name_len 1009 * Length of the caller supplied phci_name buffer. 1010 * 1011 * Returns 0 on success, -1 on failure. 1012 */ 1013 1014 static int 1015 vhci_to_phci_by_ioctl(char *vhci_name, char *phci_name, size_t phci_name_len) 1016 { 1017 sv_iocdata_t ioc; 1018 uint_t npaths; 1019 char *node_name, *at; 1020 char vhci_name_buf[MAXPATHLEN]; 1021 int ret; 1022 sv_path_info_t *pi; 1023 1024 logdmsg("vhci_to_phci_by_ioctl: client = %s\n", vhci_name); 1025 1026 if (vhci_fd < 0) { 1027 if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) 1028 return (-1); 1029 } 1030 1031 (void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN); 1032 1033 /* first get the number paths */ 1034 bzero(&ioc, sizeof (sv_iocdata_t)); 1035 ioc.client = vhci_name_buf; 1036 ioc.ret_elem = &npaths; 1037 if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, 1038 &ioc)) != 0 || npaths == 0) { 1039 logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s " 1040 "failed: %s\n", vhci_name, 1041 ret?strerror(errno):"got 0 paths"); 1042 return (-1); 1043 } 1044 1045 /* now allocate memory for the path information and get all paths */ 1046 bzero(&ioc, sizeof (sv_iocdata_t)); 1047 ioc.client = vhci_name_buf; 1048 ioc.buf_elem = npaths; 1049 ioc.ret_elem = &npaths; 1050 if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths, 1051 sizeof (sv_path_info_t))) == NULL) 1052 return (-1); 1053 if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, 1054 &ioc)) != 0 || npaths == 0) { 1055 logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s " 1056 "failed: %s\n", vhci_name, 1057 ret?strerror(errno):"got 0 paths"); 1058 goto out; 1059 } 1060 1061 if (ioc.buf_elem < npaths) 1062 npaths = ioc.buf_elem; 1063 if ((node_name = strrchr(vhci_name_buf, '/')) == NULL || 1064 (at = strchr(node_name, '@')) == NULL) 1065 goto out; 1066 1067 node_name++; 1068 *at = '\0'; 1069 1070 logdmsg("vhci_to_phci_by_ioctl: node_name is %s\n", node_name); 1071 #ifndef sparc 1072 /* 1073 * We need to use a libdevinfo call to get this info 1074 * in an architecturally-neutral fashion. Phase-II for sure! 1075 */ 1076 node_name = "sd"; 1077 #endif 1078 1079 /* 1080 * return the first online paths as non-online paths may 1081 * not be accessible in the target environment. 1082 */ 1083 pi = (sv_path_info_t *)ioc.ret_buf; 1084 while (npaths--) { 1085 if (MDI_PATHINFO_STATE_ONLINE == pi->ret_state) { 1086 (void) snprintf(phci_name, phci_name_len, "%s/%s@%s", 1087 pi->device.ret_phci, node_name, 1088 pi->ret_addr); 1089 logdmsg("vhci_to_phci_by_ioctl: %s maps to %s\n", 1090 vhci_name, phci_name); 1091 free(ioc.ret_buf); 1092 return (0); 1093 } 1094 pi++; 1095 } 1096 1097 out: 1098 logdmsg("vhci_to_phci_by_ioctl: couldn't get phci name for %s\n", 1099 vhci_name); 1100 free(ioc.ret_buf); 1101 return (-1); 1102 1103 } 1104 1105 /* 1106 * Map physname from phci name space to vhci name space or vice-versa 1107 * 1108 * physname 1109 * phci or vhci based client /devices name without the /devices prefix and 1110 * minor name component. 1111 * 1112 * new_physname 1113 * Caller supplied buffer where the mapped physical name is stored on 1114 * return (without the /devices prefix and minor name component). 1115 * 1116 * len 1117 * Length of the caller supplied new_physname buffer. 1118 * 1119 * Returns 0 on success, -1 on failure. 1120 */ 1121 static int 1122 map_physname(char *physname, char *new_physname, size_t len) 1123 { 1124 int type; 1125 int rv; 1126 1127 type = client_name_type(physname); 1128 logdmsg("map_physname: type (%d) physname = %s\n", 1129 type, physname); 1130 1131 if (type == CLIENT_TYPE_VHCI) 1132 rv = vhci_to_phci(physname, new_physname, len); 1133 else if (type == CLIENT_TYPE_PHCI) 1134 rv = phci_to_vhci(physname, new_physname, len); 1135 else 1136 rv = -1; 1137 1138 logdmsg("map_physname: returning %d\n", rv); 1139 return (rv); 1140 } 1141 1142 /* 1143 * Given a phci or vhci devname which is either a /dev link or /devices name 1144 * get the corresponding physical node path (without the /devices prefix) 1145 * and minor name. 1146 * 1147 * Returns 0 on success, -1 on failure. 1148 */ 1149 static int 1150 get_physname_minor(char *devname, char *physname, int physname_len, 1151 char *minorname, int minorname_len) 1152 { 1153 int linksize; 1154 char buf[MAXPATHLEN]; 1155 char *p, *m; 1156 1157 if (strncmp(devname, DEV_DSK, sizeof (DEV_DSK) - 1) == 0 || 1158 strncmp(devname, DEV_RDSK, sizeof (DEV_RDSK) - 1) == 0) { 1159 if ((linksize = readlink(devname, buf, MAXPATHLEN)) 1160 > 0 && linksize <= (MAXPATHLEN - 1)) { 1161 buf[linksize] = '\0'; 1162 } else 1163 return (-1); 1164 } else 1165 s_strlcpy(buf, devname, MAXPATHLEN); 1166 1167 if ((p = strstr(buf, SLASH_DEVICES)) == NULL) 1168 return (-1); 1169 1170 /* point to '/' after /devices */ 1171 p += sizeof (SLASH_DEVICES) - 2; 1172 1173 if ((m = strrchr(p, ':')) == NULL) { 1174 logdmsg("get_physname_minor: no minor name component in %s\n", 1175 buf); 1176 return (-1); 1177 } 1178 1179 *m = '\0'; 1180 m++; 1181 1182 if (client_name_type(p) == CLIENT_TYPE_UNKNOWN) 1183 return (-1); 1184 1185 s_strlcpy(physname, p, physname_len); 1186 s_strlcpy(minorname, m, minorname_len); 1187 logdmsg("get_physname_minor: %s: physname = %s, minor = %s\n", 1188 devname, physname, minorname); 1189 return (0); 1190 } 1191 1192 static int 1193 devlink_callback(di_devlink_t devlink, void *argptr) 1194 { 1195 const char *link; 1196 struct devlink_cbarg *argp = argptr; 1197 1198 if ((link = di_devlink_path(devlink)) != NULL) { 1199 s_strlcpy(argp->devlink, link, argp->len); 1200 return (DI_WALK_TERMINATE); 1201 } 1202 1203 return (DI_WALK_CONTINUE); 1204 } 1205 1206 /* 1207 * Lookup the /dev link corresponding to physname and minorname. 1208 * 1209 * physname client /devices path without the /devices prefix and minor 1210 * name component. 1211 * minorname client minor name. 1212 * devlink caller supplied buffer where the /dev link is placed on return. 1213 * len caller supplied devlink buffer length 1214 * 1215 * Returns 0 on success, -1 on failure. 1216 */ 1217 static int 1218 lookup_devlink(char *physname, char *minorname, char *devlink, size_t len) 1219 { 1220 char buf[MAXPATHLEN]; 1221 struct devlink_cbarg arg; 1222 1223 if (devlink_hdl == NULL) { 1224 logdmsg("lookup_devlink: taking devlink snapshot\n"); 1225 if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) { 1226 logerr(gettext("di_devlink_init failed: %s\n"), 1227 strerror(errno)); 1228 clean_exit(1); 1229 } 1230 } 1231 1232 *devlink = '\0'; 1233 (void) snprintf(buf, MAXPATHLEN, "%s:%s", physname, minorname); 1234 arg.devlink = devlink; 1235 arg.len = len; 1236 if (di_devlink_walk(devlink_hdl, NULL, buf, DI_PRIMARY_LINK, &arg, 1237 devlink_callback) != 0) { 1238 logdmsg("lookup_devlink: di_devlink_walk on %s failed: %s\n", 1239 buf, strerror(errno)); 1240 return (-1); 1241 } 1242 1243 if (*devlink == '\0') { 1244 logdmsg("lookup_devlink: failed to lookup devlink for %s\n", 1245 buf); 1246 return (-1); 1247 } 1248 1249 logdmsg("lookup_devlink: /dev link for %s:%s = %s\n", physname, 1250 minorname, devlink); 1251 return (0); 1252 } 1253 1254 /* 1255 * open infile for reading and return its file pointer in *fp_in. 1256 * open outfile for writing and return its file pointer in *fp_out. 1257 * 1258 * Returns 0 on success, -1 on failure. 1259 */ 1260 static int 1261 open_in_out_files(char *infile, char *outfile, FILE **fp_in, FILE **fp_out) 1262 { 1263 FILE *fin = NULL; 1264 FILE *fout = NULL; 1265 struct stat sbuf; 1266 1267 if ((fin = fopen(infile, "r")) == NULL) { 1268 logerr(gettext("failed to fopen %1$s: %2$s\n"), 1269 infile, strerror(errno)); 1270 goto out; 1271 } 1272 1273 if (fstat(fileno(fin), &sbuf) != 0) { 1274 logerr(gettext("fstat failed on %1$s: %2$s\n"), 1275 infile, strerror(errno)); 1276 goto out; 1277 } 1278 1279 if ((fout = fopen(outfile, "w")) == NULL) { 1280 logerr(gettext("failed to fopen %1$s: %2$s\n"), 1281 outfile, strerror(errno)); 1282 goto out; 1283 } 1284 1285 if (fchmod(fileno(fout), (sbuf.st_mode & 0777)) != 0) { 1286 logerr(gettext("failed to fchmod %1$s to 0%2$o: %3$s\n"), 1287 outfile, sbuf.st_mode & 0777, strerror(errno)); 1288 goto out; 1289 } 1290 1291 if (fchown(fileno(fout), sbuf.st_uid, sbuf.st_gid) != 0) { 1292 logerr(gettext("failed to fchown %1$s to uid %2$d and " 1293 "gid %3$d: %4$s\n"), 1294 outfile, sbuf.st_uid, sbuf.st_gid, strerror(errno)); 1295 goto out; 1296 } 1297 1298 *fp_in = fin; 1299 *fp_out = fout; 1300 return (0); 1301 1302 out: 1303 if (fin != NULL) 1304 (void) fclose(fin); 1305 if (fout != NULL) 1306 (void) fclose(fout); 1307 return (-1); 1308 } 1309 1310 /* 1311 * If the devname is a phci based name and not open-able, map it to vhci 1312 * based name. If the devname is a vhci based name and not open-able, map it 1313 * to phci based name. 1314 * 1315 * devname either a /dev link or /devices name to client device 1316 * new_devname caller supplied buffer where the mapped device name is 1317 * placed on return. 1318 * len caller supplied new_devname buffer length 1319 * devlink_flag pass 1 if requesting the /dev link to the mapped device. 1320 * pass 0 if requesting the /devices name of the mapped device. 1321 * 1322 * Returns 0 on success, -1 on failure. 1323 */ 1324 static int 1325 map_devname(char *devname, char *new_devname, size_t len, int devlink_flag) 1326 { 1327 char physname[MAXPATHLEN]; 1328 char minor[MAXNAMELEN]; 1329 char new_physname[MAXPATHLEN]; 1330 1331 logdmsg("map_devname: checking devname %s\n", devname); 1332 if ((get_physname_minor(devname, physname, sizeof (physname), 1333 minor, sizeof (minor)) == 0) && 1334 (canopen(devname) == 0) && 1335 (map_physname(physname, new_physname, 1336 sizeof (new_physname)) == 0)) { 1337 1338 logdmsg("map_devname: now looking up devlink\n"); 1339 1340 if (devlink_flag) { 1341 if (lookup_devlink(new_physname, minor, new_devname, 1342 len) == 0) 1343 return (0); 1344 } else { 1345 (void) snprintf(new_devname, len, "/devices%s:%s", 1346 new_physname, minor); 1347 return (0); 1348 } 1349 } 1350 1351 logdmsg("map_devname: failed to find mapping for %s\n", devname); 1352 return (-1); 1353 } 1354 1355 /* 1356 * If the devname is a vhci based name and open-able, map it to phci 1357 * based name. 1358 * 1359 * devname either a /dev link or /devices name to client device 1360 * new_devname caller supplied buffer where the mapped device name without 1361 * /devices prefix is placed on return. 1362 * len caller supplied new_devname buffer length 1363 */ 1364 static int 1365 map_openable_vhciname(char *devname, char *new_devname, size_t len) 1366 { 1367 char physname[MAXPATHLEN]; 1368 char minor[MAXNAMELEN]; 1369 char new_physname[MAXPATHLEN]; 1370 1371 if (get_physname_minor(devname, physname, sizeof (physname), 1372 minor, sizeof (minor)) == 0 && 1373 canopen(devname) == 1 && 1374 client_name_type(physname) == CLIENT_TYPE_VHCI && 1375 vhci_to_phci_by_ioctl(physname, new_physname, 1376 sizeof (new_physname)) == 0) { 1377 (void) snprintf(new_devname, len, "%s:%s", 1378 new_physname, minor); 1379 return (0); 1380 } 1381 1382 return (-1); 1383 } 1384 /* 1385 * Make a new /etc/vfstab: 1386 * Read vfstab_in, convert the device name entries to appropriate vhci or phci 1387 * based names, and write to vfstab_out. Only device names whose physical 1388 * paths are either phci or vhci based names and not open-able are considered 1389 * for conversion. Open-able device name entries are not converted as it 1390 * means that the device is already accessible; hence no need to convert. 1391 * 1392 * Returns: 1393 * 0 successful but vfstab_out contents are the same as vfstab_in 1394 * 1 successful and vfstab_out changed from vfstab_in 1395 * -1 failed 1396 */ 1397 static int 1398 update_vfstab(char *vfstab_in, char *vfstab_out) 1399 { 1400 FILE *fp_in, *fp_out; 1401 char *buf, *tmpbuf; 1402 char *vfs_cache[2]; 1403 int idx = 0, count = 0; 1404 int rv = -1; 1405 int vfstab_updated = 0; 1406 int i; 1407 char cdev[MAXPATHLEN]; 1408 char bdev[MAXPATHLEN]; 1409 char mntpt[MAXPATHLEN]; 1410 char fstype[512]; 1411 char fsckpass[512]; 1412 char mntboot[512]; 1413 char mntopt[MAX_MNTOPT_STR]; 1414 char phys_bdev[MAXPATHLEN], phys_cdev[MAXPATHLEN]; 1415 char bdev_minor[MAXNAMELEN], cdev_minor[MAXNAMELEN]; 1416 char new_physname[MAXPATHLEN]; 1417 char new_bdevlink[MAXPATHLEN], new_cdevlink[MAXPATHLEN]; 1418 char fmt[80]; 1419 1420 if (open_in_out_files(vfstab_in, vfstab_out, &fp_in, &fp_out) != 0) 1421 return (-1); 1422 1423 /* 1424 * Read one line at time from vfstab_in. If no conversion is needed 1425 * for the line simply write the line to vfstab_out. If conversion is 1426 * needed, first write the existing line as a comment to vfstab_out 1427 * and then write the converted line. 1428 * 1429 * To avoid commented entries piling up in vfstab in case if the 1430 * user runs stmsboot multiple times to switch on and off from mpxio, 1431 * add the commented line only if not already there. To do this 1432 * cache the last two vfstab lines processed and add the commented 1433 * entry only if it is not found in the cache. We only need to cache 1434 * the last two lines because a device can have at most two names - 1435 * one mpxio and one non-mpxio name. Therefore for any device name 1436 * entry we at most add two comments - one with mpxio name and one 1437 * with non-mpxio name - no matter how many times stmsboot is run. 1438 */ 1439 buf = (char *)s_malloc(VFS_LINE_MAX); 1440 tmpbuf = (char *)s_malloc(VFS_LINE_MAX); 1441 vfs_cache[0] = (char *)s_malloc(VFS_LINE_MAX); 1442 vfs_cache[1] = (char *)s_malloc(VFS_LINE_MAX); 1443 1444 (void) snprintf(fmt, sizeof (fmt), 1445 "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1, 1446 sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1, 1447 sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1); 1448 1449 while (fgets(buf, VFS_LINE_MAX, fp_in) != NULL) { 1450 if (strlen(buf) == (VFS_LINE_MAX - 1) && 1451 buf[VFS_LINE_MAX-2] != '\n') { 1452 logerr(gettext("%1$s line size too long, " 1453 "exceeded %2$d: \"%3$s\"\n"), 1454 VFSTAB, VFS_LINE_MAX - 2, buf); 1455 goto out; 1456 } 1457 1458 /* LINTED - format specifier */ 1459 if ((sscanf(buf, fmt, bdev, cdev, mntpt, 1460 fstype, fsckpass, mntboot, mntopt) != 7) || 1461 (bdev[0] == '#') || 1462 (get_physname_minor(bdev, phys_bdev, sizeof (phys_bdev), 1463 bdev_minor, sizeof (bdev_minor)) != 0) || 1464 1465 (strcmp(fstype, "swap") != 0 && 1466 ((get_physname_minor(cdev, phys_cdev, sizeof (phys_cdev), 1467 cdev_minor, sizeof (cdev_minor)) != 0) || 1468 (strcmp(phys_bdev, phys_cdev) != 0))) || 1469 1470 canopen(bdev) || 1471 (map_physname(phys_bdev, new_physname, 1472 sizeof (new_physname)) != 0) || 1473 (lookup_devlink(new_physname, bdev_minor, new_bdevlink, 1474 sizeof (new_bdevlink)) != 0) || 1475 1476 (strcmp(fstype, "swap") != 0 && 1477 (lookup_devlink(new_physname, cdev_minor, new_cdevlink, 1478 sizeof (new_cdevlink)) != 0))) { 1479 1480 /* cache the last two entries */ 1481 (void) strlcpy(vfs_cache[idx], buf, VFS_LINE_MAX); 1482 idx = (idx == 0) ? 1 : 0; 1483 if (count < 2) 1484 count++; 1485 1486 if (fputs(buf, fp_out) == EOF) { 1487 logerr(gettext("fputs \"%1$s\" to %2$s " 1488 "failed: %3$s\n"), 1489 buf, vfstab_out, strerror(errno)); 1490 goto out; 1491 } 1492 1493 } else { 1494 /* 1495 * comment the entry in vfstab only if it is not 1496 * already in the cache. 1497 */ 1498 if (client_name_type(phys_bdev) == CLIENT_TYPE_VHCI) 1499 (void) snprintf(tmpbuf, VFS_LINE_MAX, 1500 "# mpxio: %s", buf); 1501 else 1502 (void) snprintf(tmpbuf, VFS_LINE_MAX, 1503 "# non-mpxio: %s", buf); 1504 1505 for (i = 0; i < count; i++) { 1506 if (strcmp(vfs_cache[i], tmpbuf) == 0) 1507 break; 1508 } 1509 1510 if (i == count) { 1511 if (fputs(tmpbuf, fp_out) == EOF) { 1512 logerr(gettext("fputs \"%1$s\" to %2$s " 1513 "failed: %3$s\n"), tmpbuf, 1514 vfstab_out, strerror(errno)); 1515 goto out; 1516 } 1517 } 1518 1519 count = 0; 1520 idx = 0; 1521 1522 if (fprintf(fp_out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 1523 new_bdevlink, 1524 (strcmp(fstype, "swap") != 0) ? new_cdevlink : cdev, 1525 mntpt, fstype, fsckpass, mntboot, mntopt) < 0) { 1526 logerr(gettext("fprintf failed to write to " 1527 "%1$s: %2$s\n"), 1528 vfstab_out, strerror(errno)); 1529 goto out; 1530 } 1531 vfstab_updated = 1; 1532 } 1533 } 1534 1535 rv = vfstab_updated; 1536 out: 1537 (void) fclose(fp_in); 1538 (void) fclose(fp_out); 1539 free(buf); 1540 free(tmpbuf); 1541 free(vfs_cache[0]); 1542 free(vfs_cache[1]); 1543 return (rv); 1544 } 1545 1546 /* 1547 * if guidmap is 0, list non-STMS to STMS device name mappings for the 1548 * specified controller. 1549 * if guidmap is 1, list non-STMS to GUID mappings for the specified controller. 1550 * If controller is -1 list mappings for all controllers. 1551 * 1552 * Returns 0 on success, -1 on failure. 1553 */ 1554 static int 1555 list_mappings(int controller, int guidmap) 1556 { 1557 int cnum, len, mapped; 1558 int header = 1; 1559 char *p1, *p2; 1560 DIR *dirp; 1561 struct dirent *direntry; 1562 char devname[MAXPATHLEN]; 1563 char physname[MAXPATHLEN]; 1564 char new_devname[MAXPATHLEN]; 1565 char new_physname[MAXPATHLEN]; 1566 char guid[MAXPATHLEN]; 1567 char minor[MAXNAMELEN]; 1568 1569 if ((dirp = opendir("/dev/rdsk")) == NULL) 1570 return (-1); 1571 1572 while ((direntry = readdir(dirp)) != NULL) { 1573 if (strcmp(direntry->d_name, ".") == 0 || 1574 strcmp(direntry->d_name, "..") == 0 || 1575 (len = strlen(direntry->d_name)) < 2 || 1576 strcmp(direntry->d_name + len - 2, "s0") != 0 || 1577 sscanf(direntry->d_name, "c%dt", &cnum) != 1 || 1578 (controller != -1 && controller != cnum)) 1579 continue; 1580 1581 (void) snprintf(devname, MAXPATHLEN, "/dev/rdsk/%s", 1582 direntry->d_name); 1583 1584 if (get_physname_minor(devname, physname, sizeof (physname), 1585 minor, sizeof (minor)) != 0 || 1586 client_name_type(physname) != CLIENT_TYPE_PHCI) { 1587 logdmsg("list_mappings: continuing\n"); 1588 continue; 1589 } 1590 1591 /* 1592 * First try phci_to_vhci() mapping. It will work if the 1593 * device is under MPxIO control. If the device is not under 1594 * MPxIO, phci_to_vhci() will fail in which case try to lookup 1595 * if an old mapping exists using guid lookup. 1596 */ 1597 mapped = 1; 1598 if (phci_to_vhci(physname, new_physname, 1599 sizeof (new_physname)) != 0) { 1600 if (get_guid(physname, guid, sizeof (guid), 1, 1601 DI_NODE_NIL) == 0) 1602 (void) snprintf(new_physname, MAXPATHLEN, 1603 "/scsi_vhci/%s%s", DISK_AT_G, guid); 1604 else 1605 mapped = 0; 1606 } 1607 1608 if (mapped == 0) 1609 continue; 1610 1611 /* strip the slice number part */ 1612 devname[strlen(devname) - 2] = '\0'; 1613 1614 if (guidmap == 0) { 1615 if (lookup_devlink(new_physname, minor, 1616 new_devname, sizeof (new_devname)) != 0) 1617 continue; 1618 1619 /* strip the slice number part */ 1620 new_devname[strlen(new_devname) - 2] = '\0'; 1621 1622 if (header) { 1623 (void) printf( 1624 gettext("non-STMS device name\t\t\t" 1625 "STMS device name\n" 1626 "------------------------------------------" 1627 "------------------------\n")); 1628 header = 0; 1629 } 1630 (void) printf("%s\t\t%s\n", devname, new_devname); 1631 } else { 1632 /* extract guid part */ 1633 /* we should be using a getguid() call instead */ 1634 if ((p1 = strstr(new_physname, "@")) 1635 == NULL) { 1636 logdmsg("invalid vhci: %s\n", new_physname); 1637 continue; 1638 } 1639 1640 logdmsg("\tp1 = %s\n", p1); 1641 1642 p1 += 2; /* "@" + [nwg] */ 1643 if ((p2 = strrchr(p1, ':')) != NULL) 1644 *p2 = '\0'; 1645 1646 if (header) { 1647 (void) printf( 1648 gettext("non-STMS device name\t\t\tGUID\n" 1649 "------------------------------------------" 1650 "------------------------\n")); 1651 header = 0; 1652 } 1653 (void) printf("%s\t\t%s\n", devname, p1); 1654 } 1655 } 1656 1657 (void) closedir(dirp); 1658 return (0); 1659 } 1660 1661 /* 1662 * Check if the file can be opened. 1663 * 1664 * Return 1 if the file can be opened, 0 otherwise. 1665 */ 1666 static int 1667 canopen(char *filename) 1668 { 1669 int fd; 1670 1671 if ((fd = open(filename, O_RDONLY)) == -1) 1672 return (0); 1673 1674 logdmsg("canopen: was able to open %s\n", filename); 1675 (void) close(fd); 1676 return (1); 1677 } 1678 1679 1680 /* 1681 * This function traverses the device tree looking for nodes 1682 * which have "drivername" as a property. We return a list of 1683 * these nodes, without duplicate entries. 1684 * Since there can be many different pci/pcie devices that all 1685 * share the same driver but which have different pci vid/did 1686 * combinations, we have to be smart about returning only those 1687 * pci vid/dids which have the "sas-*" property unless the 1688 * drivername is "fp", in which case we're searching for "node-wwn" 1689 */ 1690 static void 1691 list_nodes(char *drivername) 1692 { 1693 di_node_t devroot = DI_NODE_NIL; 1694 di_node_t thisnode = DI_NODE_NIL; 1695 char *aliaslist; 1696 char *iitype = NULL; /* the "initiator-interconnect-type" property */ 1697 int *intprop = NULL; 1698 int i = 1; /* fencepost */ 1699 int irval = 0; 1700 int crval = 0; 1701 1702 /* 1703 * Since the "fp" driver enumerates with its own name, 1704 * we can special-case its handling. 1705 */ 1706 if (strcmp(drvname, "fp") == 0) { 1707 (void) fprintf(stdout, "fp\n"); 1708 } else { 1709 1710 if ((devroot = di_init("/", DINFOCPYALL | DINFOFORCE)) 1711 == DI_NODE_NIL) { 1712 logerr(gettext("list_nodes: di_init failed: " 1713 "%s\n"), strerror(errno)); 1714 } 1715 1716 if ((thisnode = di_drv_first_node(drivername, devroot)) 1717 != NULL) { 1718 logdmsg("list_nodes: searching for property " 1719 "%s\n", drvprop); 1720 1721 aliaslist = s_malloc(1024 * sizeof (char)); 1722 bzero(aliaslist, 1024); 1723 while (thisnode != DI_NODE_NIL) { 1724 logdmsg("devfs-name %s driver-name %s " 1725 "node-name %s\n", 1726 di_devfs_path(thisnode), 1727 di_driver_name(thisnode), 1728 di_node_name(thisnode)); 1729 1730 /* We check the child node for drvprop */ 1731 irval = di_prop_lookup_ints(DDI_DEV_T_ANY, 1732 di_child_node(thisnode), drvprop, &intprop); 1733 /* and this node for the correct initiator type */ 1734 crval = di_prop_lookup_strings(DDI_DEV_T_ANY, 1735 thisnode, "initiator-interconnect-type", &iitype); 1736 1737 /* 1738 * examine the return codes from di_prop_lookup*() 1739 * functions to guard against library errors 1740 */ 1741 if ((irval > -1) || ((crval > -1) && 1742 (strncmp(iitype, "SATA", 4) == 0))) { 1743 1744 if (strstr(aliaslist, 1745 di_node_name(thisnode)) == (char *)NULL) { 1746 char *nname; 1747 1748 nname = di_node_name(thisnode); 1749 1750 if (i) { 1751 (void) snprintf(aliaslist, 1752 strlen(nname) + 1, "%s", nname); 1753 --i; 1754 } else { 1755 if (strstr(aliaslist, 1756 di_node_name(thisnode)) == 1757 (char *)NULL) { 1758 /* add 2 for the n-1 + "|" */ 1759 (void) snprintf(aliaslist, 1760 strlen(nname) + 2 + 1761 strlen(aliaslist), 1762 "%s|%s", aliaslist, 1763 nname); 1764 } 1765 } 1766 } 1767 } else { 1768 logdmsg("unable to lookup property %s " 1769 "for node %s. Error %d: %s\n", 1770 drvprop, di_devfs_path(thisnode), 1771 errno, strerror(errno)); 1772 } 1773 thisnode = di_drv_next_node(thisnode); 1774 } 1775 (void) fprintf(stdout, "%s\n", aliaslist); 1776 } 1777 1778 di_fini(devroot); 1779 } 1780 } 1781 1782 static void 1783 logerr(char *msg, ...) 1784 { 1785 va_list ap; 1786 1787 (void) fprintf(stderr, "%s: ", stmsboot); 1788 va_start(ap, msg); 1789 /* LINTED - format specifier */ 1790 (void) vfprintf(stderr, msg, ap); 1791 va_end(ap); 1792 } 1793 1794 /* log debug message */ 1795 static void 1796 logdmsg(char *msg, ...) 1797 { 1798 va_list ap; 1799 1800 if (debug) { 1801 va_start(ap, msg); 1802 /* LINTED - format specifier */ 1803 (void) vprintf(msg, ap); 1804 va_end(ap); 1805 } 1806 } 1807 1808 static void * 1809 s_malloc(const size_t size) 1810 { 1811 void *rp; 1812 1813 if ((rp = malloc(size)) == NULL) { 1814 logerr(gettext("malloc failed to allocate %d bytes\n"), size); 1815 clean_exit(1); 1816 } 1817 return (rp); 1818 } 1819 1820 static char * 1821 s_strdup(const char *ptr) 1822 { 1823 void *rp; 1824 1825 if ((rp = strdup(ptr)) == NULL) { 1826 logerr(gettext("strdup failed to dup %s\n"), ptr); 1827 clean_exit(1); 1828 } 1829 return (rp); 1830 } 1831 1832 static void 1833 s_strlcpy(char *dst, const char *src, size_t dstsize) 1834 { 1835 int n; 1836 1837 if ((n = strlcpy(dst, src, dstsize)) >= dstsize) { 1838 logerr(gettext("strlcpy: destination buffer size is %1$d " 1839 "bytes, need to at least %2$d bytes\n"), dstsize, n + 1); 1840 clean_exit(1); 1841 } 1842 } 1843 1844 static void 1845 clean_exit(int status) 1846 { 1847 if (devinfo_root != DI_NODE_NIL) 1848 di_fini(devinfo_root); 1849 1850 if (devlink_hdl != NULL) 1851 (void) di_devlink_fini(&devlink_hdl); 1852 1853 if (vhci_fd != -1) 1854 (void) close(vhci_fd); 1855 1856 exit(status); 1857 } 1858