1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 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 <sys/stat.h> 30 #include <sys/types.h> 31 32 /* 33 * Dependent on types.h, but not including it... 34 */ 35 #include <stdio.h> 36 #include <sys/types.h> 37 #include <sys/dkio.h> 38 #include <sys/dktp/fdisk.h> 39 #include <sys/mnttab.h> 40 #include <sys/mntent.h> 41 #include <sys/sysmacros.h> 42 #include <sys/mkdev.h> 43 #include <sys/vfs.h> 44 #include <nfs/nfs.h> 45 #include <nfs/nfs_clnt.h> 46 #include <kstat.h> 47 #include <ctype.h> 48 #include <dirent.h> 49 #include <libdevinfo.h> 50 #include <limits.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <errno.h> 55 #include <devid.h> 56 57 #include "dsr.h" 58 #include "statcommon.h" 59 60 static void rummage_dev(ldinfo_t *); 61 static void do_snm(char *, char *); 62 static int look_up_name(const char *, disk_list_t *); 63 static disk_list_t *make_an_entry(char *, char *, 64 char *, dir_info_t *, int, ldinfo_t *); 65 static char *trim(char *, char *, int); 66 static ldinfo_t *rummage_devinfo(void); 67 static void pline(char *, int, char *, char *, ldinfo_t **); 68 static void insert_dlist_ent(disk_list_t *, disk_list_t **); 69 static int str_is_digit(char *); 70 static ldinfo_t *find_ldinfo_match(char *, ldinfo_t *); 71 72 static void insert_into_dlist(dir_info_t *, disk_list_t *); 73 static void cleanup_dlist(dir_info_t *); 74 static void cleanup_ldinfo(ldinfo_t *); 75 static int devinfo_ident_disks(di_node_t, void *); 76 static int devinfo_ident_tapes(di_node_t, void *); 77 static void process_dir_ent(char *dent, int curr_type, 78 char *last_snm, dir_info_t *, ldinfo_t *); 79 80 static char *get_nfs_by_minor(uint_t); 81 static char *cur_hostname(uint_t, kstat_ctl_t *); 82 static char *cur_special(char *, char *); 83 84 extern kstat_ctl_t *kc; 85 extern mnt_t *nfs; 86 87 /* 88 * To do: add VXVM support: /dev/vx/dsk and ap support: /dev/ap/ 89 * 90 * Note: Adding support for VxVM is *not* as simple as adding another 91 * entry in the table and magically getting to see stuff related to 92 * VxVM. The structure is radically different *AND* they don't produce 93 * any IO kstats. 94 */ 95 96 #define OSA_DISK 0 97 #define DISK 1 98 #define MD_DISK 2 99 #define TAPE 3 100 101 #define MAX_TYPES 4 102 103 #define OSA_DISK_PATH "/dev/osa/dev/dsk" 104 #define MD_DISK_PATH "/dev/md/dsk" 105 #define DISK_PATH "/dev/dsk" 106 #define TAPE_PATH "/dev/rmt" 107 108 #define BASE_TRIM "../../devices" 109 #define MD_TRIM "../../../devices" 110 #define COLON ':' 111 #define COMMA ',' 112 113 #define NAME_BUFLEN 256 114 115 static dir_info_t dlist[MAX_TYPES] = { 116 OSA_DISK_PATH, 0, 0, 0, 0, "sd", BASE_TRIM, COLON, 117 DISK_PATH, 0, 0, 0, 0, "sd", BASE_TRIM, COLON, 118 MD_DISK_PATH, 0, 0, 0, 1, "md", MD_TRIM, COMMA, 119 TAPE_PATH, 0, 0, 0, 0, "st", BASE_TRIM, COLON, 120 }; 121 122 /* 123 * Build a list of disks attached to the system. 124 */ 125 static void 126 build_disk_list(void) 127 { 128 ldinfo_t *ptoi; 129 130 /* 131 * Build the list of devices connected to the system. 132 */ 133 ptoi = rummage_devinfo(); 134 rummage_dev(ptoi); 135 cleanup_ldinfo(ptoi); 136 } 137 138 /* 139 * Walk the /dev/dsk and /dev/rmt directories building a 140 * list of interesting devices. Interesting is everything in the 141 * /dev/dsk directory. We skip some of the stuff in the /dev/rmt 142 * directory. 143 * 144 * Note that not finding one or more of the directories is not an 145 * error. 146 */ 147 static void 148 rummage_dev(ldinfo_t *ptoi) 149 { 150 DIR *dskp; 151 int i; 152 struct stat buf; 153 154 for (i = 0; i < MAX_TYPES; i++) { 155 if (stat(dlist[i].name, &buf) == 0) { 156 if (dlist[i].mtime != buf.st_mtime) { 157 /* 158 * We've found a change. We need to cleanup 159 * old information and then rebuild the list 160 * for this device type. 161 */ 162 cleanup_dlist(&dlist[i]); 163 dlist[i].mtime = buf.st_mtime; 164 if ((dskp = opendir(dlist[i].name))) { 165 struct dirent *bpt; 166 char last_snm[NAME_BUFLEN]; 167 168 last_snm[0] = NULL; 169 while ((bpt = readdir(dskp)) != NULL) { 170 if (bpt->d_name[0] != '.') { 171 process_dir_ent( 172 bpt->d_name, 173 i, last_snm, 174 &dlist[i], 175 ptoi); 176 } 177 } 178 } 179 (void) closedir(dskp); 180 } 181 } 182 } 183 } 184 185 /* 186 * Walk the list of located devices and see if we've 187 * seen this device before. We look at the short name. 188 */ 189 static int 190 look_up_name(const char *nm, disk_list_t *list) 191 { 192 while (list) { 193 if (strcmp(list->dsk, nm) != 0) 194 list = list->next; 195 else { 196 return (1); 197 } 198 } 199 return (0); 200 } 201 202 /* 203 * Take a name of the form cNtNdNsN or cNtNdNpN 204 * or /dev/dsk/CNtNdNsN or /dev/dsk/cNtNdNpN 205 * remove the trailing sN or pN. Simply looking 206 * for the first 's' or 'p' doesn't cut it. 207 */ 208 static void 209 do_snm(char *orig, char *shortnm) 210 { 211 char *tmp; 212 char *ptmp; 213 int done = 0; 214 char repl_char = 0; 215 216 tmp = strrchr(orig, 's'); 217 if (tmp) { 218 ptmp = tmp; 219 ptmp++; 220 done = str_is_digit(ptmp); 221 } 222 if (done == 0) { 223 /* 224 * The string either has no 's' in it 225 * or the stuff trailing the s has a 226 * non-numeric in it. Look to see if 227 * we have an ending 'p' followed by 228 * numerics. 229 */ 230 tmp = strrchr(orig, 'p'); 231 if (tmp) { 232 ptmp = tmp; 233 ptmp++; 234 if (str_is_digit(ptmp)) 235 repl_char = 'p'; 236 else 237 tmp = 0; 238 } 239 } else { 240 repl_char = 's'; 241 } 242 if (tmp) 243 *tmp = '\0'; 244 (void) strcpy(shortnm, orig); 245 if (repl_char) 246 *tmp = repl_char; 247 } 248 249 /* 250 * Create and insert an entry into the device list. 251 */ 252 static disk_list_t * 253 make_an_entry(char *lname, char *shortnm, char *longnm, 254 dir_info_t *drent, int devtype, ldinfo_t *ptoi) 255 { 256 disk_list_t *entry; 257 char *nlnm; 258 char snm[NAME_BUFLEN]; 259 ldinfo_t *p; 260 261 entry = safe_alloc(sizeof (disk_list_t)); 262 263 nlnm = trim(lname, drent->trimstr, drent->trimchr); 264 entry->dsk = safe_strdup(shortnm); 265 do_snm(longnm, snm); 266 entry->dname = safe_strdup(snm); 267 entry->devtype = devtype; 268 entry->devidstr = NULL; 269 if ((p = find_ldinfo_match(nlnm, ptoi))) { 270 entry->dnum = p->dnum; 271 entry->dtype = safe_strdup(p->dtype); 272 if (p->devidstr) 273 entry->devidstr = safe_strdup(p->devidstr); 274 } else { 275 entry->dtype = safe_strdup(drent->dtype); 276 entry->dnum = -1; 277 if (drent->dtype) { 278 if (strcmp(drent->dtype, "md") == 0) { 279 (void) sscanf(shortnm, "d%d", &entry->dnum); 280 } 281 } 282 } 283 entry->seen = 0; 284 entry->next = 0; 285 insert_dlist_ent(entry, &drent->list); 286 return (entry); 287 } 288 289 /* 290 * slice stuff off beginning and end of /devices directory names derived from 291 * device links. 292 */ 293 static char * 294 trim(char *fnm, char *lname, int rchr) 295 { 296 char *ptr; 297 298 while (*lname == *fnm) { 299 lname++; 300 fnm++; 301 } 302 if ((ptr = strrchr(fnm, rchr))) 303 *ptr = NULL; 304 return (fnm); 305 } 306 307 /* 308 * Find an entry matching the name passed in 309 */ 310 static ldinfo_t * 311 find_ldinfo_match(char *name, ldinfo_t *ptoi) 312 { 313 if (name) { 314 while (ptoi) { 315 if (strcmp(ptoi->name, name)) 316 ptoi = ptoi->next; 317 else 318 return (ptoi); 319 } 320 } 321 return (NULL); 322 } 323 324 /* 325 * Determine if a name is already in the list of disks. If not, insert the 326 * name in the list. 327 */ 328 static void 329 insert_dlist_ent(disk_list_t *n, disk_list_t **hd) 330 { 331 disk_list_t *tmp_ptr; 332 333 if (n->dtype != NULL) { 334 tmp_ptr = *hd; 335 while (tmp_ptr) { 336 if (strcmp(n->dsk, tmp_ptr->dsk) != 0) 337 tmp_ptr = tmp_ptr->next; 338 else 339 break; 340 } 341 if (tmp_ptr == NULL) { 342 /* 343 * We don't do anything with MD_DISK types here 344 * since they don't have partitions. 345 */ 346 if (n->devtype == DISK || n->devtype == OSA_DISK) { 347 n->flags = SLICES_OK; 348 #if defined(i386) || defined(__ia64) 349 n->flags |= PARTITIONS_OK; 350 #endif 351 } else { 352 n->flags = 0; 353 } 354 /* 355 * Figure out where to insert the name. The list is 356 * ostensibly in sorted order. 357 */ 358 if (*hd) { 359 disk_list_t *follw; 360 int mv; 361 362 tmp_ptr = *hd; 363 364 /* 365 * Look through the list. While the strcmp 366 * value is less than the current value, 367 */ 368 while (tmp_ptr) { 369 if ((mv = strcmp(n->dtype, 370 tmp_ptr->dtype)) < 0) { 371 follw = tmp_ptr; 372 tmp_ptr = tmp_ptr->next; 373 } else 374 break; 375 } 376 if (mv == 0) { 377 /* 378 * We're now in the area where the 379 * leading chars of the kstat name 380 * match. We need to insert in numeric 381 * order after that. 382 */ 383 while (tmp_ptr) { 384 if (strcmp(n->dtype, 385 tmp_ptr->dtype) != 0) 386 break; 387 if (n->dnum > tmp_ptr->dnum) { 388 follw = tmp_ptr; 389 tmp_ptr = tmp_ptr->next; 390 } else 391 break; 392 } 393 } 394 /* 395 * We should now be ready to insert an 396 * entry... 397 */ 398 if (mv >= 0) { 399 if (tmp_ptr == *hd) { 400 n->next = tmp_ptr; 401 *hd = n; 402 } else { 403 n->next = follw->next; 404 follw->next = n; 405 } 406 } else { 407 /* 408 * insert at the end of the 409 * list 410 */ 411 follw->next = n; 412 n->next = 0; 413 } 414 } else { 415 *hd = n; 416 n->next = 0; 417 } 418 } 419 } 420 } 421 422 /* 423 * find an entry matching the given kstat name in the list 424 * of disks, tapes and metadevices. 425 */ 426 disk_list_t * 427 lookup_ks_name(char *dev_nm) 428 { 429 int tried = 0; 430 int dv; 431 int len; 432 char cmpbuf[PATH_MAX + 1]; 433 struct list_of_disks *list; 434 char *nm; 435 dev_name_t *tmp; 436 uint_t i; 437 438 /* 439 * extract the device type from the kstat name. We expect the 440 * name to be one or more alphabetics followed by the device 441 * numeric id. We do this solely for speed purposes . 442 */ 443 len = 0; 444 nm = dev_nm; 445 while (*nm) { 446 if (isalpha(*nm)) { 447 nm++; 448 len++; 449 } else 450 break; 451 } 452 453 if (!*nm) 454 return (NULL); 455 456 /* 457 * For each of the elements in the dlist array we keep 458 * an array of pointers to chains for each of the kstat 459 * prefixes found within that directory. This is typically 460 * 'sd' and 'ssd'. We walk the list in the directory and 461 * match on that type. Since the same prefixes can be 462 * in multiple places we keep checking if we don't find 463 * it in the first place. 464 */ 465 466 (void) strncpy(cmpbuf, dev_nm, len); 467 cmpbuf[len] = NULL; 468 dv = atoi(nm); 469 470 retry: 471 for (i = 0; i < MAX_TYPES; i++) { 472 tmp = dlist[i].nf; 473 while (tmp) { 474 if (strcmp(tmp->name, cmpbuf) == 0) { 475 /* 476 * As an optimization we keep mins 477 * and maxes for the devices found. 478 * This helps chop the lists up and 479 * avoid some really long chains as 480 * we would get if we kept only prefix 481 * lists. 482 */ 483 if (dv >= tmp->min && dv <= tmp->max) { 484 list = tmp->list_start; 485 while (list) { 486 if (list->dnum < dv) 487 list = list->next; 488 else 489 break; 490 } 491 if (list && list->dnum == dv) { 492 return (list); 493 } 494 } 495 } 496 tmp = tmp->next; 497 } 498 } 499 500 if (!tried) { 501 tried = 1; 502 build_disk_list(); 503 goto retry; 504 } 505 506 return (0); 507 } 508 509 static int 510 str_is_digit(char *str) 511 { 512 while (*str) { 513 if (isdigit(*str)) 514 str++; 515 else 516 return (0); 517 } 518 return (1); 519 } 520 521 static void 522 insert_into_dlist(dir_info_t *d, disk_list_t *e) 523 { 524 dev_name_t *tmp; 525 526 tmp = d->nf; 527 while (tmp) { 528 if (strcmp(e->dtype, tmp->name) != 0) { 529 tmp = tmp->next; 530 } else { 531 if (e->dnum < tmp->min) { 532 tmp->min = e->dnum; 533 tmp->list_start = e; 534 } else if (e->dnum > tmp->max) { 535 tmp->max = e->dnum; 536 tmp->list_end = e; 537 } 538 break; 539 } 540 } 541 if (tmp == NULL) { 542 tmp = safe_alloc(sizeof (dev_name_t)); 543 tmp->name = e->dtype; 544 tmp->min = e->dnum; 545 tmp->max = e->dnum; 546 tmp->list_start = e; 547 tmp->list_end = e; 548 tmp->next = d->nf; 549 d->nf = tmp; 550 } 551 } 552 553 /* 554 * devinfo_ident_disks() and devinfo_ident_tapes() are the callback functions we 555 * use while walking the device tree snapshot provided by devinfo. If 556 * devinfo_ident_disks() identifies that the device being considered has one or 557 * more minor nodes _and_ is a block device, then it is a potential disk. 558 * Similarly for devinfo_ident_tapes(), except that the second criterion is that 559 * the minor_node be a character device. (This is more inclusive than only 560 * tape devices, but will match any entries in /dev/rmt/.) 561 * 562 * Note: if a driver was previously loaded but is now unloaded, the kstat may 563 * still be around (e.g., st) but no information will be found in the 564 * libdevinfo tree. 565 */ 566 567 static int 568 devinfo_ident_disks(di_node_t node, void *arg) 569 { 570 di_minor_t minor = DI_MINOR_NIL; 571 572 if ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 573 int spectype = di_minor_spectype(minor); 574 575 if (S_ISBLK(spectype)) { 576 char *physical_path = di_devfs_path(node); 577 int instance = di_instance(node); 578 char *driver_name = di_driver_name(node); 579 char *devidstr; 580 581 /* lookup the devid, devt specific first */ 582 if ((di_prop_lookup_strings(di_minor_devt(minor), node, 583 DEVID_PROP_NAME, &devidstr) == -1) && 584 (di_prop_lookup_strings(DDI_DEV_T_ANY, node, 585 DEVID_PROP_NAME, &devidstr) == -1)) 586 devidstr = NULL; 587 588 if (driver_name == NULL) 589 driver_name = "<nil>"; 590 591 pline(physical_path, instance, 592 driver_name, devidstr, arg); 593 di_devfs_path_free(physical_path); 594 } 595 } 596 return (DI_WALK_CONTINUE); 597 } 598 599 static int 600 devinfo_ident_tapes(di_node_t node, void *arg) 601 { 602 di_minor_t minor = DI_MINOR_NIL; 603 604 if ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 605 int spectype = di_minor_spectype(minor); 606 607 if (S_ISCHR(spectype)) { 608 char *physical_path = di_devfs_path(node); 609 int instance = di_instance(node); 610 char *binding_name = di_binding_name(node); 611 612 pline(physical_path, instance, 613 binding_name, NULL, arg); 614 di_devfs_path_free(physical_path); 615 } 616 } 617 return (DI_WALK_CONTINUE); 618 } 619 620 /* 621 * rummage_devinfo() is the driver routine that walks the devinfo snapshot. 622 */ 623 static ldinfo_t * 624 rummage_devinfo(void) 625 { 626 di_node_t root_node; 627 ldinfo_t *rv = NULL; 628 629 if ((root_node = di_init("/", DINFOCPYALL)) != DI_NODE_NIL) { 630 (void) di_walk_node(root_node, DI_WALK_CLDFIRST, (void *)&rv, 631 devinfo_ident_disks); 632 (void) di_walk_node(root_node, DI_WALK_CLDFIRST, (void *)&rv, 633 devinfo_ident_tapes); 634 di_fini(root_node); 635 } 636 return (rv); 637 } 638 639 /* 640 * pline() performs the lookup of the device path in the current list of disks, 641 * and adds the appropriate information to the nms list in the case of a match. 642 */ 643 static void 644 pline(char *devfs_path, int instance, 645 char *driver_name, char *devidstr, ldinfo_t **list) 646 { 647 ldinfo_t *entry; 648 649 entry = safe_alloc(sizeof (ldinfo_t)); 650 entry->dnum = instance; 651 entry->name = safe_strdup(devfs_path); 652 entry->dtype = safe_strdup(driver_name); 653 entry->devidstr = safe_strdup(devidstr); 654 entry->next = *list; 655 *list = entry; 656 } 657 658 /* 659 * Cleanup space allocated in dlist processing. 660 * We're only interested in cleaning up the list and nf 661 * fields in the structure. Everything else is static 662 * data. 663 */ 664 static void 665 cleanup_dlist(dir_info_t *d) 666 { 667 dev_name_t *tmp; 668 dev_name_t *t1; 669 disk_list_t *t2; 670 disk_list_t *t3; 671 672 /* 673 * All of the entries in a dev_name_t use information 674 * from a disk_list_t structure that is freed later. 675 * All we need do here is free the dev_name_t 676 * structure itself. 677 */ 678 tmp = d->nf; 679 while (tmp) { 680 t1 = tmp->next; 681 free(tmp); 682 tmp = t1; 683 } 684 d->nf = 0; 685 /* 686 * "Later". Free the disk_list_t structures and their 687 * data attached to this portion of the dir_info 688 * structure. 689 */ 690 t2 = d->list; 691 while (t2) { 692 if (t2->dtype) { 693 free(t2->dtype); 694 t2->dtype = NULL; 695 } 696 if (t2->dsk) { 697 free(t2->dsk); 698 t2->dsk = NULL; 699 } 700 if (t2->dname) { 701 free(t2->dname); 702 t2->dname = NULL; 703 } 704 t3 = t2->next; 705 free(t2); 706 t2 = t3; 707 } 708 d->list = 0; 709 } 710 711 static void 712 process_dir_ent(char *dent, int curr_type, char *last_snm, 713 dir_info_t *dp, ldinfo_t *ptoi) 714 { 715 struct stat sbuf; 716 char dnmbuf[PATH_MAX + 1]; 717 char lnm[NAME_BUFLEN]; 718 char snm[NAME_BUFLEN]; 719 char *npt; 720 721 snm[0] = NULL; 722 if (curr_type == DISK || curr_type == OSA_DISK) { 723 /* 724 * get the short name - omitting 725 * the trailing sN or PN 726 */ 727 (void) strcpy(lnm, dent); 728 do_snm(dent, snm); 729 } else if (curr_type == MD_DISK) { 730 (void) strcpy(lnm, dent); 731 (void) strcpy(snm, dent); 732 } else { 733 /* 734 * don't want all rewind/etc 735 * devices for a tape 736 */ 737 if (!str_is_digit(dent)) 738 return; 739 (void) snprintf(snm, sizeof (snm), "rmt/%s", dent); 740 (void) snprintf(lnm, sizeof (snm), "rmt/%s", dent); 741 } 742 /* 743 * See if we've already processed an entry for this device. 744 * If so, we're just another partition so we get another 745 * entry. 746 * 747 * last_snm is an optimization to avoid the function call 748 * and lookup since we'll often see partition records 749 * immediately after the disk record. 750 */ 751 if (dp->skip_lookup == 0) { 752 if (strcmp(snm, last_snm) != 0) { 753 /* 754 * a zero return means that 755 * no record was found. We'd 756 * return a pointer otherwise. 757 */ 758 if (look_up_name(snm, 759 dp->list) == 0) { 760 (void) strcpy(last_snm, snm); 761 } else 762 return; 763 } else 764 return; 765 } 766 /* 767 * Get the real device name for this beast 768 * by following the link into /devices. 769 */ 770 (void) snprintf(dnmbuf, sizeof (dnmbuf), "%s/%s", dp->name, dent); 771 if (lstat(dnmbuf, &sbuf) != -1) { 772 if ((sbuf.st_mode & S_IFMT) == S_IFLNK) { 773 /* 774 * It's a link. Get the real name. 775 */ 776 char nmbuf[PATH_MAX + 1]; 777 int nbyr; 778 779 if ((nbyr = readlink(dnmbuf, nmbuf, 780 sizeof (nmbuf))) != 1) { 781 npt = nmbuf; 782 /* 783 * readlink does not terminate 784 * the string so we have to 785 * do it. 786 */ 787 nmbuf[nbyr] = NULL; 788 } else 789 npt = NULL; 790 } else 791 npt = lnm; 792 /* 793 * make an entry in the device list 794 */ 795 if (npt) { 796 disk_list_t *d; 797 798 d = make_an_entry(npt, snm, 799 dnmbuf, dp, 800 curr_type, ptoi); 801 insert_into_dlist(dp, d); 802 } 803 } 804 } 805 static void 806 cleanup_ldinfo(ldinfo_t *list) 807 { 808 ldinfo_t *tmp; 809 while (list) { 810 tmp = list; 811 list = list->next; 812 free(tmp->name); 813 free(tmp->dtype); 814 if (tmp->devidstr) 815 free(tmp->devidstr); 816 free(tmp); 817 } 818 } 819 820 char * 821 lookup_nfs_name(char *ks, kstat_ctl_t *kc) 822 { 823 int tried = 0; 824 uint_t minor; 825 char *host, *path; 826 char *cp; 827 char *rstr = 0; 828 size_t len; 829 830 if (sscanf(ks, "nfs%u", &minor) == 1) { 831 retry: 832 cp = get_nfs_by_minor(minor); 833 if (cp) { 834 if (strchr(cp, ',') == NULL) { 835 rstr = safe_strdup(cp); 836 return (rstr); 837 } 838 host = cur_hostname(minor, kc); 839 if (host) { 840 if (*host) { 841 path = cur_special(host, cp); 842 if (path) { 843 len = strlen(host); 844 len += strlen(path); 845 len += 2; 846 rstr = safe_alloc(len); 847 (void) snprintf(rstr, len, 848 "%s:%s", host, path); 849 } else { 850 rstr = safe_strdup(cp); 851 } 852 } else { 853 rstr = safe_strdup(ks); 854 } 855 free(host); 856 } else { 857 rstr = safe_strdup(cp); 858 } 859 } else if (!tried) { 860 tried = 1; 861 do_mnttab(); 862 goto retry; 863 } 864 } 865 return (rstr); 866 } 867 868 static char * 869 get_nfs_by_minor(uint_t minor) 870 { 871 mnt_t *localnfs; 872 873 localnfs = nfs; 874 while (localnfs) { 875 if (localnfs->minor == minor) { 876 return (localnfs->device_name); 877 } 878 localnfs = localnfs->next; 879 } 880 return (0); 881 } 882 883 /* 884 * Read the cur_hostname from the mntinfo kstat 885 */ 886 static char * 887 cur_hostname(uint_t minor, kstat_ctl_t *kc) 888 { 889 kstat_t *ksp; 890 static struct mntinfo_kstat mik; 891 char *rstr; 892 893 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 894 if (ksp->ks_type != KSTAT_TYPE_RAW) 895 continue; 896 if (ksp->ks_instance != minor) 897 continue; 898 if (strcmp(ksp->ks_module, "nfs")) 899 continue; 900 if (strcmp(ksp->ks_name, "mntinfo")) 901 continue; 902 if (ksp->ks_flags & KSTAT_FLAG_INVALID) 903 return (NULL); 904 if (kstat_read(kc, ksp, &mik) == -1) 905 return (NULL); 906 rstr = safe_strdup(mik.mik_curserver); 907 return (rstr); 908 } 909 return (NULL); 910 } 911 912 /* 913 * Given the hostname of the mounted server, extract the server 914 * mount point from the mnttab string. 915 * 916 * Common forms: 917 * server1,server2,server3:/path 918 * server1:/path,server2:/path 919 * or a hybrid of the two 920 */ 921 static char * 922 cur_special(char *hostname, char *special) 923 { 924 char *cp; 925 char *path; 926 size_t hlen = strlen(hostname); 927 928 /* 929 * find hostname in string 930 */ 931 again: 932 if ((cp = strstr(special, hostname)) == NULL) 933 return (NULL); 934 935 /* 936 * hostname must be followed by ',' or ':' 937 */ 938 if (cp[hlen] != ',' && cp[hlen] != ':') { 939 special = &cp[hlen]; 940 goto again; 941 } 942 943 /* 944 * If hostname is followed by a ',' eat all characters until a ':' 945 */ 946 cp = &cp[hlen]; 947 if (*cp == ',') { 948 cp++; 949 while (*cp != ':') { 950 if (*cp == NULL) 951 return (NULL); 952 cp++; 953 } 954 } 955 path = ++cp; /* skip ':' */ 956 957 /* 958 * path is terminated by either 0, or space or ',' 959 */ 960 while (*cp) { 961 if (isspace(*cp) || *cp == ',') { 962 *cp = NULL; 963 return (path); 964 } 965 cp++; 966 } 967 return (path); 968 } 969