1/*	$NetBSD: showmount.c,v 1.23 2018/01/09 03:31:15 christos Exp $	*/
2
3/*
4 * Copyright (c) 1989, 1993, 1995
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Rick Macklem at The University of Guelph.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36#ifndef lint
37__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1995\
38 The Regents of the University of California.  All rights reserved.");
39#endif /* not lint */
40
41#ifndef lint
42#if 0
43static char sccsid[] = "@(#)showmount.c	8.3 (Berkeley) 3/29/95";
44#endif
45__RCSID("$NetBSD: showmount.c,v 1.23 2018/01/09 03:31:15 christos Exp $");
46#endif /* not lint */
47
48#include <sys/types.h>
49#include <sys/file.h>
50#include <sys/socket.h>
51
52#include <netdb.h>
53#include <rpc/rpc.h>
54#include <rpc/pmap_clnt.h>
55#include <rpc/pmap_prot.h>
56#include <nfs/rpcv2.h>
57
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <unistd.h>
62#include <err.h>
63#include <vis.h>
64
65/* Constant defs */
66#define	ALL	1
67#define	DIRS	2
68
69#define	DODUMP		0x1
70#define	DOEXPORTS	0x2
71#define	DOPARSABLEEXPORTS	0x4
72
73struct mountlist {
74	struct mountlist *ml_left;
75	struct mountlist *ml_right;
76	char	ml_host[RPCMNT_NAMELEN+1];
77	char	ml_dirp[RPCMNT_PATHLEN+1];
78};
79
80struct grouplist {
81	struct grouplist *gr_next;
82	char	gr_name[RPCMNT_NAMELEN+1];
83};
84
85struct exportslist {
86	struct exportslist *ex_next;
87	struct grouplist *ex_groups;
88	char	ex_dirp[RPCMNT_PATHLEN+1];
89};
90
91static struct mountlist *mntdump;
92static struct exportslist *exports;
93static int type = 0;
94
95static void	print_dump(struct mountlist *);
96__dead static void	usage(void);
97static int	xdr_mntdump(XDR *, struct mountlist **);
98static int	xdr_exports(XDR *, struct exportslist **);
99static int	tcp_callrpc(const char *host, int prognum, int versnum,
100    int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out);
101
102/*
103 * This command queries the NFS mount daemon for its mount list and/or
104 * its exports list and prints them out.
105 * See "NFS: Network File System Protocol Specification, RFC1094, Appendix A"
106 * and the "Network File System Protocol XXX.."
107 * for detailed information on the protocol.
108 */
109int
110main(int argc, char **argv)
111{
112	struct exportslist *exp;
113	struct grouplist *grp;
114	int estat, rpcs = 0, mntvers = 1;
115	const char *host;
116	int ch;
117	int len;
118	int nbytes;
119	char strvised[1024 * 4 + 1];
120
121	while ((ch = getopt(argc, argv, "adEe3")) != -1)
122		switch((char)ch) {
123		case 'a':
124			if (type == 0) {
125				type = ALL;
126				rpcs |= DODUMP;
127			} else
128				usage();
129			break;
130		case 'd':
131			if (type == 0) {
132				type = DIRS;
133				rpcs |= DODUMP;
134			} else
135				usage();
136			break;
137		case 'E':
138			rpcs |= DOPARSABLEEXPORTS;
139			break;
140		case 'e':
141			rpcs |= DOEXPORTS;
142			break;
143		case '3':
144			mntvers = 3;
145			break;
146		case '?':
147		default:
148			usage();
149		}
150	argc -= optind;
151	argv += optind;
152
153	if ((rpcs & DOPARSABLEEXPORTS) != 0) {
154		if ((rpcs & DOEXPORTS) != 0)
155			errx(1, "-E cannot be used with -e");
156		if ((rpcs & DODUMP) != 0)
157			errx(1, "-E cannot be used with -a or -d");
158	}
159
160	if (argc > 0)
161		host = *argv;
162	else
163		host = "localhost";
164
165	if (rpcs == 0)
166		rpcs = DODUMP;
167
168	if (rpcs & DODUMP)
169		if ((estat = tcp_callrpc(host, RPCPROG_MNT, mntvers,
170			 RPCMNT_DUMP, (xdrproc_t)xdr_void, NULL,
171			 (xdrproc_t)xdr_mntdump, (char *)&mntdump)) != 0) {
172			fprintf(stderr, "showmount: Can't do Mountdump rpc: ");
173			clnt_perrno(estat);
174			exit(1);
175		}
176	if (rpcs & (DOEXPORTS | DOPARSABLEEXPORTS))
177		if ((estat = tcp_callrpc(host, RPCPROG_MNT, mntvers,
178			 RPCMNT_EXPORT, (xdrproc_t)xdr_void, NULL,
179			 (xdrproc_t)xdr_exports, (char *)&exports)) != 0) {
180			fprintf(stderr, "showmount: Can't do Exports rpc: ");
181			clnt_perrno(estat);
182			exit(1);
183		}
184
185	/* Now just print out the results */
186	if (rpcs & DODUMP) {
187		switch (type) {
188		case ALL:
189			printf("All mount points on %s:\n", host);
190			break;
191		case DIRS:
192			printf("Directories on %s:\n", host);
193			break;
194		default:
195			printf("Hosts on %s:\n", host);
196			break;
197		};
198		print_dump(mntdump);
199	}
200	if (rpcs & DOEXPORTS) {
201		printf("Exports list on %s:\n", host);
202		exp = exports;
203		while (exp) {
204			len = printf("%-35s", exp->ex_dirp);
205			if (len > 35)
206				printf("\t");
207			grp = exp->ex_groups;
208			if (grp == NULL) {
209				printf("Everyone\n");
210			} else {
211				while (grp) {
212					printf("%s ", grp->gr_name);
213					grp = grp->gr_next;
214				}
215				printf("\n");
216			}
217			exp = exp->ex_next;
218		}
219	}
220	if (rpcs & DOPARSABLEEXPORTS) {
221		exp = exports;
222		while (exp) {
223			nbytes = strnvis(strvised, sizeof(strvised),
224			    exp->ex_dirp, VIS_GLOB | VIS_NL);
225			if (nbytes == -1)
226				err(1, "strsnvis");
227			printf("%s\n", strvised);
228			exp = exp->ex_next;
229		}
230	}
231	exit(0);
232}
233
234/*
235 * tcp_callrpc has the same interface as callrpc, but tries to
236 * use tcp as transport method in order to handle large replies.
237 */
238
239static int
240tcp_callrpc(const char *host, int prognum, int versnum, int procnum,
241    xdrproc_t inproc, char *in, xdrproc_t outproc, char *out)
242{
243	CLIENT *client;
244	struct timeval timeout;
245	int rval;
246
247	if ((client = clnt_create(host, prognum, versnum, "tcp")) == NULL &&
248	    (client = clnt_create(host, prognum, versnum, "udp")) == NULL)
249		return ((int) rpc_createerr.cf_stat);
250
251	timeout.tv_sec = 25;
252	timeout.tv_usec = 0;
253	rval = (int) clnt_call(client, procnum,
254			       inproc, in,
255			       outproc, out,
256			       timeout);
257	clnt_destroy(client);
258 	return rval;
259}
260
261static void
262mountlist_free(struct mountlist *ml)
263{
264	if (ml == NULL)
265		return;
266	mountlist_free(ml->ml_left);
267	mountlist_free(ml->ml_right);
268	free(ml);
269}
270
271/*
272 * Xdr routine for retrieving the mount dump list
273 */
274static int
275xdr_mntdump(XDR *xdrsp, struct mountlist **mlp)
276{
277	struct mountlist *mp, **otp, *tp;
278	int bool_int, val, val2;
279	char *strp;
280
281	otp = NULL;
282	*mlp = NULL;
283	if (!xdr_bool(xdrsp, &bool_int))
284		return 0;
285	while (bool_int) {
286		mp = malloc(sizeof(*mp));
287		if (mp == NULL)
288			goto out;
289		mp->ml_left = mp->ml_right = NULL;
290		strp = mp->ml_host;
291		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) {
292			free(mp);
293			goto out;
294		}
295		strp = mp->ml_dirp;
296		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) {
297			free(mp);
298			goto out;
299		}
300
301		/*
302		 * Build a binary tree on sorted order of either host or dirp.
303		 * Drop any duplications.
304		 */
305		if (*mlp == NULL) {
306			*mlp = mp;
307		} else {
308			tp = *mlp;
309			while (tp) {
310				val = strcmp(mp->ml_host, tp->ml_host);
311				val2 = strcmp(mp->ml_dirp, tp->ml_dirp);
312				switch (type) {
313				case ALL:
314					if (val == 0) {
315						if (val2 == 0) {
316							free(mp);
317							goto next;
318						}
319						val = val2;
320					}
321					break;
322				case DIRS:
323					if (val2 == 0) {
324						free(mp);
325						goto next;
326					}
327					val = val2;
328					break;
329				default:
330					if (val == 0) {
331						free(mp);
332						goto next;
333					}
334					break;
335				};
336				if (val < 0) {
337					otp = &tp->ml_left;
338					tp = tp->ml_left;
339				} else {
340					otp = &tp->ml_right;
341					tp = tp->ml_right;
342				}
343			}
344			*otp = mp;
345		}
346next:
347		if (!xdr_bool(xdrsp, &bool_int))
348			goto out;
349	}
350	return 1;
351out:
352	mountlist_free(*mlp);
353	return 0;
354}
355
356static void
357grouplist_free(struct grouplist *gp)
358{
359	if (gp == NULL)
360		return;
361	grouplist_free(gp->gr_next);
362	free(gp);
363}
364
365static void
366exportslist_free(struct exportslist *ep)
367{
368	if (ep == NULL)
369		return;
370	exportslist_free(ep->ex_next);
371	grouplist_free(ep->ex_groups);
372	free(ep);
373}
374
375/*
376 * Xdr routine to retrieve exports list
377 */
378static int
379xdr_exports(XDR *xdrsp, struct exportslist **exp)
380{
381	struct exportslist *ep = NULL;
382	struct grouplist *gp;
383	int bool_int, grpbool;
384	char *strp;
385
386	*exp = NULL;
387	if (!xdr_bool(xdrsp, &bool_int))
388		return 0;
389	while (bool_int) {
390		ep = malloc(sizeof(*ep));
391		if (ep == NULL)
392			goto out;
393		ep->ex_groups = NULL;
394		strp = ep->ex_dirp;
395		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
396			goto out;
397		if (!xdr_bool(xdrsp, &grpbool))
398			goto out;
399		while (grpbool) {
400			gp = malloc(sizeof(*gp));
401			if (gp == NULL)
402				goto out;
403			gp->gr_next = ep->ex_groups;
404			ep->ex_groups = gp;
405			strp = gp->gr_name;
406			if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
407				goto out;
408			if (!xdr_bool(xdrsp, &grpbool))
409				goto out;
410		}
411		ep->ex_next = *exp;
412		*exp = ep;
413		ep = NULL;
414		if (!xdr_bool(xdrsp, &bool_int))
415			goto out;
416	}
417	return 1;
418out:
419	exportslist_free(ep);
420	exportslist_free(*exp);
421	return 0;
422}
423
424static void
425usage(void)
426{
427
428	fprintf(stderr, "usage: showmount [-ade3] host\n");
429	exit(1);
430}
431
432/*
433 * Print the binary tree in inorder so that output is sorted.
434 */
435static void
436print_dump(struct mountlist *mp)
437{
438
439	if (mp == NULL)
440		return;
441	if (mp->ml_left)
442		print_dump(mp->ml_left);
443	switch (type) {
444	case ALL:
445		printf("%s:%s\n", mp->ml_host, mp->ml_dirp);
446		break;
447	case DIRS:
448		printf("%s\n", mp->ml_dirp);
449		break;
450	default:
451		printf("%s\n", mp->ml_host);
452		break;
453	};
454	if (mp->ml_right)
455		print_dump(mp->ml_right);
456}
457