Bug 29177

Summary: [PATCH] rpc client create functions with additional timeout
Product: Base System Reporter: mb <mb>
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 5.0-CURRENT   
Hardware: Any   
OS: Any   

Description mb 2001-07-23 22:20:01 UTC
Add timed functions for the client create routines. This should help us a lot 
with timeouts, so we don't have to wait everytime the default timeout
of 25 seconds. The logic has changed a bit, so we do a check first if the
local rpcbind is up before we do a query. Part of the code was from
tircp 2.8 (tirpc1999).

Because this code depends on tirpc1999 code, we should add this license
to the head of all changes files:

http://www.opensource.org/licenses/sisslpl.html

Any comments on this ? I already asked once, but got no answer :-(

Fix: 

Add my patch:

http://home.teleport.ch/freebsd/userland/timed_clnt_create.diff
Comment 1 mb 2001-10-07 01:28:00 UTC
License change reviewed by Warner Losh. I'll send a mail again to
freebsd-audit to get this ok'ed.

-----------------------------------------------------------------

Last modified:

2001-10-05 23:35

Changed files:

include/rpc/clnt.h
lib/libc/rpc/clnt_generic.c
lib/libc/rpc/rpc_clnt_create.3
lib/libc/rpc/rpcb_clnt.c
lib/libc/rpc/Makefile.inc

New files:

lib/libc/rpc/LICENSE

Comment:

Add timed functions for the client create routines. This should help us a lot
with timeouts, so we don't have to wait everytime the default timeout
of 25 seconds. The logic has changed a bit, so we do a check first if the
local rpcbind is up before we do a query. Part of the code was from
tircp 2.8 (tirpc1999).

There is also a new function __rpc_raise_fd available, to avoid conflicts
with the "magic" file descriptors (0, 1, and 2).

I added the new license as needed in the modified files.

Index: include/rpc/clnt.h
===================================================================
RCS file: /usr/home/ncvs/src/include/rpc/clnt.h,v
retrieving revision 1.14
diff -u -r1.14 clnt.h
--- include/rpc/clnt.h	23 Jun 2001 19:43:16 -0000	1.14
+++ include/rpc/clnt.h	5 Oct 2001 21:25:13 -0000
@@ -36,7 +36,8 @@
 /*
  * clnt.h - Client side remote procedure call interface.
  *
- * Copyright (C) 1984, Sun Microsystems, Inc.
+ * Copyright (c) 1986-1991,1994-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
  */

 #ifndef _RPC_CLNT_H_
@@ -257,6 +258,7 @@

 #define NULLPROC ((rpcproc_t)0)

+__BEGIN_DECLS
 /*
  * Below are the client handle creation routines for the various
  * implementations of client side rpc.  They can return NULL if a
@@ -266,16 +268,9 @@
 /*
  * Generic client creation routine. Supported protocols are those that
  * belong to the nettype namespace (/etc/netconfig).
- * CLIENT *
- * clnt_create(host, prog, vers, prot);
- *	const char *host; 	-- hostname
- *	const rpcprog_t prog;	-- program number
- *	const rpcvers_t vers;	-- version number
- *	const char *prot;	-- protocol
  */
-__BEGIN_DECLS
-extern CLIENT *clnt_create __P((const char *, const rpcprog_t, const rpcvers_t,
-				const char *));
+extern CLIENT *clnt_create (const char *, const rpcprog_t, const rpcvers_t,
+	const char *);
 /*
  *
  * 	const char *hostname;			-- hostname
@@ -285,12 +280,26 @@
  */

 /*
+ * Generic client creation routine. Just like clnt_create(), except
+ * it takes an additional timeout parameter.
+ */
+extern CLIENT * clnt_create_timed(const char *, const rpcprog_t,
+	const rpcvers_t, const char *, const struct timeval *);
+/*
+ *
+ *	const char *hostname;			-- hostname
+ *	const rpcprog_t prog;			-- program number
+ *	const rpcvers_t vers;			-- version number
+ *	const char *nettype;			-- network type
+ *	const struct timeval *tp;		-- timeout
+ */
+
+/*
  * Generic client creation routine. Supported protocols are which belong
  * to the nettype name space.
  */
-extern CLIENT *clnt_create_vers __P((const char *, const rpcprog_t, rpcvers_t *,
-				     const rpcvers_t, const rpcvers_t,
-				     const char *));
+extern CLIENT *clnt_create_vers (const char *, const rpcprog_t, rpcvers_t *,
+	const rpcvers_t, const rpcvers_t, const char *);
 /*
  *	const char *host;		-- hostname
  *	const rpcprog_t prog;		-- program number
@@ -300,13 +309,29 @@
  *	const char *nettype;		-- network type
  */

+/*
+ * Generic client creation routine. Supported protocols are which belong
+ * to the nettype name space.
+ */
+extern CLIENT * clnt_create_vers_timed(const char *, const rpcprog_t,
+	rpcvers_t *, const rpcvers_t, const rpcvers_t, const char *,
+	const struct timeval *);
+/*
+ *	const char *host;		-- hostname
+ *	const rpcprog_t prog;		-- program number
+ *	rpcvers_t *vers_out;		-- servers highest available version
+ *	const rpcvers_t vers_low;	-- low version number
+ *	const rpcvers_t vers_high;	-- high version number
+ *	const char *nettype;		-- network type
+ *	const struct timeval *tp	-- timeout
+ */

 /*
  * Generic client creation routine. It takes a netconfig structure
  * instead of nettype
  */
-extern CLIENT *clnt_tp_create __P((const char *, const rpcprog_t,
-				   const rpcvers_t, const struct netconfig *));
+extern CLIENT *clnt_tp_create (const char *, const rpcprog_t,
+	const rpcvers_t, const struct netconfig *);
 /*
  *	const char *hostname;			-- hostname
  *	const rpcprog_t prog;			-- program number
@@ -315,16 +340,29 @@
  */

 /*
- * Generic TLI create routine. Only provided for compatibility.
+ * Generic client creation routine. Just like clnt_tp_create(), except
+ * it takes an additional timeout parameter.
+ */
+extern CLIENT * clnt_tp_create_timed(const char *, const rpcprog_t,
+	const rpcvers_t, const struct netconfig *, const struct timeval *);
+/*
+ *	const char *hostname;			-- hostname
+ *	const rpcprog_t prog;			-- program number
+ *	const rpcvers_t vers;			-- version number
+ *	const struct netconfig *netconf; 	-- network config structure
+ *	const struct timeval *tp		-- timeout
  */

-extern CLIENT *clnt_tli_create __P((const int, const struct netconfig *,
-				    const struct netbuf *, const rpcprog_t,
-				    const rpcvers_t, const u_int, const u_int));
+/*
+ * Generic TLI create routine. Only provided for compatibility.
+ */
+extern CLIENT *clnt_tli_create (const int, const struct netconfig *,
+	struct netbuf *, const rpcprog_t,
+	const rpcvers_t, const u_int, const u_int);
 /*
  *	const register int fd;		-- fd
  *	const struct netconfig *nconf;	-- netconfig structure
- *	const struct netbuf *svcaddr;		-- servers address
+ *	struct netbuf *svcaddr;		-- servers address
  *	const u_long prog;			-- program number
  *	const u_long vers;			-- version number
  *	const u_int sendsz;			-- send size
@@ -334,9 +372,8 @@
 /*
  * Low level clnt create routine for connectionful transports, e.g. tcp.
  */
-extern CLIENT *clnt_vc_create __P((const int, const struct netbuf *,
-				   const rpcprog_t, const rpcvers_t,
-				   const u_int, const u_int));
+extern CLIENT *clnt_vc_create (const int, const struct netbuf *,
+	const rpcprog_t, const rpcvers_t, const u_int, const u_int);
 /*
  *	const int fd;				-- open file descriptor
  *	const struct netbuf *svcaddr;		-- servers address
@@ -349,9 +386,9 @@
 /*
  * Low level clnt create routine for connectionless transports, e.g. udp.
  */
-extern CLIENT *clnt_dg_create __P((const int, const struct netbuf *,
-				   const rpcprog_t, const rpcvers_t,
-				   const u_int, const u_int));
+extern CLIENT *clnt_dg_create (const int, const struct netbuf *,
+	const rpcprog_t, const rpcvers_t,
+	const u_int, const u_int);
 /*
  *	const int fd;				-- open file descriptor
  *	const struct netbuf *svcaddr;		-- servers address
@@ -368,7 +405,7 @@
  *	u_long prog;
  *	u_long vers;
  */
-extern CLIENT *clnt_raw_create	__P((rpcprog_t, rpcvers_t));
+extern CLIENT *clnt_raw_create	(rpcprog_t, rpcvers_t);

 __END_DECLS

Index: lib/libc/rpc/Makefile.inc
===================================================================
RCS file: /usr/home/ncvs/src/lib/libc/rpc/Makefile.inc,v
retrieving revision 1.20
diff -u -r1.20 Makefile.inc
--- lib/libc/rpc/Makefile.inc	27 Mar 2001 17:26:51 -0000	1.20
+++ lib/libc/rpc/Makefile.inc	5 Oct 2001 21:25:13 -0000
@@ -78,7 +78,9 @@
 		rpc_clnt_calls.3 clnt_geterr.3 \
 		rpc_clnt_create.3 clnt_control.3 \
 		rpc_clnt_create.3 clnt_create.3 \
+		rpc_clnt_create.3 clnt_create_timed.3 \
 		rpc_clnt_create.3 clnt_create_vers.3 \
+		rpc_clnt_create.3 clnt_create_vers_timed.3 \
 		rpc_clnt_create.3 clnt_destroy.3 \
 		rpc_clnt_create.3 clnt_pcreateerror.3 \
 		rpc_clnt_create.3 clnt_spcreateerror.3 \
@@ -86,6 +88,7 @@
 		rpc_clnt_create.3 clnt_raw_create.3 \
 		rpc_clnt_create.3 clnt_tli_create.3 \
 		rpc_clnt_create.3 clnt_tp_create.3 \
+		rpc_clnt_create.3 clnt_tp_create_timed.3 \
 		rpc_clnt_create.3 clnt_vc_create.3 \
 		rpc_svc_calls.3 svc_dg_enablecache.3 \
 		rpc_svc_calls.3 svc_exit.3 \
Index: lib/libc/rpc/clnt_generic.c
===================================================================
RCS file: /usr/home/ncvs/src/lib/libc/rpc/clnt_generic.c,v
retrieving revision 1.12
diff -u -r1.12 clnt_generic.c
--- lib/libc/rpc/clnt_generic.c	2 Apr 2001 21:41:43 -0000	1.12
+++ lib/libc/rpc/clnt_generic.c	5 Oct 2001 21:25:13 -0000
@@ -1,6 +1,23 @@
 /*	$NetBSD: clnt_generic.c,v 1.18 2000/07/06 03:10:34 christos Exp $	*/

 /*
+ * The contents of this file are subject to the Sun Standards
+ * License Version 1.0 the (the "License";) You may not use
+ * this file except in compliance with the License.  You may
+ * obtain a copy of the License at lib/libc/rpc/LICENSE
+ *
+ * Software distributed under the License is distributed on
+ * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied.  See the License for the specific
+ * language governing rights and limitations under the License.
+ *
+ * The Original Code is Copyright 1998 by Sun Microsystems, Inc
+ *
+ * The Initial Developer of the Original Code is:  Sun
+ * Microsystems, Inc.
+ *
+ * All Rights Reserved.
+ *
  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
  * unrestricted use provided that this legend is included on all tape
  * media and as a part of the software program in whole or part.  Users
@@ -29,7 +46,7 @@
  * Mountain View, California  94043
  */

-/* #ident	"@(#)clnt_generic.c	1.20	94/05/03 SMI" */
+/* #ident	"@(#)clnt_generic.c	1.40	99/04/21 SMI" */

 #if defined(LIBC_SCCS) && !defined(lint)
 /*static char *sccsid = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI";*/
@@ -38,17 +55,20 @@
 #endif

 /*
- * Copyright (c) 1986-1991 by Sun Microsystems Inc.
+ * Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc.
+ * All rights reserved.
  */
 #include "namespace.h"
 #include "reentrant.h"
 #include <sys/types.h>
+#include <sys/fcntl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <stdio.h>
 #include <errno.h>
 #include <netdb.h>
+#include <syslog.h>
 #include <rpc/rpc.h>
 #include <rpc/nettype.h>
 #include <string.h>
@@ -57,55 +77,79 @@
 #include "un-namespace.h"
 #include "rpc_com.h"

+extern bool_t __rpc_is_local_host(const char *);
+int __rpc_raise_fd(int);
+
+#ifndef NETIDLEN
+#define	NETIDLEN 32
+#endif
+
+
 /*
  * Generic client creation with version checking the value of
  * vers_out is set to the highest server supported value
  * vers_low <= vers_out <= vers_high  AND an error results
  * if this can not be done.
+ *
+ * It calls clnt_create_vers_timed() with a NULL value for the timeout
+ * pointer, which indicates that the default timeout should be used.
  */
 CLIENT *
-clnt_create_vers(hostname, prog, vers_out, vers_low, vers_high, nettype)
-	const char *hostname;
-	rpcprog_t prog;
-	rpcvers_t *vers_out;
-	rpcvers_t vers_low;
-	rpcvers_t vers_high;
-	const char *nettype;
+clnt_create_vers(const char *hostname, rpcprog_t prog, rpcvers_t *vers_out,
+	rpcvers_t vers_low, rpcvers_t vers_high, const char *nettype)
+{
+
+	return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low,
+				vers_high, nettype, NULL));
+}
+
+/*
+ * This the routine has the same definition as clnt_create_vers(),
+ * except it takes an additional timeout parameter - a pointer to
+ * a timeval structure.  A NULL value for the pointer indicates
+ * that the default timeout value should be used.
+ */
+CLIENT *
+clnt_create_vers_timed(const char *hostname, rpcprog_t prog,
+    rpcvers_t *vers_out, rpcvers_t vers_low, rpcvers_t vers_high,
+    const char *nettype, const struct timeval *tp)
 {
 	CLIENT *clnt;
 	struct timeval to;
 	enum clnt_stat rpc_stat;
 	struct rpc_err rpcerr;

-	clnt = clnt_create(hostname, prog, vers_high, nettype);
+	clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);
 	if (clnt == NULL) {
 		return (NULL);
 	}
 	to.tv_sec = 10;
 	to.tv_usec = 0;
