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 #define SLASH_DISK_AT "/ssd@" 61 #else /* sparc */ 62 #define DISK_NODE_NAME "disk" 63 #define DISK_DRV_NAME "sd" 64 #define SLASH_DISK_AT "/disk@" 65 #endif 66 67 #define DISK_AT_G "disk@g" 68 #define SLASH_FP_AT "/fp@" 69 #define SLASH_SCSI_VHCI "/scsi_vhci" 70 #define SLASH_SD_AT "/sd@" 71 #define DEV_DSK "/dev/dsk/" 72 #define DEV_RDSK "/dev/rdsk/" 73 #define SYS_FILENAME_LEN 256 74 75 /* 76 * Save directory is the directory in which system files are saved. 77 * Save directory must be under the root filesystem, as this program is 78 * typically run before any other filesystems are mounted. 79 */ 80 #define SAVE_DIR "/etc/mpxio" 81 82 /* fcp driver publishes this property */ 83 #define NODE_WWN_PROP "node-wwn" 84 85 /* 86 * For SAS, we look for "sas-$drivername", eg sas-mpt, but 87 * we strncat the driver name later once we've parsed the 88 * args passed in from the shell. 89 */ 90 #define SASPROP "sas-" 91 92 93 typedef enum { 94 CLIENT_TYPE_UNKNOWN, 95 CLIENT_TYPE_PHCI, 96 CLIENT_TYPE_VHCI 97 } client_type_t; 98 99 struct devlink_cbarg { 100 char *devlink; 101 size_t len; 102 }; 103 104 static di_node_t devinfo_root = DI_NODE_NIL; 105 static di_devlink_handle_t devlink_hdl = NULL; 106 static int vhci_fd = -1; 107 static int patch_vfstab, cap_m_option, debug; 108 static int list_option, list_guid_mappings, list_controllernum = -1; 109 static char *mapdev = ""; 110 static char *map_vhciname = ""; 111 static char *stmsboot = "stmsboot"; 112 113 char *drvname = (char *)NULL; /* "fp" or "mpt" or ... */ 114 /* "node-wwn" if drvname=fp, or "sas-$drivername" otherwise */ 115 char *drvprop = (char *)NULL; 116 static int parent = 0; /* for "-n" usage */ 117 118 static int make_temp(char *, char *, char *, size_t); 119 static void commit_change(char *, char *, char *, int); 120 static int map_devname(char *, char *, size_t, int); 121 static int update_vfstab(char *, char *); 122 static int list_mappings(int, int); 123 static int canopen(char *); 124 static client_type_t client_by_props(char *path); 125 static void list_nodes(char *drivername); 126 127 static void logerr(char *, ...); 128 static void logdmsg(char *, ...); 129 static void *s_malloc(const size_t); 130 static char *s_strdup(const char *); 131 static void s_strlcpy(char *, const char *, size_t); 132 static int map_openable_vhciname(char *, char *, size_t); 133 /* 134 * Using an exit function not marked __NORETURN causes a warning with gcc. 135 * To suppress the warning, use __NORETURN attribute. 136 */ 137 static void clean_exit(int)__NORETURN; 138 139 /* 140 * Print usage and exit. 141 */ 142 static void 143 usage(char *argv0) 144 { 145 char *progname; 146 147 progname = strrchr(argv0, '/'); 148 if (progname != NULL) 149 progname++; 150 else 151 progname = argv0; 152 153 /* 154 * -u update /etc/vfstab 155 * -m devname 156 * if devname is phci based name and not open-able, map it to 157 * vhci based /devices name. 158 * if devname is vhci based name and not open-able, map it to 159 * phci based /devices name. 160 * -M devname 161 * same as -m except that /dev link is printed instead of 162 * /devices name. 163 * -l controller 164 * list non-STMS to STMS device name mappings for the specific 165 * controller 166 * -L list non-STMS to STMS device name mappings for all controllers 167 * -p devname 168 * if devname is vhci based name and open-able, get the first 169 * onlined phci based name without /devices prefix. 170 * Used in stmsboot to update the phci based bootpath. 171 * -D drvname 172 * if supplied, indicates that we're going to operate on 173 * devices attached to this driver 174 * -n 175 * if supplied, returns name of the node containing "fp" or 176 * "sas-$driver", appends "sd@" or "ssd@" or "disk@". Can only 177 * be used if -D drv is specified as well 178 */ 179 (void) fprintf(stderr, gettext("usage: %s -u | -m devname | " 180 "-M devname | -l controller | -L | \n" 181 "\t\t-p devname | -D { fp | mpt } | -n\n"), progname); 182 exit(2); 183 } 184 185 /* 186 * Parse command line arguments. 187 */ 188 static void 189 parse_args(int argc, char *argv[]) 190 { 191 char opt; 192 int n = 0; 193 194 if (argc == 1) { 195 usage(argv[0]); 196 /*NOTREACHED*/ 197 } 198 199 while ((opt = getopt(argc, argv, "udm:M:Ll:gp:D:n")) != EOF) { 200 switch (opt) { 201 case 'u': 202 patch_vfstab = 1; 203 n++; 204 break; 205 206 case 'd': 207 debug = 1; 208 break; 209 210 case 'm': 211 mapdev = s_strdup(optarg); 212 n++; 213 break; 214 215 case 'M': 216 mapdev = s_strdup(optarg); 217 cap_m_option = 1; 218 n++; 219 break; 220 221 case 'L': 222 list_option = 1; 223 n++; 224 break; 225 226 case 'l': 227 list_option = 1; 228 list_controllernum = (int)atol(optarg); 229 if (list_controllernum < 0) { 230 logerr(gettext("controller number %d is " 231 "invalid\n"), list_controllernum); 232 clean_exit(1); 233 } 234 n++; 235 break; 236 237 case 'g': 238 /* 239 * private option to display non-STMS device name 240 * to GUID mappings. 241 */ 242 list_guid_mappings = 1; 243 break; 244 245 case 'p': 246 /* 247 * map openable vhci based name to phci base name 248 */ 249 map_vhciname = s_strdup(optarg); 250 n++; 251 break; 252 253 case 'D': 254 /* 255 * Grab the driver name we need to look for. Each 256 * time we add support for a new SAS or FC driver 257 * to this utility, make sure that its driver name 258 * is checked here. 259 */ 260 drvname = s_malloc(sizeof (optarg) + 1); 261 drvname = s_strdup(optarg); 262 if (strcmp(drvname, "fp") == 0) { 263 drvprop = s_malloc(sizeof (NODE_WWN_PROP)); 264 (void) snprintf(drvprop, sizeof (NODE_WWN_PROP), 265 NODE_WWN_PROP); 266 } else if (strcmp(drvname, "mpt") == 0) { 267 drvprop = s_malloc(sizeof (SASPROP) + 268 sizeof (drvname) + 1); 269 (void) snprintf(drvprop, sizeof (SASPROP) + 270 sizeof (drvname), "%s%s", 271 SASPROP, drvname); 272 } else { 273 logerr(gettext("Driver %s is not supported\n"), 274 drvname); 275 clean_exit(1); 276 } 277 278 break; 279 280 case 'n': 281 ++parent; 282 n++; 283 break; 284 285 default: 286 usage(argv[0]); 287 /*NOTREACHED*/ 288 } 289 } 290 291 if (n != 1) { 292 usage(argv[0]); 293 /*NOTREACHED*/ 294 } 295 } 296 297 int 298 main(int argc, char *argv[]) 299 { 300 char save_vfstab[SYS_FILENAME_LEN], tmp_vfstab[SYS_FILENAME_LEN]; 301 int vfstab_updated; 302 303 (void) setlocale(LC_ALL, ""); 304 (void) textdomain(TEXT_DOMAIN); 305 306 if (getuid() != 0) { 307 logerr(gettext("must be super-user to run this program\n")); 308 clean_exit(1); 309 } 310 311 parse_args(argc, argv); 312 (void) umask(022); 313 314 /* 315 * NOTE: The mpxio boot-up script executes this program with the 316 * mapping (-m) option before the /usr is even mounted and when the 317 * root filesystem is still mounted read-only. 318 */ 319 if (*mapdev != '\0') { 320 char newname[MAXPATHLEN]; 321 322 if (map_devname(mapdev, newname, sizeof (newname), 323 cap_m_option) == 0) { 324 (void) printf("%s\n", newname); 325 clean_exit(0); 326 } 327 clean_exit(1); 328 } 329 if (*map_vhciname != '\0') { 330 char newname[MAXPATHLEN]; 331 332 if (map_openable_vhciname(map_vhciname, newname, 333 sizeof (newname)) == 0) { 334 (void) printf("%s\n", newname); 335 clean_exit(0); 336 } 337 clean_exit(1); 338 } 339 340 if (list_option || list_guid_mappings) { 341 if (list_mappings(list_controllernum, list_guid_mappings) == 0) 342 clean_exit(0); 343 clean_exit(1); 344 } 345 346 if (parent > 0) { 347 if (strcmp(drvname, "") == 0) { 348 usage(argv[0]); 349 clean_exit(1); 350 } else { 351 list_nodes(drvname); 352 clean_exit(0); 353 } 354 } 355 356 /* create a directory where a copy of the system files are saved */ 357 if (patch_vfstab) { 358 if (mkdirp(SAVE_DIR, 0755) != 0 && errno != EEXIST) { 359 logerr(gettext("mkdirp: failed to create %1$s: %2$s\n"), 360 SAVE_DIR, strerror(errno)); 361 clean_exit(1); 362 } 363 364 if (make_temp(VFSTAB, save_vfstab, tmp_vfstab, 365 SYS_FILENAME_LEN) != 0) 366 clean_exit(1); 367 368 /* build new vfstab without modifying the existing one */ 369 if ((vfstab_updated = update_vfstab(VFSTAB, tmp_vfstab)) 370 == -1) { 371 logerr(gettext("failed to update %s\n"), VFSTAB); 372 clean_exit(1); 373 } 374 375 commit_change(VFSTAB, save_vfstab, tmp_vfstab, vfstab_updated); 376 } 377 378 clean_exit(0); 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 * Map phci based client name to vhci based client name. 762 * 763 * phci_name 764 * phci based client /devices name without the /devices prefix and 765 * minor name component. 766 * ex: 767 * 768 * (FC) 769 * for sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 770 * for x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 771 * 772 * (SAS) 773 * for sparc: /pci@0,2/LSILogic,sas@1/disk@6,0 774 * for x86: /pci1000,3060@3/sd@0,0 775 * 776 * vhci_name 777 * Caller supplied buffer where vhci /devices name will be placed on 778 * return (without the /devices prefix and minor name component). 779 * ex: 780 * 781 * (FC) 782 * for sparc: /scsi_vhci/ssd@g2000002037cd9f72 783 * for x86: /scsi_vhci/disk@g2000002037cd9f72 784 * 785 * (SAS) 786 * both: /scsi_vhci/disk@g600a0b8000254d3e00000284453ed8ac 787 * 788 * vhci_name_len 789 * Length of the caller supplied vhci_name buffer. 790 * 791 * Returns 0 on success, -1 on failure. 792 */ 793 static int 794 phci_to_vhci(char *phci_name, char *vhci_name, size_t vhci_name_len) 795 { 796 sv_iocdata_t ioc; 797 char *slash, *at; 798 char vhci_name_buf[MAXPATHLEN]; 799 char phci_name_buf[MAXPATHLEN]; 800 char addr_buf[MAXNAMELEN]; 801 802 logdmsg("phci_to_vhci: client = %s\n", phci_name); 803 804 s_strlcpy(phci_name_buf, phci_name, MAXPATHLEN); 805 806 if (client_name_type(phci_name_buf) != CLIENT_TYPE_PHCI || 807 (slash = strrchr(phci_name_buf, '/')) == NULL || 808 ((strncmp(slash, "/ssd@", sizeof ("/ssd@") - 1) != 0) && 809 (strncmp(slash, "/sd@", sizeof ("/sd@") - 1) != 0) && 810 (strncmp(slash, "/disk@", sizeof ("/disk@") - 1) != 0))) { 811 logdmsg("phci_to_vhci: %s is not of CLIENT_TYPE_PHCI\n", 812 phci_name); 813 return (-1); 814 } 815 816 if (vhci_fd < 0) { 817 if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) 818 return (-1); 819 } 820 821 *slash = '\0'; 822 823 at = strchr(slash + 1, '@'); 824 s_strlcpy(addr_buf, at + 1, MAXNAMELEN); 825 826 bzero(&ioc, sizeof (sv_iocdata_t)); 827 ioc.client = vhci_name_buf; 828 ioc.phci = phci_name_buf; 829 ioc.addr = addr_buf; 830 831 if (ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_NAME, &ioc) != 0) { 832 logdmsg("SCSI_VHCI_GET_CLIENT_NAME on %s " 833 "failed: %s\n", phci_name, strerror(errno)); 834 return (-1); 835 } 836 837 s_strlcpy(vhci_name, vhci_name_buf, vhci_name_len); 838 logdmsg("phci_to_vhci: %s maps to %s\n", phci_name, vhci_name); 839 return (0); 840 } 841 842 /* 843 * Map vhci based client name to phci based client name. 844 * If the client has multiple paths, only one of the paths with which client 845 * can be accessed is returned. This function does not use SCSI_VHCI ioctls 846 * as it is called on mpxio disabled paths. 847 * 848 * vhci_name 849 * vhci based client /devices name without the /devices prefix and 850 * minor name component. 851 * ex: 852 * sparc: /scsi_vhci/ssd@g2000002037cd9f72 853 * x86: /scsi_vhci/disk@g2000002037cd9f72 854 * 855 * phci_name 856 * Caller supplied buffer where phci /devices name will be placed on 857 * return (without the /devices prefix and minor name component). 858 * ex: 859 * sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 860 * x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 861 * 862 * phci_name_len 863 * Length of the caller supplied phci_name buffer. 864 * 865 * Returns 0 on success, -1 on failure. 866 */ 867 static int 868 vhci_to_phci(char *vhci_name, char *phci_name, size_t phci_name_len) 869 { 870 di_node_t node = DI_NODE_NIL; 871 char *vhci_guid, *devfspath; 872 char phci_guid[MAXPATHLEN]; 873 char *node_name; 874 875 logdmsg("vhci_to_phci: client = %s\n", vhci_name); 876 877 if (client_name_type(vhci_name) != CLIENT_TYPE_VHCI) { 878 logdmsg("vhci_to_phci: %s is not of CLIENT_TYPE_VHCI\n", 879 vhci_name); 880 return (-1); 881 } 882 883 884 if ((vhci_guid = strrchr(vhci_name, '@')) == NULL || 885 *(++vhci_guid) != 'g') { 886 logerr(gettext("couldn't get guid from %s\n"), vhci_name); 887 return (-1); 888 } 889 890 /* point to guid */ 891 ++vhci_guid; 892 893 /* 894 * Get devinfo snapshot and walk all ssd nodes whose parent is fp. 895 * For each node get the guid and match it with vhci_guid. 896 */ 897 if (devinfo_root == DI_NODE_NIL) { 898 logdmsg("vhci_to_phci: taking devinfo snapshot\n"); 899 if ((devinfo_root = di_init("/", DINFOCPYALL | DINFOFORCE)) 900 == DI_NODE_NIL) { 901 logerr(gettext("di_init failed: %s\n"), 902 strerror(errno)); 903 return (-1); 904 } 905 logdmsg("vhci_to_phci: done taking devinfo snapshot\n"); 906 } 907 908 909 /* 910 * When we finally get a unified "sd" driver for all 911 * architectures that Solaris runs on, we can remove this 912 * first loop around for "ssd" 913 */ 914 for (node = di_drv_first_node("ssd", devinfo_root); 915 node != DI_NODE_NIL; node = di_drv_next_node(node)) { 916 917 if ((node_name = di_node_name(node)) == NULL) 918 continue; 919 920 if ((strcmp(node_name, "disk") != 0) && 921 (strcmp(node_name, "sd") != 0) && 922 (strcmp(node_name, "ssd") != 0)) 923 continue; 924 925 if (di_parent_node(node) == DI_NODE_NIL) 926 continue; 927 928 if ((devfspath = di_devfs_path(node)) == NULL) 929 continue; 930 931 /* 932 * Don't set no_delay_flag to have get_guid() fail on 933 * standby paths of T3. So we'll find the preferred paths. 934 */ 935 if (get_guid(devfspath, phci_guid, 936 sizeof (phci_guid), 0, node) != 0) 937 continue; 938 939 if (strcmp(phci_guid, vhci_guid) == 0) { 940 s_strlcpy(phci_name, devfspath, phci_name_len); 941 di_devfs_path_free(devfspath); 942 logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name, 943 phci_name); 944 return (0); 945 } 946 947 di_devfs_path_free(devfspath); 948 } 949 950 for (node = di_drv_first_node("sd", devinfo_root); 951 node != DI_NODE_NIL; node = di_drv_next_node(node)) { 952 953 if ((node_name = di_node_name(node)) == NULL) 954 continue; 955 956 if ((strcmp(node_name, "disk") != 0) && 957 (strcmp(node_name, "sd") != 0) && 958 (strcmp(node_name, "ssd") != 0)) 959 continue; 960 961 if (di_parent_node(node) == DI_NODE_NIL) 962 continue; 963 964 if ((devfspath = di_devfs_path(node)) == NULL) 965 continue; 966 967 /* 968 * Don't set no_delay_flag to have get_guid() fail on 969 * standby paths of T3. So we'll find the preferred paths. 970 */ 971 if (get_guid(devfspath, phci_guid, 972 sizeof (phci_guid), 0, node) != 0) 973 continue; 974 975 if (strcmp(phci_guid, vhci_guid) == 0) { 976 s_strlcpy(phci_name, devfspath, phci_name_len); 977 di_devfs_path_free(devfspath); 978 logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name, 979 phci_name); 980 return (0); 981 } 982 983 di_devfs_path_free(devfspath); 984 } 985 986 logdmsg("vhci_to_phci: couldn't get phci name for %s\n", vhci_name); 987 return (-1); 988 } 989 990 /* 991 * Map vhci based client name to phci based client name. 992 * If the client has multiple paths, only one of the paths with which client 993 * can be accessed is returned. 994 * This function uses SCSI_VHCI ioctls to get the phci paths 995 * 996 * vhci_name 997 * vhci based client /devices name without the /devices prefix and 998 * minor name component. 999 * ex: 1000 * sparc: /scsi_vhci/ssd@g2000002037cd9f72 1001 * x86: /scsi_vhci/disk@g2000002037cd9f72 1002 * 1003 * phci_name 1004 * Caller supplied buffer where phci /devices name will be placed on 1005 * return (without the /devices prefix and minor name component). 1006 * ex: 1007 * sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 1008 * x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 1009 * 1010 * phci_name_len 1011 * Length of the caller supplied phci_name buffer. 1012 * 1013 * Returns 0 on success, -1 on failure. 1014 */ 1015 1016 static int 1017 vhci_to_phci_by_ioctl(char *vhci_name, char *phci_name, size_t phci_name_len) 1018 { 1019 sv_iocdata_t ioc; 1020 uint_t npaths; 1021 char *node_name, *at; 1022 char vhci_name_buf[MAXPATHLEN]; 1023 int ret; 1024 sv_path_info_t *pi; 1025 1026 logdmsg("vhci_to_phci_by_ioctl: client = %s\n", vhci_name); 1027 1028 if (vhci_fd < 0) { 1029 if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) 1030 return (-1); 1031 } 1032 1033 (void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN); 1034 1035 /* first get the number paths */ 1036 bzero(&ioc, sizeof (sv_iocdata_t)); 1037 ioc.client = vhci_name_buf; 1038 ioc.ret_elem = &npaths; 1039 if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, 1040 &ioc)) != 0 || npaths == 0) { 1041 logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s " 1042 "failed: %s\n", vhci_name, 1043 ret?strerror(errno):"got 0 paths"); 1044 return (-1); 1045 } 1046 1047 /* now allocate memory for the path information and get all paths */ 1048 bzero(&ioc, sizeof (sv_iocdata_t)); 1049 ioc.client = vhci_name_buf; 1050 ioc.buf_elem = npaths; 1051 ioc.ret_elem = &npaths; 1052 if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths, 1053 sizeof (sv_path_info_t))) == NULL) 1054 return (-1); 1055 if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, 1056 &ioc)) != 0 || npaths == 0) { 1057 logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s " 1058 "failed: %s\n", vhci_name, 1059 ret?strerror(errno):"got 0 paths"); 1060 goto out; 1061 } 1062 1063 if (ioc.buf_elem < npaths) 1064 npaths = ioc.buf_elem; 1065 if ((node_name = strrchr(vhci_name_buf, '/')) == NULL || 1066 (at = strchr(node_name, '@')) == NULL) 1067 goto out; 1068 1069 node_name++; 1070 *at = '\0'; 1071 1072 logdmsg("vhci_to_phci_by_ioctl: node_name is %s\n", node_name); 1073 #ifndef sparc 1074 /* 1075 * We need to use a libdevinfo call to get this info 1076 * in an architecturally-neutral fashion. Phase-II for sure! 1077 */ 1078 node_name = "sd"; 1079 #endif 1080 1081 /* 1082 * return the first online paths as non-online paths may 1083 * not be accessible in the target environment. 1084 */ 1085 pi = (sv_path_info_t *)ioc.ret_buf; 1086 while (npaths--) { 1087 if (MDI_PATHINFO_STATE_ONLINE == pi->ret_state) { 1088 (void) snprintf(phci_name, phci_name_len, "%s/%s@%s", 1089 pi->device.ret_phci, node_name, 1090 pi->ret_addr); 1091 logdmsg("vhci_to_phci_by_ioctl: %s maps to %s\n", 1092 vhci_name, phci_name); 1093 free(ioc.ret_buf); 1094 return (0); 1095 } 1096 pi++; 1097 } 1098 1099 out: 1100 logdmsg("vhci_to_phci_by_ioctl: couldn't get phci name for %s\n", 1101 vhci_name); 1102 free(ioc.ret_buf); 1103 return (-1); 1104 1105 } 1106 1107 /* 1108 * Map physname from phci name space to vhci name space or vice-versa 1109 * 1110 * physname 1111 * phci or vhci based client /devices name without the /devices prefix and 1112 * minor name component. 1113 * 1114 * new_physname 1115 * Caller supplied buffer where the mapped physical name is stored on 1116 * return (without the /devices prefix and minor name component). 1117 * 1118 * len 1119 * Length of the caller supplied new_physname buffer. 1120 * 1121 * Returns 0 on success, -1 on failure. 1122 */ 1123 static int 1124 map_physname(char *physname, char *new_physname, size_t len) 1125 { 1126 int type; 1127 int rv; 1128 1129 type = client_name_type(physname); 1130 logdmsg("map_physname: type (%d) physname = %s\n", 1131 type, physname); 1132 1133 if (type == CLIENT_TYPE_VHCI) 1134 rv = vhci_to_phci(physname, new_physname, len); 1135 else if (type == CLIENT_TYPE_PHCI) 1136 rv = phci_to_vhci(physname, new_physname, len); 1137 else 1138 rv = -1; 1139 1140 logdmsg("map_physname: returning %d\n", rv); 1141 return (rv); 1142 } 1143 1144 /* 1145 * Given a phci or vhci devname which is either a /dev link or /devices name 1146 * get the corresponding physical node path (without the /devices prefix) 1147 * and minor name. 1148 * 1149 * Returns 0 on success, -1 on failure. 1150 */ 1151 static int 1152 get_physname_minor(char *devname, char *physname, int physname_len, 1153 char *minorname, int minorname_len) 1154 { 1155 int linksize; 1156 char buf[MAXPATHLEN]; 1157 char *p, *m; 1158 1159 if (strncmp(devname, DEV_DSK, sizeof (DEV_DSK) - 1) == 0 || 1160 strncmp(devname, DEV_RDSK, sizeof (DEV_RDSK) - 1) == 0) { 1161 if ((linksize = readlink(devname, buf, MAXPATHLEN)) 1162 > 0 && linksize <= (MAXPATHLEN - 1)) { 1163 buf[linksize] = '\0'; 1164 } else 1165 return (-1); 1166 } else 1167 s_strlcpy(buf, devname, MAXPATHLEN); 1168 1169 if ((p = strstr(buf, SLASH_DEVICES)) == NULL) 1170 return (-1); 1171 1172 /* point to '/' after /devices */ 1173 p += sizeof (SLASH_DEVICES) - 2; 1174 1175 if ((m = strrchr(p, ':')) == NULL) { 1176 logdmsg("get_physname_minor: no minor name component in %s\n", 1177 buf); 1178 return (-1); 1179 } 1180 1181 *m = '\0'; 1182 m++; 1183 1184 if (client_name_type(p) == CLIENT_TYPE_UNKNOWN) 1185 return (-1); 1186 1187 s_strlcpy(physname, p, physname_len); 1188 s_strlcpy(minorname, m, minorname_len); 1189 logdmsg("get_physname_minor: %s: physname = %s, minor = %s\n", 1190 devname, physname, minorname); 1191 return (0); 1192 } 1193 1194 static int 1195 devlink_callback(di_devlink_t devlink, void *argptr) 1196 { 1197 const char *link; 1198 struct devlink_cbarg *argp = argptr; 1199 1200 if ((link = di_devlink_path(devlink)) != NULL) { 1201 s_strlcpy(argp->devlink, link, argp->len); 1202 return (DI_WALK_TERMINATE); 1203 } 1204 1205 return (DI_WALK_CONTINUE); 1206 } 1207 1208 /* 1209 * Lookup the /dev link corresponding to physname and minorname. 1210 * 1211 * physname client /devices path without the /devices prefix and minor 1212 * name component. 1213 * minorname client minor name. 1214 * devlink caller supplied buffer where the /dev link is placed on return. 1215 * len caller supplied devlink buffer length 1216 * 1217 * Returns 0 on success, -1 on failure. 1218 */ 1219 static int 1220 lookup_devlink(char *physname, char *minorname, char *devlink, size_t len) 1221 { 1222 char buf[MAXPATHLEN]; 1223 struct devlink_cbarg arg; 1224 1225 if (devlink_hdl == NULL) { 1226 logdmsg("lookup_devlink: taking devlink snapshot\n"); 1227 if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) { 1228 logerr(gettext("di_devlink_init failed: %s\n"), 1229 strerror(errno)); 1230 clean_exit(1); 1231 } 1232 } 1233 1234 *devlink = '\0'; 1235 (void) snprintf(buf, MAXPATHLEN, "%s:%s", physname, minorname); 1236 arg.devlink = devlink; 1237 arg.len = len; 1238 if (di_devlink_walk(devlink_hdl, NULL, buf, DI_PRIMARY_LINK, &arg, 1239 devlink_callback) != 0) { 1240 logdmsg("lookup_devlink: di_devlink_walk on %s failed: %s\n", 1241 buf, strerror(errno)); 1242 return (-1); 1243 } 1244 1245 if (*devlink == '\0') { 1246 logdmsg("lookup_devlink: failed to lookup devlink for %s\n", 1247 buf); 1248 return (-1); 1249 } 1250 1251 logdmsg("lookup_devlink: /dev link for %s:%s = %s\n", physname, 1252 minorname, devlink); 1253 return (0); 1254 } 1255 1256 /* 1257 * open infile for reading and return its file pointer in *fp_in. 1258 * open outfile for writing and return its file pointer in *fp_out. 1259 * 1260 * Returns 0 on success, -1 on failure. 1261 */ 1262 static int 1263 open_in_out_files(char *infile, char *outfile, FILE **fp_in, FILE **fp_out) 1264 { 1265 FILE *fin = NULL; 1266 FILE *fout = NULL; 1267 struct stat sbuf; 1268 1269 if ((fin = fopen(infile, "r")) == NULL) { 1270 logerr(gettext("failed to fopen %1$s: %2$s\n"), 1271 infile, strerror(errno)); 1272 goto out; 1273 } 1274 1275 if (fstat(fileno(fin), &sbuf) != 0) { 1276 logerr(gettext("fstat failed on %1$s: %2$s\n"), 1277 infile, strerror(errno)); 1278 goto out; 1279 } 1280 1281 if ((fout = fopen(outfile, "w")) == NULL) { 1282 logerr(gettext("failed to fopen %1$s: %2$s\n"), 1283 outfile, strerror(errno)); 1284 goto out; 1285 } 1286 1287 if (fchmod(fileno(fout), (sbuf.st_mode & 0777)) != 0) { 1288 logerr(gettext("failed to fchmod %1$s to 0%2$o: %3$s\n"), 1289 outfile, sbuf.st_mode & 0777, strerror(errno)); 1290 goto out; 1291 } 1292 1293 if (fchown(fileno(fout), sbuf.st_uid, sbuf.st_gid) != 0) { 1294 logerr(gettext("failed to fchown %1$s to uid %2$d and " 1295 "gid %3$d: %4$s\n"), 1296 outfile, sbuf.st_uid, sbuf.st_gid, strerror(errno)); 1297 goto out; 1298 } 1299 1300 *fp_in = fin; 1301 *fp_out = fout; 1302 return (0); 1303 1304 out: 1305 if (fin != NULL) 1306 (void) fclose(fin); 1307 if (fout != NULL) 1308 (void) fclose(fout); 1309 return (-1); 1310 } 1311 1312 /* 1313 * If the devname is a phci based name and not open-able, map it to vhci 1314 * based name. If the devname is a vhci based name and not open-able, map it 1315 * to phci based name. 1316 * 1317 * devname either a /dev link or /devices name to client device 1318 * new_devname caller supplied buffer where the mapped device name is 1319 * placed on return. 1320 * len caller supplied new_devname buffer length 1321 * devlink_flag pass 1 if requesting the /dev link to the mapped device. 1322 * pass 0 if requesting the /devices name of the mapped device. 1323 * 1324 * Returns 0 on success, -1 on failure. 1325 */ 1326 static int 1327 map_devname(char *devname, char *new_devname, size_t len, int devlink_flag) 1328 { 1329 char physname[MAXPATHLEN]; 1330 char minor[MAXNAMELEN]; 1331 char new_physname[MAXPATHLEN]; 1332 1333 logdmsg("map_devname: checking devname %s\n", devname); 1334 if ((get_physname_minor(devname, physname, sizeof (physname), 1335 minor, sizeof (minor)) == 0) && 1336 (canopen(devname) == 0) && 1337 (map_physname(physname, new_physname, 1338 sizeof (new_physname)) == 0)) { 1339 1340 logdmsg("map_devname: now looking up devlink\n"); 1341 1342 if (devlink_flag) { 1343 if (lookup_devlink(new_physname, minor, new_devname, 1344 len) == 0) 1345 return (0); 1346 } else { 1347 (void) snprintf(new_devname, len, "/devices%s:%s", 1348 new_physname, minor); 1349 return (0); 1350 } 1351 } 1352 1353 logdmsg("map_devname: failed to find mapping for %s\n", devname); 1354 return (-1); 1355 } 1356 1357 /* 1358 * If the devname is a vhci based name and open-able, map it to phci 1359 * based name. 1360 * 1361 * devname either a /dev link or /devices name to client device 1362 * new_devname caller supplied buffer where the mapped device name without 1363 * /devices prefix is placed on return. 1364 * len caller supplied new_devname buffer length 1365 */ 1366 static int 1367 map_openable_vhciname(char *devname, char *new_devname, size_t len) 1368 { 1369 char physname[MAXPATHLEN]; 1370 char minor[MAXNAMELEN]; 1371 char new_physname[MAXPATHLEN]; 1372 1373 if (get_physname_minor(devname, physname, sizeof (physname), 1374 minor, sizeof (minor)) == 0 && 1375 canopen(devname) == 1 && 1376 client_name_type(physname) == CLIENT_TYPE_VHCI && 1377 vhci_to_phci_by_ioctl(physname, new_physname, 1378 sizeof (new_physname)) == 0) { 1379 (void) snprintf(new_devname, len, "%s:%s", 1380 new_physname, minor); 1381 return (0); 1382 } 1383 1384 return (-1); 1385 } 1386 /* 1387 * Make a new /etc/vfstab: 1388 * Read vfstab_in, convert the device name entries to appropriate vhci or phci 1389 * based names, and write to vfstab_out. Only device names whose physical 1390 * paths are either phci or vhci based names and not open-able are considered 1391 * for conversion. Open-able device name entries are not converted as it 1392 * means that the device is already accessible; hence no need to convert. 1393 * 1394 * Returns: 1395 * 0 successful but vfstab_out contents are the same as vfstab_in 1396 * 1 successful and vfstab_out changed from vfstab_in 1397 * -1 failed 1398 */ 1399 static int 1400 update_vfstab(char *vfstab_in, char *vfstab_out) 1401 { 1402 FILE *fp_in, *fp_out; 1403 char *buf, *tmpbuf; 1404 char *vfs_cache[2]; 1405 int idx = 0, count = 0; 1406 int rv = -1; 1407 int vfstab_updated = 0; 1408 int i; 1409 char cdev[MAXPATHLEN]; 1410 char bdev[MAXPATHLEN]; 1411 char mntpt[MAXPATHLEN]; 1412 char fstype[512]; 1413 char fsckpass[512]; 1414 char mntboot[512]; 1415 char mntopt[MAX_MNTOPT_STR]; 1416 char phys_bdev[MAXPATHLEN], phys_cdev[MAXPATHLEN]; 1417 char bdev_minor[MAXNAMELEN], cdev_minor[MAXNAMELEN]; 1418 char new_physname[MAXPATHLEN]; 1419 char new_bdevlink[MAXPATHLEN], new_cdevlink[MAXPATHLEN]; 1420 char fmt[80]; 1421 1422 if (open_in_out_files(vfstab_in, vfstab_out, &fp_in, &fp_out) != 0) 1423 return (-1); 1424 1425 /* 1426 * Read one line at time from vfstab_in. If no conversion is needed 1427 * for the line simply write the line to vfstab_out. If conversion is 1428 * needed, first write the existing line as a comment to vfstab_out 1429 * and then write the converted line. 1430 * 1431 * To avoid commented entries piling up in vfstab in case if the 1432 * user runs stmsboot multiple times to switch on and off from mpxio, 1433 * add the commented line only if not already there. To do this 1434 * cache the last two vfstab lines processed and add the commented 1435 * entry only if it is not found in the cache. We only need to cache 1436 * the last two lines because a device can have at most two names - 1437 * one mpxio and one non-mpxio name. Therefore for any device name 1438 * entry we at most add two comments - one with mpxio name and one 1439 * with non-mpxio name - no matter how many times stmsboot is run. 1440 */ 1441 buf = (char *)s_malloc(VFS_LINE_MAX); 1442 tmpbuf = (char *)s_malloc(VFS_LINE_MAX); 1443 vfs_cache[0] = (char *)s_malloc(VFS_LINE_MAX); 1444 vfs_cache[1] = (char *)s_malloc(VFS_LINE_MAX); 1445 1446 (void) snprintf(fmt, sizeof (fmt), 1447 "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1, 1448 sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1, 1449 sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1); 1450 1451 while (fgets(buf, VFS_LINE_MAX, fp_in) != NULL) { 1452 if (strlen(buf) == (VFS_LINE_MAX - 1) && 1453 buf[VFS_LINE_MAX-2] != '\n') { 1454 logerr(gettext("%1$s line size too long, " 1455 "exceeded %2$d: \"%3$s\"\n"), 1456 VFSTAB, VFS_LINE_MAX - 2, buf); 1457 goto out; 1458 } 1459 1460 /* LINTED - format specifier */ 1461 if ((sscanf(buf, fmt, bdev, cdev, mntpt, 1462 fstype, fsckpass, mntboot, mntopt) != 7) || 1463 (bdev[0] == '#') || 1464 (get_physname_minor(bdev, phys_bdev, sizeof (phys_bdev), 1465 bdev_minor, sizeof (bdev_minor)) != 0) || 1466 1467 (strcmp(fstype, "swap") != 0 && 1468 ((get_physname_minor(cdev, phys_cdev, sizeof (phys_cdev), 1469 cdev_minor, sizeof (cdev_minor)) != 0) || 1470 (strcmp(phys_bdev, phys_cdev) != 0))) || 1471 1472 canopen(bdev) || 1473 (map_physname(phys_bdev, new_physname, 1474 sizeof (new_physname)) != 0) || 1475 (lookup_devlink(new_physname, bdev_minor, new_bdevlink, 1476 sizeof (new_bdevlink)) != 0) || 1477 1478 (strcmp(fstype, "swap") != 0 && 1479 (lookup_devlink(new_physname, cdev_minor, new_cdevlink, 1480 sizeof (new_cdevlink)) != 0))) { 1481 1482 /* cache the last two entries */ 1483 (void) strlcpy(vfs_cache[idx], buf, VFS_LINE_MAX); 1484 idx = (idx == 0) ? 1 : 0; 1485 if (count < 2) 1486 count++; 1487 1488 if (fputs(buf, fp_out) == EOF) { 1489 logerr(gettext("fputs \"%1$s\" to %2$s " 1490 "failed: %3$s\n"), 1491 buf, vfstab_out, strerror(errno)); 1492 goto out; 1493 } 1494 1495 } else { 1496 /* 1497 * comment the entry in vfstab only if it is not 1498 * already in the cache. 1499 */ 1500 if (client_name_type(phys_bdev) == CLIENT_TYPE_VHCI) 1501 (void) snprintf(tmpbuf, VFS_LINE_MAX, 1502 "# mpxio: %s", buf); 1503 else 1504 (void) snprintf(tmpbuf, VFS_LINE_MAX, 1505 "# non-mpxio: %s", buf); 1506 1507 for (i = 0; i < count; i++) { 1508 if (strcmp(vfs_cache[i], tmpbuf) == 0) 1509 break; 1510 } 1511 1512 if (i == count) { 1513 if (fputs(tmpbuf, fp_out) == EOF) { 1514 logerr(gettext("fputs \"%1$s\" to %2$s " 1515 "failed: %3$s\n"), tmpbuf, 1516 vfstab_out, strerror(errno)); 1517 goto out; 1518 } 1519 } 1520 1521 count = 0; 1522 idx = 0; 1523 1524 if (fprintf(fp_out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 1525 new_bdevlink, 1526 (strcmp(fstype, "swap") != 0) ? new_cdevlink : cdev, 1527 mntpt, fstype, fsckpass, mntboot, mntopt) < 0) { 1528 logerr(gettext("fprintf failed to write to " 1529 "%1$s: %2$s\n"), 1530 vfstab_out, strerror(errno)); 1531 goto out; 1532 } 1533 vfstab_updated = 1; 1534 } 1535 } 1536 1537 rv = vfstab_updated; 1538 out: 1539 (void) fclose(fp_in); 1540 (void) fclose(fp_out); 1541 free(buf); 1542 free(tmpbuf); 1543 free(vfs_cache[0]); 1544 free(vfs_cache[1]); 1545 return (rv); 1546 } 1547 1548 /* 1549 * if guidmap is 0, list non-STMS to STMS device name mappings for the 1550 * specified controller. 1551 * if guidmap is 1, list non-STMS to GUID mappings for the specified controller. 1552 * If controller is -1 list mappings for all controllers. 1553 * 1554 * Returns 0 on success, -1 on failure. 1555 */ 1556 static int 1557 list_mappings(int controller, int guidmap) 1558 { 1559 int cnum, len, mapped; 1560 int header = 1; 1561 char *p1, *p2; 1562 DIR *dirp; 1563 struct dirent *direntry; 1564 char devname[MAXPATHLEN]; 1565 char physname[MAXPATHLEN]; 1566 char new_devname[MAXPATHLEN]; 1567 char new_physname[MAXPATHLEN]; 1568 char guid[MAXPATHLEN]; 1569 char minor[MAXNAMELEN]; 1570 1571 if ((dirp = opendir("/dev/rdsk")) == NULL) 1572 return (-1); 1573 1574 while ((direntry = readdir(dirp)) != NULL) { 1575 if (strcmp(direntry->d_name, ".") == 0 || 1576 strcmp(direntry->d_name, "..") == 0 || 1577 (len = strlen(direntry->d_name)) < 2 || 1578 strcmp(direntry->d_name + len - 2, "s0") != 0 || 1579 sscanf(direntry->d_name, "c%dt", &cnum) != 1 || 1580 (controller != -1 && controller != cnum)) 1581 continue; 1582 1583 (void) snprintf(devname, MAXPATHLEN, "/dev/rdsk/%s", 1584 direntry->d_name); 1585 1586 if (get_physname_minor(devname, physname, sizeof (physname), 1587 minor, sizeof (minor)) != 0 || 1588 client_name_type(physname) != CLIENT_TYPE_PHCI) { 1589 logdmsg("list_mappings: continuing\n"); 1590 continue; 1591 } 1592 1593 /* 1594 * First try phci_to_vhci() mapping. It will work if the 1595 * device is under MPxIO control. If the device is not under 1596 * MPxIO, phci_to_vhci() will fail in which case try to lookup 1597 * if an old mapping exists using guid lookup. 1598 */ 1599 mapped = 1; 1600 if (phci_to_vhci(physname, new_physname, 1601 sizeof (new_physname)) != 0) { 1602 if (get_guid(physname, guid, sizeof (guid), 1, 1603 DI_NODE_NIL) == 0) 1604 (void) snprintf(new_physname, MAXPATHLEN, 1605 "/scsi_vhci/%s%s", DISK_AT_G, guid); 1606 else 1607 mapped = 0; 1608 } 1609 1610 if (mapped == 0) 1611 continue; 1612 1613 /* strip the slice number part */ 1614 devname[strlen(devname) - 2] = '\0'; 1615 1616 if (guidmap == 0) { 1617 if (lookup_devlink(new_physname, minor, 1618 new_devname, sizeof (new_devname)) != 0) 1619 continue; 1620 1621 /* strip the slice number part */ 1622 new_devname[strlen(new_devname) - 2] = '\0'; 1623 1624 if (header) { 1625 (void) printf( 1626 gettext("non-STMS device name\t\t\t" 1627 "STMS device name\n" 1628 "------------------------------------------" 1629 "------------------------\n")); 1630 header = 0; 1631 } 1632 (void) printf("%s\t\t%s\n", devname, new_devname); 1633 } else { 1634 /* extract guid part */ 1635 /* we should be using a getguid() call instead */ 1636 if ((p1 = strstr(new_physname, "@")) 1637 == NULL) { 1638 logdmsg("invalid vhci: %s\n", new_physname); 1639 continue; 1640 } 1641 1642 logdmsg("\tp1 = %s\n", p1); 1643 1644 p1 += 2; /* "@" + [nwg] */ 1645 if ((p2 = strrchr(p1, ':')) != NULL) 1646 *p2 = '\0'; 1647 1648 if (header) { 1649 (void) printf( 1650 gettext("non-STMS device name\t\t\tGUID\n" 1651 "------------------------------------------" 1652 "------------------------\n")); 1653 header = 0; 1654 } 1655 (void) printf("%s\t\t%s\n", devname, p1); 1656 } 1657 } 1658 1659 (void) closedir(dirp); 1660 return (0); 1661 } 1662 1663 /* 1664 * Check if the file can be opened. 1665 * 1666 * Return 1 if the file can be opened, 0 otherwise. 1667 */ 1668 static int 1669 canopen(char *filename) 1670 { 1671 int fd; 1672 1673 if ((fd = open(filename, O_RDONLY)) == -1) 1674 return (0); 1675 1676 logdmsg("canopen: was able to open %s\n", filename); 1677 (void) close(fd); 1678 return (1); 1679 } 1680 1681 1682 /* 1683 * This function traverses the device tree looking for nodes 1684 * which have "drivername" as a property. We return a list of 1685 * these nodes, with SLASH_DISK_AT appended. 1686 * Since there can be many different pci/pcie devices that all 1687 * share the same driver but which have different pci vid/did 1688 * combinations, we have to be smart about returning only those 1689 * pci vid/dids which have the "sas-*" property unless the 1690 * drivername is "fp", in which case we're searching for "node-wwn" 1691 */ 1692 static void 1693 list_nodes(char *drivername) 1694 { 1695 char *aliaslist; 1696 char *mpxprop = NULL; 1697 di_node_t devroot = DI_NODE_NIL; 1698 di_node_t thisnode = DI_NODE_NIL; 1699 int *intprop = NULL; 1700 int i = 1; /* fencepost */ 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 if ((di_prop_lookup_ints(DDI_DEV_T_ANY, 1732 di_child_node(thisnode), drvprop, 1733 &intprop) > -1) || 1734 (((di_prop_lookup_strings(DDI_DEV_T_ANY, 1735 thisnode, "mpxio-disable", &mpxprop) > -1) && 1736 strcmp(di_driver_name(thisnode), 1737 drivername)) == 0)) { 1738 1739 if (strstr(aliaslist, 1740 di_node_name(thisnode)) 1741 == (char *)NULL) { 1742 char *nname; 1743 1744 nname = di_node_name(thisnode); 1745 1746 if (i) { 1747 (void) snprintf(aliaslist, 1748 strlen(nname), "%s", nname); 1749 --i; 1750 } else { 1751 if (strstr(aliaslist, 1752 di_node_name(thisnode)) == 1753 (char *)NULL) { 1754 (void) snprintf(aliaslist, 1755 strlen(nname) + 1756 strlen(aliaslist), 1757 "%s|%s", aliaslist, 1758 nname); 1759 } 1760 } 1761 } 1762 } else { 1763 logdmsg("unable to lookup property %s " 1764 "for node %s. Error %d: %s\n", 1765 drvprop, di_devfs_path(thisnode), 1766 errno, strerror(errno)); 1767 } 1768 thisnode = di_drv_next_node(thisnode); 1769 } 1770 (void) fprintf(stdout, "%s\n", aliaslist); 1771 } 1772 1773 di_fini(devroot); 1774 } 1775 } 1776 1777 static void 1778 logerr(char *msg, ...) 1779 { 1780 va_list ap; 1781 1782 (void) fprintf(stderr, "%s: ", stmsboot); 1783 va_start(ap, msg); 1784 /* LINTED - format specifier */ 1785 (void) vfprintf(stderr, msg, ap); 1786 va_end(ap); 1787 } 1788 1789 /* log debug message */ 1790 static void 1791 logdmsg(char *msg, ...) 1792 { 1793 va_list ap; 1794 1795 if (debug) { 1796 va_start(ap, msg); 1797 /* LINTED - format specifier */ 1798 (void) vprintf(msg, ap); 1799 va_end(ap); 1800 } 1801 } 1802 1803 static void * 1804 s_malloc(const size_t size) 1805 { 1806 void *rp; 1807 1808 if ((rp = malloc(size)) == NULL) { 1809 logerr(gettext("malloc failed to allocate %d bytes\n"), size); 1810 clean_exit(1); 1811 } 1812 return (rp); 1813 } 1814 1815 static char * 1816 s_strdup(const char *ptr) 1817 { 1818 void *rp; 1819 1820 if ((rp = strdup(ptr)) == NULL) { 1821 logerr(gettext("strdup failed to dup %s\n"), ptr); 1822 clean_exit(1); 1823 } 1824 return (rp); 1825 } 1826 1827 static void 1828 s_strlcpy(char *dst, const char *src, size_t dstsize) 1829 { 1830 int n; 1831 1832 if ((n = strlcpy(dst, src, dstsize)) >= dstsize) { 1833 logerr(gettext("strlcpy: destination buffer size is %1$d " 1834 "bytes, need to at least %2$d bytes\n"), dstsize, n + 1); 1835 clean_exit(1); 1836 } 1837 } 1838 1839 static void 1840 clean_exit(int status) 1841 { 1842 if (devinfo_root != DI_NODE_NIL) 1843 di_fini(devinfo_root); 1844 1845 if (devlink_hdl != NULL) 1846 (void) di_devlink_fini(&devlink_hdl); 1847 1848 if (vhci_fd != -1) 1849 (void) close(vhci_fd); 1850 1851 exit(status); 1852 } 1853