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