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