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