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