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 #pragma ident "%Z%%M% %I% %E% SMI"
41
42 /*
43 * nfs dfmounts
44 */
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <stdarg.h>
48 #include <string.h>
49 #include <rpc/rpc.h>
50 #include <rpc/rpcb_clnt.h>
51 #include <sys/socket.h>
52 #include <netdb.h>
53 #include <sys/time.h>
54 #include <sys/errno.h>
55 #include <nfs/nfs.h>
56 #include <rpcsvc/mount.h>
57 #include <locale.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 static void pr_err(char *, ...);
66
67 int
main(int argc,char * argv[])68 main(int argc, char *argv[])
69 {
70
71 char hostbuf[256];
72 extern int optind;
73 int i, c;
74
75 (void) setlocale(LC_ALL, "");
76
77 #if !defined(TEXT_DOMAIN)
78 #define TEXT_DOMAIN "SYS_TEST"
79 #endif
80 (void) textdomain(TEXT_DOMAIN);
81
82 while ((c = getopt(argc, argv, "h")) != EOF) {
83 switch (c) {
84 case 'h':
85 hflg++;
86 break;
87 default:
88 usage();
89 exit(1);
90 }
91 }
92
93 if (optind < argc) {
94 for (i = optind; i < argc; i++)
95 pr_mounts(argv[i]);
96 } else {
97 if (gethostname(hostbuf, sizeof (hostbuf)) < 0) {
98 perror("nfs dfmounts: gethostname");
99 exit(1);
100 }
101 pr_mounts(hostbuf);
102 }
103
104 return (0);
105 }
106
107 #define NTABLEENTRIES 2048
108 static struct mountbody *table[NTABLEENTRIES];
109 static struct timeval rpc_totout_new = {15, 0};
110
111 /*
112 * Print the filesystems on "host" that are currently mounted by a client.
113 */
114
115 static void
pr_mounts(host)116 pr_mounts(host)
117 char *host;
118 {
119 CLIENT *cl;
120 struct mountbody *ml = NULL;
121 struct mountbody **tb, **endtb;
122 enum clnt_stat err;
123 char *lastpath;
124 char *lastclient;
125 int tail = 0;
126 struct timeval tout, rpc_totout_old;
127
128 (void) __rpc_control(CLCR_GET_RPCB_TIMEOUT, &rpc_totout_old);
129 (void) __rpc_control(CLCR_SET_RPCB_TIMEOUT, &rpc_totout_new);
130
131 /*
132 * First try circuit, then drop back to datagram if
133 * circuit is unavailable (an old version of mountd perhaps)
134 * Using circuit is preferred because it can handle
135 * arbitrarily long export lists.
136 */
137 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "circuit_n");
138 if (cl == NULL) {
139 if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED)
140 cl = clnt_create(host, MOUNTPROG, MOUNTVERS,
141 "datagram_n");
142 if (cl == NULL) {
143 pr_err(gettext("can't contact server: %s\n"),
144 clnt_spcreateerror(host));
145 (void) __rpc_control(CLCR_SET_RPCB_TIMEOUT,
146 &rpc_totout_old);
147 return;
148 }
149 }
150
151 (void) __rpc_control(CLCR_SET_RPCB_TIMEOUT, &rpc_totout_old);
152 tout.tv_sec = 10;
153 tout.tv_usec = 0;
154
155 if (err = clnt_call(cl, MOUNTPROC_DUMP, xdr_void,
156 0, xdr_mountlist, (caddr_t)&ml, tout)) {
157 pr_err("%s\n", clnt_sperrno(err));
158 clnt_destroy(cl);
159 return;
160 }
161
162 if (ml == NULL)
163 return; /* no mounts */
164
165 if (!hflg) {
166 printf("%-8s %10s %-24s %s",
167 gettext("RESOURCE"), gettext("SERVER"),
168 gettext("PATHNAME"), gettext("CLIENTS"));
169 hflg++;
170 }
171
172 /*
173 * Create an array describing the mounts, so that we can sort them.
174 */
175 tb = table;
176 for (; ml != NULL && tb < &table[NTABLEENTRIES]; ml = ml->ml_next)
177 *tb++ = ml;
178 if (ml != NULL && tb == &table[NTABLEENTRIES])
179 pr_err(gettext("table overflow: only %d entries shown\n"),
180 NTABLEENTRIES);
181 endtb = tb;
182 qsort(table, endtb - table, sizeof (struct mountbody *), sortpath);
183
184 /*
185 * Print out the sorted array. Group entries for the same
186 * filesystem together, and ignore duplicate entries.
187 */
188 lastpath = "";
189 lastclient = "";
190 for (tb = table; tb < endtb; tb++) {
191 if (*((*tb)->ml_directory) == '\0' ||
192 *((*tb)->ml_hostname) == '\0')
193 continue;
194 if (strcmp(lastpath, (*tb)->ml_directory) == 0) {
195 if (strcmp(lastclient, (*tb)->ml_hostname) == 0) {
196 continue; /* ignore duplicate */
197 }
198 } else {
199 printf("\n%-8s %10s %-24s ",
200 " -", host, (*tb)->ml_directory);
201 lastpath = (*tb)->ml_directory;
202 tail = 0;
203 }
204 if (tail++)
205 printf(",");
206 printf("%s", (*tb)->ml_hostname);
207 lastclient = (*tb)->ml_hostname;
208 }
209 printf("\n");
210
211 freemntlist(ml);
212 clnt_destroy(cl);
213 }
214
215 static void
freemntlist(ml)216 freemntlist(ml)
217 struct mountbody *ml;
218 {
219 register struct mountbody *old;
220
221 while (ml) {
222 if (ml->ml_hostname)
223 free(ml->ml_hostname);
224 if (ml->ml_directory)
225 free(ml->ml_directory);
226 old = ml;
227 ml = ml->ml_next;
228 free(old);
229 }
230 }
231
232 /*
233 * Compare two structs for mounted filesystems. The primary sort key is
234 * the name of the exported filesystem. There is also a secondary sort on
235 * the name of the client, so that duplicate entries (same path and
236 * hostname) will sort together.
237 *
238 * Returns < 0 if the first entry sorts before the second entry, 0 if they
239 * sort the same, and > 0 if the first entry sorts after the second entry.
240 */
241
242 static int
sortpath(a,b)243 sortpath(a, b)
244 const void *a, *b;
245 {
246 const struct mountbody **m1, **m2;
247 int result;
248
249 m1 = (const struct mountbody **)a;
250 m2 = (const struct mountbody **)b;
251
252 result = strcmp((*m1)->ml_directory, (*m2)->ml_directory);
253 if (result == 0) {
254 result = strcmp((*m1)->ml_hostname, (*m2)->ml_hostname);
255 }
256
257 return (result);
258 }
259
260 static void
usage()261 usage()
262 {
263 (void) fprintf(stderr, gettext("Usage: dfmounts [-h] [host ...]\n"));
264 }
265
266 /* VARARGS1 */
267 static void
pr_err(char * fmt,...)268 pr_err(char *fmt, ...)
269 {
270 va_list ap;
271
272 va_start(ap, fmt);
273 (void) fprintf(stderr, "nfs dfmounts: ");
274 (void) vfprintf(stderr, fmt, ap);
275 va_end(ap);
276 }
277