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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <stdarg.h> 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include <fcntl.h> 33 #include <errno.h> 34 #include <unistd.h> 35 #include <stropts.h> 36 #include <strings.h> 37 #include <sys/param.h> 38 #include <libdevinfo.h> 39 #include <locale.h> 40 #include <libintl.h> 41 #include <devid.h> 42 #include <sys/libdevid.h> 43 #include <sys/modctl.h> /* for MAXMODCONFNAME */ 44 #include <sys/scsi/adapters/scsi_vhci.h> 45 46 /* 47 * SAVE_DIR is the directory in which system files are saved. 48 * SAVE_DIR must be under the root filesystem, as this program is 49 * typically run before any other filesystems are mounted. 50 */ 51 #define SAVE_DIR "/etc/mpxio" 52 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl" 53 54 /* nvlist property names, these are ALL string types */ 55 #define NVL_DEVID "nvl-devid" 56 #define NVL_PATH "nvl-path" 57 #define NVL_PHYSPATH "nvl-physpath" 58 #define NVL_MPXPATH "nvl-mpxiopath" 59 #define NVL_MPXEN "nvl-mpxioenabled" 60 61 #define MPX_LIST 0x01 62 #define MPX_MAP 0x02 63 #define MPX_CAPABLE_CTRL 0x04 64 #define MPX_INIT 0x08 65 #define MPX_PHYSICAL 0x10 66 #define MPX_BOOTPATH 0x20 67 #define MPX_UPDATEVFSTAB 0x40 68 #define MPX_USAGE 0x80 69 #define MSG_INFO 0x01 70 #define MSG_ERROR 0x02 71 #define MSG_PANIC 0x04 72 73 #define BOOT 0x01 74 #define NONBOOT 0x00 75 76 static di_node_t devinfo_root = DI_NODE_NIL; 77 static char *ondiskname = "/etc/mpxio/devid_path.cache"; 78 79 /* 80 * We use devid-keyed nvlists to keep track of the guid, traditional and 81 * MPxIO-enabled /dev/rdsk paths. Each of these nvlists is eventually 82 * added to our global nvlist and our on-disk nvlist. 83 */ 84 static nvlist_t *mapnvl; 85 static int mpxenabled = 0; 86 static int limctrl = -1; 87 static int mpxprop = 0; 88 static int guid = 0; 89 static char *drvlimit; 90 static int globarg = 0; 91 static int debugflag = 0; 92 static char *devicep; 93 static int readonlyroot = 0; 94 static int cap_N_option = 0; 95 96 static void print_mpx_capable(di_node_t curnode); 97 static int popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, 98 char *strdevid); 99 static int mpxio_nvl_boilerplate(di_node_t curnode); 100 static int validate_devnvl(); 101 static void report_map(char *argdev, int physpath); 102 static void list_devs(int listguids, int ctrl); 103 static void logmsg(int level, const char *msg, ...); 104 static char *find_link(di_node_t cnode); 105 static void usage(); 106 static void parse_args(int argc, char *argv[]); 107 static void get_devid(di_node_t node, ddi_devid_t *thisdevid); 108 static int print_bootpath(); 109 static void vhci_to_phci(char *devpath, char *physpath); 110 static int update_vfstab(); 111 112 int 113 main(int argc, char **argv) 114 { 115 struct stat cachestat; 116 int mapfd = 0; 117 int rv = 0; 118 char *ondiskbuf; 119 size_t newsz = 0; 120 121 parse_args(argc, argv); 122 errno = 0; 123 devinfo_root = di_init("/", DINFOCPYALL|DINFOFORCE); 124 logmsg(MSG_INFO, "errno = %d after " 125 "di_init(/,DINFOCPYALL|DINFOFORCE)\n", errno); 126 if (devinfo_root == NULL) { 127 logmsg(MSG_ERROR, 128 gettext("Unable to take device tree snapshot " 129 "(%s: %d)\n"), strerror(errno), errno); 130 return (-1); 131 } 132 logmsg(MSG_INFO, "opened root di_node\n"); 133 134 if (globarg == MPX_CAPABLE_CTRL) { 135 /* we just want to find MPxIO-capable controllers and exit */ 136 if (drvlimit != NULL) { 137 print_mpx_capable(di_drv_first_node(drvlimit, 138 devinfo_root)); 139 } else { 140 print_mpx_capable(di_drv_first_node("fp", 141 devinfo_root)); 142 print_mpx_capable(di_drv_first_node("mpt", 143 devinfo_root)); 144 print_mpx_capable(di_drv_first_node("mpt_sas", 145 devinfo_root)); 146 } 147 di_fini(devinfo_root); 148 return (0); 149 } 150 151 mapfd = open(ondiskname, O_RDWR|O_CREAT|O_SYNC, S_IRUSR | S_IWUSR); 152 if (mapfd < 0) { 153 /* we could be in single-user, so try for RO */ 154 if ((mapfd = open(ondiskname, O_RDONLY)) < 0) { 155 logmsg(MSG_ERROR, 156 gettext("Unable to open or create %s:%s\n"), 157 ondiskname, strerror(errno)); 158 return (errno); 159 } 160 readonlyroot = 1; 161 } 162 163 if (stat(ondiskname, &cachestat) != 0) { 164 logmsg(MSG_ERROR, 165 gettext("Unable to stat() %s: %s\n"), 166 ondiskname, strerror(errno)); 167 return (errno); 168 } 169 ondiskbuf = calloc(1, cachestat.st_size); 170 if (ondiskbuf == NULL) { 171 logmsg(MSG_ERROR, 172 gettext("Unable to allocate memory for the devid " 173 "cache file: %s\n"), strerror(errno)); 174 return (errno); 175 } 176 rv = read(mapfd, ondiskbuf, cachestat.st_size); 177 if (rv != cachestat.st_size) { 178 logmsg(MSG_ERROR, 179 gettext("Unable to read all of devid cache file (got %d " 180 "from expected %d bytes): %s\n"), 181 rv, cachestat.st_size, strerror(errno)); 182 return (errno); 183 } 184 errno = 0; 185 rv = nvlist_unpack(ondiskbuf, cachestat.st_size, &mapnvl, 0); 186 if (rv) { 187 logmsg(MSG_INFO, 188 "Unable to unpack devid cache file %s: %s (%d)\n", 189 ondiskname, strerror(rv), rv); 190 if (nvlist_alloc(&mapnvl, NV_UNIQUE_NAME, 0) != 0) { 191 logmsg(MSG_ERROR, 192 gettext("Unable to allocate root property" 193 "list\n")); 194 return (errno); 195 } 196 } 197 free(ondiskbuf); 198 199 if (validate_devnvl() < 0) { 200 logmsg(MSG_ERROR, 201 gettext("unable to validate kernel with on-disk devid " 202 "cache file\n")); 203 return (errno); 204 } 205 206 /* 207 * If we're in single-user mode or maintenance mode, we won't 208 * necessarily have a writable root device (ZFSroot; ufs root is 209 * different in that we _do_ have a writable root device. 210 * This causes problems for the devlink calls (see 211 * $SRC/lib/libdevinfo/devinfo_devlink.c) and we do not try to 212 * write out the devnvl if root is readonly. 213 */ 214 if (!readonlyroot) { 215 rv = nvlist_size(mapnvl, &newsz, NV_ENCODE_NATIVE); 216 if (rv) { 217 logmsg(MSG_ERROR, 218 gettext("Unable to determine size of packed " 219 "on-disk devid cache file %s: %s (%d).\n"), 220 ondiskname, strerror(rv), rv); 221 logmsg(MSG_ERROR, gettext("Terminating\n")); 222 nvlist_free(mapnvl); 223 (void) close(mapfd); 224 return (rv); 225 } 226 227 if ((ondiskbuf = calloc(1, newsz)) == NULL) { 228 logmsg(MSG_ERROR, 229 "Unable to allocate space for writing out new " 230 "on-disk devid cache file: %s\n", strerror(errno)); 231 (void) close(mapfd); 232 nvlist_free(mapnvl); 233 return (errno); 234 } 235 236 rv = nvlist_pack(mapnvl, &ondiskbuf, &newsz, 237 NV_ENCODE_NATIVE, 0); 238 if (rv) { 239 logmsg(MSG_ERROR, 240 gettext("Unable to pack on-disk devid cache " 241 "file: %s (%d)\n"), strerror(rv), rv); 242 (void) close(mapfd); 243 free(ondiskbuf); 244 nvlist_free(mapnvl); 245 return (rv); 246 } 247 248 rv = lseek(mapfd, 0, 0); 249 if (rv == -1) { 250 logmsg(MSG_ERROR, 251 gettext("Unable to seek to start of devid cache " 252 "file: %s (%d)\n"), strerror(errno), errno); 253 (void) close(mapfd); 254 free(ondiskbuf); 255 nvlist_free(mapnvl); 256 return (-1); 257 } 258 259 if (write(mapfd, ondiskbuf, newsz) != newsz) { 260 logmsg(MSG_ERROR, 261 gettext("Unable to completely write out " 262 "on-disk devid cache file: %s\n"), strerror(errno)); 263 (void) close(mapfd); 264 nvlist_free(mapnvl); 265 free(ondiskbuf); 266 return (errno); 267 } 268 } /* !readonlyroot */ 269 270 /* Now we can process the command line args */ 271 if (globarg == MPX_PHYSICAL) { 272 report_map(devicep, BOOT); 273 } else if (globarg == MPX_BOOTPATH) { 274 rv = print_bootpath(); 275 di_fini(devinfo_root); 276 return (rv); 277 } else if (globarg == MPX_UPDATEVFSTAB) { 278 rv = update_vfstab(); 279 di_fini(devinfo_root); 280 return (rv); 281 } else if (globarg != MPX_INIT) { 282 if (globarg & MPX_LIST) 283 list_devs(guid, limctrl); 284 285 if (globarg == MPX_MAP) 286 report_map(devicep, NONBOOT); 287 } else { 288 logmsg(MSG_INFO, "\nprivate devid cache file initialised\n"); 289 } 290 291 nvlist_free(mapnvl); 292 di_fini(devinfo_root); 293 return (0); 294 } 295 296 static void 297 usage() 298 { 299 (void) fprintf(stderr, 300 gettext("usage: stmsboot_util -b | -m devname | " 301 "-l <ctrl> | -L | [-g] | -n | -N | -i | -p devname\n")); 302 (void) fprintf(stderr, "\n\n"); 303 (void) fprintf(stderr, gettext("\t-h\tprint this usage message\n")); 304 (void) fprintf(stderr, gettext("\t-b\tretrieve the system's bootpath " 305 "setting\n")); 306 (void) fprintf(stderr, gettext("\t-m devname\n")); 307 (void) fprintf(stderr, gettext("\t\tReports the current mapping for " 308 "devname\n")); 309 (void) fprintf(stderr, gettext("\t-g\tprint the GUID for MPxIO-capable " 310 "devices. This\n")); 311 (void) fprintf(stderr, gettext("\t\toption is only valid with the -L " 312 "or -l options\n")); 313 (void) fprintf(stderr, gettext("\t-L | -l <ctrl>\n")); 314 (void) fprintf(stderr, gettext("\t\tList the 'native' to 'MPxIO' " 315 "device mappings. If <ctrl>\n")); 316 (void) fprintf(stderr, gettext("\t\tis specified, only print mappings " 317 "for those devices\n")); 318 (void) fprintf(stderr, gettext("\t\tattached via the specified " 319 "controller.\n")); 320 (void) fprintf(stderr, gettext("\t-i\tinitialise the private devid " 321 "cache file and exit\n")); 322 (void) fprintf(stderr, gettext("\t\tThis option excludes all " 323 "others.\n")); 324 (void) fprintf(stderr, gettext("\t-n\tprint the devfs paths for " 325 "multipath-capable\n")); 326 (void) fprintf(stderr, gettext("\t\tcontroller ports.\n")); 327 (void) fprintf(stderr, gettext("\t-N\tprint the device aliases of " 328 "multipath-capable\n")); 329 (void) fprintf(stderr, gettext("\t\tcontroller ports.\n")); 330 (void) fprintf(stderr, gettext("\t-p\tdevname\n")); 331 (void) fprintf(stderr, gettext("\t\tThis option provides the physical " 332 "devfs path for\n")); 333 (void) fprintf(stderr, gettext("\t\ta specific device (devname). Used " 334 "to set the bootpath\n")); 335 (void) fprintf(stderr, gettext("\t\tvariable on x86/x64 systems\n")); 336 (void) fprintf(stderr, gettext("\t-u\ttranslates device mappings in " 337 "/etc/vfstab as \n")); 338 (void) fprintf(stderr, gettext("\t\trequired. The output is written " 339 "to /etc/mpxio/vfstab.new\n\n")); 340 exit(2); 341 } 342 343 static void 344 parse_args(int argc, char *argv[]) 345 { 346 char opt; 347 348 if (argc == 1) 349 usage(); 350 351 /* 352 * -b prints the bootpath property 353 * -d turns on debug mode for this utility (copious output!) 354 * -D drvname 355 * if supplied, indicates that we're going to operate on 356 * devices attached to this driver. 357 * -g if (-l or -L), prints guids for devices rather than paths 358 * -h prints the usage() help text. 359 * -i initialises the cache file and exits. 360 * -l controller 361 * list non-STMS to STMS device name mappings for the specific 362 * controller, when MPxIO is enabled only. 363 * -L list non-STMS to STMS device name mappings for all controllers 364 * when MPxIO is enabled only. 365 * -m devname 366 * prints the device path (/dev/rdsk) that devname maps to 367 * in the currently-running system. 368 * -n 369 * if supplied, returns name of STMS-capable controller nodes. 370 * If the -D drvname option is specified as well, we only report 371 * nodes attached with drvname. 372 * -N 373 * same as the -n option, except that we only print the 374 * node-name (dev_info :: devi_node_name). Multiple instances 375 * through the libdevinfo snapshot are uniqified and separated 376 * by the "|" character for direct use by egrep(1). 377 * -p devname 378 * prints the physical devfs path for devname. Only used to 379 * determine the bootpath. 380 * -u 381 * remaps devices in /etc/vfstab, saving the newly generated 382 * file to /etc/mpxio/vfstab.new. If we have any remapped 383 * devices, exit with status 0, otherwise -1 for error. 384 */ 385 while ((opt = getopt(argc, argv, "bdD:ghil:Lm:nNp:u")) != EOF) { 386 switch (opt) { 387 case 'b': 388 globarg = MPX_BOOTPATH; 389 break; 390 case 'd': 391 debugflag = 1; 392 break; 393 case 'D': 394 if ((drvlimit = calloc(1, MAXMODCONFNAME)) == NULL) { 395 logmsg(MSG_ERROR, 396 gettext("Unable to allocate memory for a " 397 "driver name: %s\n"), strerror(errno)); 398 exit(errno); 399 } 400 bcopy(optarg, drvlimit, strlen(optarg)); 401 /* update this if adding support for a new driver */ 402 if ((strncmp(drvlimit, "fp", 2) == NULL) && 403 (strncmp(drvlimit, "mpt", 3) == NULL) && 404 (strncmp(drvlimit, "mpt_sas", 7) == NULL)) { 405 logmsg(MSG_ERROR, 406 gettext("invalid parent driver (%s) " 407 "specified"), drvlimit); 408 usage(); 409 } 410 break; 411 case 'h': 412 /* Just drop out and print the usage() output */ 413 globarg = MPX_USAGE; 414 break; 415 case 'i': 416 globarg = MPX_INIT; 417 break; 418 case 'l': 419 globarg |= MPX_LIST; 420 limctrl = (int)atol(optarg); 421 if (limctrl < 0) { 422 logmsg(MSG_INFO, 423 gettext("invalid controller number " 424 "(%d), checking all controllers\n"), 425 limctrl); 426 } 427 break; 428 case 'L': 429 globarg |= MPX_LIST; 430 break; 431 case 'g': 432 guid = 1; 433 break; 434 case 'm': 435 globarg = MPX_MAP; 436 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) { 437 logmsg(MSG_ERROR, 438 gettext("Unable to allocate space for a " 439 "device name\n")); 440 exit(errno); 441 } 442 devicep = strdup(optarg); 443 break; 444 case 'N': 445 cap_N_option = 1; 446 globarg = MPX_CAPABLE_CTRL; 447 break; 448 case 'n': 449 globarg = MPX_CAPABLE_CTRL; 450 break; 451 case 'p': 452 globarg = MPX_PHYSICAL; 453 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) { 454 logmsg(MSG_ERROR, 455 gettext("Unable to allocate space for a " 456 "device name\n")); 457 exit(errno); 458 } 459 devicep = strdup(optarg); 460 break; 461 case 'u': 462 globarg = MPX_UPDATEVFSTAB; 463 break; 464 default: 465 logmsg(MSG_ERROR, 466 gettext("Invalid command line option (%c)\n"), 467 opt); 468 usage(); 469 } 470 } 471 472 if ((globarg >= MPX_USAGE) || (guid && (globarg != MPX_LIST))) 473 usage(); 474 475 if ((drvlimit != NULL) && 476 ((globarg != MPX_LIST) && 477 (globarg != MPX_CAPABLE_CTRL))) 478 usage(); 479 } 480 481 static void 482 logmsg(int level, const char *msg, ...) 483 { 484 va_list ap; 485 486 if ((level >= MSG_ERROR) || 487 ((debugflag > 0) && (level >= MSG_INFO))) { 488 (void) fprintf(stdout, "stmsboot: "); 489 va_start(ap, msg); 490 (void) vfprintf(stdout, msg, ap); 491 va_end(ap); 492 } 493 } 494 495 /* 496 * It's up to the caller to do any sorting or pretty-printing of the device 497 * mappings we report. Since we're storing the device links as just the cXtYdZ 498 * part, we'll add /dev/rdsk/ back on when we print the listing so we maintain 499 * compatibility with previous versions of this tool. There's a little bit 500 * of footwork involved to make sure that we show all the paths to a device 501 * rather than just the first one we stashed away. 502 */ 503 static void 504 list_devs(int listguids, int ctrl) 505 { 506 nvlist_t *thisdevnvl; 507 nvpair_t *pair; 508 char *diskpath, *livepath, *key, *querydev; 509 char *matchctrl = NULL; 510 char checkctrl[MAXPATHLEN]; 511 int rv; 512 513 if (!mpxenabled) { 514 if (mpxprop) { 515 logmsg(MSG_ERROR, gettext("MPXIO disabled\n")); 516 } else { 517 logmsg(MSG_ERROR, gettext("No STMS devices have " 518 "been found\n")); 519 } 520 return; 521 } 522 523 if (listguids) { 524 (void) printf(gettext("non-STMS device name\t\t\tGUID\n" 525 "------------------------------------------" 526 "------------------------\n")); 527 } else { 528 (void) printf(gettext("non-STMS device name\t\t\t" 529 "STMS device name\n" 530 "------------------------------------------" 531 "------------------------\n")); 532 } 533 534 bzero(checkctrl, MAXPATHLEN); 535 pair = NULL; 536 while ((pair = nvlist_next_nvpair(mapnvl, pair)) 537 != NULL) { 538 boolean_t livescsivhcip = B_FALSE; 539 540 if ((((rv = nvpair_value_string(pair, &querydev)) < 0) || 541 ((key = nvpair_name(pair)) == NULL)) || 542 ((strstr(key, "/pci") != NULL) || 543 (strstr(key, "/sbus") != NULL) || 544 (strstr(key, "/scsi_vhci") != NULL) || 545 (strncmp(key, "id1", 3) == 0))) { 546 logmsg(MSG_INFO, 547 "list_devs: rv = %d; (%s) is not a devlink, " 548 "continuing.\n", rv, 549 (key != NULL) ? key : "null"); 550 querydev = NULL; 551 continue; 552 } 553 554 (void) nvlist_lookup_nvlist(mapnvl, querydev, &thisdevnvl); 555 (void) nvlist_lookup_boolean_value(thisdevnvl, NVL_MPXEN, 556 &livescsivhcip); 557 (void) nvlist_lookup_string(thisdevnvl, NVL_MPXPATH, 558 &livepath); 559 560 if ((!livescsivhcip) || 561 (livescsivhcip && 562 (strncmp(key, livepath, strlen(key)) == 0))) 563 continue; 564 565 (void) nvlist_lookup_string(thisdevnvl, NVL_PATH, 566 &diskpath); 567 568 logmsg(MSG_INFO, 569 "list_devs: %s :: %s ::%s :: MPXEN (%s)\n", 570 key, diskpath, livepath, 571 ((livescsivhcip) ? "TRUE" : "FALSE")); 572 573 if (ctrl > -1) { 574 (void) sprintf(checkctrl, "c%dt", ctrl); 575 matchctrl = strstr(key, checkctrl); 576 if (matchctrl == NULL) 577 continue; 578 } 579 if (listguids != 0) { 580 char *tempguid; 581 ddi_devid_t curdevid; 582 int rv; 583 584 rv = devid_str_decode(querydev, &curdevid, NULL); 585 if (rv == -1) { 586 logmsg(MSG_INFO, "Unable to decode devid %s\n", 587 key); 588 continue; 589 } 590 tempguid = devid_to_guid(curdevid); 591 if (tempguid != NULL) 592 (void) printf("/dev/rdsk/%s\t%s\n", 593 diskpath, tempguid); 594 595 devid_free_guid(tempguid); 596 devid_free(curdevid); 597 continue; 598 } 599 600 (void) printf("/dev/rdsk/%s\t/dev/rdsk/%s\n", 601 (strstr(key, diskpath) == NULL) ? key : diskpath, 602 livepath); 603 } 604 } 605 606 /* 607 * We get passed a device name which we search the mapnvl for. If we find 608 * it, we print the mapping as it is found. It is up to the caller of this 609 * utility to do any pretty-printing of the results. If a device listed on 610 * the command line does not exist in the mapnvl, then we print NOT_MAPPED. 611 * Otherwise we print the command-line device name as it maps to what is 612 * stashed in the mapnvl - even if that's a "no change" device mapping. 613 * 614 * Example output (-p maps to physpath=BOOT) 615 * # /lib/mpxio/stmsboot_util -p \ 616 * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a 617 * /scsi_vhci/disk@g500000e011e17720:a 618 * 619 * Or the reverse: 620 * # /lib/mpxio/stmsboot_util -p /scsi_vhci/disk@g500000e011e17720:a 621 * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a 622 * 623 * For the -m option, used when we're trying to find the root device mapping: 624 * 625 * # /lib/mpxio/stmsboot_util -m /dev/dsk/c2t0d0s2 626 * /dev/dsk/c3t500000E011637CF0d0s2 627 */ 628 static void 629 report_map(char *argdev, int physpath) 630 { 631 nvlist_t *thisdev; 632 int rv = 0; 633 char *thisdevid; 634 char *mpxpath = NULL; 635 char *prefixt = NULL; 636 char *prefixp = NULL; 637 char *stripdev = NULL; 638 char *slice = NULL; 639 boolean_t mpxenp; 640 uint_t slicelen = 0; 641 642 mpxenp = B_FALSE; 643 644 if ((prefixt = calloc(1, strlen(argdev) + 1)) == NULL) { 645 logmsg(MSG_INFO, "Unable to allocate memory\n"); 646 (void) printf("NOT_MAPPED\n"); 647 return; 648 } 649 650 (void) strlcpy(prefixt, argdev, strlen(argdev) + 1); 651 652 slice = strrchr(argdev, (physpath == BOOT) ? ':' : 's'); 653 if (slice != NULL) { 654 slicelen = strlen(slice); 655 if (slicelen > 3) 656 /* invalid size - max is 3 chars */ 657 slicelen = 0; 658 } 659 660 if ((stripdev = calloc(1, strlen(prefixt) + 1)) == NULL) { 661 logmsg(MSG_INFO, "Unable to allocate memory\n"); 662 (void) printf("NOT_MAPPED\n"); 663 free(prefixt); 664 return; 665 } 666 667 if ((strstr(prefixt, "/scsi_vhci") == NULL) && 668 (strstr(prefixt, "/pci") == NULL) && 669 (strstr(prefixt, "/sbus") == NULL)) { 670 prefixp = strrchr(prefixt, '/'); 671 (void) strlcpy(stripdev, 672 (prefixp == NULL) ? prefixt : prefixp + 1, 673 (prefixp == NULL) ? 674 strlen(prefixt) + 1: strlen(prefixp) + 1); 675 if (prefixp != NULL) 676 prefixt[strlen(argdev) - strlen(prefixp) + 1] = '\0'; 677 } else { 678 if (physpath != BOOT) { 679 logmsg(MSG_INFO, "Invalid device path provided\n"); 680 (void) printf("NOT_MAPPED\n"); 681 free(stripdev); 682 free(prefixt); 683 return; 684 } 685 (void) strlcpy(stripdev, argdev, strlen(argdev) + 1); 686 } 687 688 logmsg(MSG_INFO, 689 "stripdev (%s), prefixt(%s), prefixp(%s), slice(%s)\n", 690 (stripdev == NULL) ? "null" : stripdev, 691 (prefixt == NULL) ? "null" : prefixt, 692 (prefixp == NULL) ? "null" : prefixp, 693 (slice == NULL) ? "null" : slice); 694 695 if (slicelen > 0) 696 stripdev[strlen(stripdev) - slicelen] = '\0'; 697 698 /* search for the shortened version */ 699 rv = nvlist_lookup_string(mapnvl, stripdev, &thisdevid); 700 if (rv) { 701 if (physpath != BOOT) { 702 logmsg(MSG_INFO, 703 "searched mapnvl for '%s', got %s (%d)\n", 704 stripdev, strerror(rv), rv); 705 (void) printf("NOT_MAPPED\n"); 706 free(stripdev); 707 free(prefixt); 708 return; 709 } 710 } 711 712 logmsg(MSG_INFO, "device %s has devid %s\n", stripdev, thisdevid); 713 714 if (nvlist_lookup_nvlist(mapnvl, thisdevid, &thisdev) != 0) { 715 logmsg(MSG_INFO, "device (%s) in mapnvl but " 716 "not mapped!\n", thisdevid); 717 (void) printf("NOT_MAPPED\n"); 718 free(stripdev); 719 free(prefixt); 720 return; 721 } 722 723 /* quick exit */ 724 if (!mpxenabled && (strstr(argdev, "/pci") != NULL || 725 strstr(argdev, "/sbus") != NULL)) { 726 (void) printf("%s\n", argdev); 727 free(stripdev); 728 free(prefixt); 729 return; 730 } 731 732 (void) nvlist_lookup_boolean_value(thisdev, NVL_MPXEN, &mpxenp); 733 734 if (physpath == BOOT) { 735 (void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath); 736 if ((strstr(argdev, "/scsi_vhci") != NULL) && 737 (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) { 738 /* Need to translate vhci to phci */ 739 char *realpath; 740 741 if ((realpath = calloc(1, MAXPATHLEN + 1)) == NULL) { 742 logmsg(MSG_ERROR, 743 gettext("Unable to allocate " 744 "memory for a path element\n")); 745 free(stripdev); 746 free(prefixt); 747 return; 748 } 749 vhci_to_phci(stripdev, realpath); 750 (void) printf("%s%s\n", realpath, 751 ((slicelen > 0) && slice != NULL) ? slice : ""); 752 free(realpath); 753 } else { 754 (void) printf("%s%s\n", mpxpath, 755 ((slicelen > 0) && slice != NULL) ? slice : ""); 756 } 757 } else { 758 (void) nvlist_lookup_string(thisdev, 759 ((readonlyroot) ? NVL_PHYSPATH : 760 ((mpxenp == B_TRUE) ? NVL_MPXPATH : NVL_PATH)), 761 &mpxpath); 762 logmsg(MSG_INFO, "mpxpath = %s\n", 763 (mpxpath == NULL) ? "null" : mpxpath); 764 if (readonlyroot || 765 (strstr(mpxpath, "/scsi_vhci") != NULL) || 766 (strstr(mpxpath, "/pci") != NULL) || 767 (strstr(mpxpath, "/sbus") != NULL)) { 768 /* 769 * If we see a physical path here it means that 770 * devlinks aren't fully initialised yet, so we 771 * are still in maintenance/single-user mode. 772 */ 773 (void) printf("/devices%s:%c\n", mpxpath, 774 slice[1] + '1'); 775 } else { 776 (void) printf("%s%s%s\n", 777 (prefixt[0] == '/') ? prefixt : "", 778 mpxpath, 779 ((slicelen > 0) && slice != NULL) ? slice : ""); 780 } 781 } 782 free(prefixt); 783 free(stripdev); 784 } 785 786 /* 787 * Validate the in-kernel and on-disk forms of our devid cache, 788 * returns -1 for unfixable error and 0 for success. 789 */ 790 static int 791 validate_devnvl() 792 { 793 di_node_t curnode; 794 int rv1 = -1; 795 int rv2 = -1; 796 797 /* 798 * Method: we walk through the kernel's concept of the device tree 799 * looking for "ssd" then "sd" nodes. 800 * We check to see whether the device's devid is already in our nvlist 801 * (on disk) nvlist cache file. If it is, we check that it's components 802 * match what we've got already and fill any missing fields. 803 * If the devid isn't in our on-disk nvlist already then we add it 804 * and populate the property nvpairs. 805 * 806 * At the end of this function we should have this program's concept 807 * of the devid-keyed nvlist matching what is in the ondisk form which 808 * is ready to be written out. 809 * If we can't do this, then we return -1. 810 */ 811 curnode = di_drv_first_node("ssd", devinfo_root); 812 if (curnode != DI_NODE_NIL) 813 rv1 = mpxio_nvl_boilerplate(curnode); 814 815 curnode = di_drv_first_node("sd", devinfo_root); 816 if (curnode != DI_NODE_NIL) 817 rv2 = mpxio_nvl_boilerplate(curnode); 818 819 if (rv1 + rv2 == -2) 820 return (-1); 821 822 return (0); 823 } 824 825 static int 826 mpxio_nvl_boilerplate(di_node_t curnode) 827 { 828 int rv; 829 char *strdevid; 830 ddi_devid_t curdevid; 831 nvlist_t *newnvl; 832 833 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) { 834 errno = 0; 835 836 curdevid = NULL; 837 get_devid(curnode, &curdevid); 838 if (curdevid == NULL) 839 /* 840 * There's no devid registered for this device 841 * so it's not cool enough to play with us 842 */ 843 continue; 844 845 strdevid = devid_str_encode(curdevid, NULL); 846 /* does this exist in the on-disk cache? */ 847 rv = nvlist_lookup_nvlist(mapnvl, strdevid, &newnvl); 848 if (rv == ENOENT) { 849 logmsg(MSG_INFO, "nvlist for %s not found\n", strdevid); 850 /* no, so alloc a new nvl to store it */ 851 if (nvlist_alloc(&newnvl, NV_UNIQUE_NAME, 0) != 0) { 852 logmsg(MSG_ERROR, 853 gettext("Unable to allocate space for " 854 "a devid property list: %s\n"), 855 strerror(errno)); 856 return (-1); 857 } 858 } else { 859 if ((rv != ENOTSUP) && (rv != EINVAL)) 860 logmsg(MSG_INFO, 861 "%s exists in ondisknvl, verifying\n", 862 strdevid); 863 } 864 865 if (popcheck_devnvl(curnode, newnvl, strdevid) != 0) { 866 logmsg(MSG_ERROR, 867 gettext("Unable to populate devid nvpair " 868 "for device with devid %s\n"), 869 strdevid); 870 devid_str_free(strdevid); 871 nvlist_free(newnvl); 872 return (-1); 873 } 874 875 /* Now add newnvl into our cache. */ 876 errno = 0; 877 rv = nvlist_add_nvlist(mapnvl, strdevid, newnvl); 878 if (rv) { 879 logmsg(MSG_ERROR, 880 gettext("Unable to add device (devid %s) " 881 "to in-kernel nvl: %s (%d)\n"), 882 strdevid, strerror(rv), rv); 883 devid_str_free(strdevid); 884 nvlist_free(newnvl); 885 return (-1); 886 } 887 logmsg(MSG_INFO, 888 gettext("added device (devid %s) to mapnvl\n\n"), 889 strdevid); 890 devid_str_free(strdevid); 891 } 892 return (0); 893 } 894 895 /* 896 * Operates on a single di_node_t, collecting all the device properties 897 * that we need. devnvl is allocated by the caller, and we add our nvpairs 898 * to it if they don't already exist. 899 * 900 * We are _only_ interested in devices which have a devid. We pull in 901 * devices even when they're excluded via stmsboot -D (driver), because 902 * we don't want to miss out on any devid data that might be handy later. 903 */ 904 static int 905 popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, char *strdevid) 906 { 907 char *path = NULL; 908 char *curpath = NULL; 909 char *devfspath = NULL; 910 char *prop = NULL; 911 int scsivhciparent = 0; 912 int rv = 0; 913 boolean_t mpxenp = B_FALSE; 914 915 errno = 0; 916 devfspath = di_devfs_path(thisnode); 917 if (devfspath == NULL) { 918 logmsg(MSG_ERROR, 919 gettext("Unable to determine devfs path for node: %s\n"), 920 strerror(errno)); 921 return (-1); 922 } 923 924 /* Add a convenient devfspath to devid inverse map */ 925 if (nvlist_add_string(mapnvl, devfspath, strdevid) != 0) { 926 logmsg(MSG_ERROR, 927 gettext("Unable to add device path %s with devid " 928 "%s to mapnvl\n"), devfspath, strdevid); 929 return (-1); 930 } 931 if (di_prop_lookup_strings(DDI_DEV_T_ANY, di_parent_node(thisnode), 932 "mpxio-disable", &prop) >= 0) { 933 if (strncmp(prop, "yes", 3) == 0) { 934 if (!mpxprop) 935 mpxprop++; 936 } 937 } 938 939 if (strncmp(di_driver_name(di_parent_node(thisnode)), 940 "scsi_vhci", 9) == 0) { 941 scsivhciparent = 1; 942 if (!mpxenabled) 943 mpxenabled++; 944 945 rv = nvlist_lookup_boolean_value(devnvl, NVL_MPXEN, &mpxenp); 946 if (rv || (mpxenp == B_FALSE)) { 947 rv = nvlist_add_boolean_value(devnvl, 948 NVL_MPXEN, B_TRUE); 949 if (rv) { 950 logmsg(MSG_ERROR, 951 gettext("Unable to add property %s " 952 "(set to B_TRUE) for device %s: " 953 "%s (%d)\n"), 954 NVL_MPXEN, devfspath, 955 strerror(rv), rv); 956 return (-1); 957 } 958 logmsg(MSG_INFO, "NVL_MPXEN :: (B_FALSE->B_TRUE)\n"); 959 } 960 } else { 961 /* turn _off_ the flag if it was enabled */ 962 rv = nvlist_add_boolean_value(devnvl, NVL_MPXEN, B_FALSE); 963 if (rv) { 964 logmsg(MSG_ERROR, 965 gettext("Unable to add property %s " 966 "(set to B_FALSE) for device %s: %s (%d)\n"), 967 NVL_MPXEN, devfspath, 968 strerror(rv), rv); 969 return (-1); 970 } 971 logmsg(MSG_INFO, "NVL_MPXEN :: (B_TRUE-> B_FALSE)\n"); 972 } 973 974 rv = nvlist_add_string(devnvl, NVL_PHYSPATH, devfspath); 975 if (rv) { 976 logmsg(MSG_ERROR, 977 gettext("Unable to add physical device path (%s) " 978 "property to nvl\n")); 979 return (-1); 980 } 981 982 if ((curpath = calloc(1, MAXPATHLEN)) == NULL) { 983 logmsg(MSG_ERROR, 984 gettext("Unable to allocate space for current path\n")); 985 return (-1); 986 } 987 curpath = find_link(thisnode); 988 if (curpath == NULL) { 989 if (readonlyroot) { 990 return (0); 991 } 992 logmsg(MSG_ERROR, 993 gettext("Unable to determine device path for node %s\n"), 994 devfspath); 995 return (-1); 996 } 997 998 rv = nvlist_lookup_string(devnvl, NVL_MPXPATH, &path); 999 1000 if (path == NULL && scsivhciparent) 1001 (void) nvlist_add_string(devnvl, NVL_MPXPATH, curpath); 1002 1003 if (!scsivhciparent) { 1004 (void) nvlist_add_string(devnvl, NVL_PATH, curpath); 1005 path = curpath; 1006 } 1007 1008 /* 1009 * This next block provides the path to devid inverse mapping 1010 * that other functions require 1011 */ 1012 if (path != NULL) { 1013 if (nvlist_add_string(mapnvl, path, strdevid) != 0) { 1014 logmsg(MSG_ERROR, 1015 gettext("Unable to add device %s with devid " 1016 "%s to mapnvl\n"), path, strdevid); 1017 return (-1); 1018 } 1019 logmsg(MSG_INFO, "popcheck_devnvl: added path %s :: %s\n", 1020 path, strdevid); 1021 if (nvlist_add_string(mapnvl, curpath, strdevid) != 0) { 1022 logmsg(MSG_ERROR, 1023 gettext("Unable to add device %s with devid " 1024 "%s to mapnvl: %s\n"), 1025 curpath, strdevid, strerror(errno)); 1026 return (-1); 1027 } 1028 logmsg(MSG_INFO, "popcheck_devnvl: added curpath %s :: %s\n", 1029 curpath, strdevid); 1030 } 1031 if (scsivhciparent) { 1032 if (nvlist_add_string(devnvl, NVL_MPXPATH, curpath) != 0) { 1033 logmsg(MSG_ERROR, 1034 gettext("Unable to add property %s for device " 1035 "%s: %s\n"), 1036 NVL_MPXPATH, devfspath, strerror(errno)); 1037 return (-1); 1038 } else { 1039 logmsg(MSG_INFO, "added curpath (%s) as NVL_MPXPATH " 1040 "to devnvl for devid %s\n", curpath, strdevid); 1041 } 1042 } 1043 return (0); 1044 } 1045 1046 static void 1047 print_mpx_capable(di_node_t curnode) 1048 { 1049 char *prop; 1050 char *path; 1051 char *aliases = NULL; 1052 1053 if (cap_N_option) { 1054 aliases = calloc(1, MAXPATHLEN + 1); 1055 if (aliases == NULL) { 1056 logmsg(MSG_ERROR, 1057 gettext("Unable to allocate memory for a device " 1058 "alias list\n")); 1059 return; 1060 } 1061 } 1062 1063 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) { 1064 if (di_prop_lookup_strings(DDI_DEV_T_ANY, curnode, 1065 "initiator-port", &prop) >= 0) { 1066 if ((path = di_devfs_path(curnode)) == NULL) { 1067 logmsg(MSG_INFO, 1068 "Unable to find devfs path for device " 1069 "%s: %s\n", &curnode, strerror(errno)); 1070 continue; 1071 } 1072 if (cap_N_option) { 1073 char *nodename = di_node_name(curnode); 1074 /* nodename is never going to be null */ 1075 if (strstr(aliases, nodename) == NULL) 1076 /* haven't seen this nodename before */ 1077 (void) snprintf(aliases, 1078 MAXPATHLEN + 1, "%s|%s", 1079 ((aliases != NULL) ? aliases : ""), 1080 nodename); 1081 } else 1082 (void) printf("%s\n", path); 1083 } 1084 } 1085 if (cap_N_option) 1086 (void) printf("%s\n", aliases); 1087 } 1088 1089 static int 1090 link_cb(di_devlink_t devlink, void *arg) 1091 { 1092 const char *result; 1093 1094 result = di_devlink_path(devlink); 1095 if (result == NULL) { 1096 arg = (void *)"(null)"; 1097 } else { 1098 (void) strlcpy(arg, result, strlen(result)); 1099 } 1100 logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n", 1101 ((result != NULL) ? result : "(null)")); 1102 return (DI_WALK_CONTINUE); 1103 } 1104 1105 static char * 1106 find_link(di_node_t cnode) 1107 { 1108 di_minor_t devminor = DI_MINOR_NIL; 1109 di_devlink_handle_t hdl; 1110 char *devfspath = NULL; 1111 char *minorpath = NULL; 1112 char *linkname = NULL; 1113 char *cbresult = NULL; 1114 1115 devfspath = di_devfs_path(cnode); 1116 if (cnode == DI_NODE_NIL) { 1117 logmsg(MSG_ERROR, 1118 gettext("find_ctrl must be called with non-null " 1119 "di_node_t\n")); 1120 return (NULL); 1121 } 1122 logmsg(MSG_INFO, "find_link: devfspath %s\n", devfspath); 1123 1124 if (((cbresult = calloc(1, MAXPATHLEN)) == NULL) || 1125 ((minorpath = calloc(1, MAXPATHLEN)) == NULL) || 1126 ((linkname = calloc(1, MAXPATHLEN)) == NULL)) { 1127 logmsg(MSG_ERROR, "unable to allocate space for dev link\n"); 1128 return (NULL); 1129 } 1130 1131 devminor = di_minor_next(cnode, devminor); 1132 hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK); 1133 if (hdl == NULL) { 1134 logmsg((readonlyroot ? MSG_INFO : MSG_ERROR), 1135 gettext("unable to take devlink snapshot: %s\n"), 1136 strerror(errno)); 1137 return (NULL); 1138 } 1139 1140 linkname = "^dsk/"; 1141 (void) snprintf(minorpath, MAXPATHLEN, "%s:c", devfspath); 1142 1143 errno = 0; 1144 if (di_devlink_walk(hdl, linkname, minorpath, DI_PRIMARY_LINK, 1145 (void *)cbresult, link_cb) < 0) { 1146 logmsg(MSG_ERROR, 1147 gettext("Unable to walk devlink snapshot for %s: %s\n"), 1148 minorpath, strerror(errno)); 1149 return (NULL); 1150 } 1151 1152 if (di_devlink_fini(&hdl) < 0) { 1153 logmsg(MSG_ERROR, 1154 gettext("Unable to close devlink snapshot: %s\n"), 1155 strerror(errno)); 1156 } 1157 if (strstr(cbresult, "dsk/") == NULL) 1158 return (devfspath); 1159 1160 bzero(minorpath, MAXPATHLEN); 1161 /* strip off the trailing "s2" */ 1162 bcopy(cbresult, minorpath, strlen(cbresult) - 1); 1163 /* Now strip off the /dev/dsk/ prefix for output flexibility */ 1164 linkname = strrchr(minorpath, '/'); 1165 return (++linkname); 1166 } 1167 1168 /* 1169 * handle case where device has been probed but its target driver is not 1170 * attached so enumeration has not quite finished. Opening the /devices 1171 * pathname will force the kernel to finish the enumeration process and 1172 * let us get the data we need. 1173 */ 1174 static void 1175 get_devid(di_node_t node, ddi_devid_t *thisdevid) 1176 { 1177 int fd; 1178 char realpath[MAXPATHLEN]; 1179 char *openpath = di_devfs_path(node); 1180 1181 errno = 0; 1182 bzero(realpath, MAXPATHLEN); 1183 if (strstr(openpath, "/devices") == NULL) { 1184 (void) snprintf(realpath, MAXPATHLEN, 1185 "/devices%s:c,raw", openpath); 1186 fd = open(realpath, O_RDONLY|O_NDELAY); 1187 } else { 1188 fd = open(openpath, O_RDONLY|O_NDELAY); 1189 } 1190 1191 if (fd < 0) { 1192 logmsg(MSG_INFO, "Unable to open path %s: %s\n", 1193 openpath, strerror(errno)); 1194 return; 1195 } 1196 1197 if (devid_get(fd, thisdevid) != 0) { 1198 logmsg(MSG_INFO, 1199 "'%s' node (%s) without a devid registered\n", 1200 di_driver_name(node), di_devfs_path(node)); 1201 } 1202 (void) close(fd); 1203 } 1204 1205 static int 1206 print_bootpath() 1207 { 1208 char *bootprop = NULL; 1209 1210 if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root, 1211 "bootpath", &bootprop) >= 0) { 1212 (void) printf("%s\n", bootprop); 1213 return (0); 1214 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root, 1215 "boot-path", &bootprop) >= 0) { 1216 (void) printf("%s\n", bootprop); 1217 return (0); 1218 } else { 1219 (void) printf("ERROR: no bootpath/boot-path property found\n"); 1220 return (ENOENT); 1221 } 1222 } 1223 1224 static void 1225 get_phci_driver_name(char *phci_path, char **driver_name) 1226 { 1227 di_node_t phci_node = DI_NODE_NIL; 1228 char *tmp = NULL; 1229 1230 phci_node = di_init(phci_path, DINFOCPYONE); 1231 if (phci_node == DI_NODE_NIL) { 1232 logmsg(MSG_ERROR, 1233 gettext("Unable to take phci snapshot " 1234 "(%s: %d)\n"), strerror(errno), errno); 1235 return; 1236 } 1237 tmp = di_driver_name(phci_node); 1238 if (tmp != NULL) { 1239 (void) strncpy(*driver_name, tmp, 10); 1240 } 1241 di_fini(phci_node); 1242 } 1243 /* 1244 * We only call this routine if we have a scsi_vhci node and must 1245 * determine the actual physical path of its first online client 1246 * path. 1247 */ 1248 static void 1249 vhci_to_phci(char *devpath, char *physpath) 1250 { 1251 sv_iocdata_t ioc; 1252 sv_path_info_t *pi; 1253 int vhci_fd; 1254 int rv; 1255 uint_t npaths = 0; 1256 1257 vhci_fd = open(VHCI_CTL_NODE, O_RDWR); 1258 if (vhci_fd < 0) 1259 goto failure; 1260 1261 bzero(&ioc, sizeof (sv_iocdata_t)); 1262 ioc.client = devpath; 1263 ioc.ret_elem = &npaths; 1264 rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc); 1265 if (rv || npaths == 0) { 1266 logmsg(MSG_INFO, 1267 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() failed, " 1268 "%s (%d)\n", strerror(rv), rv); 1269 goto failure; 1270 } 1271 1272 bzero(&ioc, sizeof (sv_iocdata_t)); 1273 ioc.client = devpath; 1274 ioc.buf_elem = npaths; 1275 ioc.ret_elem = &npaths; 1276 if ((ioc.ret_buf = calloc(npaths, sizeof (sv_path_info_t))) 1277 == NULL) 1278 goto failure; 1279 rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc); 1280 if (rv || npaths == 0) { 1281 logmsg(MSG_INFO, 1282 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() (#2) " 1283 "failed, %s (%d)\n", strerror(rv), rv); 1284 goto failure; 1285 } 1286 1287 if (ioc.buf_elem < npaths) 1288 npaths = ioc.buf_elem; 1289 1290 pi = (sv_path_info_t *)ioc.ret_buf; 1291 while (npaths--) { 1292 if (pi->ret_state == MDI_PATHINFO_STATE_ONLINE) { 1293 char nodename[5]; 1294 char *phci_driver = NULL; 1295 1296 bzero(nodename, 5); 1297 phci_driver = malloc(10); 1298 if (phci_driver == NULL) { 1299 logmsg(MSG_INFO, 1300 "vhci_to_phci: Memory allocation failed\n"); 1301 goto failure; 1302 } 1303 bzero(phci_driver, 10); 1304 get_phci_driver_name(pi->device.ret_phci, 1305 &phci_driver); 1306 logmsg(MSG_INFO, "phci driver name: %s\n", phci_driver); 1307 /* 1308 * A hack, but nicer than a platform-specific ifdef 1309 * fp on SPARC using "ssd" as nodename 1310 * mpt use "sd" when mpxio disabled, use "disk" when 1311 * mpxio is enabled 1312 * for alll other cases, "disk" should be used as the 1313 * nodename 1314 */ 1315 if (strstr(devpath, "ssd") != NULL) { 1316 (void) snprintf(nodename, 5, "ssd"); 1317 } else if (strncmp(phci_driver, "mpt", 10) == 0) { 1318 (void) snprintf(nodename, 5, "sd"); 1319 } else { 1320 (void) snprintf(nodename, 5, "disk"); 1321 } 1322 (void) snprintf(physpath, MAXPATHLEN, "%s/%s@%s", 1323 pi->device.ret_phci, nodename, pi->ret_addr); 1324 free(ioc.ret_buf); 1325 free(phci_driver); 1326 return; 1327 } 1328 pi++; 1329 } 1330 1331 failure: 1332 (void) snprintf(physpath, MAXPATHLEN, "NOT_MAPPED"); 1333 } 1334 1335 /* 1336 * Write /etc/vfstab to /etc/vfstab.new, with any remapped device 1337 * names substituted. 1338 * 1339 * Returns: 1340 * 0 successful operation 1341 * -1 failed 1342 */ 1343 static int 1344 update_vfstab() 1345 { 1346 FILE *fdin, *fdout; 1347 char *buf, *tmpbuf; 1348 char fname[MAXPATHLEN]; 1349 int rv = -1, rval = -1; 1350 char cdev[MAXPATHLEN]; 1351 char bdev[MAXPATHLEN]; 1352 char mntpt[MAXPATHLEN]; 1353 char fstype[512]; 1354 char fsckpass[512]; 1355 char mntboot[512]; 1356 char mntopt[MAXPATHLEN]; 1357 char fmt[80]; 1358 char *prefixt = NULL; 1359 char *curdev = NULL; 1360 char *thisdevid = NULL; 1361 char *slice = NULL; 1362 nvlist_t *thisdev; 1363 boolean_t devmpx = B_FALSE; 1364 1365 buf = calloc(1, MAXPATHLEN); 1366 tmpbuf = calloc(1, MAXPATHLEN); 1367 if (buf == NULL || tmpbuf == NULL) 1368 return (-1); 1369 1370 (void) snprintf(fname, MAXPATHLEN, "/etc/mpxio/vfstab.new"); 1371 1372 fdin = fopen("/etc/vfstab", "r"); 1373 fdout = fopen(fname, "w+"); 1374 if (fdin == NULL || fdout == NULL) { 1375 logmsg(MSG_INFO, "Unable to open vfstab or create a backup " 1376 "vfstab %s\n"); 1377 return (-1); 1378 } 1379 1380 (void) snprintf(fmt, sizeof (fmt), 1381 "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1, 1382 sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1, 1383 sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1); 1384 1385 while (fgets(buf, MAXPATHLEN, fdin) != NULL) { 1386 if (strlen(buf) == (MAXPATHLEN - 1) && 1387 buf[MAXPATHLEN-2] != '\n') { 1388 logmsg(MSG_ERROR, 1389 gettext("/etc/vfstab line length too long, " 1390 "exceeded %2$d: \"%3$s\"\n"), 1391 MAXPATHLEN - 2, buf); 1392 goto out; 1393 } 1394 1395 prefixt = NULL; 1396 curdev = NULL; 1397 slice = NULL; 1398 thisdevid = NULL; 1399 thisdev = NULL; 1400 1401 /* LINTED - variable format specifier */ 1402 rv = sscanf(buf, fmt, bdev, cdev, mntpt, fstype, fsckpass, 1403 mntboot, mntopt); 1404 1405 /* 1406 * Walk through the lines in the input file (/etc/vfstab), 1407 * skipping anything which is _not_ a COGD (common or garden 1408 * disk), ie all the /devices, /system, /dev/md, /dev/vx and 1409 * /dev/zvol and so forth. 1410 */ 1411 if ((rv == 7) && (bdev[0] == '/') && 1412 (strstr(bdev, "/dev/dsk"))) { 1413 slice = strrchr(bdev, 's'); 1414 /* take a copy, strip off /dev/dsk/ */ 1415 prefixt = strrchr(bdev, 'c'); 1416 prefixt[strlen(bdev) - 9 - strlen(slice)] = '\0'; 1417 slice++; /* advance past the s */ 1418 rval = nvlist_lookup_string(mapnvl, prefixt, 1419 &thisdevid); 1420 if (rval) { 1421 /* Whoa, where did this device go?! */ 1422 logmsg(MSG_INFO, 1423 "error looking up device %s\n", prefixt); 1424 /* Comment-out this line in the new version */ 1425 (void) snprintf(tmpbuf, MAXPATHLEN, 1426 "# DEVICE NOT FOUND %s", buf); 1427 (void) fprintf(fdout, "%s", tmpbuf); 1428 continue; 1429 } else { 1430 /* The device exists in our mapnvl */ 1431 (void) nvlist_lookup_nvlist(mapnvl, thisdevid, 1432 &thisdev); 1433 (void) nvlist_lookup_boolean_value(thisdev, 1434 NVL_MPXEN, &devmpx); 1435 (void) nvlist_lookup_string(thisdev, 1436 ((devmpx == B_TRUE) 1437 ? NVL_MPXPATH : NVL_PATH), 1438 &curdev); 1439 } 1440 } 1441 1442 if ((prefixt != NULL) && (curdev != NULL) && 1443 (rv = (strncmp(prefixt, curdev, strlen(prefixt)) != 0))) { 1444 /* Mapping change for this device */ 1445 if (strcmp(fstype, "swap") == 0) { 1446 (void) snprintf(tmpbuf, MAXPATHLEN, 1447 "/dev/dsk/%ss%s\t-\t-\tswap\t" 1448 "%s\t%s\t%s\n", 1449 curdev, slice, fsckpass, mntboot, mntopt); 1450 } else { 1451 (void) snprintf(tmpbuf, MAXPATHLEN, 1452 "/dev/dsk/%ss%s\t/dev/rdsk/%ss%s\t" 1453 "%s\t%s\t%s\t%s\t%s\n", 1454 curdev, slice, curdev, slice, 1455 mntpt, fstype, fsckpass, mntboot, mntopt); 1456 } 1457 errno = 0; 1458 (void) fprintf(fdout, "%s", tmpbuf); 1459 } else { 1460 (void) fprintf(fdout, "%s", buf); 1461 } 1462 1463 errno = 0; 1464 if (fflush(fdout) != 0) { 1465 logmsg(MSG_ERROR, 1466 gettext("fprintf failed to write to %s: %s (%d)\n"), 1467 fname, strerror(errno), errno); 1468 goto out; 1469 } 1470 } 1471 out: 1472 (void) fclose(fdin); 1473 (void) fclose(fdout); 1474 free(buf); 1475 free(tmpbuf); 1476 return (errno); 1477 } 1478