xref: /illumos-gate/usr/src/cmd/stat/common/dsr.c (revision 4e78ab0b)
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