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