-	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void,
-			(char *) NULL, (xdrproc_t) xdr_void, (char *) NULL, to);
+	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
+			(char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to);
 	if (rpc_stat == RPC_SUCCESS) {
 		*vers_out = vers_high;
 		return (clnt);
 	}
-	if (rpc_stat == RPC_PROGVERSMISMATCH) {
-		unsigned long minvers, maxvers;
+	while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) {
+		unsigned int minvers, maxvers;

 		clnt_geterr(clnt, &rpcerr);
 		minvers = rpcerr.re_vers.low;
 		maxvers = rpcerr.re_vers.high;
 		if (maxvers < vers_high)
-			vers_high = (rpcvers_t)maxvers;
+			vers_high = maxvers;
+		else
+			vers_high--;
 		if (minvers > vers_low)
-			vers_low = (rpcvers_t)minvers;
+			vers_low = minvers;
 		if (vers_low > vers_high) {
 			goto error;
 		}
-		CLNT_CONTROL(clnt, CLSET_VERS, (char *)(void *)&vers_high);
-		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void,
-				(char *) NULL, (xdrproc_t) xdr_void,
-				(char *) NULL, to);
+		CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high);
+		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
+				(char *)NULL, (xdrproc_t)xdr_void,
+				(char *)NULL, to);
 		if (rpc_stat == RPC_SUCCESS) {
 			*vers_out = vers_high;
 			return (clnt);
@@ -131,23 +175,48 @@
  * XXX The error message in the case of failure will be the one
  * pertaining to the last create error.
  *
- * It calls clnt_tp_create();
+ * It calls clnt_create_timed() with the default timeout.
  */
 CLIENT *
-clnt_create(hostname, prog, vers, nettype)
-	const char *hostname;				/* server name */
-	rpcprog_t prog;				/* program number */
-	rpcvers_t vers;				/* version number */
-	const char *nettype;				/* net type */
+clnt_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
+    const char *nettype)
+{
+
+	return (clnt_create_timed(hostname, prog, vers, nettype, NULL));
+}
+
+/*
+ * This the routine has the same definition as clnt_create(),
+ * except it takes an additional timeout parameter - a pointer to
+ * a timeval structure.  A NULL value for the pointer indicates
+ * that the default timeout value should be used.
+ *
+ * This function calls clnt_tp_create_timed().
+ */
+CLIENT *
+clnt_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
+    const char *netclass, const struct timeval *tp)
 {
 	struct netconfig *nconf;
 	CLIENT *clnt = NULL;
 	void *handle;
 	enum clnt_stat	save_cf_stat = RPC_SUCCESS;
 	struct rpc_err	save_cf_error;
+	char nettype_array[NETIDLEN];
+	char *nettype = &nettype_array[0];

+	if (netclass == NULL)
+		nettype = NULL;
+	else {
+		size_t len = strlen(netclass);
+		if (len >= sizeof (nettype_array)) {
+			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+			return (NULL);
+		}
+		strcpy(nettype, netclass);
+	}

-	if ((handle = __rpc_setconf(nettype)) == NULL) {
+	if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
 		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
 		return (NULL);
 	}
@@ -161,7 +230,7 @@
 #ifdef CLNT_DEBUG
 		printf("trying netid %s\n", nconf->nc_netid);
 #endif
-		clnt = clnt_tp_create(hostname, prog, vers, nconf);
+		clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp);
 		if (clnt)
 			break;
 		else
