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