1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 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 #define SLASH_SSD_AT "/ssd@" 57 #define SLASH_FP_AT "/fp@" 58 #define SLASH_SCSI_VHCI "/scsi_vhci" 59 #define DEV_DSK "/dev/dsk/" 60 #define DEV_RDSK "/dev/rdsk/" 61 #define SYS_FILENAME_LEN 256 62 63 /* 64 * Save directory is the directory in which system files are saved. 65 * Save directory must be under the root filesystem, as this program is 66 * typically run before any other filesystems are mounted. 67 */ 68 #define SAVE_DIR "/etc/mpxio" 69 70 /* fcp driver publishes this property */ 71 #define NODE_WWN_PROP "node-wwn" 72 73 typedef enum { 74 CLIENT_TYPE_UNKNOWN, 75 CLIENT_TYPE_PHCI, 76 CLIENT_TYPE_VHCI 77 } client_type_t; 78 79 struct devlink_cbarg { 80 char *devlink; 81 size_t len; 82 }; 83 84 static di_node_t devinfo_root = DI_NODE_NIL; 85 static di_devlink_handle_t devlink_hdl = NULL; 86 static int vhci_fd = -1; 87 static int patch_vfstab, cap_m_option, debug; 88 static int list_option, list_guid_mappings, list_controllernum = -1; 89 static char *mapdev = ""; 90 static char *stmsboot = "stmsboot"; 91 92 static int make_temp(char *, char *, char *, size_t); 93 static void commit_change(char *, char *, char *, int); 94 static int map_devname(char *, char *, size_t, int); 95 static int update_vfstab(char *, char *); 96 static int list_mappings(int, int); 97 static int canopen(char *); 98 static void logerr(char *, ...); 99 static void logdmsg(char *, ...); 100 static void *s_malloc(const size_t); 101 static char *s_strdup(const char *); 102 static void s_strlcpy(char *, const char *, size_t); 103 static void clean_exit(int); 104 105 /* 106 * Print usage and exit. 107 */ 108 static void 109 usage(char *argv0) 110 { 111 char *progname; 112 113 progname = strrchr(argv0, '/'); 114 if (progname != NULL) 115 progname++; 116 else 117 progname = argv0; 118 119 /* 120 * -u update /etc/vfstab 121 * -m devname 122 * if devname is phci based name and not open-able, map it to 123 * vhci based /devices name. 124 * if devname is vhci based name and not open-able, map it to 125 * phci based /devices name. 126 * -M devname 127 * same as -m except that /dev link is printed instead of 128 * /devices name. 129 * -l controller 130 * list non-STMS to STMS device name mappings for the specific 131 * controller 132 * -L list non-STMS to STMS device name mappings for all controllers 133 */ 134 (void) fprintf(stderr, gettext("usage: %s -u | -m devname | " 135 "-M devname | -l controller | -L\n"), progname); 136 exit(2); 137 } 138 139 /* 140 * Parse command line arguments. 141 */ 142 static void 143 parse_args(int argc, char *argv[]) 144 { 145 char opt; 146 int n = 0; 147 148 if (argc == 1) { 149 usage(argv[0]); 150 /*NOTREACHED*/ 151 } 152 153 while ((opt = getopt(argc, argv, "udm:M:Ll:g")) != EOF) { 154 switch (opt) { 155 case 'u': 156 patch_vfstab = 1; 157 n++; 158 break; 159 160 case 'd': 161 debug = 1; 162 break; 163 164 case 'm': 165 mapdev = s_strdup(optarg); 166 n++; 167 break; 168 169 case 'M': 170 mapdev = s_strdup(optarg); 171 cap_m_option = 1; 172 n++; 173 break; 174 175 case 'L': 176 list_option = 1; 177 n++; 178 break; 179 180 case 'l': 181 list_option = 1; 182 list_controllernum = (int)atol(optarg); 183 if (list_controllernum < 0) { 184 logerr(gettext("controller number %d is " 185 "invalid\n"), list_controllernum); 186 clean_exit(1); 187 } 188 n++; 189 break; 190 191 case 'g': 192 /* 193 * private option to display non-STMS device name 194 * to GUID mappings. 195 */ 196 list_guid_mappings = 1; 197 n++; 198 break; 199 200 default: 201 usage(argv[0]); 202 /*NOTREACHED*/ 203 } 204 } 205 206 if (n != 1) 207 usage(argv[0]); 208 /*NOTREACHED*/ 209 } 210 211 void 212 main(int argc, char *argv[]) 213 { 214 char save_vfstab[SYS_FILENAME_LEN], tmp_vfstab[SYS_FILENAME_LEN]; 215 int vfstab_updated; 216 217 (void) setlocale(LC_ALL, ""); 218 (void) textdomain(TEXT_DOMAIN); 219 220 if (getuid() != 0) { 221 logerr(gettext("must be super-user to run this program\n")); 222 clean_exit(1); 223 } 224 225 parse_args(argc, argv); 226 (void) umask(022); 227 228 /* 229 * NOTE: The mpxio boot-up script executes this program with the 230 * mapping (-m) option before the /usr is even mounted and when the 231 * root filesystem is still mounted read-only. 232 */ 233 if (*mapdev != '\0') { 234 char newname[MAXPATHLEN]; 235 236 if (map_devname(mapdev, newname, sizeof (newname), 237 cap_m_option) == 0) { 238 (void) printf("%s\n", newname); 239 clean_exit(0); 240 } 241 clean_exit(1); 242 } 243 244 if (list_option || list_guid_mappings) { 245 if (list_mappings(list_controllernum, list_guid_mappings) == 0) 246 clean_exit(0); 247 clean_exit(1); 248 } 249 250 /* create a directory where a copy of the system files are saved */ 251 if (patch_vfstab) { 252 if (mkdirp(SAVE_DIR, 0755) != 0 && errno != EEXIST) { 253 logerr(gettext("mkdirp: failed to create %1$s: %2$s\n"), 254 SAVE_DIR, strerror(errno)); 255 clean_exit(1); 256 } 257 258 if (make_temp(VFSTAB, save_vfstab, tmp_vfstab, 259 SYS_FILENAME_LEN) != 0) 260 clean_exit(1); 261 262 /* build new vfstab without modifying the existing one */ 263 if ((vfstab_updated = update_vfstab(VFSTAB, tmp_vfstab)) 264 == -1) { 265 logerr(gettext("failed to update %s\n"), VFSTAB); 266 clean_exit(1); 267 } 268 269 commit_change(VFSTAB, save_vfstab, tmp_vfstab, vfstab_updated); 270 } 271 272 clean_exit(0); 273 } 274 275 /* 276 * Make saved and temporary filenames in SAVE_DIR. 277 * 278 * ex: if the filename is /etc/vfstab then the save_filename and tmp_filename 279 * would be SAVE_DIR/vfstab and SAVE_DIR/vfstab.tmp respectively. 280 * 281 * Returns 0 on success, -1 on failure. 282 */ 283 static int 284 make_temp(char *filename, char *save_filename, char *tmp_filename, size_t len) 285 { 286 char *ptr; 287 288 if ((ptr = strrchr(filename, '/')) == NULL) { 289 logdmsg("invalid file %s\n", filename); 290 return (-1); 291 } 292 (void) snprintf(save_filename, len, "%s%s", SAVE_DIR, ptr); 293 (void) snprintf(tmp_filename, len, "%s%s.tmp", SAVE_DIR, ptr); 294 logdmsg("make_temp: %s: save = %s, temp = %s\n", filename, 295 save_filename, tmp_filename); 296 return (0); 297 } 298 299 /* 300 * Commit the changes made to the system file 301 */ 302 static void 303 commit_change(char *filename, char *save_filename, char *tmp_filename, 304 int updated) 305 { 306 int x; 307 308 if (updated) { 309 /* save the original */ 310 if ((x = rename(filename, save_filename)) != 0) { 311 logerr(gettext("rename %1$s to %2$s failed: %3$s\n"), 312 filename, save_filename, strerror(errno)); 313 } 314 315 /* now rename the new file to the actual file */ 316 if (rename(tmp_filename, filename) != 0) { 317 logerr(gettext("rename %1$s to %2$s failed: %3$s\n"), 318 tmp_filename, filename, strerror(errno)); 319 320 /* restore the original */ 321 if (x == 0 && rename(save_filename, filename) != 0) { 322 logerr( 323 gettext("rename %1$s to %2$s failed: %3$s\n" 324 "%4$s is a copy of the original %5$s file" 325 "\n"), 326 save_filename, filename, strerror(errno), 327 save_filename, filename); 328 } 329 } else 330 (void) printf(gettext("%1$s: %2$s has been updated.\n"), 331 stmsboot, filename); 332 } else { 333 /* remove the temp file */ 334 (void) unlink(tmp_filename); 335 (void) printf(gettext("%1$s: %2$s was not modified as no " 336 "changes were needed.\n"), stmsboot, filename); 337 } 338 } 339 340 /* 341 * Get the GUID of the device. 342 * 343 * physpath /devices name without the /devices prefix and minor name 344 * component. 345 * guid caller supplied buffer where the GUID will be placed on return 346 * guid_len length of the caller supplied guid buffer. 347 * no_dealy_flag if set open the device with O_NDELAY 348 * node di_node corresponding to physpath if already available, 349 * otherwise pass DI_NODE_NIL. 350 * 351 * Returns 0 on success, -1 on failure. 352 */ 353 static int 354 get_guid(char *physpath, char *guid, int guid_len, int no_delay_flag, 355 di_node_t node) 356 { 357 int fd; 358 ddi_devid_t devid; 359 int rv = -1; 360 char *i_guid = NULL; 361 char physpath_raw[MAXPATHLEN]; 362 uchar_t *wwnp; 363 int i, n, snapshot_taken = 0; 364 365 logdmsg("get_guid: physpath = %s\n", physpath); 366 367 (void) snprintf(physpath_raw, MAXPATHLEN, 368 "/devices%s:a,raw", physpath); 369 370 *guid = '\0'; 371 372 if (no_delay_flag) 373 no_delay_flag = O_NDELAY; 374 375 /* 376 * Open the raw device 377 * Without the O_DELAY flag, the open will fail on standby paths of 378 * T3 if its mp_support mode is "mpxio". 379 */ 380 if ((fd = open(physpath_raw, O_RDONLY | no_delay_flag)) == -1) { 381 logdmsg("get_guid: failed to open %s: %s\n", physpath_raw, 382 strerror(errno)); 383 return (-1); 384 } 385 386 if (devid_get(fd, &devid) == 0) { 387 i_guid = devid_to_guid(devid); 388 devid_free(devid); 389 390 if (i_guid != NULL) { 391 s_strlcpy(guid, i_guid, guid_len); 392 devid_free_guid(i_guid); 393 rv = 0; 394 goto out; 395 } else 396 logdmsg("get_guid: devid_to_guid() failed\n"); 397 } else 398 logdmsg("get_guid: devid_get() failed: %s\n", strerror(errno)); 399 400 /* fallback to node name as the guid as this is what fcp driver does */ 401 if (node == DI_NODE_NIL) { 402 if ((node = di_init(physpath, DINFOCPYALL | DINFOFORCE)) 403 == DI_NODE_NIL) { 404 logdmsg("get_guid: di_init on %s failed: %s\n", 405 physpath, strerror(errno)); 406 goto out; 407 } 408 snapshot_taken = 1; 409 } 410 411 if ((n = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, NODE_WWN_PROP, 412 &wwnp)) == -1) { 413 logdmsg("get_guid: di_prop_lookup_bytes() failed to lookup " 414 "%s: %s\n", NODE_WWN_PROP, strerror(errno)); 415 goto out; 416 } 417 418 if (guid_len >= ((n * 2) + 1)) { 419 for (i = 0; i < n; i++) { 420 (void) sprintf(guid + (i * 2), "%02x", (uint_t)(*wwnp)); 421 wwnp++; 422 } 423 rv = 0; 424 } else 425 logerr(gettext("insufficient buffer size: need %1$d " 426 "bytes, passed %2$d bytes\n"), (n * 2) + 1, guid_len); 427 428 out: 429 if (snapshot_taken) 430 di_fini(node); 431 432 (void) close(fd); 433 logdmsg("get_guid: GUID = %s\n", guid); 434 return (rv); 435 } 436 437 /* 438 * Given client_name return whether it is a phci or vhci based name. 439 * client_name is /devices name of a client without the /devices prefix. 440 * 441 * client_name Return value 442 * .../fp@xxx/ssd@yyy CLIENT_TYPE_PHCI 443 * .../scsi_vhci/ssd@yyy CLIENT_TYPE_VHCI 444 * other CLIENT_TYPE_UNKNOWN 445 */ 446 static client_type_t 447 client_name_type(char *client_name) 448 { 449 client_type_t client_type = CLIENT_TYPE_UNKNOWN; 450 char *p1, *p2; 451 452 if (*client_name != '/') 453 return (CLIENT_TYPE_UNKNOWN); 454 455 /* we only care for ssd devices */ 456 if ((p1 = strrchr(client_name, '/')) == NULL || 457 strncmp(p1, SLASH_SSD_AT, sizeof (SLASH_SSD_AT) - 1) != 0) 458 return (CLIENT_TYPE_UNKNOWN); 459 460 *p1 = '\0'; 461 462 if ((p2 = strrchr(client_name, '/')) != NULL) { 463 if (strncmp(p2, SLASH_FP_AT, sizeof (SLASH_FP_AT) - 1) == 0) 464 client_type = CLIENT_TYPE_PHCI; 465 else if (strncmp(p2, SLASH_SCSI_VHCI, 466 sizeof (SLASH_SCSI_VHCI) - 1) == 0) 467 client_type = CLIENT_TYPE_VHCI; 468 } 469 470 *p1 = '/'; 471 return (client_type); 472 } 473 474 /* 475 * Map phci based client name to vhci based client name. 476 * 477 * phci_name 478 * phci based client /devices name without the /devices prefix and 479 * minor name component. 480 * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 481 * 482 * vhci_name 483 * Caller supplied buffer where vhci /devices name will be placed on 484 * return (without the /devices prefix and minor name component). 485 * ex: /scsi_vhci/ssd@g2000002037cd9f72 486 * 487 * vhci_name_len 488 * Length of the caller supplied vhci_name buffer. 489 * 490 * Returns 0 on success, -1 on failure. 491 */ 492 static int 493 phci_to_vhci(char *phci_name, char *vhci_name, size_t vhci_name_len) 494 { 495 sv_iocdata_t ioc; 496 char *slash; 497 char vhci_name_buf[MAXPATHLEN]; 498 char phci_name_buf[MAXPATHLEN]; 499 char addr_buf[MAXNAMELEN]; 500 501 logdmsg("phci_to_vhci: client = %s\n", phci_name); 502 503 s_strlcpy(phci_name_buf, phci_name, MAXPATHLEN); 504 505 if (client_name_type(phci_name_buf) != CLIENT_TYPE_PHCI || 506 (slash = strrchr(phci_name_buf, '/')) == NULL || 507 strncmp(slash, SLASH_SSD_AT, sizeof (SLASH_SSD_AT) - 1) != 0) { 508 logdmsg("phci_to_vhci: %s is not of CLIENT_TYPE_PHCI\n", 509 phci_name); 510 return (-1); 511 } 512 513 if (vhci_fd < 0) { 514 if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) 515 return (-1); 516 } 517 518 *slash = '\0'; 519 s_strlcpy(addr_buf, slash + sizeof (SLASH_SSD_AT) - 1, MAXNAMELEN); 520 521 bzero(&ioc, sizeof (sv_iocdata_t)); 522 ioc.client = vhci_name_buf; 523 ioc.phci = phci_name_buf; 524 ioc.addr = addr_buf; 525 526 if (ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_NAME, &ioc) != 0) { 527 logdmsg("SCSI_VHCI_GET_CLIENT_NAME on %s " 528 "failed: %s\n", phci_name, strerror(errno)); 529 return (-1); 530 } 531 532 s_strlcpy(vhci_name, vhci_name_buf, vhci_name_len); 533 logdmsg("phci_to_vhci: %s maps to %s\n", phci_name, vhci_name); 534 return (0); 535 } 536 537 /* 538 * Map vhci based client name to phci based client name. 539 * If the client has multiple paths, only one of the paths with which client 540 * can be accessed is returned. This function does not use SCSI_VHCI ioctls 541 * as it is called on mpxio disabled paths. 542 * 543 * vhci_name 544 * vhci based client /devices name without the /devices prefix and 545 * minor name component. 546 * ex: /scsi_vhci/ssd@g2000002037cd9f72 547 * 548 * phci_name 549 * Caller supplied buffer where phci /devices name will be placed on 550 * return (without the /devices prefix and minor name component). 551 * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 552 * 553 * phci_name_len 554 * Length of the caller supplied phci_name buffer. 555 * 556 * Returns 0 on success, -1 on failure. 557 */ 558 static int 559 vhci_to_phci(char *vhci_name, char *phci_name, size_t phci_name_len) 560 { 561 di_node_t node, parent; 562 char *vhci_guid, *devfspath; 563 char phci_guid[MAXPATHLEN]; 564 char *parent_name, *node_name; 565 566 logdmsg("vhci_to_phci: client = %s\n", vhci_name); 567 568 if (client_name_type(vhci_name) != CLIENT_TYPE_VHCI) { 569 logdmsg("vhci_to_phci: %s is not of CLIENT_TYPE_VHCI\n", 570 vhci_name); 571 return (-1); 572 } 573 574 if ((vhci_guid = strrchr(vhci_name, '@')) == NULL || 575 *(++vhci_guid) != 'g') { 576 logerr(gettext("couldn't get guid from %s\n"), vhci_name); 577 return (-1); 578 } 579 580 /* point to guid */ 581 ++vhci_guid; 582 583 /* 584 * Get devinfo snapshot and walk all ssd nodes whose parent is fp. 585 * For each node get the guid and match it with vhci_guid. 586 */ 587 if (devinfo_root == DI_NODE_NIL) { 588 logdmsg("vhci_to_phci: taking devinfo snapshot\n"); 589 if ((devinfo_root = di_init("/", DINFOCPYALL | DINFOFORCE)) 590 == DI_NODE_NIL) { 591 logerr(gettext("di_init failed: %s\n"), 592 strerror(errno)); 593 return (-1); 594 } 595 logdmsg("vhci_to_phci: done taking devinfo snapshot\n"); 596 } 597 598 for (node = di_drv_first_node("ssd", devinfo_root); 599 node != DI_NODE_NIL; node = di_drv_next_node(node)) { 600 if ((node_name = di_node_name(node)) == NULL || 601 strcmp(node_name, "ssd") != 0 || 602 (parent = di_parent_node(node)) == DI_NODE_NIL || 603 (parent_name = di_node_name(parent)) == NULL || 604 strcmp(parent_name, "fp") != 0 || 605 (devfspath = di_devfs_path(node)) == NULL) 606 continue; 607 608 /* 609 * Don't set no_delay_flag to have get_guid() fail on 610 * standby paths of T3. So we'll find the preferred paths. 611 */ 612 if (get_guid(devfspath, phci_guid, 613 sizeof (phci_guid), 0, node) == 0 && 614 strcmp(phci_guid, vhci_guid) == 0) { 615 s_strlcpy(phci_name, devfspath, phci_name_len); 616 di_devfs_path_free(devfspath); 617 logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name, 618 phci_name); 619 return (0); 620 } 621 622 di_devfs_path_free(devfspath); 623 } 624 625 logdmsg("vhci_to_phci: couldn't get phci name for %s\n", vhci_name); 626 return (-1); 627 } 628 629 /* 630 * Map physname from phci name space to vhci name space or vice-versa 631 * 632 * physname 633 * phci or vhci based client /devices name without the /devices prefix and 634 * minor name component. 635 * 636 * new_physname 637 * Caller supplied buffer where the mapped physical name is stored on 638 * return (without the /devices prefix and minor name component). 639 * 640 * len 641 * Length of the caller supplied new_physname buffer. 642 * 643 * Returns 0 on success, -1 on failure. 644 */ 645 static int 646 map_physname(char *physname, char *new_physname, size_t len) 647 { 648 int type; 649 int rv; 650 651 if ((type = client_name_type(physname)) == CLIENT_TYPE_VHCI) 652 rv = vhci_to_phci(physname, new_physname, len); 653 else if (type == CLIENT_TYPE_PHCI) 654 rv = phci_to_vhci(physname, new_physname, len); 655 else 656 rv = -1; 657 658 return (rv); 659 } 660 661 /* 662 * Given a phci or vhci devname which is either a /dev link or /devices name 663 * get the corresponding physical node path (without the /devices prefix) 664 * and minor name. 665 * 666 * Returns 0 on success, -1 on failure. 667 */ 668 static int 669 get_physname_minor(char *devname, char *physname, int physname_len, 670 char *minorname, int minorname_len) 671 { 672 int linksize; 673 char buf[MAXPATHLEN]; 674 char *p, *m; 675 676 if (strncmp(devname, DEV_DSK, sizeof (DEV_DSK) - 1) == 0 || 677 strncmp(devname, DEV_RDSK, sizeof (DEV_RDSK) - 1) == 0) { 678 if ((linksize = readlink(devname, buf, MAXPATHLEN)) 679 > 0 && linksize <= (MAXPATHLEN - 1)) { 680 buf[linksize] = '\0'; 681 } else 682 return (-1); 683 } else 684 s_strlcpy(buf, devname, MAXPATHLEN); 685 686 if ((p = strstr(buf, SLASH_DEVICES)) == NULL) 687 return (-1); 688 689 /* point to '/' after /devices */ 690 p += sizeof (SLASH_DEVICES) - 2; 691 692 if ((m = strrchr(p, ':')) == NULL) { 693 logdmsg("get_physname_minor: no minor name component in %s\n", 694 buf); 695 return (-1); 696 } 697 698 *m = '\0'; 699 m++; 700 701 if (client_name_type(p) == CLIENT_TYPE_UNKNOWN) 702 return (-1); 703 704 s_strlcpy(physname, p, physname_len); 705 s_strlcpy(minorname, m, minorname_len); 706 logdmsg("get_physname_minor: %s: physname = %s, minor = %s\n", 707 devname, physname, minorname); 708 return (0); 709 } 710 711 static int 712 devlink_callback(di_devlink_t devlink, void *argptr) 713 { 714 const char *link; 715 struct devlink_cbarg *argp = argptr; 716 717 if ((link = di_devlink_path(devlink)) != NULL) { 718 s_strlcpy(argp->devlink, link, argp->len); 719 return (DI_WALK_TERMINATE); 720 } 721 722 return (DI_WALK_CONTINUE); 723 } 724 725 /* 726 * Lookup the /dev link corresponding to physname and minorname. 727 * 728 * physname client /devices path without the /devices prefix and minor 729 * name component. 730 * minorname client minor name. 731 * devlink caller supplied buffer where the /dev link is placed on return. 732 * len caller supplied devlink buffer length 733 * 734 * Returns 0 on success, -1 on failure. 735 */ 736 static int 737 lookup_devlink(char *physname, char *minorname, char *devlink, size_t len) 738 { 739 char buf[MAXPATHLEN]; 740 struct devlink_cbarg arg; 741 742 if (devlink_hdl == NULL) { 743 logdmsg("lookup_devlink: taking devlink snapshot\n"); 744 if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) { 745 logerr(gettext("di_devlink_init failed: %s\n"), 746 strerror(errno)); 747 clean_exit(1); 748 } 749 } 750 751 *devlink = '\0'; 752 (void) snprintf(buf, MAXPATHLEN, "%s:%s", physname, minorname); 753 arg.devlink = devlink; 754 arg.len = len; 755 if (di_devlink_walk(devlink_hdl, NULL, buf, DI_PRIMARY_LINK, &arg, 756 devlink_callback) != 0) { 757 logdmsg("lookup_devlink: di_devlink_walk on %s failed: %s\n", 758 buf, strerror(errno)); 759 return (-1); 760 } 761 762 if (*devlink == '\0') { 763 logdmsg("lookup_devlink: failed to lookup devlink for %s\n", 764 buf); 765 return (-1); 766 } 767 768 logdmsg("lookup_devlink: /dev link for %s:%s = %s\n", physname, 769 minorname, devlink); 770 return (0); 771 } 772 773 /* 774 * open infile for reading and return its file pointer in *fp_in. 775 * open outfile for writing and return its file pointer in *fp_out. 776 * 777 * Returns 0 on success, -1 on failure. 778 */ 779 static int 780 open_in_out_files(char *infile, char *outfile, FILE **fp_in, FILE **fp_out) 781 { 782 FILE *fin = NULL; 783 FILE *fout = NULL; 784 struct stat sbuf; 785 786 if ((fin = fopen(infile, "r")) == NULL) { 787 logerr(gettext("failed to fopen %1$s: %2$s\n"), 788 infile, strerror(errno)); 789 goto out; 790 } 791 792 if (fstat(fileno(fin), &sbuf) != 0) { 793 logerr(gettext("fstat failed on %1$s: %2$s\n"), 794 infile, strerror(errno)); 795 goto out; 796 } 797 798 if ((fout = fopen(outfile, "w")) == NULL) { 799 logerr(gettext("failed to fopen %1$s: %2$s\n"), 800 outfile, strerror(errno)); 801 goto out; 802 } 803 804 if (fchmod(fileno(fout), (sbuf.st_mode & 0777)) != 0) { 805 logerr(gettext("failed to fchmod %1$s to 0%2$o: %3$s\n"), 806 outfile, sbuf.st_mode & 0777, strerror(errno)); 807 goto out; 808 } 809 810 if (fchown(fileno(fout), sbuf.st_uid, sbuf.st_gid) != 0) { 811 logerr(gettext("failed to fchown %1$s to uid %2$d and " 812 "gid %3$d: %4$s\n"), 813 outfile, sbuf.st_uid, sbuf.st_gid, strerror(errno)); 814 goto out; 815 } 816 817 *fp_in = fin; 818 *fp_out = fout; 819 return (0); 820 821 out: 822 if (fin != NULL) 823 (void) fclose(fin); 824 if (fout != NULL) 825 (void) fclose(fout); 826 return (-1); 827 } 828 829 /* 830 * If the devname is a phci based name and not open-able, map it to vhci 831 * based name. If the devname is a vhci based name and not open-able, map it 832 * to phci based name. 833 * 834 * devname either a /dev link or /devices name to client device 835 * new_devname caller supplied buffer where the mapped device name is 836 * placed on return. 837 * len caller supplied new_devname buffer length 838 * devlink_flag pass 1 if requesting the /dev link to the mapped device. 839 * pass 0 if requesting the /devices name of the mapped device. 840 * 841 * Returns 0 on success, -1 on failure. 842 */ 843 static int 844 map_devname(char *devname, char *new_devname, size_t len, int devlink_flag) 845 { 846 char physname[MAXPATHLEN]; 847 char minor[MAXNAMELEN]; 848 char new_physname[MAXNAMELEN]; 849 850 if (get_physname_minor(devname, physname, sizeof (physname), 851 minor, sizeof (minor)) == 0 && 852 canopen(devname) == 0 && 853 map_physname(physname, new_physname, sizeof (new_physname)) == 0) { 854 855 if (devlink_flag) { 856 if (lookup_devlink(new_physname, minor, new_devname, 857 len) == 0) 858 return (0); 859 } else { 860 (void) snprintf(new_devname, len, "/devices%s:%s", 861 new_physname, minor); 862 return (0); 863 } 864 } 865 866 return (-1); 867 } 868 869 /* 870 * Make a new /etc/vfstab: 871 * Read vfstab_in, convert the device name entries to appropriate vhci or phci 872 * based names, and write to vfstab_out. Only device names whose physical 873 * paths are either phci or vhci based names and not open-able are considered 874 * for conversion. Open-able device name entries are not converted as it 875 * means that the device is already accessible; hence no need to convert. 876 * 877 * Returns: 878 * 0 successful but vfstab_out contents are the same as vfstab_in 879 * 1 successful and vfstab_out changed from vfstab_in 880 * -1 failed 881 */ 882 static int 883 update_vfstab(char *vfstab_in, char *vfstab_out) 884 { 885 FILE *fp_in, *fp_out; 886 char *buf, *tmpbuf; 887 char *vfs_cache[2]; 888 int idx = 0, count = 0; 889 int rv = -1; 890 int vfstab_updated = 0; 891 int i; 892 char cdev[MAXPATHLEN]; 893 char bdev[MAXPATHLEN]; 894 char mntpt[MAXPATHLEN]; 895 char fstype[512]; 896 char fsckpass[512]; 897 char mntboot[512]; 898 char mntopt[MAX_MNTOPT_STR]; 899 char phys_bdev[MAXPATHLEN], phys_cdev[MAXPATHLEN]; 900 char bdev_minor[MAXNAMELEN], cdev_minor[MAXNAMELEN]; 901 char new_physname[MAXPATHLEN]; 902 char new_bdevlink[MAXPATHLEN], new_cdevlink[MAXPATHLEN]; 903 char fmt[80]; 904 905 if (open_in_out_files(vfstab_in, vfstab_out, &fp_in, &fp_out) != 0) 906 return (-1); 907 908 /* 909 * Read one line at time from vfstab_in. If no conversion is needed 910 * for the line simply write the line to vfstab_out. If conversion is 911 * needed, first write the existing line as a comment to vfstab_out 912 * and then write the converted line. 913 * 914 * To avoid commented entries piling up in vfstab in case if the 915 * user runs stmsboot multiple times to switch on and off from mpxio, 916 * add the commented line only if not already there. To do this 917 * cache the last two vfstab lines processed and add the commented 918 * entry only if it is not found in the cache. We only need to cache 919 * the last two lines because a device can have at most two names - 920 * one mpxio and one non-mpxio name. Therefore for any device name 921 * entry we at most add two comments - one with mpxio name and one 922 * with non-mpxio name - no matter how many times stmsboot is run. 923 */ 924 buf = (char *)s_malloc(VFS_LINE_MAX); 925 tmpbuf = (char *)s_malloc(VFS_LINE_MAX); 926 vfs_cache[0] = (char *)s_malloc(VFS_LINE_MAX); 927 vfs_cache[1] = (char *)s_malloc(VFS_LINE_MAX); 928 929 (void) snprintf(fmt, sizeof (fmt), 930 "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1, 931 sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1, 932 sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1); 933 934 while (fgets(buf, VFS_LINE_MAX, fp_in) != NULL) { 935 if (strlen(buf) == (VFS_LINE_MAX - 1) && 936 buf[VFS_LINE_MAX-2] != '\n') { 937 logerr(gettext("%1$s line size too long, " 938 "exceeded %2$d: \"%3$s\"\n"), 939 VFSTAB, VFS_LINE_MAX - 2, buf); 940 goto out; 941 } 942 943 /* LINTED - format specifier */ 944 if ((sscanf(buf, fmt, bdev, cdev, mntpt, 945 fstype, fsckpass, mntboot, mntopt) != 7) || 946 (bdev[0] == '#') || 947 (get_physname_minor(bdev, phys_bdev, sizeof (phys_bdev), 948 bdev_minor, sizeof (bdev_minor)) != 0) || 949 950 (strcmp(fstype, "swap") != 0 && 951 ((get_physname_minor(cdev, phys_cdev, sizeof (phys_cdev), 952 cdev_minor, sizeof (cdev_minor)) != 0) || 953 (strcmp(phys_bdev, phys_cdev) != 0))) || 954 955 canopen(bdev) || 956 (map_physname(phys_bdev, new_physname, 957 sizeof (new_physname)) != 0) || 958 (lookup_devlink(new_physname, bdev_minor, new_bdevlink, 959 sizeof (new_bdevlink)) != 0) || 960 961 (strcmp(fstype, "swap") != 0 && 962 (lookup_devlink(new_physname, cdev_minor, new_cdevlink, 963 sizeof (new_cdevlink)) != 0))) { 964 965 /* cache the last two entries */ 966 (void) strlcpy(vfs_cache[idx], buf, VFS_LINE_MAX); 967 idx = (idx == 0) ? 1 : 0; 968 if (count < 2) 969 count++; 970 971 if (fputs(buf, fp_out) == EOF) { 972 logerr(gettext("fputs \"%1$s\" to %2$s " 973 "failed: %3$s\n"), 974 buf, vfstab_out, strerror(errno)); 975 goto out; 976 } 977 978 } else { 979 /* 980 * comment the entry in vfstab only if it is not 981 * already in the cache. 982 */ 983 if (client_name_type(phys_bdev) == CLIENT_TYPE_VHCI) 984 (void) snprintf(tmpbuf, VFS_LINE_MAX, 985 "# mpxio: %s", buf); 986 else 987 (void) snprintf(tmpbuf, VFS_LINE_MAX, 988 "# non-mpxio: %s", buf); 989 990 for (i = 0; i < count; i++) { 991 if (strcmp(vfs_cache[i], tmpbuf) == 0) 992 break; 993 } 994 995 if (i == count) { 996 if (fputs(tmpbuf, fp_out) == EOF) { 997 logerr(gettext("fputs \"%1$s\" to %2$s " 998 "failed: %3$s\n"), tmpbuf, 999 vfstab_out, strerror(errno)); 1000 goto out; 1001 } 1002 } 1003 1004 count = 0; 1005 idx = 0; 1006 1007 if (fprintf(fp_out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 1008 new_bdevlink, 1009 (strcmp(fstype, "swap") != 0) ? new_cdevlink : cdev, 1010 mntpt, fstype, fsckpass, mntboot, mntopt) < 0) { 1011 logerr(gettext("fprintf failed to write to " 1012 "%1$s: %2$s\n"), 1013 vfstab_out, strerror(errno)); 1014 goto out; 1015 } 1016 vfstab_updated = 1; 1017 } 1018 } 1019 1020 rv = vfstab_updated; 1021 out: 1022 (void) fclose(fp_in); 1023 (void) fclose(fp_out); 1024 free(buf); 1025 free(tmpbuf); 1026 free(vfs_cache[0]); 1027 free(vfs_cache[1]); 1028 return (rv); 1029 } 1030 1031 /* 1032 * if guidmap is 0, list non-STMS to STMS device name mappings for the 1033 * specified controller. 1034 * if guidmap is 1, list non-STMS to GUID mappings for the specified controller. 1035 * If controller is -1 list mappings for all controllers. 1036 * 1037 * Returns 0 on success, -1 on failure. 1038 */ 1039 static int 1040 list_mappings(int controller, int guidmap) 1041 { 1042 int cnum, len, mapped; 1043 int header = 1; 1044 char *p1, *p2; 1045 DIR *dirp; 1046 struct dirent *direntry; 1047 char devname[MAXPATHLEN]; 1048 char physname[MAXPATHLEN]; 1049 char new_devname[MAXPATHLEN]; 1050 char new_physname[MAXPATHLEN]; 1051 char guid[MAXPATHLEN]; 1052 char minor[MAXNAMELEN]; 1053 1054 if ((dirp = opendir("/dev/rdsk")) == NULL) 1055 return (-1); 1056 1057 while ((direntry = readdir(dirp)) != NULL) { 1058 if (strcmp(direntry->d_name, ".") == 0 || 1059 strcmp(direntry->d_name, "..") == 0 || 1060 (len = strlen(direntry->d_name)) < 2 || 1061 strcmp(direntry->d_name + len - 2, "s0") != 0 || 1062 sscanf(direntry->d_name, "c%dt", &cnum) != 1 || 1063 (controller != -1 && controller != cnum)) 1064 continue; 1065 1066 (void) snprintf(devname, MAXPATHLEN, "/dev/rdsk/%s", 1067 direntry->d_name); 1068 1069 if (get_physname_minor(devname, physname, sizeof (physname), 1070 minor, sizeof (minor)) != 0 || 1071 client_name_type(physname) != CLIENT_TYPE_PHCI) 1072 continue; 1073 1074 /* 1075 * First try phci_to_vhci() mapping. It will work if the 1076 * device is under MPxIO control. If the device is not under 1077 * MPxIO, phci_to_vhci() will fail in which case try to lookup 1078 * if an old mapping exists using guid lookup. 1079 */ 1080 mapped = 1; 1081 if (phci_to_vhci(physname, new_physname, 1082 sizeof (new_physname)) != 0) { 1083 if (get_guid(physname, guid, sizeof (guid), 1, 1084 DI_NODE_NIL) == 0) 1085 (void) snprintf(new_physname, MAXPATHLEN, 1086 "/scsi_vhci/ssd@g%s", guid); 1087 else 1088 mapped = 0; 1089 } 1090 1091 if (mapped == 0) 1092 continue; 1093 1094 /* strip the slice number part */ 1095 devname[strlen(devname) - 2] = '\0'; 1096 1097 if (guidmap == 0) { 1098 if (lookup_devlink(new_physname, minor, 1099 new_devname, sizeof (new_devname)) != 0) 1100 continue; 1101 1102 /* strip the slice number part */ 1103 new_devname[strlen(new_devname) - 2] = '\0'; 1104 1105 if (header) { 1106 (void) printf( 1107 gettext("non-STMS device name\t\t\t" 1108 "STMS device name\n" 1109 "------------------------------------------" 1110 "------------------------\n")); 1111 header = 0; 1112 } 1113 (void) printf("%s\t\t%s\n", devname, new_devname); 1114 } else { 1115 /* extract guid part */ 1116 if ((p1 = strstr(new_physname, "ssd@g")) == NULL) { 1117 logdmsg("invalid vhci: %s\n", new_physname); 1118 continue; 1119 } 1120 p1 += sizeof ("ssd@g") - 1; 1121 if ((p2 = strrchr(p1, ':')) != NULL) 1122 *p2 = '\0'; 1123 1124 if (header) { 1125 (void) printf( 1126 gettext("non-STMS device name\t\t\tGUID\n" 1127 "------------------------------------------" 1128 "------------------------\n")); 1129 header = 0; 1130 } 1131 (void) printf("%s\t\t%s\n", devname, p1); 1132 } 1133 } 1134 1135 (void) closedir(dirp); 1136 return (0); 1137 } 1138 1139 /* 1140 * Check if the file can be opened. 1141 * 1142 * Return 1 if the file can be opened, 0 otherwise. 1143 */ 1144 static int 1145 canopen(char *filename) 1146 { 1147 int fd; 1148 1149 if ((fd = open(filename, O_RDONLY)) == -1) 1150 return (0); 1151 1152 (void) close(fd); 1153 return (1); 1154 } 1155 1156 static void 1157 logerr(char *msg, ...) 1158 { 1159 va_list ap; 1160 1161 (void) fprintf(stderr, "%s: ", stmsboot); 1162 va_start(ap, msg); 1163 /* LINTED - format specifier */ 1164 (void) vfprintf(stderr, msg, ap); 1165 va_end(ap); 1166 } 1167 1168 /* log debug message */ 1169 static void 1170 logdmsg(char *msg, ...) 1171 { 1172 va_list ap; 1173 1174 if (debug) { 1175 va_start(ap, msg); 1176 /* LINTED - format specifier */ 1177 (void) vprintf(msg, ap); 1178 va_end(ap); 1179 } 1180 } 1181 1182 static void * 1183 s_malloc(const size_t size) 1184 { 1185 void *rp; 1186 1187 if ((rp = malloc(size)) == NULL) { 1188 logerr(gettext("malloc failed to allocate %d bytes\n"), size); 1189 clean_exit(1); 1190 } 1191 return (rp); 1192 } 1193 1194 static char * 1195 s_strdup(const char *ptr) 1196 { 1197 void *rp; 1198 1199 if ((rp = strdup(ptr)) == NULL) { 1200 logerr(gettext("strdup failed to dup %s\n"), ptr); 1201 clean_exit(1); 1202 } 1203 return (rp); 1204 } 1205 1206 static void 1207 s_strlcpy(char *dst, const char *src, size_t dstsize) 1208 { 1209 int n; 1210 1211 if ((n = strlcpy(dst, src, dstsize)) >= dstsize) { 1212 logerr(gettext("strlcpy: destination buffer size is %1$d " 1213 "bytes, need to at least %2$d bytes\n"), dstsize, n + 1); 1214 clean_exit(1); 1215 } 1216 } 1217 1218 static void 1219 clean_exit(int status) 1220 { 1221 if (devinfo_root != DI_NODE_NIL) 1222 di_fini(devinfo_root); 1223 1224 if (devlink_hdl != NULL) 1225 (void) di_devlink_fini(&devlink_hdl); 1226 1227 if (vhci_fd != -1) 1228 (void) close(vhci_fd); 1229 1230 exit(status); 1231 } 1232