@@ -177,9 +246,12 @@
 			 *	the local loopbacks are typically the
 			 *	last ones in /etc/netconfig and the most
 			 *	likely to be unable to translate a host
-			 *	name).
+			 *	name).  We also check for a more
+			 *	meaningful error than ``unknown host
+			 *	name'' for the same reasons.
 			 */
-			if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE) {
+			if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE &&
+			    rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
 				save_cf_stat = rpc_createerr.cf_stat;
 				save_cf_error = rpc_createerr.cf_error;
 			}
@@ -187,10 +259,11 @@

 	/*
 	 *	Attempt to return an error more specific than ``Name to address
-	 *	translation failed''
+	 *	translation failed'' or ``unknown host name''
 	 */
-	if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE) &&
-		(save_cf_stat != RPC_SUCCESS)) {
+	if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE ||
+				rpc_createerr.cf_stat == RPC_UNKNOWNHOST) &&
+					(save_cf_stat != RPC_SUCCESS)) {
 		rpc_createerr.cf_stat = save_cf_stat;
 		rpc_createerr.cf_error = save_cf_error;
 	}
@@ -202,14 +275,27 @@
  * Generic client creation: takes (servers name, program-number, netconf) and
  * returns client handle. Default options are set, which the user can
  * change using the rpc equivalent of _ioctl()'s : clnt_control()
