Subject: bin/15181: [PATCH] showmount can't handle long output
To: None <gnats-bugs@gnats.netbsd.org>
From: None <stolz@i2.informatik.rwth-aachen.de>
List: netbsd-bugs
Date: 01/08/2002 15:56:44
>Number:         15181
>Category:       bin
>Synopsis:       [PATCH] showmount can't handle long output
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Jan 08 07:02:00 PST 2002
>Closed-Date:
>Last-Modified:
>Originator:     Volker Stolz <stolz@i2.informatik.rwth-aachen.de>
>Release:        NetBSD 1.5Y
>Organization:
Lehrstuhl für Informatik II
>Environment:
	
	
System: NetBSD menelaos 1.5Y NetBSD 1.5Y (MENELAOS) #1: Wed Nov 28 11:57:45 CET 2001 root@menelaos:/usr/src/sys/arch/sparc64/compile/MENELAOS sparc64
Architecture: sparc64
Machine: sparc64
>Description:
When invoking 'showmount' to list the exports of our local Solaris 7 server the RPC failed with:

showmount: Can't do Exports rpc: RPC: Can't decode result

For another host it works fine. By checking with FreeBSD-STABLE sources you will find that
a workaround is required for "long" export lists (in my case 72 single fully qualified hostnames for a single export).
>How-To-Repeat:
Have a NFS server export a FS to a couple of hosts and specify them on a per-host basis.
Invoke 'showmount -e host' -> RPC error. I didn't check the exact number of hosts and/or bytes in the RPC answer needed for
breaking 'showmount'.
>Fix:
After diffing NetBSD's showmount against the version in FreeBSD-STABLE, it looks as if there are two improvements
in the FREEBSD-code:

1) It fixes the above borkedness by switching to TCP if available
2) It includes several gratuitious "register"ized variables supposedly to speed things up. Feel free to
     strip these when merging.

One manual intervention was required: NetBSD requires a prototype for main(). The remainder of the diff includes
minimal changes, although the error-messages have been changed, too. However, this syncing with FreeBSD
reduces friction and entropy which in my opinion should make further maintenance much easier. Some whitespaces
have been kept to stay closer to the original NetBSD-source and minimize the diff.


--- showmount.c.orig	Tue Jan  8 15:08:38 2002
+++ showmount.c	Tue Jan  8 15:51:34 2002
@@ -50,9 +50,12 @@
 #endif /* not lint */
 
 #include <sys/types.h>
+#include <sys/queue.h>
 #include <sys/file.h>
 #include <sys/socket.h>
+#include <sys/socketvar.h>
 
+#include <err.h>
 #include <netdb.h>
 #include <rpc/rpc.h>
 #include <rpc/pmap_clnt.h>
@@ -95,9 +98,13 @@
 
 int	main __P((int, char **));
 void	print_dump __P((struct mountlist *));
-void	usage __P((void));
+static void usage __P((void));
 int	xdr_mntdump __P((XDR *, struct mountlist **));
 int	xdr_exports __P((XDR *, struct exportslist **));
+int	tcp_callrpc __P((char *host,
+		     int prognum, int versnum, int procnum,
+		     xdrproc_t inproc, char *in,
+		     xdrproc_t outproc, char *out));
 
 /*
  * This command queries the NFS mount daemon for it's mount list and/or
@@ -111,12 +118,12 @@
 	int argc;
 	char **argv;
 {
-	struct exportslist *exp;
-	struct grouplist *grp;
-	int estat, rpcs = 0, mntvers = 1;
+	register struct exportslist *exp;
+	register struct grouplist *grp;
+	register int rpcs = 0, mntvers = 1;
+	char ch;
 	char *host;
-	int ch;
-	int len;
+	int estat;
 
 	while ((ch = getopt(argc, argv, "ade3")) != -1)
 		switch((char)ch) {
@@ -156,20 +163,18 @@
 		rpcs = DODUMP;
 
 	if (rpcs & DODUMP)
-		if ((estat = callrpc(host, RPCPROG_MNT, mntvers,
+		if ((estat = tcp_callrpc(host, RPCPROG_MNT, mntvers,
 			RPCMNT_DUMP, xdr_void, (char *)0,
 			xdr_mntdump, (char *)&mntdump)) != 0) {
-			fprintf(stderr, "showmount: Can't do Mountdump rpc: ");
 			clnt_perrno(estat);
-			exit(1);
+			errx(1, "can't do mountdump rpc");
 		}
 	if (rpcs & DOEXPORTS)
-		if ((estat = callrpc(host, RPCPROG_MNT, mntvers,
+		if ((estat = tcp_callrpc(host, RPCPROG_MNT, mntvers,
 			RPCMNT_EXPORT, xdr_void, (char *)0,
 			xdr_exports, (char *)&exports)) != 0) {
-			fprintf(stderr, "showmount: Can't do Exports rpc: ");
 			clnt_perrno(estat);
-			exit(1);
+			errx(1, "can't do exports rpc");
 		}
 
 	/* Now just print out the results */
