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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 /*
41  * nfs dfmounts
42  */
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46 #include <string.h>
47 #include <rpc/rpc.h>
48 #include <rpc/rpcb_clnt.h>
49 #include <sys/socket.h>
50 #include <netdb.h>
51 #include <sys/time.h>
52 #include <sys/errno.h>
53 #include <nfs/nfs.h>
54 #include <rpcsvc/mount.h>
55 #include <locale.h>
56 #include <unistd.h>
57 #include <clnt_subr.h>
58 
59 static int hflg;
60 
61 static void pr_mounts(char *);
62 static void freemntlist(struct mountbody *);
63 static int sortpath(const void *, const void *);
64 static void usage(void);
65 
66 int
main(int argc,char * argv[])67 main(int argc, char *argv[])
68 {
69 
70 	char hostbuf[256];
71 	extern int optind;
72 	int i, c;
73 
74 	(void) setlocale(LC_ALL, "");
75 
76 #if !defined(TEXT_DOMAIN)
77 #define	TEXT_DOMAIN "SYS_TEST"
78 #endif
79 	(void) textdomain(TEXT_DOMAIN);
80 
81 	while ((c = getopt(argc, argv, "h")) != EOF) {
82 		switch (c) {
83 		case 'h':
84 			hflg++;
85 			break;
86 		default:
87 			usage();
88 			exit(1);
89 		}
90 	}
91 
92 	if (optind < argc) {
93 		for (i = optind; i < argc; i++)
94 			pr_mounts(argv[i]);
95 	} else {
96 		if (gethostname(hostbuf, sizeof (hostbuf)) < 0) {
97 			perror("nfs dfmounts: gethostname");
98 			exit(1);
99 		}
100 		pr_mounts(hostbuf);
101 	}
102 
103 	return (0);
104 }
105 
106 #define	NTABLEENTRIES	2048
107 static struct mountbody *table[NTABLEENTRIES];
108 static struct timeval	rpc_totout_new = {15, 0};
109 
110 /*
111  * Print the filesystems on "host" that are currently mounted by a client.
112  */
113 
114 static void
pr_mounts(char * host)115 pr_mounts(char *host)
116 {
117 	CLIENT *cl;
118 	struct mountbody *ml = NULL;
119 	struct mountbody **tb, **endtb;
120 	enum clnt_stat err;
121 	char *lastpath;
122 	char *lastclient;
123 	int tail = 0;
124 	struct	timeval	tout, rpc_totout_old;
125 
126 	(void) __rpc_control(CLCR_GET_RPCB_TIMEOUT, &rpc_totout_old);
127 	(void) __rpc_control(CLCR_SET_RPCB_TIMEOUT, &rpc_totout_new);
128 
129 	cl = mountprog_client_create(host, &rpc_totout_old);
130 	if (cl == NULL) {
131 		return;
132 	}
133 
134 	(void) __rpc_control(CLCR_SET_RPCB_TIMEOUT, &rpc_totout_old);
135 	tout.tv_sec = 10;
136 	tout.tv_usec = 0;
137 
138 	err = clnt_call(cl, MOUNTPROC_DUMP, xdr_void, 0, xdr_mountlist,
139 	    (caddr_t)&ml, tout);
140 	if (err != 0) {
141 		pr_err("%s\n", clnt_sperrno(err));
142 		clnt_destroy(cl);
143 		return;
144 	}
145 
146 	if (ml == NULL)
147 		return;	/* no mounts */
148 
149 	if (!hflg) {
150 		printf("%-8s %10s %-24s  %s",
151 		    gettext("RESOURCE"), gettext("SERVER"),
152 		    gettext("PATHNAME"), gettext("CLIENTS"));
153 		hflg++;
154 	}
155 
156 	/*
157 	 * Create an array describing the mounts, so that we can sort them.
158 	 */
159 	tb = table;
160 	for (; ml != NULL && tb < &table[NTABLEENTRIES]; ml = ml->ml_next)
161 		*tb++ = ml;
162 	if (ml != NULL && tb == &table[NTABLEENTRIES])
163 		pr_err(gettext("table overflow:  only %d entries shown\n"),
164 		    NTABLEENTRIES);
165 	endtb = tb;
166 	qsort(table, endtb - table, sizeof (struct mountbody *), sortpath);
167 
168 	/*
169 	 * Print out the sorted array.  Group entries for the same
170 	 * filesystem together, and ignore duplicate entries.
171 	 */
172 	lastpath = "";
173 	lastclient = "";
174 	for (tb = table; tb < endtb; tb++) {
175 		if (*((*tb)->ml_directory) == '\0' ||
176 		    *((*tb)->ml_hostname) == '\0')
177 			continue;
178 		if (strcmp(lastpath, (*tb)->ml_directory) == 0) {
179 			if (strcmp(lastclient, (*tb)->ml_hostname) == 0) {
180 				continue;	/* ignore duplicate */
181 			}
182 		} else {
183 			printf("\n%-8s %10s %-24s ",
184 			    "  -", host, (*tb)->ml_directory);
185 			lastpath = (*tb)->ml_directory;
186 			tail = 0;
187 		}
188 		if (tail++)
189 			printf(",");
190 		printf("%s", (*tb)->ml_hostname);
191 		lastclient = (*tb)->ml_hostname;
192 	}
193 	printf("\n");
194 
195 	freemntlist(ml);
196 	clnt_destroy(cl);
197 }
198 
199 static void
freemntlist(struct mountbody * ml)200 freemntlist(struct mountbody *ml)
201 {
202 	struct mountbody *old;
203 
204 	while (ml) {
205 		if (ml->ml_hostname)
206 			free(ml->ml_hostname);
207 		if (ml->ml_directory)
208 			free(ml->ml_directory);
209 		old = ml;
210 		ml = ml->ml_next;
211 		free(old);
212 	}
213 }
214 
215 /*
216  * Compare two structs for mounted filesystems.  The primary sort key is
217  * the name of the exported filesystem.  There is also a secondary sort on
218  * the name of the client, so that duplicate entries (same path and
219  * hostname) will sort together.
220  *
221  * Returns < 0 if the first entry sorts before the second entry, 0 if they
222  * sort the same, and > 0 if the first entry sorts after the second entry.
223  */
224 
225 static int
sortpath(const void * a,const void * b)226 sortpath(const void *a, const void *b)
227 {
228 	const struct mountbody **m1, **m2;
229 	int result;
230 
231 	m1 = (const struct mountbody **)a;
232 	m2 = (const struct mountbody **)b;
233 
234 	result = strcmp((*m1)->ml_directory, (*m2)->ml_directory);
235 	if (result == 0) {
236 		result = strcmp((*m1)->ml_hostname, (*m2)->ml_hostname);
237 	}
238 
239 	return (result);
240 }
241 
242 static void
usage(void)243 usage(void)
244 {
245 	(void) fprintf(stderr, gettext("Usage: dfmounts [-h] [host ...]\n"));
246 }
247 
248 void
pr_err(char * fmt,...)249 pr_err(char *fmt, ...)
250 {
251 	va_list ap;
252 
253 	va_start(ap, fmt);
254 	(void) fprintf(stderr, "nfs dfmounts: ");
255 	(void) vfprintf(stderr, fmt, ap);
256 	va_end(ap);
257 }
258