xref: /illumos-gate/usr/src/cmd/stat/common/dsr.c (revision ea429a71)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54e78ab0bScth  * Common Development and Distribution License (the "License").
64e78ab0bScth  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
215f10ef69SYuri Pankov 
227c478bd9Sstevel@tonic-gate /*
234e78ab0bScth  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
255f10ef69SYuri Pankov  * Copyright 2016 Nexenta Systems, Inc.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/stat.h>
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate /*
327c478bd9Sstevel@tonic-gate  * Dependent on types.h, but not including it...
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate #include <stdio.h>
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/dkio.h>
377c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
387c478bd9Sstevel@tonic-gate #include <sys/mnttab.h>
397c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
407c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
417c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
427c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
437c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
447c478bd9Sstevel@tonic-gate #include <nfs/nfs_clnt.h>
457c478bd9Sstevel@tonic-gate #include <kstat.h>
467c478bd9Sstevel@tonic-gate #include <ctype.h>
477c478bd9Sstevel@tonic-gate #include <dirent.h>
487c478bd9Sstevel@tonic-gate #include <libdevinfo.h>
497c478bd9Sstevel@tonic-gate #include <limits.h>
507c478bd9Sstevel@tonic-gate #include <stdlib.h>
517c478bd9Sstevel@tonic-gate #include <string.h>
5237fbbce5Scth #include <strings.h>
537c478bd9Sstevel@tonic-gate #include <unistd.h>
547c478bd9Sstevel@tonic-gate #include <errno.h>
557c478bd9Sstevel@tonic-gate #include <devid.h>
5637fbbce5Scth #include <sys/scsi/adapters/scsi_vhci.h>
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate #include "dsr.h"
597c478bd9Sstevel@tonic-gate #include "statcommon.h"
607c478bd9Sstevel@tonic-gate 
6137fbbce5Scth /* where we get kstat name translation information from */
6237fbbce5Scth static di_node_t	di_root;	/* from di_init: for devid */
6337fbbce5Scth static di_dim_t		di_dim;		/* from di_dim_init: for /dev names */
6437fbbce5Scth static int		scsi_vhci_fd = -1; /* from scsi_vhci: for mpxio path */
6537fbbce5Scth 
6637fbbce5Scth /* disk/tape/misc info */
67a08731ecScth typedef struct {
68a08731ecScth 	char		*minor_name;
69a08731ecScth 	int		minor_isdisk;
70a08731ecScth } minor_match_t;
71a08731ecScth static minor_match_t	mm_disk = {"a", 1};
72a08731ecScth static minor_match_t	mm_tape	= {"", 0};
7337fbbce5Scth static minor_match_t	mm_misc	= {"0", 0};
745f10ef69SYuri Pankov static minor_match_t	*mma_disk_tape_misc[] =
7537fbbce5Scth 			    {&mm_disk, &mm_tape, &mm_misc, NULL};
765f10ef69SYuri Pankov #define	DISKLIST_MOD	256		/* ^2 instance mod hash */
77a08731ecScth static disk_list_t	*disklist[DISKLIST_MOD];
78a08731ecScth 
7937fbbce5Scth 
80a08731ecScth /* nfs info */
81a08731ecScth extern kstat_ctl_t	*kc;
82a08731ecScth extern mnt_t		*nfs;
83a08731ecScth static int		nfs_tried;
84a08731ecScth static char		*get_nfs_by_minor(uint_t);
85a08731ecScth static char		*cur_hostname(uint_t, kstat_ctl_t *);
86a08731ecScth static char		*cur_special(char *, char *);
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate /*
89a08731ecScth  * Clear the snapshot so a cache miss in lookup_ks_name() will cause a fresh
905f10ef69SYuri Pankov  * snapshot in drvinstpart2dev().
917c478bd9Sstevel@tonic-gate  */
92a08731ecScth void
cleanup_iodevs_snapshot()93a08731ecScth cleanup_iodevs_snapshot()
947c478bd9Sstevel@tonic-gate {
95a08731ecScth 	if (di_dim) {
96a08731ecScth 		di_dim_fini(di_dim);
97a08731ecScth 		di_dim = NULL;
98a08731ecScth 	}
997c478bd9Sstevel@tonic-gate 
100a08731ecScth 	if (di_root) {
101a08731ecScth 		di_fini(di_root);
102a08731ecScth 		di_root = DI_NODE_NIL;
1037c478bd9Sstevel@tonic-gate 	}
104a08731ecScth 
105a08731ecScth 	nfs_tried = 0;
1067c478bd9Sstevel@tonic-gate }
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate /*
1095f10ef69SYuri Pankov  * Find information for (driver, instance) device: return zero on failure.
110a08731ecScth  *
1115f10ef69SYuri Pankov  * NOTE: Failure of drvinstpart2dev works out OK for the caller if the kstat
112a08731ecScth  * name is the same as public name: the caller will just use kstat name.
1137c478bd9Sstevel@tonic-gate  */
1147c478bd9Sstevel@tonic-gate static int
drvinstpart2dev(char * driver,int instance,char * part,char ** devpathp,char ** adevpathp,char ** devidp)1155f10ef69SYuri Pankov drvinstpart2dev(char *driver, int instance, char *part,
11637fbbce5Scth     char **devpathp, char **adevpathp, char **devidp)
1177c478bd9Sstevel@tonic-gate {
118*ea429a71SToomas Soome 	minor_match_t	*mm = NULL, **mma = mma_disk_tape_misc;
119*ea429a71SToomas Soome 	char		*devpath = NULL;
120a08731ecScth 	char		*devid;
121a08731ecScth 	char		*devicespath;
122a08731ecScth 	di_node_t	node;
123a08731ecScth 
124a08731ecScth 	/* setup "no result" return values */
1255f10ef69SYuri Pankov 	if (devpathp != NULL)
126a08731ecScth 		*devpathp = NULL;
1275f10ef69SYuri Pankov 	if (adevpathp != NULL)
128a08731ecScth 		*adevpathp = NULL;
1295f10ef69SYuri Pankov 	if (devidp != NULL)
130a08731ecScth 		*devidp = NULL;
131a08731ecScth 
1325f10ef69SYuri Pankov 	/* take <driver><instance><minor> snapshot if not established */
133a08731ecScth 	if (di_dim == NULL) {
134a08731ecScth 		di_dim = di_dim_init();
135a08731ecScth 		if (di_dim == NULL)
136a08731ecScth 			return (0);
1377c478bd9Sstevel@tonic-gate 	}
1387c478bd9Sstevel@tonic-gate 
1395f10ef69SYuri Pankov 	if (part != NULL) {
14037fbbce5Scth 		devpath = di_dim_path_dev(di_dim, driver, instance, part);
14137fbbce5Scth 	} else  {
14237fbbce5Scth 		/* Try to find a minor_match that works */
1435f10ef69SYuri Pankov 		for (mm = *mma++; mm != NULL; mm = *mma++) {
14437fbbce5Scth 			if ((devpath = di_dim_path_dev(di_dim,
14537fbbce5Scth 			    driver, instance, mm->minor_name)) != NULL)
14637fbbce5Scth 				break;
14737fbbce5Scth 		}
1487c478bd9Sstevel@tonic-gate 	}
149a08731ecScth 	if (devpath == NULL)
150a08731ecScth 		return (0);
1517c478bd9Sstevel@tonic-gate 
152a08731ecScth 	/*
153a08731ecScth 	 * At this point we have a devpath result. Return the information about
154a08731ecScth 	 * the result that the caller is asking for.
155a08731ecScth 	 */
1565f10ef69SYuri Pankov 	if (devpathp != NULL)			/* devpath */
157a08731ecScth 		*devpathp = safe_strdup(devpath);
1587c478bd9Sstevel@tonic-gate 
1595f10ef69SYuri Pankov 	if (adevpathp != NULL) {		/* abbreviated devpath */
1605f10ef69SYuri Pankov 		char	*a;
1615f10ef69SYuri Pankov 
1625f10ef69SYuri Pankov 		a = strrchr(devpath, '/');
1635f10ef69SYuri Pankov 		if (a == NULL) {
1645f10ef69SYuri Pankov 			free(devpath);
1655f10ef69SYuri Pankov 			return (0);
1665f10ef69SYuri Pankov 		}
1675f10ef69SYuri Pankov 		a++;
1685f10ef69SYuri Pankov 		if (part == NULL && mm->minor_isdisk) {
1697c478bd9Sstevel@tonic-gate 			/*
17037fbbce5Scth 			 * For disk kstats without a partition we return the
17137fbbce5Scth 			 * last component with trailing "s#" or "p#" stripped
17237fbbce5Scth 			 * off (i.e. partition/slice information is removed).
173a08731ecScth 			 * For example for devpath of "/dev/dsk/c0t0d0s0" the
174a08731ecScth 			 * abbreviated devpath would be "c0t0d0".
1757c478bd9Sstevel@tonic-gate 			 */
1765f10ef69SYuri Pankov 			char	*s;
1775f10ef69SYuri Pankov 
1785f10ef69SYuri Pankov 			if ((s = strrchr(a, 's')) == NULL &&
1795f10ef69SYuri Pankov 			    (s = strrchr(a, 'p')) == NULL) {
180a08731ecScth 				free(devpath);
181a08731ecScth 				return (0);
1827c478bd9Sstevel@tonic-gate 			}
183a08731ecScth 			/* don't include slice information in devpath */
184a08731ecScth 			*s = '\0';
1857c478bd9Sstevel@tonic-gate 		}
186a08731ecScth 		*adevpathp = safe_strdup(a);
1877c478bd9Sstevel@tonic-gate 	}
1887c478bd9Sstevel@tonic-gate 
1895f10ef69SYuri Pankov 	if (devidp != NULL) {		/* lookup the devid */
19037fbbce5Scth 		/* take snapshot if not established */
1915f10ef69SYuri Pankov 		if (di_root == DI_NODE_NIL)
192a08731ecScth 			di_root = di_init("/", DINFOCACHE);
1935f10ef69SYuri Pankov 		if (di_root != NULL) {
194a08731ecScth 			/* get path to /devices devinfo node */
195a08731ecScth 			devicespath = di_dim_path_devices(di_dim,
196a08731ecScth 			    driver, instance, NULL);
197a08731ecScth 			if (devicespath) {
198a08731ecScth 				/* find the node in the snapshot */
199a08731ecScth 				node = di_lookup_node(di_root, devicespath);
200a08731ecScth 				free(devicespath);
201a08731ecScth 
202a08731ecScth 				/* and lookup devid property on the node */
203a08731ecScth 				if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
204a08731ecScth 				    DEVID_PROP_NAME, &devid) != -1)
205a08731ecScth 					*devidp = devid;
2067c478bd9Sstevel@tonic-gate 			}
2077c478bd9Sstevel@tonic-gate 		}
2087c478bd9Sstevel@tonic-gate 	}
2097c478bd9Sstevel@tonic-gate 
210a08731ecScth 	free(devpath);
211a08731ecScth 	return (1);				/* success */
2127c478bd9Sstevel@tonic-gate }
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate /*
21537fbbce5Scth  * Do <pid> to 'target-port' translation
21637fbbce5Scth  */
21737fbbce5Scth static int
drvpid2port(uint_t pid,char ** target_portp)21837fbbce5Scth drvpid2port(uint_t pid, char **target_portp)
21937fbbce5Scth {
22037fbbce5Scth 	sv_iocdata_t	ioc;
22137fbbce5Scth 	char		target_port[MAXNAMELEN];
22237fbbce5Scth 
22337fbbce5Scth 	/* setup "no result" return values */
22437fbbce5Scth 	*target_portp = NULL;
22537fbbce5Scth 
22637fbbce5Scth 	/* open scsi_vhci if not already done */
22737fbbce5Scth 	if (scsi_vhci_fd == -1) {
22837fbbce5Scth 		scsi_vhci_fd = open("/devices/scsi_vhci:devctl", O_RDONLY);
22937fbbce5Scth 		if (scsi_vhci_fd == -1)
23037fbbce5Scth 			return (0);		/* failure */
23137fbbce5Scth 	}
23237fbbce5Scth 
23337fbbce5Scth 	/*
23437fbbce5Scth 	 * Perform ioctl for <pid> -> 'target-port' translation.
23537fbbce5Scth 	 *
23637fbbce5Scth 	 * NOTE: it is legimite for this ioctl to fail for transports
23737fbbce5Scth 	 * that use mpxio, but don't set a 'target-port' pathinfo property.
23837fbbce5Scth 	 * On failure we return the the "<pid>" as the target port string.
23937fbbce5Scth 	 */
24037fbbce5Scth 	bzero(&ioc, sizeof (sv_iocdata_t));
24137fbbce5Scth 	ioc.buf_elem = pid;
24237fbbce5Scth 	ioc.addr = target_port;
24337fbbce5Scth 	if (ioctl(scsi_vhci_fd, SCSI_VHCI_GET_TARGET_LONGNAME, &ioc) < 0) {
24437fbbce5Scth 		(void) snprintf(target_port, sizeof (target_port), "%d", pid);
24537fbbce5Scth 	}
24637fbbce5Scth 
24737fbbce5Scth 	*target_portp = safe_strdup(target_port);
24837fbbce5Scth 	return (1);				/* success */
24937fbbce5Scth }
25037fbbce5Scth 
25137fbbce5Scth /*
2525f10ef69SYuri Pankov  * Find/create a disk_list entry for the given kstat name.
25337fbbce5Scth  * The basic format of a kstat name is
25437fbbce5Scth  *
2555f10ef69SYuri Pankov  *	"<driver><instance>[.<pid>.<phci-driver><instance>][,<partition>]".
25637fbbce5Scth  *
2575f10ef69SYuri Pankov  * The <instance> is a decimal number. The ".<pid>.<phci-driver><instance>",
25837fbbce5Scth  * which describes mpxio path stat information, and ",<partition>" parts are
25937fbbce5Scth  * optional. The <pid> consists of the letter 't' followed by a decimal number.
26037fbbce5Scth  * When available, we use the <pid> to find the 'target-port' via ioctls to
26137fbbce5Scth  * the scsi_vhci driver.
2627c478bd9Sstevel@tonic-gate  */
263a08731ecScth disk_list_t *
lookup_ks_name(char * ks_name,int want_devid)264a08731ecScth lookup_ks_name(char *ks_name, int want_devid)
2657c478bd9Sstevel@tonic-gate {
26637fbbce5Scth 	char		*pidp;		/* ".<pid>... */
26737fbbce5Scth 	char		*part;		/* ",partition... */
26837fbbce5Scth 	char		*initiator;	/* ".<phci-driver>... */
269a08731ecScth 	char		*p;
270a08731ecScth 	int		len;
27137fbbce5Scth 	char		driver[KSTAT_STRLEN];
2725f10ef69SYuri Pankov 	int		instance;
273a08731ecScth 	disk_list_t	**dlhp;		/* disklist head */
274a08731ecScth 	disk_list_t	*entry;
27537fbbce5Scth 	char		*devpath = NULL;
276a08731ecScth 	char		*adevpath = NULL;
277a08731ecScth 	char		*devid = NULL;
27837fbbce5Scth 	int		pid;
27937fbbce5Scth 	char		*target_port = NULL;
28037fbbce5Scth 	char		portform[MAXPATHLEN];
281a08731ecScth 
2825f10ef69SYuri Pankov 	/* Filter out illegal forms (like all digits) */
2835f10ef69SYuri Pankov 	if (ks_name == NULL || *ks_name == '\0' ||
2845f10ef69SYuri Pankov 	    strspn(ks_name, "0123456789") == strlen(ks_name))
28537fbbce5Scth 		goto fail;
28637fbbce5Scth 
28737fbbce5Scth 	/* parse ks_name to create new entry */
28837fbbce5Scth 	pidp = strchr(ks_name, '.');		/* start of ".<pid>" */
28937fbbce5Scth 	initiator = strrchr(ks_name, '.');	/* start of ".<pHCI-driver>" */
2905f10ef69SYuri Pankov 	if (pidp != NULL && pidp == initiator)	/* can't have same start */
29137fbbce5Scth 		goto fail;
29237fbbce5Scth 
29337fbbce5Scth 	part = strchr(ks_name, ',');		/* start of ",<partition>" */
294f9409f99SToomas Soome 	p = strchr(ks_name, ':');		/* start of ":<partition>" */
2955f10ef69SYuri Pankov 	if (part != NULL && p != NULL)
29637fbbce5Scth 		goto fail;			/* can't have both */
2975f10ef69SYuri Pankov 	if (p != NULL)
29837fbbce5Scth 		part = p;
2995f10ef69SYuri Pankov 	if (part != NULL && pidp != NULL)
30037fbbce5Scth 		goto fail;			/* <pid> and partition: bad */
30137fbbce5Scth 
3025f10ef69SYuri Pankov 	p = (part != NULL) ? part : pidp;
303a08731ecScth 	if (p == NULL)
304a08731ecScth 		p = &ks_name[strlen(ks_name) - 1];	/* last char */
305a08731ecScth 	else
30637fbbce5Scth 		p--;				/* before ',' or '.' */
307a08731ecScth 
308a08731ecScth 	while ((p >= ks_name) && isdigit(*p))
309a08731ecScth 		p--;				/* backwards over digits */
3105f10ef69SYuri Pankov 	p++;					/* start of instance */
31137fbbce5Scth 	if ((*p == '\0') || (*p == ',') || (*p == '.') || (*p == ':'))
3125f10ef69SYuri Pankov 		goto fail;			/* no <instance> */
313a08731ecScth 	len = p - ks_name;
314a08731ecScth 	(void) strncpy(driver, ks_name, len);
315a08731ecScth 	driver[len] = '\0';
3165f10ef69SYuri Pankov 	instance = atoi(p);
3175f10ef69SYuri Pankov 	if (part != NULL)
31837fbbce5Scth 		part++;				/* skip ',' */
319a08731ecScth 
3205f10ef69SYuri Pankov 	/* hash by instance and search for existing entry */
3215f10ef69SYuri Pankov 	dlhp = &disklist[instance & (DISKLIST_MOD - 1)];
322a08731ecScth 	for (entry = *dlhp; entry; entry = entry->next) {
3235f10ef69SYuri Pankov 		if (strcmp(entry->ks_name, ks_name) == 0)
324a08731ecScth 			return (entry);
3257c478bd9Sstevel@tonic-gate 	}
3267c478bd9Sstevel@tonic-gate 
32737fbbce5Scth 	/* not found, translate kstat_name components and create new entry */
32837fbbce5Scth 
32937fbbce5Scth 	/* translate kstat_name dev information */
3305f10ef69SYuri Pankov 	if (drvinstpart2dev(driver, instance, part,
3315f10ef69SYuri Pankov 	    &devpath, &adevpath, want_devid ? &devid : NULL) == 0)
33237fbbce5Scth 		goto fail;
33337fbbce5Scth 
33437fbbce5Scth 	/* parse and translate path information */
3355f10ef69SYuri Pankov 	if (pidp != NULL) {
33637fbbce5Scth 		/* parse path information: ".t#.<phci-driver><instance>" */
33737fbbce5Scth 		pidp++;				/* skip '.' */
33837fbbce5Scth 		initiator++;			/* skip '.' */
3395f10ef69SYuri Pankov 		if (*pidp != 't' || !isdigit(pidp[1]))
34037fbbce5Scth 			goto fail;		/* not ".t#" */
34137fbbce5Scth 		pid = atoi(&pidp[1]);
34237fbbce5Scth 
34337fbbce5Scth 		/* translate <pid> to 'target-port' */
34437fbbce5Scth 		if (drvpid2port(pid, &target_port) == 0)
34537fbbce5Scth 			goto fail;
34637fbbce5Scth 
34737fbbce5Scth 		/* Establish 'target-port' form. */
34837fbbce5Scth 		(void) snprintf(portform, sizeof (portform),
34937fbbce5Scth 		    "%s.t%s.%s", adevpath, target_port, initiator);
35037fbbce5Scth 		free(target_port);
35137fbbce5Scth 		free(adevpath);
35237fbbce5Scth 		adevpath = strdup(portform);
3537c478bd9Sstevel@tonic-gate 	}
3547c478bd9Sstevel@tonic-gate 
35537fbbce5Scth 	/* make a new entry ... */
356a08731ecScth 	entry = safe_alloc(sizeof (disk_list_t));
35737fbbce5Scth 	entry->ks_name = safe_strdup(ks_name);
358a08731ecScth 	entry->dname = devpath;
359a08731ecScth 	entry->dsk = adevpath;
360a08731ecScth 	entry->devidstr = devid;
3617c478bd9Sstevel@tonic-gate 
36237fbbce5Scth #ifdef	DEBUG
36337fbbce5Scth 	(void) printf("lookup_ks_name:    new: %s	%s\n",
36437fbbce5Scth 	    ks_name, entry->dsk ? entry->dsk : "NULL");
36537fbbce5Scth #endif	/* DEBUG */
36637fbbce5Scth 
36737fbbce5Scth 	/* add new entry to head of hashed list */
368a08731ecScth 	entry->next = *dlhp;
369a08731ecScth 	*dlhp = entry;
370a08731ecScth 	return (entry);
37137fbbce5Scth 
37237fbbce5Scth fail:
37337fbbce5Scth 	free(devpath);
37437fbbce5Scth 	free(adevpath);
37537fbbce5Scth 	free(devid);
37637fbbce5Scth #ifdef	DEBUG
37737fbbce5Scth 	(void) printf("lookup_ks_name: failed: %s\n", ks_name);
37837fbbce5Scth #endif	/* DEBUG */
37937fbbce5Scth 	return (NULL);
3807c478bd9Sstevel@tonic-gate }
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate char *
lookup_nfs_name(char * ks,kstat_ctl_t * kc)3837c478bd9Sstevel@tonic-gate lookup_nfs_name(char *ks, kstat_ctl_t *kc)
3847c478bd9Sstevel@tonic-gate {
3857c478bd9Sstevel@tonic-gate 	uint_t minor;
3867c478bd9Sstevel@tonic-gate 	char *host, *path;
3877c478bd9Sstevel@tonic-gate 	char *cp;
3887c478bd9Sstevel@tonic-gate 	char *rstr = 0;
3897c478bd9Sstevel@tonic-gate 	size_t len;
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 	if (sscanf(ks, "nfs%u", &minor) == 1) {
3927c478bd9Sstevel@tonic-gate retry:
3937c478bd9Sstevel@tonic-gate 		cp = get_nfs_by_minor(minor);
3947c478bd9Sstevel@tonic-gate 		if (cp) {
3957c478bd9Sstevel@tonic-gate 			if (strchr(cp, ',') == NULL) {
3967c478bd9Sstevel@tonic-gate 				rstr = safe_strdup(cp);
3977c478bd9Sstevel@tonic-gate 				return (rstr);
3987c478bd9Sstevel@tonic-gate 			}
3997c478bd9Sstevel@tonic-gate 			host = cur_hostname(minor, kc);
4007c478bd9Sstevel@tonic-gate 			if (host) {
4017c478bd9Sstevel@tonic-gate 				if (*host) {
4027c478bd9Sstevel@tonic-gate 					path = cur_special(host, cp);
4037c478bd9Sstevel@tonic-gate 					if (path) {
4047c478bd9Sstevel@tonic-gate 						len = strlen(host);
4057c478bd9Sstevel@tonic-gate 						len += strlen(path);
4067c478bd9Sstevel@tonic-gate 						len += 2;
4077c478bd9Sstevel@tonic-gate 						rstr = safe_alloc(len);
4087c478bd9Sstevel@tonic-gate 						(void) snprintf(rstr, len,
4097c478bd9Sstevel@tonic-gate 						    "%s:%s", host, path);
4107c478bd9Sstevel@tonic-gate 					} else {
4117c478bd9Sstevel@tonic-gate 						rstr = safe_strdup(cp);
4127c478bd9Sstevel@tonic-gate 					}
4137c478bd9Sstevel@tonic-gate 				} else {
4147c478bd9Sstevel@tonic-gate 					rstr = safe_strdup(ks);
4157c478bd9Sstevel@tonic-gate 				}
4167c478bd9Sstevel@tonic-gate 				free(host);
4177c478bd9Sstevel@tonic-gate 			} else {
4187c478bd9Sstevel@tonic-gate 				rstr = safe_strdup(cp);
4197c478bd9Sstevel@tonic-gate 			}
420a08731ecScth 		} else if (nfs_tried == 0) {
421a08731ecScth 			nfs_tried = 1;
4227c478bd9Sstevel@tonic-gate 			do_mnttab();
4237c478bd9Sstevel@tonic-gate 			goto retry;
4247c478bd9Sstevel@tonic-gate 		}
4257c478bd9Sstevel@tonic-gate 	}
4267c478bd9Sstevel@tonic-gate 	return (rstr);
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate static char *
get_nfs_by_minor(uint_t minor)4307c478bd9Sstevel@tonic-gate get_nfs_by_minor(uint_t minor)
4317c478bd9Sstevel@tonic-gate {
4327c478bd9Sstevel@tonic-gate 	mnt_t *localnfs;
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	localnfs = nfs;
4357c478bd9Sstevel@tonic-gate 	while (localnfs) {
4367c478bd9Sstevel@tonic-gate 		if (localnfs->minor == minor) {
4377c478bd9Sstevel@tonic-gate 			return (localnfs->device_name);
4387c478bd9Sstevel@tonic-gate 		}
4397c478bd9Sstevel@tonic-gate 		localnfs = localnfs->next;
4407c478bd9Sstevel@tonic-gate 	}
4417c478bd9Sstevel@tonic-gate 	return (0);
4427c478bd9Sstevel@tonic-gate }
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate /*
4457c478bd9Sstevel@tonic-gate  * Read the cur_hostname from the mntinfo kstat
4467c478bd9Sstevel@tonic-gate  */
4477c478bd9Sstevel@tonic-gate static char *
cur_hostname(uint_t minor,kstat_ctl_t * kc)4487c478bd9Sstevel@tonic-gate cur_hostname(uint_t minor, kstat_ctl_t *kc)
4497c478bd9Sstevel@tonic-gate {
4507c478bd9Sstevel@tonic-gate 	kstat_t *ksp;
4517c478bd9Sstevel@tonic-gate 	static struct mntinfo_kstat mik;
4527c478bd9Sstevel@tonic-gate 	char *rstr;
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
4557c478bd9Sstevel@tonic-gate 		if (ksp->ks_type != KSTAT_TYPE_RAW)
4567c478bd9Sstevel@tonic-gate 			continue;
4577c478bd9Sstevel@tonic-gate 		if (ksp->ks_instance != minor)
4587c478bd9Sstevel@tonic-gate 			continue;
4597c478bd9Sstevel@tonic-gate 		if (strcmp(ksp->ks_module, "nfs"))
4607c478bd9Sstevel@tonic-gate 			continue;
4617c478bd9Sstevel@tonic-gate 		if (strcmp(ksp->ks_name, "mntinfo"))
4627c478bd9Sstevel@tonic-gate 			continue;
4637c478bd9Sstevel@tonic-gate 		if (ksp->ks_flags & KSTAT_FLAG_INVALID)
4647c478bd9Sstevel@tonic-gate 			return (NULL);
4657c478bd9Sstevel@tonic-gate 		if (kstat_read(kc, ksp, &mik) == -1)
4667c478bd9Sstevel@tonic-gate 			return (NULL);
4677c478bd9Sstevel@tonic-gate 		rstr = safe_strdup(mik.mik_curserver);
4687c478bd9Sstevel@tonic-gate 		return (rstr);
4697c478bd9Sstevel@tonic-gate 	}
4707c478bd9Sstevel@tonic-gate 	return (NULL);
4717c478bd9Sstevel@tonic-gate }
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate /*
4747c478bd9Sstevel@tonic-gate  * Given the hostname of the mounted server, extract the server
4757c478bd9Sstevel@tonic-gate  * mount point from the mnttab string.
4767c478bd9Sstevel@tonic-gate  *
4777c478bd9Sstevel@tonic-gate  * Common forms:
4787c478bd9Sstevel@tonic-gate  *	server1,server2,server3:/path
4797c478bd9Sstevel@tonic-gate  *	server1:/path,server2:/path
4807c478bd9Sstevel@tonic-gate  * or a hybrid of the two
4817c478bd9Sstevel@tonic-gate  */
4827c478bd9Sstevel@tonic-gate static char *
cur_special(char * hostname,char * special)4837c478bd9Sstevel@tonic-gate cur_special(char *hostname, char *special)
4847c478bd9Sstevel@tonic-gate {
4857c478bd9Sstevel@tonic-gate 	char *cp;
4867c478bd9Sstevel@tonic-gate 	char *path;
4877c478bd9Sstevel@tonic-gate 	size_t hlen = strlen(hostname);
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	/*
4907c478bd9Sstevel@tonic-gate 	 * find hostname in string
4917c478bd9Sstevel@tonic-gate 	 */
4927c478bd9Sstevel@tonic-gate again:
4937c478bd9Sstevel@tonic-gate 	if ((cp = strstr(special, hostname)) == NULL)
4947c478bd9Sstevel@tonic-gate 		return (NULL);
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	/*
4977c478bd9Sstevel@tonic-gate 	 * hostname must be followed by ',' or ':'
4987c478bd9Sstevel@tonic-gate 	 */
4997c478bd9Sstevel@tonic-gate 	if (cp[hlen] != ',' && cp[hlen] != ':') {
5007c478bd9Sstevel@tonic-gate 		special = &cp[hlen];
5017c478bd9Sstevel@tonic-gate 		goto again;
5027c478bd9Sstevel@tonic-gate 	}
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	/*
5057c478bd9Sstevel@tonic-gate 	 * If hostname is followed by a ',' eat all characters until a ':'
5067c478bd9Sstevel@tonic-gate 	 */
5077c478bd9Sstevel@tonic-gate 	cp = &cp[hlen];
5087c478bd9Sstevel@tonic-gate 	if (*cp == ',') {
5097c478bd9Sstevel@tonic-gate 		cp++;
5107c478bd9Sstevel@tonic-gate 		while (*cp != ':') {
511f9409f99SToomas Soome 			if (*cp == '\0')
5127c478bd9Sstevel@tonic-gate 				return (NULL);
5137c478bd9Sstevel@tonic-gate 			cp++;
5147c478bd9Sstevel@tonic-gate 		}
5157c478bd9Sstevel@tonic-gate 	}
5167c478bd9Sstevel@tonic-gate 	path = ++cp;			/* skip ':' */
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 	/*
5197c478bd9Sstevel@tonic-gate 	 * path is terminated by either 0, or space or ','
5207c478bd9Sstevel@tonic-gate 	 */
5217c478bd9Sstevel@tonic-gate 	while (*cp) {
5227c478bd9Sstevel@tonic-gate 		if (isspace(*cp) || *cp == ',') {
523f9409f99SToomas Soome 			*cp = '\0';
5247c478bd9Sstevel@tonic-gate 			return (path);
5257c478bd9Sstevel@tonic-gate 		}
5267c478bd9Sstevel@tonic-gate 		cp++;
5277c478bd9Sstevel@tonic-gate 	}
5287c478bd9Sstevel@tonic-gate 	return (path);
5297c478bd9Sstevel@tonic-gate }
530