@@ -191,9 +196,7 @@
 		printf("Exports list on %s:\n", host);
 		exp = exports;
 		while (exp) {
-			len = printf("%-35s", exp->ex_dirp);
-			if (len > 35)
-				printf("\t");
+			printf("%-35s", exp->ex_dirp);
 			grp = exp->ex_groups;
 			if (grp == NULL) {
 				printf("Everyone\n");
@@ -212,6 +215,71 @@
 }
 
 /*
+ * tcp_callrpc has the same interface as callrpc, but tries to
+ * use tcp as transport method in order to handle large replies.
+ */
+
+int 
+tcp_callrpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
+	char *host;
+	int prognum;
+	int versnum;
+	int procnum;
+	xdrproc_t inproc;
+	char *in;
+	xdrproc_t outproc;
+	char *out;
+{
+	struct hostent *hp;
+	struct sockaddr_in server_addr;
+	CLIENT *client;
+	int sock;	
+	struct timeval timeout;
+	int rval;
+	
+	hp = gethostbyname(host);
+
+	if (!hp)
+		return ((int) RPC_UNKNOWNHOST);
+
+	memset(&server_addr,0,sizeof(server_addr));
+	memcpy((char *) &server_addr.sin_addr,
+	       hp->h_addr,
+	       hp->h_length);
+	server_addr.sin_len = sizeof(struct sockaddr_in);
+	server_addr.sin_family =AF_INET;
+	server_addr.sin_port = 0;
+			
+	sock = RPC_ANYSOCK;
+			
+	client = clnttcp_create(&server_addr,
+				(u_long) prognum,
+				(u_long) versnum, &sock, 0, 0);
+	if (!client) {
+		timeout.tv_sec = 5;
+		timeout.tv_usec = 0;
+		server_addr.sin_port = 0;
+		sock = RPC_ANYSOCK;
+		client = clntudp_create(&server_addr,
+					(u_long) prognum,
+					(u_long) versnum,
+					timeout,
+					&sock);
+	}
+	if (!client)
+		return ((int) rpc_createerr.cf_stat);
+
+	timeout.tv_sec = 25;
+	timeout.tv_usec = 0;
+	rval = (int) clnt_call(client, procnum, 
+			       inproc, in,
+			       outproc, out,
+			       timeout);
+	clnt_destroy(client);
+ 	return rval;
+}
+
+/*
  * Xdr routine for retrieving the mount dump list
  */
 int
@@ -219,11 +287,13 @@
 	XDR *xdrsp;
 	struct mountlist **mlp;
 {
-	struct mountlist *mp, **otp, *tp;
-	int bool, val, val2;
+	register struct mountlist *mp;
+	register struct mountlist *tp;
+	register struct mountlist **otp;
+	int val, val2;
+	int bool;
 	char *strp;
 
-	otp = NULL;
 	*mlp = (struct mountlist *)0;
 	if (!xdr_bool(xdrsp, &bool))
 		return (0);
@@ -299,8 +369,8 @@
 	XDR *xdrsp;
 	struct exportslist **exp;
 {
-	struct exportslist *ep;
-	struct grouplist *gp;
+	register struct exportslist *ep;
+	register struct grouplist *gp;
 	int bool, grpbool;
 	char *strp;
 
@@ -337,7 +407,7 @@
 	return (1);
 }
 
-void
+static void
 usage()
 {
 
>Release-Note:
>Audit-Trail:
>Unformatted: