Linux compatible rpc.lockd

Björn Grönvall bg at sics.se
Thu Nov 25 11:18:51 PST 2004


Hi,

I have made a patch to address PR kern/56461, in short the patch
provides two different options to be compatible with Linux lockd
implementations. It can also serve as a basis for a future more robust
rpc.lockd.

The new options to rpc.lockd are:

     -s      The -s option makes the client portion of rpc.lockd use synchro-
             nous RPC calls rather than the corresponding asynchronous proce-
             dures. For instance one NLM_LOCK call is used instead of two
             asynchronous NLM_LOCK_MSG and NLM_LOCK_RES calls. Locks origi-
             nated locally will use short cookies (see the -8 option below)
             but will not be susceptible to pid recycling problems.

     -8      The -8 option allows for usage of short 8 byte client cookies
             compatible with limitations of Linux servers. This option only
             effects locks of local origin and will not effect any of the
             rpc.lockd server functionality. Short cookies are susceptible to
             pid recycling problems and are thus turned off by default.

When rebuilding, be careful with the copy of nfs_lock.h in
/usr/include/nfsclient/.

If you would like to enable this patch please make this change to
/etc/rc.conf:

--- /etc/rc.conf~	Thu Nov 25 19:55:08 2004
+++ /etc/rc.conf	Thu Nov 25 19:57:01 2004
@@ -19,6 +19,7 @@
 nfs_access_cache=""
 rpc_statd_enable="YES"
 rpc_lockd_enable="YES"
+lockd_flags="-s"
 amd_enable="YES"

Cheers,
/b

diff -u sys/nfsclient/nfs_lock.c sys/nfsclient.new/nfs_lock.c
--- sys/nfsclient/nfs_lock.c	Fri Nov 14 21:54:08 2003
+++ sys/nfsclient.new/nfs_lock.c	Tue Nov 23 15:27:24 2004
@@ -260,6 +260,10 @@
 	if ((targetp = pfind(ansp->la_msg_ident.pid)) == NULL)
 		return (ESRCH);
 
+	/* Fake pid_start for Linux compat (sic.). */
+	if (!timevalisset(&ansp->la_msg_ident.pid_start))
+		ansp->la_msg_ident.pid_start = targetp->p_nlminfo->pid_start;
+
 	/* verify the pid hasn't been reused (if we can), and it isn't waiting
 	 * for an answer from a more recent request.  We return an EPIPE if
 	 * the match fails, because we've already used ESRCH above, and this
diff -u sys/nfsclient/nfs_lock.h sys/nfsclient.new/nfs_lock.h
--- sys/nfsclient/nfs_lock.h	Thu Aug 15 23:52:22 2002
+++ sys/nfsclient.new/nfs_lock.h	Tue Nov 23 16:11:46 2004
@@ -51,14 +51,17 @@
  * a particular message to lockd.  A sequence number is used to differentiate
  * multiple messages from the same process.  A process start time is used to
  * detect the unlikely, but possible, event of the recycling of a pid.
+ *
+ * The first eight bytes of this struct may be used to generate short
+ * client cookies that are not robust to pid recycling.
  */
 struct lockd_msg_ident {
-	pid_t		pid;            /* The process ID. */
+	int32_t		msg_seq;	/* Sequence number of message */
+	pid_t		pid;		/* The process ID. */
 	struct timeval	pid_start;	/* Start time of process id */
-	int		msg_seq;	/* Sequence number of message */
 };
 
-#define LOCKD_MSG_VERSION	2
+#define LOCKD_MSG_VERSION	3
 
 /*
  * The structure that the kernel hands us for each lock request.
@@ -76,7 +79,7 @@
 	u_int8_t		lm_fh[NFS_SMALLFH];/* The file handle. */
 } LOCKD_MSG;
 
-#define LOCKD_ANS_VERSION	1
+#define LOCKD_ANS_VERSION	3
 
 struct lockd_ans {
 	int		la_vers;
diff -u usr.sbin/rpc.lockd/kern.c usr.sbin/rpc.lockd.new/kern.c
--- usr.sbin/rpc.lockd/kern.c	Sun Oct 26 07:10:44 2003
+++ usr.sbin/rpc.lockd.new/kern.c	Thu Nov 25 17:55:55 2004
@@ -58,6 +58,9 @@
 #include "lockd_lock.h"
 #include <nfsclient/nfs.h>
 
+#define MINIMAL_CLIENT_COOKIE_LEN \
+	((size_t) &((struct lockd_msg_ident *)0)->pid_start)
+
 #define DAEMON_USERNAME	"daemon"
 
 #define nfslockdans(_v, _ansp)	\
@@ -81,6 +84,11 @@
 int	unlock_request(LOCKD_MSG *);
 
 /*
+ * The cookie size we use by default.
+ */
+int		client_cookie_len = sizeof(struct lockd_msg_ident);
+
+/*
  * will break because fifo needs to be repopened when EOF'd
  */
 #define lockd_seteuid(uid)	seteuid(uid)
@@ -124,6 +132,13 @@
 	mode_t old_umask;
 	struct passwd *pw;
 
+	if (client_cookie_len != sizeof(struct lockd_msg_ident) &&
+	    client_cookie_len != MINIMAL_CLIENT_COOKIE_LEN) {
+		syslog(LOG_ERR, "disable -8 switch");
+		client_cookie_len = sizeof(struct lockd_msg_ident);
+	}
+
+
 	/* Recreate the NLM fifo. */
 	(void)unlink(_PATH_LCKFIFO);
 	old_umask = umask(S_IXGRP|S_IXOTH);
@@ -261,7 +276,24 @@
 {
 	CLIENT *cli;
 	struct timeval timeout = {0, 0};	/* No timeout, no response. */
-	char dummy;
+	int nlm_version;
+	xdrproc_t xdrargs;
+	union {
+		nlm_testargs arg;
+		nlm4_testargs arg4;
+	} arg ;
+	xdrproc_t xdrres;
+	union {
+		char dummy;
+		nlm_testres res;
+		nlm4_testres res4;
+	} res;
+	int cookie_len;
+
+	if (asynchronous_client_rpc)
+		cookie_len = client_cookie_len;	/* Long cookies */
+	else
+		cookie_len = MINIMAL_CLIENT_COOKIE_LEN;
 
 	if (d_calls)
 		syslog(LOG_DEBUG, "test request: %s: %s to %s",
@@ -270,53 +302,65 @@
 		    from_addr((struct sockaddr *)&msg->lm_addr));
 
 	if (msg->lm_nfsv3) {
-		struct nlm4_testargs arg4;
-
-		arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
-		arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
-		arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
-		arg4.alock.caller_name = hostname;
-		arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
-		arg4.alock.fh.n_len = msg->lm_fh_len;
-		arg4.alock.oh.n_bytes = (char *)&owner;
-		arg4.alock.oh.n_len = sizeof(owner);
-		arg4.alock.svid = msg->lm_msg_ident.pid;
-		arg4.alock.l_offset = msg->lm_fl.l_start;
-		arg4.alock.l_len = msg->lm_fl.l_len;
-
-		if ((cli = get_client(
-		    (struct sockaddr *)&msg->lm_addr,
-		    NLM_VERS4)) == NULL)
-			return (1);
-
-		set_auth(cli, &msg->lm_cred);
-		(void)clnt_call(cli, NLM_TEST_MSG,
-		    (xdrproc_t)xdr_nlm4_testargs, &arg4,
-		    (xdrproc_t)xdr_void, &dummy, timeout);
+		nlm_version = NLM_VERS4;
+		xdrargs = (xdrproc_t)xdr_nlm4_testargs;
+		xdrres = (xdrproc_t)xdr_nlm4_testres;
+		arg.arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+		arg.arg4.cookie.n_len = cookie_len;
+		arg.arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+		arg.arg4.alock.caller_name = hostname;
+		arg.arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
+		arg.arg4.alock.fh.n_len = msg->lm_fh_len;
+		arg.arg4.alock.oh.n_bytes = (char *)&owner;
+		arg.arg4.alock.oh.n_len = sizeof(owner);
+		arg.arg4.alock.svid = msg->lm_msg_ident.pid;
+		arg.arg4.alock.l_offset = msg->lm_fl.l_start;
+		arg.arg4.alock.l_len = msg->lm_fl.l_len;
 	} else {
-		struct nlm_testargs arg;
+		nlm_version = NLM_VERS ;
+		xdrargs = (xdrproc_t)xdr_nlm_testargs;
+		xdrres = (xdrproc_t)xdr_nlm_testres;
+		arg.arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+		arg.arg.cookie.n_len = cookie_len;
+		arg.arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+		arg.arg.alock.caller_name = hostname;
+		arg.arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
+		arg.arg.alock.fh.n_len = msg->lm_fh_len;
+		arg.arg.alock.oh.n_bytes = (char *)&owner;
+		arg.arg.alock.oh.n_len = sizeof(owner);
+		arg.arg.alock.svid = msg->lm_msg_ident.pid;
+		arg.arg.alock.l_offset = msg->lm_fl.l_start;
+		arg.arg.alock.l_len = msg->lm_fl.l_len;
+	}
 
-		arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
-		arg.cookie.n_len = sizeof(msg->lm_msg_ident);
-		arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
-		arg.alock.caller_name = hostname;
-		arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
-		arg.alock.fh.n_len = msg->lm_fh_len;
-		arg.alock.oh.n_bytes = (char *)&owner;
-		arg.alock.oh.n_len = sizeof(owner);
-		arg.alock.svid = msg->lm_msg_ident.pid;
-		arg.alock.l_offset = msg->lm_fl.l_start;
-		arg.alock.l_len = msg->lm_fl.l_len;
-
-		if ((cli = get_client(
-		    (struct sockaddr *)&msg->lm_addr,
-		    NLM_VERS)) == NULL)
-			return (1);
-
-		set_auth(cli, &msg->lm_cred);
-		(void)clnt_call(cli, NLM_TEST_MSG,
-		    (xdrproc_t)xdr_nlm_testargs, &arg,
-		    (xdrproc_t)xdr_void, &dummy, timeout);
+	cli = get_client((struct sockaddr *)&msg->lm_addr, nlm_version);
+	if (cli == NULL)
+		return (1);
+
+	set_auth(cli, &msg->lm_cred);
+	if (asynchronous_client_rpc)
+		clnt_call(cli, NLM_TEST_MSG,
+			  xdrargs, &arg, (xdrproc_t)xdr_void, &res, timeout);
+	else {
+		enum clnt_stat st;
+		timeout.tv_sec = synchronous_client_rpc_timeout;
+		bzero(&res, sizeof(res));
+		st = clnt_call(cli, NLM_TEST,
+			       xdrargs, &arg, xdrres, &res, timeout);
+		if (st != RPC_SUCCESS) {
+			expire_client(cli);
+			syslog(LOG_ERR, "NLM_TEST %s, server %s",
+			       clnt_sperrno(st),
+			       from_addr((struct sockaddr *)&msg->lm_addr));
+		} else {
+			touch_client(cli);
+			lock_answer(msg->lm_msg_ident.pid,
+				    &res.res.cookie,
+				    res.res.stat.stat,	/* union XXX */
+				    &res.res.stat.nlm_testrply_u.holder.svid,
+				    nlm_version);
+		}
+		xdr_free(xdrres, &res);
 	}
 	return (0);
 }
@@ -329,10 +373,25 @@
 lock_request(LOCKD_MSG *msg)
 {
 	CLIENT *cli;
-	struct nlm4_lockargs arg4;
-	struct nlm_lockargs arg;
 	struct timeval timeout = {0, 0};	/* No timeout, no response. */
-	char dummy;
+	int nlm_version;
+	xdrproc_t xdrargs;
+	union {
+		nlm_lockargs arg;
+		nlm4_lockargs arg4;
+	} arg;
+	xdrproc_t xdrres;
+	union {
+		char dummy;
+		nlm_res res;
+		nlm4_res res4;
+	} res;
+	int cookie_len;
+
+	if (asynchronous_client_rpc)
+		cookie_len = client_cookie_len;	/* Long cookies */
+	else
+		cookie_len = MINIMAL_CLIENT_COOKIE_LEN;
 
 	if (d_calls)
 		syslog(LOG_DEBUG, "lock request: %s: %s to %s",
@@ -341,55 +400,72 @@
 		    from_addr((struct sockaddr *)&msg->lm_addr));
 
 	if (msg->lm_nfsv3) {
-		arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
-		arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
-		arg4.block = msg->lm_wait ? 1 : 0;
-		arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
-		arg4.alock.caller_name = hostname;
-		arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
-		arg4.alock.fh.n_len = msg->lm_fh_len;
-		arg4.alock.oh.n_bytes = (char *)&owner;
-		arg4.alock.oh.n_len = sizeof(owner);
-		arg4.alock.svid = msg->lm_msg_ident.pid;
-		arg4.alock.l_offset = msg->lm_fl.l_start;
-		arg4.alock.l_len = msg->lm_fl.l_len;
-		arg4.reclaim = 0;
-		arg4.state = nsm_state;
-
-		if ((cli = get_client(
-		    (struct sockaddr *)&msg->lm_addr,
-		    NLM_VERS4)) == NULL)
-			return (1);
-
-		set_auth(cli, &msg->lm_cred);
-		(void)clnt_call(cli, NLM_LOCK_MSG,
-		    (xdrproc_t)xdr_nlm4_lockargs, &arg4,
-		    (xdrproc_t)xdr_void, &dummy, timeout);
+		nlm_version = NLM_VERS4;
+		xdrargs = (xdrproc_t)xdr_nlm4_lockargs;
+		xdrres = (xdrproc_t)xdr_nlm4_res;
+		arg.arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+		arg.arg4.cookie.n_len = cookie_len;
+		arg.arg4.block = msg->lm_wait ? 1 : 0;
+		arg.arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+		arg.arg4.alock.caller_name = hostname;
+		arg.arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
+		arg.arg4.alock.fh.n_len = msg->lm_fh_len;
+		arg.arg4.alock.oh.n_bytes = (char *)&owner;
+		arg.arg4.alock.oh.n_len = sizeof(owner);
+		arg.arg4.alock.svid = msg->lm_msg_ident.pid;
+		arg.arg4.alock.l_offset = msg->lm_fl.l_start;
+		arg.arg4.alock.l_len = msg->lm_fl.l_len;
+		arg.arg4.reclaim = 0;
+		arg.arg4.state = nsm_state;
 	} else {
-		arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
-		arg.cookie.n_len = sizeof(msg->lm_msg_ident);
-		arg.block = msg->lm_wait ? 1 : 0;
-		arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
-		arg.alock.caller_name = hostname;
-		arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
-		arg.alock.fh.n_len = msg->lm_fh_len;
-		arg.alock.oh.n_bytes = (char *)&owner;
-		arg.alock.oh.n_len = sizeof(owner);
-		arg.alock.svid = msg->lm_msg_ident.pid;
-		arg.alock.l_offset = msg->lm_fl.l_start;
-		arg.alock.l_len = msg->lm_fl.l_len;
-		arg.reclaim = 0;
-		arg.state = nsm_state;
-
-		if ((cli = get_client(
-		    (struct sockaddr *)&msg->lm_addr,
-		    NLM_VERS)) == NULL)
-			return (1);
-
-		set_auth(cli, &msg->lm_cred);
-		(void)clnt_call(cli, NLM_LOCK_MSG,
-		    (xdrproc_t)xdr_nlm_lockargs, &arg,
-		    (xdrproc_t)xdr_void, &dummy, timeout);
+		nlm_version = NLM_VERS;
+		xdrargs = (xdrproc_t)xdr_nlm_lockargs;
+		xdrres = (xdrproc_t)xdr_nlm_res;
+		arg.arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+		arg.arg.cookie.n_len = cookie_len;
+		arg.arg.block = msg->lm_wait ? 1 : 0;
+		arg.arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+		arg.arg.alock.caller_name = hostname;
+		arg.arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
+		arg.arg.alock.fh.n_len = msg->lm_fh_len;
+		arg.arg.alock.oh.n_bytes = (char *)&owner;
+		arg.arg.alock.oh.n_len = sizeof(owner);
+		arg.arg.alock.svid = msg->lm_msg_ident.pid;
+		arg.arg.alock.l_offset = msg->lm_fl.l_start;
+		arg.arg.alock.l_len = msg->lm_fl.l_len;
+		arg.arg.reclaim = 0;
+		arg.arg.state = nsm_state;
+	}
+
+
+	cli = get_client((struct sockaddr *)&msg->lm_addr, nlm_version);
+	if (cli == NULL)
+		return (1);
+
+	set_auth(cli, &msg->lm_cred);
+	if (asynchronous_client_rpc)
+		clnt_call(cli, NLM_LOCK_MSG,
+			  xdrargs, &arg, (xdrproc_t)xdr_void, &res, timeout);
+	else {
+		enum clnt_stat st;
+		timeout.tv_sec = synchronous_client_rpc_timeout;
+		bzero(&res, sizeof(res));
+		st = clnt_call(cli, NLM_LOCK,
+			       xdrargs, &arg, xdrres, &res, timeout);
+		if (st != RPC_SUCCESS) {
+			expire_client(cli);
+			syslog(LOG_ERR, "NLM_LOCK %s, server %s",
+			       clnt_sperrno(st),
+			       from_addr((struct sockaddr *)&msg->lm_addr));
+		} else {
+			touch_client(cli);
+			lock_answer(msg->lm_msg_ident.pid,
+				    &res.res.cookie,
+				    res.res.stat.stat,	/* union XXX */
+				    NULL,
+				    nlm_version);
+		}
+		xdr_free(xdrres, &res);
 	}
 	return (0);
 }
@@ -402,10 +478,25 @@
 unlock_request(LOCKD_MSG *msg)
 {
 	CLIENT *cli;
-	struct nlm4_unlockargs arg4;
-	struct nlm_unlockargs arg;
 	struct timeval timeout = {0, 0};	/* No timeout, no response. */
-	char dummy;
+	int nlm_version;
+	xdrproc_t xdrargs;
+	union {
+		nlm_unlockargs arg;
+		nlm4_unlockargs arg4;
+	} arg;
+	xdrproc_t xdrres;
+	union {
+		char dummy;
+		nlm_res res;
+		nlm4_res res4;
+	} res;
+	int cookie_len;
+
+	if (asynchronous_client_rpc)
+		cookie_len = client_cookie_len;	/* Long cookies */
+	else
+		cookie_len = MINIMAL_CLIENT_COOKIE_LEN;
 
 	if (d_calls)
 		syslog(LOG_DEBUG, "unlock request: %s: to %s",
@@ -413,49 +504,69 @@
 		    from_addr((struct sockaddr *)&msg->lm_addr));
 
 	if (msg->lm_nfsv3) {
-		arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
-		arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
-		arg4.alock.caller_name = hostname;
-		arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
-		arg4.alock.fh.n_len = msg->lm_fh_len;
-		arg4.alock.oh.n_bytes = (char *)&owner;
-		arg4.alock.oh.n_len = sizeof(owner);
-		arg4.alock.svid = msg->lm_msg_ident.pid;
-		arg4.alock.l_offset = msg->lm_fl.l_start;
-		arg4.alock.l_len = msg->lm_fl.l_len;
-
-		if ((cli = get_client(
-		    (struct sockaddr *)&msg->lm_addr,
-		    NLM_VERS4)) == NULL)
-			return (1);
-
-		set_auth(cli, &msg->lm_cred);
-		(void)clnt_call(cli, NLM_UNLOCK_MSG,
-		    (xdrproc_t)xdr_nlm4_unlockargs, &arg4,
-		    (xdrproc_t)xdr_void, &dummy, timeout);
+		nlm_version = NLM_VERS4;
+		xdrargs = (xdrproc_t)xdr_nlm4_unlockargs;
+		xdrres = (xdrproc_t)xdr_nlm4_res;
+		arg.arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+		arg.arg4.cookie.n_len = cookie_len;
+		arg.arg4.alock.caller_name = hostname;
+		arg.arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
+		arg.arg4.alock.fh.n_len = msg->lm_fh_len;
+		arg.arg4.alock.oh.n_bytes = (char *)&owner;
+		arg.arg4.alock.oh.n_len = sizeof(owner);
+		arg.arg4.alock.svid = msg->lm_msg_ident.pid;
+		arg.arg4.alock.l_offset = msg->lm_fl.l_start;
+		arg.arg4.alock.l_len = msg->lm_fl.l_len;
 	} else {
-		arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
-		arg.cookie.n_len = sizeof(msg->lm_msg_ident);
-		arg.alock.caller_name = hostname;
-		arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
-		arg.alock.fh.n_len = msg->lm_fh_len;
-		arg.alock.oh.n_bytes = (char *)&owner;
-		arg.alock.oh.n_len = sizeof(owner);
-		arg.alock.svid = msg->lm_msg_ident.pid;
-		arg.alock.l_offset = msg->lm_fl.l_start;
-		arg.alock.l_len = msg->lm_fl.l_len;
-
-		if ((cli = get_client(
-		    (struct sockaddr *)&msg->lm_addr,
-		    NLM_VERS)) == NULL)
-			return (1);
-
-		set_auth(cli, &msg->lm_cred);
-		(void)clnt_call(cli, NLM_UNLOCK_MSG,
-		    (xdrproc_t)xdr_nlm_unlockargs, &arg,
-		    (xdrproc_t)xdr_void, &dummy, timeout);
+		nlm_version = NLM_VERS;
+		xdrargs = (xdrproc_t)xdr_nlm_unlockargs;
+		xdrres = (xdrproc_t)xdr_nlm_res;
+		arg.arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+		arg.arg.cookie.n_len = cookie_len;
+		arg.arg.alock.caller_name = hostname;
+		arg.arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
+		arg.arg.alock.fh.n_len = msg->lm_fh_len;
+		arg.arg.alock.oh.n_bytes = (char *)&owner;
+		arg.arg.alock.oh.n_len = sizeof(owner);
+		arg.arg.alock.svid = msg->lm_msg_ident.pid;
+		arg.arg.alock.l_offset = msg->lm_fl.l_start;
+		arg.arg.alock.l_len = msg->lm_fl.l_len;
 	}
 
+	cli = get_client((struct sockaddr *)&msg->lm_addr, nlm_version);
+	if (cli == NULL)
+		return (1);
+
+	set_auth(cli, &msg->lm_cred);
+	if (asynchronous_client_rpc)
+		clnt_call(cli, NLM_UNLOCK_MSG,
+			  xdrargs, &arg, (xdrproc_t)xdr_void, &res, timeout);
+	else {
+		enum clnt_stat st;
+		timeout.tv_sec = synchronous_client_rpc_timeout;
+		bzero(&res, sizeof(res));
+		st = clnt_call(cli, NLM_UNLOCK,
+			       xdrargs, &arg, xdrres, &res, timeout);
+		if (st != RPC_SUCCESS) {
+			expire_client(cli);
+			/*
+			 * A list of failed locks should really be
+			 * maintained per server so that unlocks may
+			 * be retried if/when the server comes back.
+			 */
+			syslog(LOG_ERR, "NLM_UNLOCK %s, server %s",
+			       clnt_sperrno(st),
+			       from_addr((struct sockaddr *)&msg->lm_addr));
+		} else {
+			touch_client(cli);
+			lock_answer(msg->lm_msg_ident.pid,
+				    &res.res.cookie,
+				    res.res.stat.stat,
+				    NULL,
+				    nlm_version);
+		}
+		xdr_free(xdrres, &res);
+	}
 	return (0);
 }
 
@@ -464,16 +575,21 @@
 {
 	struct lockd_ans ans;
 
-	if (netcookie->n_len != sizeof(ans.la_msg_ident)) {
+	if (netcookie->n_len == sizeof(ans.la_msg_ident)) {
+		memcpy(&ans.la_msg_ident, netcookie->n_bytes,
+		    sizeof(ans.la_msg_ident));
+	} else if (netcookie->n_len == MINIMAL_CLIENT_COOKIE_LEN) {
+		memcpy(&ans.la_msg_ident, netcookie->n_bytes,
+		    MINIMAL_CLIENT_COOKIE_LEN);
+		ans.la_msg_ident.pid_start.tv_sec = 0;
+		ans.la_msg_ident.pid_start.tv_usec = 0;
+	} else {
 		if (pid == -1) {	/* we're screwed */
 			syslog(LOG_ERR, "inedible nlm cookie");
 			return -1;
 		}
 		ans.la_msg_ident.pid = pid;
 		ans.la_msg_ident.msg_seq = -1;
-	} else {
-		memcpy(&ans.la_msg_ident, netcookie->n_bytes,
-		    sizeof(ans.la_msg_ident));
 	}
 
 	if (d_calls)
diff -u usr.sbin/rpc.lockd/lock_proc.c usr.sbin/rpc.lockd.new/lock_proc.c
--- usr.sbin/rpc.lockd/lock_proc.c	Fri Jul 16 21:30:59 2004
+++ usr.sbin/rpc.lockd.new/lock_proc.c	Thu Nov 25 16:14:22 2004
@@ -47,6 +47,7 @@
 #include <netdb.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 #include <syslog.h>
 #include <netconfig.h>
 
@@ -184,6 +185,40 @@
 	}
 
 	return memcmp(p1, p2, len);
+}
+
+/* Mark server/client as down! */
+void
+expire_client(CLIENT *client)
+{
+	int i;
+
+	for (i = 0; i < CLIENT_CACHE_SIZE; i++) {
+		if (client == clnt_cache_ptr[i]) {
+			clnt_cache_time[i] = 0L;
+			clnt_destroy(client);
+			clnt_cache_ptr[i] = NULL;
+			return;
+		}
+	}
+}
+
+/* Successfully contacted server/client! */
+void
+touch_client(CLIENT *client)
+{
+	int i;
+	struct timeval time_now;
+
+	gettimeofday(&time_now, NULL);
+
+	for (i = 0; i < CLIENT_CACHE_SIZE; i++) {
+		if (client == clnt_cache_ptr[i]) {
+			gettimeofday(&time_now, NULL);
+			clnt_cache_time[i] = time_now.tv_sec;
+			return;
+		}
+	}
 }
 
 CLIENT *
diff -u usr.sbin/rpc.lockd/lockd.c usr.sbin/rpc.lockd.new/lockd.c
--- usr.sbin/rpc.lockd/lockd.c	Fri Jul 16 21:30:59 2004
+++ usr.sbin/rpc.lockd.new/lockd.c	Thu Nov 25 18:38:32 2004
@@ -68,6 +68,8 @@
 #include <rpcsvc/nlm_prot.h>
 
 int		debug_level = 0;	/* 0 = no debugging syslog() calls */
+int		asynchronous_client_rpc = 1; /* XXX default to 1 */
+int		synchronous_client_rpc_timeout = 5; /* Seconds */
 int		_rpcsvcdirty = 0;
 
 int grace_expired;
@@ -98,7 +100,7 @@
 	struct netconfig *nconf;
 	int maxrec = RPC_MAXDATASIZE;
 
-	while ((ch = getopt(argc, argv, "d:g:")) != (-1)) {
+	while ((ch = getopt(argc, argv, "ad:g:s8")) != (-1)) {
 		switch (ch) {
 		case 'd':
 			debug_level = atoi(optarg);
@@ -114,6 +116,15 @@
 				/* NOTREACHED */
 			}
 			break;
+		case 'a':
+			asynchronous_client_rpc = 1;
+			break;
+		case 's':
+			asynchronous_client_rpc = 0;
+			break;
+		case '8':
+			client_cookie_len = 8;
+			break;
 		default:
 		case '?':
 			usage();
@@ -223,7 +234,7 @@
 void
 usage()
 {
-	errx(1, "usage: rpc.lockd [-d <debuglevel>] [-g <grace period>]");
+	errx(1, "usage: rpc.lockd [-d <debuglevel>] [-g <grace period>] [-a] [-s] [-8]");
 }
 
 /*
diff -u usr.sbin/rpc.lockd/lockd.h usr.sbin/rpc.lockd.new/lockd.h
--- usr.sbin/rpc.lockd/lockd.h	Thu Nov 29 18:36:45 2001
+++ usr.sbin/rpc.lockd.new/lockd.h	Thu Nov 25 16:17:06 2004
@@ -35,6 +35,9 @@
  */
 
 extern int	debug_level;
+extern int	asynchronous_client_rpc;
+extern int	synchronous_client_rpc_timeout;
+extern int	client_cookie_len;
 extern int	grace_expired;
 pid_t	client_request(void);
 extern int nsm_state;
diff -u usr.sbin/rpc.lockd/lockd_lock.h usr.sbin/rpc.lockd.new/lockd_lock.h
--- usr.sbin/rpc.lockd/lockd_lock.h	Thu Mar 21 23:52:45 2002
+++ usr.sbin/rpc.lockd.new/lockd_lock.h	Tue Nov 23 18:16:27 2004
@@ -23,3 +23,5 @@
 void	transmit_result(int, nlm_res *, struct sockaddr *);
 void	transmit4_result(int, nlm4_res *, struct sockaddr *);
 CLIENT  *get_client(struct sockaddr *, rpcvers_t);
+void	expire_client(CLIENT *);
+void	touch_client(CLIENT *);
diff -u usr.sbin/rpc.lockd/rpc.lockd.8 usr.sbin/rpc.lockd.new/rpc.lockd.8
--- usr.sbin/rpc.lockd/rpc.lockd.8	Sun Jul 14 16:45:36 2002
+++ usr.sbin/rpc.lockd.new/rpc.lockd.8	Thu Nov 25 19:09:58 2004
@@ -43,6 +43,9 @@
 .Nm
 .Op Fl d Ar debug_level
 .Op Fl g Ar grace period
+.Op Fl a
+.Op Fl s
+.Op Fl 8
 .Sh DESCRIPTION
 The
 .Nm
@@ -83,6 +86,26 @@
 only accepts requests from hosts which are reinitialising locks which
 existed before the server restart.
 Default is 30 seconds.
+.It Fl s
+The
+.Fl s
+option makes the client portion of
+.Nm
+use synchronous RPC calls rather than the corresponding asynchronous
+procedures. For instance one NLM_LOCK call is used instead of two
+asynchronous NLM_LOCK_MSG and NLM_LOCK_RES calls. Locks originated
+locally will use short cookies (see the
+.Fl 8
+option below) but will not be susceptible to pid recycling problems.
+.It Fl 8
+The
+.Fl 8
+option allows for usage of short 8 byte client cookies compatible with
+limitations of Linux servers. This option only effects locks of local
+origin and will not effect any of the
+.Nm
+server functionality. Short cookies are susceptible to pid recycling
+problems and are thus turned off by default.
 .El
 .Pp
 Error conditions are logged to syslog, irrespective of the debug level,


More information about the freebsd-net mailing list