- * It finds out the server address from rpcbind and calls clnt_tli_create()
+ * It finds out the server address from rpcbind and calls clnt_tli_create().
+ *
+ * It calls clnt_tp_create_timed() with the default timeout.
  */
 CLIENT *
-clnt_tp_create(hostname, prog, vers, nconf)
-	const char *hostname;			/* server name */
-	rpcprog_t prog;				/* program number */
-	rpcvers_t vers;				/* version number */
-	const struct netconfig *nconf;		/* net config struct */
+clnt_tp_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
+    const struct netconfig *nconf)
+{
+
+	return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL));
+}
+
+/*
+ * This has the same definition as clnt_tp_create(), except it
+ * takes an additional parameter - a pointer to a timeval structure.
+ * A NULL value for the timeout pointer indicates that the default
+ * value for the timeout should be used.
+ */
+CLIENT *
+clnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
+    const struct netconfig *nconf, const struct timeval *tp)
 {
 	struct netbuf *svcaddr;			/* servers address */
 	CLIENT *cl = NULL;			/* client handle */
@@ -222,8 +308,9 @@
 	/*
 	 * Get the address of the server
 	 */
-	if ((svcaddr = __rpcb_findaddr(prog, vers, nconf, hostname,
-		&cl)) == NULL) {
+	if ((svcaddr = __rpcb_findaddr_timed(prog, vers,
+			(struct netconfig *)nconf, (char *)hostname,
+			&cl, (struct timeval *)tp)) == NULL) {
 		/* appropriate error number is set by rpcbind libraries */
 		return (NULL);
 	}
@@ -259,20 +346,16 @@
  * If sizes are 0; appropriate defaults will be chosen.
  */
 CLIENT *
-clnt_tli_create(fd, nconf, svcaddr, prog, vers, sendsz, recvsz)
-	int fd;				/* fd */
-	const struct netconfig *nconf;	/* netconfig structure */
-	const struct netbuf *svcaddr;	/* servers address */
-	rpcprog_t prog;			/* program number */
-	rpcvers_t vers;			/* version number */
-	u_int sendsz;			/* send size */
-	u_int recvsz;			/* recv size */
+clnt_tli_create(int fd, const struct netconfig *nconf,
+	struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers,
+	uint sendsz, uint recvsz)
 {
 	CLIENT *cl;			/* client handle */
 	bool_t madefd = FALSE;		/* whether fd opened here */
 	long servtype;
 	int one = 1;
 	struct __rpc_sockinfo si;
+	extern int __rpc_minfd;

 	if (fd == RPC_ANYFD) {
 		if (nconf == NULL) {
@@ -284,12 +367,12 @@

 		if (fd == -1)
 			goto err;
-
+		if (fd < __rpc_minfd)
+			fd = __rpc_raise_fd(fd);
 		madefd = TRUE;
 		servtype = nconf->nc_semantics;
 		if (!__rpc_fd2sockinfo(fd, &si))
 			goto err;
-
 		bindresvport(fd, NULL);
 	} else {
 		if (!__rpc_fd2sockinfo(fd, &si))
@@ -297,9 +380,8 @@
 		servtype = __rpc_socktype2seman(si.si_socktype);
 		if (servtype == -1) {
 			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
-			return NULL;
+			return (NULL);
 		}
-
 	}

 	if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
@@ -308,14 +390,15 @@
 	}

 	switch (servtype) {
-	case NC_TPI_COTS_ORD:
+	case NC_TPI_COTS:
 		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
-		if (!nconf || !cl)
-			break;
-		/* XXX fvdl - is this useful? */
-		if (strncmp(nconf->nc_protofmly, "inet", 4) == 0)
+		break;
+	case NC_TPI_COTS_ORD:
+		if (nconf && ((strcmp(nconf->nc_protofmly, "inet") == 0))) {
 			_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one,
 			    sizeof (one));
+		}
+		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
 		break;
 	case NC_TPI_CLTS:
 		cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
@@ -335,7 +418,7 @@
 	}
 	if (madefd) {
 		(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
-/*		(void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, (char *) NULL);  */
+/*		(void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL);  */
 	};

 	return (cl);
@@ -346,4 +429,37 @@
 err1:	if (madefd)
 		(void)_close(fd);
 	return (NULL);
+}
+
+/*
+ *  To avoid conflicts with the "magic" file descriptors (0, 1, and 2),
+ *  we try to not use them.  The __rpc_raise_fd() routine will dup
+ *  a descriptor to a higher value.  If we fail to do it, we continue
+ *  to use the old one (and hope for the best).
+ */
+int __rpc_minfd = 3;
+
+int
+__rpc_raise_fd(int fd)
+{
+	int nfd;
+
+	if (fd >= __rpc_minfd)
+		return (fd);
+
+	if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1)
+		return (fd);
+
+	if (_fsync(nfd) == -1) {
+		_close(nfd);
+		return (fd);
+	}
+
+	if (_close(fd) == -1) {
+		/* this is okay, we will syslog an error, then use the new fd */
+		(void) syslog(LOG_ERR,
+			"could not close() fd %d; mem & fd leak", fd);
+	}
+
+	return (nfd);
 }
Index: lib/libc/rpc/rpc.3
===================================================================
RCS file: /usr/home/ncvs/src/lib/libc/rpc/rpc.3,v
retrieving revision 1.17
diff -u -r1.17 rpc.3
--- lib/libc/rpc/rpc.3	1 Oct 2001 16:08:58 -0000	1.17
+++ lib/libc/rpc/rpc.3	5 Oct 2001 21:25:13 -0000
@@ -338,6 +338,12 @@
 .Xr rpc_clnt_create 3
 .It Fn clnt_create
 .Xr rpc_clnt_create 3
+.It Fn clnt_create_timed
+.Xr rpc_clnt_create 3
+.It Fn clnt_create_vers
+.Xr rpc_clnt_create 3
+.It Fn clnt_create_vers_timed
+.Xr rpc_clnt_create 3
 .It Fn clnt_destroy
 .Xr rpc_clnt_create 3
 .It Fn clnt_dg_create
@@ -363,6 +369,8 @@
 .It Fn clnt_tli_create
 .Xr rpc_clnt_create 3
 .It Fn clnt_tp_create
+.Xr rpc_clnt_create 3
+.It Fn clnt_tp_create_timed
 .Xr rpc_clnt_create 3
 .It Fn clnt_udpcreate
 .Xr rpc_soc 3
Index: lib/libc/rpc/rpc_clnt_create.3
===================================================================
RCS file: /usr/home/ncvs/src/lib/libc/rpc/rpc_clnt_create.3,v
retrieving revision 1.5
diff -u -r1.5 rpc_clnt_create.3
--- lib/libc/rpc/rpc_clnt_create.3	3 Oct 2001 16:47:56 -0000	1.5
+++ lib/libc/rpc/rpc_clnt_create.3	5 Oct 2001 21:25:13 -0000
@@ -11,7 +11,9 @@
 .Nm rpc_clnt_create ,
 .Nm clnt_control ,
 .Nm clnt_create ,
+.Nm clnt_create_timed ,
 .Nm clnt_create_vers ,
+.Nm clnt_create_vers_timed ,
 .Nm clnt_destroy ,
 .Nm clnt_dg_create ,
 .Nm clnt_pcreateerror ,
@@ -19,6 +21,7 @@
 .Nm clnt_spcreateerror ,
 .Nm clnt_tli_create ,
 .Nm clnt_tp_create ,
+.Nm clnt_tp_create_timed ,
 .Nm clnt_vc_create ,
 .Nm rpc_createerr
 .Nd "library routines for dealing with creation and manipulation of"
@@ -33,7 +36,11 @@
 .Ft "CLIENT *"
 .Fn clnt_create "const char * host" "const rpcprog_t prognum" "const rpcvers_t versnum" "const char *nettype"
 .Ft "CLIENT *"
+.Fn clnt_create_timed "const char * host" "const rpcprog_t prognum" "const rpcvers_t versnum" "const char *nettype" "const struct timeval *timeout"
+.Ft "CLIENT *"
 .Fn clnt_create_vers "const char * host" "const rpcprog_t prognum" "rpcvers_t *vers_outp" "const rpcvers_t vers_low" "const rpcvers_t vers_high" "const char *nettype"
+.Ft "CLIENT *"
+.Fn clnt_create_vers_timed "const char * host" "const rpcprog_t prognum" "rpcvers_t *vers_outp" "const rpcvers_t vers_low" "const rpcvers_t vers_high" "char *nettype" "const struct timeval *timeout"
 .Ft void
 .Fn clnt_destroy "CLIENT *clnt"
 .Ft "CLIENT *"
@@ -49,6 +56,8 @@
 .Ft "CLIENT *"
 .Fn clnt_tp_create "const char * host" "const rpcprog_t prognum" "const rpcvers_t versnum" "const struct netconfig *netconf"
 .Ft "CLIENT *"
+.Fn clnt_tp_create_timed "const char * host" "const rpcprog_t prognum" "const rpcvers_t versnum" "const struct netconfig *netconf" "const struct timeval *timeout"
+.Ft "CLIENT *"
 .Fn clnt_vc_create "const int fildes" "const struct netbuf *svcaddr" "const rpcprog_t prognum" "const rpcvers_t versnum" "const u_int sendsz" "const u_int recvsz"
 .Sh DESCRIPTION
 RPC library routines allow C language programs to make procedure
@@ -158,6 +167,17 @@
 .Fn clnt_call
 later (see
 .Xr rpc_clnt_calls 3 ) .
+.It Fn clnt_create_timed
+Generic client creation routine which is similar to
+.Fn clnt_create
+but which also has the additional parameter
+.Fa timeout
+that specifies the maximum amount of time allowed for
+each transport class tried. In all other respects, the
+.Fn clnt_create_timed
+call behaves exactly like the
+.Fn clnt_create
+call.
 .It Fn clnt_create_vers
 Generic client creation routine which is similar to
 .Fn clnt_create
@@ -213,6 +233,17 @@
 does this for you and returns a valid handle
 only if a version within
 the range supplied is supported by the server.
+.It Fn clnt_create_vers_timed
+Generic client creation routine which is similar to
+.Fn clnt_create_vers
+but which also has the additional parameter
+.Fa timeout
+that specifies the maximum amount of time allowed for
+each transport class tried. In all other respects, the
+.Fn clnt_create_vers_timed
+call behaves exactly like the
+.Fn clnt_create_vers
+call.
 .It Fn clnt_destroy
 A function macro that destroys the client's RPC handle.
 Destruction usually involves deallocation
@@ -393,6 +424,20 @@
 The
 .Fn clnt_pcreateerror
 routine can be used to print the reason for failure.
+.It Fn clnt_tp_create_timed
+Like
+.Fn clnt_tp_create
+except
+.Fn clnt_tp_create_timed
+has the extra parameter
+.Fa timeout
+which specifies the maximum time allowed for
+for the creation attempt to succeed.
+In all other respects, the
+.Fn clnt_tp_create_timed
+call behaves exactly like the
+.Fn clnt_tp_create
+call.
 .It Fn clnt_vc_create
 This routine creates an RPC
 client for the remote program
Index: lib/libc/rpc/rpc_com.h
===================================================================
RCS file: /usr/home/ncvs/src/lib/libc/rpc/rpc_com.h,v
retrieving revision 1.1
diff -u -r1.1 rpc_com.h
--- lib/libc/rpc/rpc_com.h	19 Mar 2001 12:49:51 -0000	1.1
+++ lib/libc/rpc/rpc_com.h	5 Oct 2001 21:25:13 -0000
@@ -30,7 +30,8 @@
  * Mountain View, California  94043
  */
 /*
- * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ * Copyright (c) 1986-1991,1997-1998 by Sun Microsystems, Inc.
+ * All rights reserved.
  */

 /*
@@ -73,9 +74,10 @@
 void *rpc_nullproc __P((CLIENT *));
 int __rpc_sockisbound __P((int));

-struct netbuf *__rpcb_findaddr __P((rpcprog_t, rpcvers_t,
+struct netbuf *__rpcb_findaddr_timed __P((rpcprog_t, rpcvers_t,
 				    const struct netconfig *,
-				    const char *, CLIENT **));
+				    const char *, CLIENT **,
+				    struct timeval *));
 bool_t __rpc_control __P((int,void *));

 char *_get_next_token __P((char *, int));
Index: lib/libc/rpc/rpcb_clnt.c
===================================================================
RCS file: /usr/home/ncvs/src/lib/libc/rpc/rpcb_clnt.c,v
retrieving revision 1.4
diff -u -r1.4 rpcb_clnt.c
--- lib/libc/rpc/rpcb_clnt.c	2 Aug 2001 21:31:21 -0000	1.4
+++ lib/libc/rpc/rpcb_clnt.c	5 Oct 2001 21:25:13 -0000
@@ -2,6 +2,23 @@
 /*	$FreeBSD: src/lib/libc/rpc/rpcb_clnt.c,v 1.4 2001/08/02 21:31:21 iedowse Exp $ */

 /*
+ * The contents of this file are subject to the Sun Standards
+ * License Version 1.0 the (the "License";) You may not use
+ * this file except in compliance with the License.  You may
+ * obtain a copy of the License at lib/libc/rpc/LICENSE
+ *
+ * Software distributed under the License is distributed on
+ * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied.  See the License for the specific
+ * language governing rights and limitations under the License.
+ *
+ * The Original Code is Copyright 1998 by Sun Microsystems, Inc
+ *
+ * The Initial Developer of the Original Code is:  Sun
+ * Microsystems, Inc.
+ *
+ * All Rights Reserved.
+ *
  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
  * unrestricted use provided that this legend is included on all tape
  * media and as a part of the software program in whole or part.  Users
@@ -76,6 +93,7 @@

 static struct timeval tottimeout = { 60, 0 };
 static const struct timeval rmttimeout = { 3, 0 };
+static struct timeval rpcbrmttime = { 15, 0 };

 extern bool_t xdr_wrapstring __P((XDR *, char **));

@@ -631,6 +649,47 @@
 }

 /*
+ * Quick check to see if rpcbind is up.  Tries to connect over
+ * local transport.
+ */
+bool_t
+__rpcbind_is_up()
+{
+	struct netconfig *nconf;
+	struct sockaddr_un sun;
+	void *localhandle;
+	int sock;
+
+	nconf = NULL;
+	localhandle = setnetconfig();
+	while (nconf = getnetconfig(localhandle)){
+		if (nconf->nc_protofmly != NULL &&
+		    strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
+			 break;
+	}
+	if (nconf == NULL)
+		return (FALSE);
+
+	endnetconfig(localhandle);
+
+	memset(&sun, 0, sizeof sun);
+	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
+	if (sock < 0)
+		return (FALSE);
+	sun.sun_family = AF_LOCAL;
+	strncpy(sun.sun_path, _PATH_RPCBINDSOCK, sizeof(sun.sun_path));
+	sun.sun_len = SUN_LEN(&sun);
+
+	if (_connect(sock, (struct sockaddr *)&sun, sun.sun_len) < 0) {
+		_close(sock);
+		return (FALSE);
+	}
+
+	_close(sock);
+	return (TRUE);
+}
+
+/*
  * An internal function which optimizes rpcb_getaddr function.  It also
  * returns the client handle that it uses to contact the remote rpcbind.
  *
@@ -649,13 +708,15 @@
  * starts working properly.  Also look under clnt_vc.c.
  */
 struct netbuf *
-__rpcb_findaddr(program, version, nconf, host, clpp)
+__rpcb_findaddr_timed(program, version, nconf, host, clpp, tp)
 	rpcprog_t program;
 	rpcvers_t version;
 	const struct netconfig *nconf;
 	const char *host;
 	CLIENT **clpp;
+	struct timeval *tp;
 {
+	static bool_t check_rpcbind = TRUE;
 	CLIENT *client = NULL;
 	RPCB parms;
 	enum clnt_stat clnt_st;
@@ -673,6 +734,12 @@

 	parms.r_addr = NULL;

+	/*
+	 * Use default total timeout if no timeout is specified.
+	 */
+	if (tp == NULL)
+		tp = &tottimeout;
+
 #ifdef PORTMAP
 	/* Try version 2 for TCP or UDP */
 	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
@@ -687,22 +754,31 @@
 		 */
 		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
 			struct netconfig *newnconf;
+			void *handle;

-			if ((newnconf = getnetconfigent("udp")) == NULL) {
+			if ((handle = getnetconfigent("udp")) == NULL) {
+				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+				return (NULL);
+			}
+			if ((newnconf = __rpc_getconf(handle)) == NULL) {
+				__rpc_endconf(handle);
 				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
 				return (NULL);
 			}
 			client = getclnthandle(host, newnconf, &parms.r_addr);
-			freenetconfigent(newnconf);
+			__rpc_endconf(handle);
 		} else {
 			client = getclnthandle(host, nconf, &parms.r_addr);
 		}
-		if (client == NULL) {
+		if (client == NULL)
 			return (NULL);
-		}

-		/* Set the version */
-		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&pmapvers);
+		/*
+		 * Set version and retry timeout.
+		 */
+		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
+		CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers);
+
 		pmapparms.pm_prog = program;
 		pmapparms.pm_vers = version;
 		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
@@ -711,7 +787,7 @@
 		clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
 		    (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
 		    (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
-		    tottimeout);
+		    *tp);
 		if (clnt_st != RPC_SUCCESS) {
 			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
 				(clnt_st == RPC_PROGUNAVAIL))
@@ -725,7 +801,7 @@
 			goto error;
 		}
 		port = htons(port);
-		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)(void *)&remote);
+		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote);
 		if (((address = (struct netbuf *)
 			malloc(sizeof (struct netbuf))) == NULL) ||
 		    ((address->buf = (char *)
@@ -748,6 +824,20 @@

 try_rpcbind:
 	/*
+	 * Check if rpcbind is up.  This prevents needless delays when
+	 * accessing applications such as the keyserver while booting
+	 * disklessly.
+	 */
+	if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
+		if (!__rpcbind_is_up()) {
+			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
+			rpc_createerr.cf_error.re_errno = 0;
+			goto error;
+		}
+		check_rpcbind = FALSE;
+	}
+
+	/*
 	 * Now we try version 4 and then 3.
 	 * We also send the remote system the address we used to
 	 * contact it in case it can help to connect back with us
@@ -762,33 +852,17 @@
 	/*
 	 * If a COTS transport is being used, try getting address via CLTS
 	 * transport.  This works only with version 4.
-	 * NOTE: This is being done for all transports EXCEPT LOOPBACK
-	 * because with loopback the cost to go to a COTS is same as
-	 * the cost to go through CLTS, plus you get the advantage of
-	 * finding out immediately if the local rpcbind process is dead.
 	 */
-#if 1
-	if ((nconf->nc_semantics == NC_TPI_COTS_ORD ||
-			nconf->nc_semantics == NC_TPI_COTS) &&
-	    (strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0)) {
-#else
-	if (client != NULL) {
-		CLNT_DESTROY(client);
-		client = NULL;
-	}
-	if (nconf->nc_semantics == NC_TPI_CLTS) {
-#endif
+	if (nconf->nc_semantics == NC_TPI_COTS_ORD ||
+			nconf->nc_semantics == NC_TPI_COTS) {
+
 		void *handle;
 		struct netconfig *nconf_clts;
 		rpcb_entry_list_ptr relp = NULL;

 		if (client == NULL) {
 			/* This did not go through the above PORTMAP/TCP code */
-#if 1
 			if ((handle = __rpc_setconf("datagram_v")) != NULL) {
-#else
-			if ((handle = __rpc_setconf("circuit_v")) != NULL) {
-#endif
 				while ((nconf_clts = __rpc_getconf(handle))
 					!= NULL) {
 					if (strcmp(nconf_clts->nc_protofmly,
@@ -816,10 +890,13 @@
 			/*LINTED const castaway*/
 			parms.r_addr = (char *) &nullstring[0]; /* for XDRing */
 		}
+
+		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
+
 		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
 		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
 		    (xdrproc_t) xdr_rpcb_entry_list_ptr,
-		    (char *)(void *)&relp, tottimeout);
+		    (char *)(void *)&relp, *tp);
 		if (clnt_st == RPC_SUCCESS) {
 			if ((address = got_entry(relp, nconf)) != NULL) {
 				xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
@@ -850,12 +927,8 @@
 regular_rpcbind:

 	/* Now the same transport is to be used to get the address */
-#if 1
 	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
 			(nconf->nc_semantics == NC_TPI_COTS))) {
-#else
-	if (client && nconf->nc_semantics == NC_TPI_CLTS) {
-#endif
 		/* A CLTS type of client - destroy it */
 		CLNT_DESTROY(client);
 		client = NULL;
@@ -873,13 +946,14 @@
 	}

 	/* First try from start_vers and then version 3 (RPCBVERS) */
+
+	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *) &rpcbrmttime);
 	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
 		/* Set the version */
 		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
 		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
 		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
-		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua,
-		    tottimeout);
+		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua, *tp);
 		if (clnt_st == RPC_SUCCESS) {
 			if ((ua == NULL) || (ua[0] == NULL)) {
 				/* address unknown */
@@ -966,8 +1040,9 @@
 {
 	struct netbuf *na;

-	if ((na = __rpcb_findaddr(program, version, nconf,
-				host, (CLIENT **) NULL)) == NULL)
+	if ((na = __rpcb_findaddr_timed(program, version,
+	    (struct netconfig *) nconf, (char *) host,
+	    (CLIENT **) NULL, (struct timeval *) NULL)) == NULL)
 		return (FALSE);

 	if (na->len > address->maxlen) {
Comment 2 Alfred Perlstein freebsd_committer freebsd_triage 2002-07-11 18:12:03 UTC
State Changed
From-To: open->closed

Delta has been applied, thank you.