summaryrefslogtreecommitdiffstats
path: root/usr.sbin/rpc.lockd
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/rpc.lockd')
-rw-r--r--usr.sbin/rpc.lockd/Makefile22
-rw-r--r--usr.sbin/rpc.lockd/lock_proc.c1294
-rw-r--r--usr.sbin/rpc.lockd/lockd.c231
-rw-r--r--usr.sbin/rpc.lockd/lockd.h17
-rw-r--r--usr.sbin/rpc.lockd/lockd_lock.c759
-rw-r--r--usr.sbin/rpc.lockd/lockd_lock.h21
-rw-r--r--usr.sbin/rpc.lockd/rpc.lockd.887
-rw-r--r--usr.sbin/rpc.lockd/test.c119
8 files changed, 2390 insertions, 160 deletions
diff --git a/usr.sbin/rpc.lockd/Makefile b/usr.sbin/rpc.lockd/Makefile
index 19a6565..ba6b3c7 100644
--- a/usr.sbin/rpc.lockd/Makefile
+++ b/usr.sbin/rpc.lockd/Makefile
@@ -1,15 +1,17 @@
-# $FreeBSD$
+# $FreeBSD$
+# $NetBSD: Makefile,v 1.12 2000/08/07 16:23:31 thorpej Exp $
-PROG = rpc.lockd
-SRCS = nlm_prot_svc.c nlm_prot.h lockd.c procs.c
-MAN8 = rpc.lockd.8
+PROG= rpc.lockd
+SRCS= nlm_prot_svc.c lockd.c lock_proc.c lockd_lock.c
+MAN8= rpc.lockd.8
+MLINKS= rpc.lockd.8 lockd.8
-DPADD= ${LIBRPCSVC}
-LDADD= -lrpcsvc
+CFLAGS+= -I. -I${DESTDIR}/usr/include/rpcsvc
-CFLAGS+= -I.
+DPADD= ${LIBRPCSVC} ${LIBUTIL}
+LDADD= -lrpcsvc -lutil
-CLEANFILES= nlm_prot_svc.c nlm_prot.h
+CLEANFILES= nlm_prot_svc.c nlm_prot.h test
RPCSRC= ${DESTDIR}/usr/include/rpcsvc/nlm_prot.x
RPCGEN= rpcgen -L -C
@@ -20,7 +22,7 @@ nlm_prot_svc.c: ${RPCSRC}
nlm_prot.h: ${RPCSRC}
${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
-test: test.c
- cc -o test test.c -lrpcsvc
+test: ${.CURDIR}/test.c
+ cc -o test ${.CURDIR}/test.c -lrpcsvc
.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.lockd/lock_proc.c b/usr.sbin/rpc.lockd/lock_proc.c
new file mode 100644
index 0000000..8b08fd0
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lock_proc.c
@@ -0,0 +1,1294 @@
+/* $NetBSD: lock_proc.c,v 1.7 2000/10/11 20:23:56 is Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: lock_proc.c,v 1.7 2000/10/11 20:23:56 is Exp $");
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <netconfig.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/sm_inter.h>
+
+#include "lockd.h"
+#include <rpcsvc/nlm_prot.h>
+#include "lockd_lock.h"
+
+
+#define CLIENT_CACHE_SIZE 64 /* No. of client sockets cached */
+#define CLIENT_CACHE_LIFETIME 120 /* In seconds */
+
+static void log_from_addr __P((char *, struct svc_req *));
+static int addrcmp __P((struct sockaddr *, struct sockaddr *));
+
+/* log_from_addr ----------------------------------------------------------- */
+/*
+ * Purpose: Log name of function called and source address
+ * Returns: Nothing
+ * Notes: Extracts the source address from the transport handle
+ * passed in as part of the called procedure specification
+ */
+static void
+log_from_addr(fun_name, req)
+ char *fun_name;
+ struct svc_req *req;
+{
+ struct sockaddr *addr;
+ char hostname_buf[NI_MAXHOST];
+
+ addr = svc_getrpccaller(req->rq_xprt)->buf;
+ if (getnameinfo(addr , addr->sa_len, hostname_buf, sizeof hostname_buf,
+ NULL, 0, 0) != 0)
+ return;
+
+ syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf);
+}
+
+/* get_client -------------------------------------------------------------- */
+/*
+ * Purpose: Get a CLIENT* for making RPC calls to lockd on given host
+ * Returns: CLIENT* pointer, from clnt_udp_create, or NULL if error
+ * Notes: Creating a CLIENT* is quite expensive, involving a
+ * conversation with the remote portmapper to get the
+ * port number. Since a given client is quite likely
+ * to make several locking requests in succession, it is
+ * desirable to cache the created CLIENT*.
+ *
+ * Since we are using UDP rather than TCP, there is no cost
+ * to the remote system in keeping these cached indefinitely.
+ * Unfortunately there is a snag: if the remote system
+ * reboots, the cached portmapper results will be invalid,
+ * and we will never detect this since all of the xxx_msg()
+ * calls return no result - we just fire off a udp packet
+ * and hope for the best.
+ *
+ * We solve this by discarding cached values after two
+ * minutes, regardless of whether they have been used
+ * in the meanwhile (since a bad one might have been used
+ * plenty of times, as the host keeps retrying the request
+ * and we keep sending the reply back to the wrong port).
+ *
+ * Given that the entries will always expire in the order
+ * that they were created, there is no point in a LRU
+ * algorithm for when the cache gets full - entries are
+ * always re-used in sequence.
+ */
+static CLIENT *clnt_cache_ptr[CLIENT_CACHE_SIZE];
+static long clnt_cache_time[CLIENT_CACHE_SIZE]; /* time entry created */
+static struct sockaddr_storage clnt_cache_addr[CLIENT_CACHE_SIZE];
+static int clnt_cache_next_to_use = 0;
+
+static int
+addrcmp(sa1, sa2)
+ struct sockaddr *sa1;
+ struct sockaddr *sa2;
+{
+ int len;
+ void *p1, *p2;
+
+ if (sa1->sa_family != sa2->sa_family)
+ return -1;
+
+ switch (sa1->sa_family) {
+ case AF_INET:
+ p1 = &((struct sockaddr_in *)sa1)->sin_addr;
+ p2 = &((struct sockaddr_in *)sa2)->sin_addr;
+ len = 4;
+ break;
+ case AF_INET6:
+ p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
+ p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
+ len = 16;
+ break;
+ default:
+ return -1;
+ }
+
+ return memcmp(p1, p2, len);
+}
+
+CLIENT *
+get_client(host_addr, vers)
+ struct sockaddr *host_addr;
+ rpcvers_t vers;
+{
+ CLIENT *client;
+ struct timeval retry_time, time_now;
+ int i;
+ char *netid;
+ struct netconfig *nconf;
+ char host[NI_MAXHOST];
+
+ gettimeofday(&time_now, NULL);
+
+ /*
+ * Search for the given client in the cache, zapping any expired
+ * entries that we happen to notice in passing.
+ */
+ for (i = 0; i < CLIENT_CACHE_SIZE; i++) {
+ client = clnt_cache_ptr[i];
+ if (client && ((clnt_cache_time[i] + CLIENT_CACHE_LIFETIME)
+ < time_now.tv_sec)) {
+ /* Cache entry has expired. */
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "Expired CLIENT* in cache");
+ clnt_cache_time[i] = 0L;
+ clnt_destroy(client);
+ clnt_cache_ptr[i] = NULL;
+ client = NULL;
+ }
+ if (client && !addrcmp((struct sockaddr *)&clnt_cache_addr[i],
+ host_addr)) {
+ /* Found it! */
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "Found CLIENT* in cache");
+ return (client);
+ }
+ }
+
+ /* Not found in cache. Free the next entry if it is in use. */
+ if (clnt_cache_ptr[clnt_cache_next_to_use]) {
+ clnt_destroy(clnt_cache_ptr[clnt_cache_next_to_use]);
+ clnt_cache_ptr[clnt_cache_next_to_use] = NULL;
+ }
+
+ /*
+ * Need a host string for clnt_tp_create. Use NI_NUMERICHOST
+ * to avoid DNS lookups.
+ */
+ if (getnameinfo(host_addr, host_addr->sa_len, host, sizeof host,
+ NULL, 0, NI_NUMERICHOST) != 0) {
+ syslog(LOG_ERR, "unable to get name string for caller");
+ return NULL;
+ }
+
+#if 1
+ if (host_addr->sa_family == AF_INET6)
+ netid = "udp6";
+ else
+ netid = "udp";
+#else
+ if (host_addr->sa_family == AF_INET6)
+ netid = "tcp6";
+ else
+ netid = "tcp";
+#endif
+ nconf = getnetconfigent(netid);
+ if (nconf == NULL) {
+ syslog(LOG_ERR, "could not get netconfig info for '%s': "
+ "no /etc/netconfig file?", netid);
+ return NULL;
+ }
+
+ client = clnt_tp_create(host, NLM_PROG, vers, nconf);
+ freenetconfigent(nconf);
+
+ if (!client) {
+ syslog(LOG_ERR, "%s", clnt_spcreateerror("clntudp_create"));
+ syslog(LOG_ERR, "Unable to return result to %s", host);
+ return NULL;
+ }
+
+ /* Success - update the cache entry */
+ clnt_cache_ptr[clnt_cache_next_to_use] = client;
+ memcpy(&clnt_cache_addr[clnt_cache_next_to_use], host_addr,
+ host_addr->sa_len);
+ clnt_cache_time[clnt_cache_next_to_use] = time_now.tv_sec;
+ if (++clnt_cache_next_to_use > CLIENT_CACHE_SIZE)
+ clnt_cache_next_to_use = 0;
+
+ /*
+ * Disable the default timeout, so we can specify our own in calls
+ * to clnt_call(). (Note that the timeout is a different concept
+ * from the retry period set in clnt_udp_create() above.)
+ */
+ retry_time.tv_sec = -1;
+ retry_time.tv_usec = -1;
+ clnt_control(client, CLSET_TIMEOUT, (char *)&retry_time);
+
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "Created CLIENT* for %s", host);
+ return client;
+}
+
+
+/* transmit_result --------------------------------------------------------- */
+/*
+ * Purpose: Transmit result for nlm_xxx_msg pseudo-RPCs
+ * Returns: Nothing - we have no idea if the datagram got there
+ * Notes: clnt_call() will always fail (with timeout) as we are
+ * calling it with timeout 0 as a hack to just issue a datagram
+ * without expecting a result
+ */
+void
+transmit_result(opcode, result, addr)
+ int opcode;
+ nlm_res *result;
+ struct sockaddr *addr;
+{
+ static char dummy;
+ CLIENT *cli;
+ struct timeval timeo;
+ int success;
+
+ if ((cli = get_client(addr, NLM_VERS)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, opcode, xdr_nlm_res, result, xdr_void,
+ &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d(%s)",
+ success, clnt_sperrno(success));
+ }
+}
+/* transmit4_result --------------------------------------------------------- */
+/*
+ * Purpose: Transmit result for nlm4_xxx_msg pseudo-RPCs
+ * Returns: Nothing - we have no idea if the datagram got there
+ * Notes: clnt_call() will always fail (with timeout) as we are
+ * calling it with timeout 0 as a hack to just issue a datagram
+ * without expecting a result
+ */
+void
+transmit4_result(opcode, result, addr)
+ int opcode;
+ nlm4_res *result;
+ struct sockaddr *addr;
+{
+ static char dummy;
+ CLIENT *cli;
+ struct timeval timeo;
+ int success;
+
+ if ((cli = get_client(addr, NLM_VERS4)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, opcode, xdr_nlm4_res, result, xdr_void,
+ &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d(%s)",
+ success, clnt_sperrno(success));
+ }
+}
+
+/*
+ * converts a struct nlm_lock to struct nlm4_lock
+ */
+static void nlmtonlm4 __P((struct nlm_lock *, struct nlm4_lock *));
+static void
+nlmtonlm4(arg, arg4)
+ struct nlm_lock *arg;
+ struct nlm4_lock *arg4;
+{
+ memcpy(arg4, arg, sizeof(nlm_lock));
+ arg4->l_offset = arg->l_offset;
+ arg4->l_len = arg->l_len;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ * Functions for Unix<->Unix locking (ie. monitored locking, with rpc.statd
+ * involved to ensure reclaim of locks after a crash of the "stateless"
+ * server.
+ *
+ * These all come in two flavours - nlm_xxx() and nlm_xxx_msg().
+ * The first are standard RPCs with argument and result.
+ * The nlm_xxx_msg() calls implement exactly the same functions, but
+ * use two pseudo-RPCs (one in each direction). These calls are NOT
+ * standard use of the RPC protocol in that they do not return a result
+ * at all (NB. this is quite different from returning a void result).
+ * The effect of this is to make the nlm_xxx_msg() calls simple unacknowledged
+ * datagrams, requiring higher-level code to perform retries.
+ *
+ * Despite the disadvantages of the nlm_xxx_msg() approach (some of which
+ * are documented in the comments to get_client() above), this is the
+ * interface used by all current commercial NFS implementations
+ * [Solaris, SCO, AIX etc.]. This is presumed to be because these allow
+ * implementations to continue using the standard RPC libraries, while
+ * avoiding the block-until-result nature of the library interface.
+ *
+ * No client implementations have been identified so far that make use
+ * of the true RPC version (early SunOS releases would be a likely candidate
+ * for testing).
+ */
+
+/* nlm_test ---------------------------------------------------------------- */
+/*
+ * Purpose: Test whether a specified lock would be granted if requested
+ * Returns: nlm_granted (or error code)
+ * Notes:
+ */
+nlm_testres *
+nlm_test_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_testres res;
+ struct nlm4_lock arg4;
+ struct nlm4_holder *holder;
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_test", rqstp);
+
+ holder = testlock(&arg4, 0);
+ /*
+ * Copy the cookie from the argument into the result. Note that this
+ * is slightly hazardous, as the structure contains a pointer to a
+ * malloc()ed buffer that will get freed by the caller. However, the
+ * main function transmits the result before freeing the argument
+ * so it is in fact safe.
+ */
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm_granted;
+ } else {
+ res.stat.stat = nlm_denied;
+ memcpy(&res.stat.nlm_testrply_u.holder, holder,
+ sizeof(struct nlm_holder));
+ res.stat.nlm_testrply_u.holder.l_offset = holder->l_offset;
+ res.stat.nlm_testrply_u.holder.l_len = holder->l_len;
+ }
+ return (&res);
+}
+
+void *
+nlm_test_msg_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ nlm_testres res;
+ static char dummy;
+ struct sockaddr *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+ struct nlm4_lock arg4;
+ struct nlm4_holder *holder;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_test_msg", rqstp);
+
+ holder = testlock(&arg4, 0);
+
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm_granted;
+ } else {
+ res.stat.stat = nlm_denied;
+ memcpy(&res.stat.nlm_testrply_u.holder, holder,
+ sizeof(struct nlm_holder));
+ res.stat.nlm_testrply_u.holder.l_offset = holder->l_offset;
+ res.stat.nlm_testrply_u.holder.l_len = holder->l_len;
+ }
+
+ /*
+ * nlm_test has different result type to the other operations, so
+ * can't use transmit_result() in this case
+ */
+ addr = svc_getrpccaller(rqstp->rq_xprt)->buf;
+ if ((cli = get_client(addr, NLM_VERS)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, NLM_TEST_RES, xdr_nlm_testres,
+ &res, xdr_void, &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d", success);
+ }
+ return (NULL);
+}
+
+/* nlm_lock ---------------------------------------------------------------- */
+/*
+ * Purposes: Establish a lock
+ * Returns: granted, denied or blocked
+ * Notes: *** grace period support missing
+ */
+nlm_res *
+nlm_lock_1_svc(arg, rqstp)
+ nlm_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lockargs arg4;
+ nlmtonlm4(&arg->alock, &arg4.alock);
+ arg4.cookie = arg->cookie;
+ arg4.block = arg->block;
+ arg4.exclusive = arg->exclusive;
+ arg4.reclaim = arg->reclaim;
+ arg4.state = arg->state;
+
+ if (debug_level)
+ log_from_addr("nlm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = getlock(&arg4, rqstp, LOCK_MON);
+ return (&res);
+}
+
+void *
+nlm_lock_msg_1_svc(arg, rqstp)
+ nlm_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lockargs arg4;
+
+ nlmtonlm4(&arg->alock, &arg4.alock);
+ arg4.cookie = arg->cookie;
+ arg4.block = arg->block;
+ arg4.exclusive = arg->exclusive;
+ arg4.reclaim = arg->reclaim;
+ arg4.state = arg->state;
+
+ if (debug_level)
+ log_from_addr("nlm_lock_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = getlock(&arg4, rqstp, LOCK_ASYNC | LOCK_MON);
+ transmit_result(NLM_LOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+
+ return (NULL);
+}
+
+/* nlm_cancel -------------------------------------------------------------- */
+/*
+ * Purpose: Cancel a blocked lock request
+ * Returns: granted or denied
+ * Notes:
+ */
+nlm_res *
+nlm_cancel_1_svc(arg, rqstp)
+ nlm_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_cancel", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg4, LOCK_CANCEL);
+ return (&res);
+}
+
+void *
+nlm_cancel_msg_1_svc(arg, rqstp)
+ nlm_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_cancel_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg4, LOCK_CANCEL);
+ transmit_result(NLM_CANCEL_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* nlm_unlock -------------------------------------------------------------- */
+/*
+ * Purpose: Release an existing lock
+ * Returns: Always granted, unless during grace period
+ * Notes: "no such lock" error condition is ignored, as the
+ * protocol uses unreliable UDP datagrams, and may well
+ * re-try an unlock that has already succeeded.
+ */
+nlm_res *
+nlm_unlock_1_svc(arg, rqstp)
+ nlm_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_unlock", rqstp);
+
+ res.stat.stat = unlock(&arg4, 0);
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm_unlock_msg_1_svc(arg, rqstp)
+ nlm_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_unlock_msg", rqstp);
+
+ res.stat.stat = unlock(&arg4, 0);
+ res.cookie = arg->cookie;
+
+ transmit_result(NLM_UNLOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Client-side pseudo-RPCs for results. Note that for the client there
+ * are only nlm_xxx_msg() versions of each call, since the 'real RPC'
+ * version returns the results in the RPC result, and so the client
+ * does not normally receive incoming RPCs.
+ *
+ * The exception to this is nlm_granted(), which is genuinely an RPC
+ * call from the server to the client - a 'call-back' in normal procedure
+ * call terms.
+ */
+
+/* nlm_granted ------------------------------------------------------------- */
+/*
+ * Purpose: Receive notification that formerly blocked lock now granted
+ * Returns: always success ('granted')
+ * Notes:
+ */
+nlm_res *
+nlm_granted_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+
+ if (debug_level)
+ log_from_addr("nlm_granted", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+void *
+nlm_granted_msg_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+
+ if (debug_level)
+ log_from_addr("nlm_granted_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ transmit_result(NLM_GRANTED_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* nlm_test_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_test_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_test_res_1_svc(arg, rqstp)
+ nlm_testres *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_test_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_lock_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_lock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_lock_res_1_svc(arg, rqstp)
+ nlm_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_lock_res", rqstp);
+
+ return (NULL);
+}
+
+/* nlm_cancel_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_cancel_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_cancel_res_1_svc(arg, rqstp)
+ nlm_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_cancel_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_unlock_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_unlock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_unlock_res_1_svc(arg, rqstp)
+ nlm_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_unlock_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_granted_res --------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_granted_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_granted_res_1_svc(arg, rqstp)
+ nlm_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_granted_res", rqstp);
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Calls for PCNFS locking (aka non-monitored locking, no involvement
+ * of rpc.statd).
+ *
+ * These are all genuine RPCs - no nlm_xxx_msg() nonsense here.
+ */
+
+/* nlm_share --------------------------------------------------------------- */
+/*
+ * Purpose: Establish a DOS-style lock
+ * Returns: success or failure
+ * Notes: Blocking locks are not supported - client is expected
+ * to retry if required.
+ */
+nlm_shareres *
+nlm_share_3_svc(arg, rqstp)
+ nlm_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm_share", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm_unshare ------------------------------------------------------------ */
+/*
+ * Purpose: Release a DOS-style lock
+ * Returns: nlm_granted, unless in grace period
+ * Notes:
+ */
+nlm_shareres *
+nlm_unshare_3_svc(arg, rqstp)
+ nlm_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm_unshare", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm_nm_lock ------------------------------------------------------------ */
+/*
+ * Purpose: non-monitored version of nlm_lock()
+ * Returns: as for nlm_lock()
+ * Notes: These locks are in the same style as the standard nlm_lock,
+ * but the rpc.statd should not be called to establish a
+ * monitor for the client machine, since that machine is
+ * declared not to be running a rpc.statd, and so would not
+ * respond to the statd protocol.
+ */
+nlm_res *
+nlm_nm_lock_3_svc(arg, rqstp)
+ nlm_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+
+ if (debug_level)
+ log_from_addr("nlm_nm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+/* nlm_free_all ------------------------------------------------------------ */
+/*
+ * Purpose: Release all locks held by a named client
+ * Returns: Nothing
+ * Notes: Potential denial of service security problem here - the
+ * locks to be released are specified by a host name, independent
+ * of the address from which the request has arrived.
+ * Should probably be rejected if the named host has been
+ * using monitored locks.
+ */
+void *
+nlm_free_all_3_svc(arg, rqstp)
+ nlm_notify *arg;
+ struct svc_req *rqstp;
+{
+ static char dummy;
+
+ if (debug_level)
+ log_from_addr("nlm_free_all", rqstp);
+ return (&dummy);
+}
+
+/* calls for nlm version 4 (NFSv3) */
+/* nlm_test ---------------------------------------------------------------- */
+/*
+ * Purpose: Test whether a specified lock would be granted if requested
+ * Returns: nlm_granted (or error code)
+ * Notes:
+ */
+nlm4_testres *
+nlm4_test_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_testres res;
+ struct nlm4_holder *holder;
+
+ if (debug_level)
+ log_from_addr("nlm4_test", rqstp);
+
+ holder = testlock(&arg->alock, LOCK_V4);
+
+ /*
+ * Copy the cookie from the argument into the result. Note that this
+ * is slightly hazardous, as the structure contains a pointer to a
+ * malloc()ed buffer that will get freed by the caller. However, the
+ * main function transmits the result before freeing the argument
+ * so it is in fact safe.
+ */
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm4_granted;
+ } else {
+ res.stat.stat = nlm4_denied;
+ memcpy(&res.stat.nlm4_testrply_u.holder, holder,
+ sizeof(struct nlm4_holder));
+ }
+ return (&res);
+}
+
+void *
+nlm4_test_msg_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ nlm4_testres res;
+ static char dummy;
+ struct sockaddr *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+ struct nlm4_holder *holder;
+
+ if (debug_level)
+ log_from_addr("nlm4_test_msg", rqstp);
+
+ holder = testlock(&arg->alock, LOCK_V4);
+
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm4_granted;
+ } else {
+ res.stat.stat = nlm4_denied;
+ memcpy(&res.stat.nlm4_testrply_u.holder, holder,
+ sizeof(struct nlm4_holder));
+ }
+
+ /*
+ * nlm_test has different result type to the other operations, so
+ * can't use transmit4_result() in this case
+ */
+ addr = svc_getrpccaller(rqstp->rq_xprt)->buf;
+ if ((cli = get_client(addr, NLM_VERS4)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, NLM4_TEST_RES, xdr_nlm4_testres,
+ &res, xdr_void, &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d", success);
+ }
+ return (NULL);
+}
+
+/* nlm_lock ---------------------------------------------------------------- */
+/*
+ * Purposes: Establish a lock
+ * Returns: granted, denied or blocked
+ * Notes: *** grace period support missing
+ */
+nlm4_res *
+nlm4_lock_4_svc(arg, rqstp)
+ nlm4_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_4() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = getlock(arg, rqstp, LOCK_MON | LOCK_V4);
+ return (&res);
+}
+
+void *
+nlm4_lock_msg_4_svc(arg, rqstp)
+ nlm4_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_lock_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = getlock(arg, rqstp, LOCK_MON | LOCK_ASYNC | LOCK_V4);
+ transmit4_result(NLM4_LOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+
+ return (NULL);
+}
+
+/* nlm_cancel -------------------------------------------------------------- */
+/*
+ * Purpose: Cancel a blocked lock request
+ * Returns: granted or denied
+ * Notes:
+ */
+nlm4_res *
+nlm4_cancel_4_svc(arg, rqstp)
+ nlm4_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_cancel", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg->alock, LOCK_CANCEL);
+ return (&res);
+}
+
+void *
+nlm4_cancel_msg_4_svc(arg, rqstp)
+ nlm4_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_cancel_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg->alock, LOCK_CANCEL | LOCK_V4);
+ transmit4_result(NLM4_CANCEL_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* nlm_unlock -------------------------------------------------------------- */
+/*
+ * Purpose: Release an existing lock
+ * Returns: Always granted, unless during grace period
+ * Notes: "no such lock" error condition is ignored, as the
+ * protocol uses unreliable UDP datagrams, and may well
+ * re-try an unlock that has already succeeded.
+ */
+nlm4_res *
+nlm4_unlock_4_svc(arg, rqstp)
+ nlm4_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_unlock", rqstp);
+
+ res.stat.stat = unlock(&arg->alock, LOCK_V4);
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm4_unlock_msg_4_svc(arg, rqstp)
+ nlm4_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_unlock_msg", rqstp);
+
+ res.stat.stat = unlock(&arg->alock, LOCK_V4);
+ res.cookie = arg->cookie;
+
+ transmit4_result(NLM4_UNLOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Client-side pseudo-RPCs for results. Note that for the client there
+ * are only nlm_xxx_msg() versions of each call, since the 'real RPC'
+ * version returns the results in the RPC result, and so the client
+ * does not normally receive incoming RPCs.
+ *
+ * The exception to this is nlm_granted(), which is genuinely an RPC
+ * call from the server to the client - a 'call-back' in normal procedure
+ * call terms.
+ */
+
+/* nlm_granted ------------------------------------------------------------- */
+/*
+ * Purpose: Receive notification that formerly blocked lock now granted
+ * Returns: always success ('granted')
+ * Notes:
+ */
+nlm4_res *
+nlm4_granted_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_granted", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = nlm4_granted;
+ return (&res);
+}
+
+void *
+nlm4_granted_msg_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_granted_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm4_granted;
+ transmit4_result(NLM4_GRANTED_RES, &res,
+ (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf);
+ return (NULL);
+}
+
+/* nlm_test_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_test_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_test_res_4_svc(arg, rqstp)
+ nlm4_testres *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_test_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_lock_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_lock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_lock_res_4_svc(arg, rqstp)
+ nlm4_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_lock_res", rqstp);
+
+ return (NULL);
+}
+
+/* nlm_cancel_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_cancel_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_cancel_res_4_svc(arg, rqstp)
+ nlm4_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_cancel_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_unlock_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_unlock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_unlock_res_4_svc(arg, rqstp)
+ nlm4_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_unlock_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_granted_res --------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_granted_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_granted_res_4_svc(arg, rqstp)
+ nlm4_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_granted_res", rqstp);
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Calls for PCNFS locking (aka non-monitored locking, no involvement
+ * of rpc.statd).
+ *
+ * These are all genuine RPCs - no nlm_xxx_msg() nonsense here.
+ */
+
+/* nlm_share --------------------------------------------------------------- */
+/*
+ * Purpose: Establish a DOS-style lock
+ * Returns: success or failure
+ * Notes: Blocking locks are not supported - client is expected
+ * to retry if required.
+ */
+nlm4_shareres *
+nlm4_share_4_svc(arg, rqstp)
+ nlm4_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm4_share", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm4_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm4_unshare ------------------------------------------------------------ */
+/*
+ * Purpose: Release a DOS-style lock
+ * Returns: nlm_granted, unless in grace period
+ * Notes:
+ */
+nlm4_shareres *
+nlm4_unshare_4_svc(arg, rqstp)
+ nlm4_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm_unshare", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm4_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm4_nm_lock ------------------------------------------------------------ */
+/*
+ * Purpose: non-monitored version of nlm4_lock()
+ * Returns: as for nlm4_lock()
+ * Notes: These locks are in the same style as the standard nlm4_lock,
+ * but the rpc.statd should not be called to establish a
+ * monitor for the client machine, since that machine is
+ * declared not to be running a rpc.statd, and so would not
+ * respond to the statd protocol.
+ */
+nlm4_res *
+nlm4_nm_lock_4_svc(arg, rqstp)
+ nlm4_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_nm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm4_test_1() */
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm4_granted;
+ return (&res);
+}
+
+/* nlm4_free_all ------------------------------------------------------------ */
+/*
+ * Purpose: Release all locks held by a named client
+ * Returns: Nothing
+ * Notes: Potential denial of service security problem here - the
+ * locks to be released are specified by a host name, independent
+ * of the address from which the request has arrived.
+ * Should probably be rejected if the named host has been
+ * using monitored locks.
+ */
+void *
+nlm4_free_all_4_svc(arg, rqstp)
+ nlm_notify *arg;
+ struct svc_req *rqstp;
+{
+ static char dummy;
+
+ if (debug_level)
+ log_from_addr("nlm4_free_all", rqstp);
+ return (&dummy);
+}
+
+/* nlm_sm_notify --------------------------------------------------------- */
+/*
+ * Purpose: called by rpc.statd when a monitored host state changes.
+ * Returns: Nothing
+ */
+void *
+nlm_sm_notify_0_svc(arg, rqstp)
+ struct nlm_sm_status *arg;
+ struct svc_req *rqstp;
+{
+ static char dummy;
+ notify(arg->mon_name, arg->state);
+ return (&dummy);
+}
diff --git a/usr.sbin/rpc.lockd/lockd.c b/usr.sbin/rpc.lockd/lockd.c
index ac32856..e0ac5cf 100644
--- a/usr.sbin/rpc.lockd/lockd.c
+++ b/usr.sbin/rpc.lockd/lockd.c
@@ -1,3 +1,6 @@
+/* $NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $ */
+/* $FreeBSD$ */
+
/*
* Copyright (c) 1995
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
@@ -31,77 +34,193 @@
*
*/
+#include <sys/cdefs.h>
#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+__RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $");
+#endif
+
+/*
+ * main() function for NFS lock daemon. Most of the code in this
+ * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x.
+ *
+ * The actual program logic is in the file lock_proc.c
+ */
-/* main() function for NFS lock daemon. Most of the code in this */
-/* file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x */
-/* The actual program logic is in the file procs.c */
+#include <sys/types.h>
+#include <sys/socket.h>
#include <err.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
#include <string.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <netconfig.h>
+
#include <rpc/rpc.h>
-#include <rpc/pmap_clnt.h>
+#include <rpcsvc/sm_inter.h>
+
#include "lockd.h"
+#include <rpcsvc/nlm_prot.h>
+
+int debug_level = 0; /* 0 = no debugging syslog() calls */
+int _rpcsvcdirty = 0;
+
+int grace_expired;
-void nlm_prog_1 __P((struct svc_req *, SVCXPRT *));
-void nlm_prog_3 __P((struct svc_req *, SVCXPRT *));
-static void usage __P((void));
+int main __P((int, char **));
+void nlm_prog_0 __P((struct svc_req *, SVCXPRT *));
+void nlm_prog_1 __P((struct svc_req *, SVCXPRT *));
+void nlm_prog_3 __P((struct svc_req *, SVCXPRT *));
+void nlm_prog_4 __P((struct svc_req *, SVCXPRT *));
+void usage __P((void));
-int debug_level = 0; /* Zero means no debugging syslog() calls */
+void sigalarm_handler __P((int));
+
+char *transports[] = { "udp", "tcp", "udp6", "tcp6" };
int
-main(int argc, char **argv)
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ SVCXPRT *transp;
+ int ch, i, maxindex, s;
+ struct sigaction sigchild, sigalarm;
+ int grace_period = 30;
+ struct netconfig *nconf;
+
+ while ((ch = getopt(argc, argv, "d:g:")) != (-1)) {
+ switch (ch) {
+ case 'd':
+ debug_level = atoi(optarg);
+ if (!debug_level) {
+ usage();
+ /* NOTREACHED */
+ }
+ break;
+ case 'g':
+ grace_period = atoi(optarg);
+ if (!grace_period) {
+ usage();
+ /* NOTREACHED */
+ }
+ break;
+ default:
+ case '?':
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (geteuid()) { /* This command allowed only to root */
+ fprintf(stderr, "Sorry. You are not superuser\n");
+ exit(1);
+ }
+
+ (void)rpcb_unset(NLM_PROG, NLM_SM, NULL);
+ (void)rpcb_unset(NLM_PROG, NLM_VERS, NULL);
+ (void)rpcb_unset(NLM_PROG, NLM_VERSX, NULL);
+ (void)rpcb_unset(NLM_PROG, NLM_VERS4, NULL);
+
+ /*
+ * Check if IPv6 support is present.
+ */
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s < 0)
+ maxindex = 2;
+ else {
+ close(s);
+ maxindex = 4;
+ }
+
+ for (i = 0; i < maxindex; i++) {
+ nconf = getnetconfigent(transports[i]);
+ if (nconf == NULL)
+ errx(1, "cannot get udp netconf.");
+
+ transp = svc_tli_create(RPC_ANYFD, nconf, NULL, 0, 0);
+ if (transp == NULL) {
+ errx(1, "cannot create %s service.", transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_SM, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_VERS, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_VERSX, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_VERS4, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ freenetconfigent(nconf);
+ }
+
+ /*
+ * Note that it is NOT sensible to run this program from inetd - the
+ * protocol assumes that it will run immediately at boot time.
+ */
+ if (daemon(0, 0)) {
+ err(1, "cannot fork");
+ /* NOTREACHED */
+ }
+
+ openlog("rpc.lockd", 0, LOG_DAEMON);
+ if (debug_level)
+ syslog(LOG_INFO, "Starting, debug level %d", debug_level);
+ else
+ syslog(LOG_INFO, "Starting");
+
+ sigchild.sa_handler = sigchild_handler;
+ sigemptyset(&sigchild.sa_mask);
+ sigchild.sa_flags = SA_RESTART;
+ if (sigaction(SIGCHLD, &sigchild, NULL) != 0) {
+ syslog(LOG_WARNING, "sigaction(SIGCHLD) failed: %s",
+ strerror(errno));
+ exit(1);
+ }
+ sigalarm.sa_handler = sigalarm_handler;
+ sigemptyset(&sigalarm.sa_mask);
+ sigalarm.sa_flags = SA_RESETHAND; /* should only happen once */
+ sigalarm.sa_flags |= SA_RESTART;
+ if (sigaction(SIGALRM, &sigalarm, NULL) != 0) {
+ syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s",
+ strerror(errno));
+ exit(1);
+ }
+ grace_expired = 0;
+ if (alarm(10) < 0) {
+ syslog(LOG_WARNING, "alarm failed: %s",
+ strerror(errno));
+ exit(1);
+ }
+
+ svc_run(); /* Should never return */
+ exit(1);
+}
+
+void
+sigalarm_handler(s)
+ int s;
{
- SVCXPRT *transp;
-
- if (argc > 1)
- {
- if (strncmp(argv[1], "-d", 2))
- usage();
- if (argc > 2) debug_level = atoi(argv[2]);
- else debug_level = atoi(argv[1] + 2);
- /* Ensure at least some debug if -d with no specified level */
- if (!debug_level) debug_level = 1;
- }
-
- (void)pmap_unset(NLM_PROG, NLM_VERS);
- (void)pmap_unset(NLM_PROG, NLM_VERSX);
-
- transp = svcudp_create(RPC_ANYSOCK);
- if (transp == NULL)
- errx(1, "cannot create udp service");
- if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_UDP))
- errx(1, "unable to register (NLM_PROG, NLM_VERS, udp)");
- if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_UDP))
- errx(1, "unable to register (NLM_PROG, NLM_VERSX, udp)");
-
- transp = svctcp_create(RPC_ANYSOCK, 0, 0);
- if (transp == NULL)
- errx(1, "cannot create tcp service");
- if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_TCP))
- errx(1, "unable to register (NLM_PROG, NLM_VERS, tcp)");
- if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_TCP))
- errx(1, "unable to register (NLM_PROG, NLM_VERSX, tcp)");
-
- /* Note that it is NOT sensible to run this program from inetd - the */
- /* protocol assumes that it will run immediately at boot time. */
- if (daemon(0,0))
- err(1, "fork");
- openlog("rpc.lockd", 0, LOG_DAEMON);
- if (debug_level) syslog(LOG_INFO, "Starting, debug level %d", debug_level);
- else syslog(LOG_INFO, "Starting");
-
- svc_run(); /* Should never return */
- exit(1);
+ grace_expired = 1;
}
-static void
+void
usage()
{
- fprintf(stderr, "usage: rpc.lockd [-d [debuglevel]]\n");
- exit(1);
+ errx(1, "usage: rpc.lockd [-d <debuglevel>] [-g <grace period>]");
}
diff --git a/usr.sbin/rpc.lockd/lockd.h b/usr.sbin/rpc.lockd/lockd.h
index 39586bf..42f14f1 100644
--- a/usr.sbin/rpc.lockd/lockd.h
+++ b/usr.sbin/rpc.lockd/lockd.h
@@ -1,3 +1,6 @@
+/* $NetBSD: lockd.h,v 1.2 2000/06/07 14:34:40 bouyer Exp $ */
+/* $FreeBSD$ */
+
/*
* Copyright (c) 1995
* A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
@@ -31,14 +34,6 @@
*
*/
-
-
-#include <stdio.h>
-#include <rpc/rpc.h>
-#include <syslog.h>
-#include <rpcsvc/sm_inter.h> /* protocol to talk to rpc.statd */
-#include "nlm_prot.h" /* The protocol we are implementing */
-
-
-/* global variables ------------------------------------------------------- */
-extern int debug_level;
+extern int debug_level;
+extern int grace_expired;
+void sigchild_handler __P((int));
diff --git a/usr.sbin/rpc.lockd/lockd_lock.c b/usr.sbin/rpc.lockd/lockd_lock.c
new file mode 100644
index 0000000..998a200
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd_lock.c
@@ -0,0 +1,759 @@
+/* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2000 Manuel Bouyer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <rpcsvc/sm_inter.h>
+#include <rpcsvc/nlm_prot.h>
+#include "lockd_lock.h"
+#include "lockd.h"
+
+/* A set of utilities for managing file locking */
+LIST_HEAD(lcklst_head, file_lock);
+struct lcklst_head lcklst_head = LIST_HEAD_INITIALIZER(lcklst_head);
+
+/* struct describing a lock */
+struct file_lock {
+ LIST_ENTRY(file_lock) lcklst;
+ fhandle_t filehandle; /* NFS filehandle */
+ struct sockaddr *addr;
+ struct nlm4_holder client; /* lock holder */
+ netobj client_cookie; /* cookie sent by the client */
+ char client_name[128];
+ int nsm_status; /* status from the remote lock manager */
+ int status; /* lock status, see below */
+ int flags; /* lock flags, see lockd_lock.h */
+ pid_t locker; /* pid of the child process trying to get the lock */
+ int fd; /* file descriptor for this lock */
+};
+
+/* lock status */
+#define LKST_LOCKED 1 /* lock is locked */
+#define LKST_WAITING 2 /* file is already locked by another host */
+#define LKST_PROCESSING 3 /* child is trying to aquire the lock */
+#define LKST_DYING 4 /* must dies when we get news from the child */
+
+void lfree __P((struct file_lock *));
+enum nlm_stats do_lock __P((struct file_lock *, int));
+enum nlm_stats do_unlock __P((struct file_lock *));
+void send_granted __P((struct file_lock *, int));
+void siglock __P((void));
+void sigunlock __P((void));
+
+/* list of hosts we monitor */
+LIST_HEAD(hostlst_head, host);
+struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head);
+
+/* struct describing a lock */
+struct host {
+ LIST_ENTRY(host) hostlst;
+ char name[SM_MAXSTRLEN];
+ int refcnt;
+};
+
+void do_mon __P((char *));
+
+/*
+ * testlock(): inform the caller if the requested lock would be granted or not
+ * returns NULL if lock would granted, or pointer to the current nlm4_holder
+ * otherwise.
+ */
+
+struct nlm4_holder *
+testlock(lock, flags)
+ struct nlm4_lock *lock;
+ int flags;
+{
+ struct file_lock *fl;
+ fhandle_t filehandle;
+
+ /* convert lock to a local filehandle */
+ memcpy(&filehandle, lock->fh.n_bytes, sizeof(filehandle));
+
+ siglock();
+ /* search through the list for lock holder */
+ for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
+ fl = LIST_NEXT(fl, lcklst)) {
+ if (fl->status != LKST_LOCKED)
+ continue;
+ if (memcmp(&fl->filehandle, &filehandle, sizeof(filehandle)))
+ continue;
+ /* got it ! */
+ syslog(LOG_DEBUG, "test for %s: found lock held by %s",
+ lock->caller_name, fl->client_name);
+ sigunlock();
+ return (&fl->client);
+ }
+ /* not found */
+ sigunlock();
+ syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name);
+ return NULL;
+}
+
+/*
+ * getlock: try to aquire the lock.
+ * If file is already locked and we can sleep, put the lock in the list with
+ * status LKST_WAITING; it'll be processed later.
+ * Otherwise try to lock. If we're allowed to block, fork a child which
+ * will do the blocking lock.
+ */
+enum nlm_stats
+getlock(lckarg, rqstp, flags)
+ nlm4_lockargs * lckarg;
+ struct svc_req *rqstp;
+ int flags;
+{
+ struct file_lock *fl, *newfl;
+ enum nlm_stats retval;
+
+ if (grace_expired == 0 && lckarg->reclaim == 0)
+ return (flags & LOCK_V4) ?
+ nlm4_denied_grace_period : nlm_denied_grace_period;
+
+ /* allocate new file_lock for this request */
+ newfl = malloc(sizeof(struct file_lock));
+ if (newfl == NULL) {
+ syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
+ /* failed */
+ return (flags & LOCK_V4) ?
+ nlm4_denied_nolock : nlm_denied_nolocks;
+ }
+ if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) {
+ syslog(LOG_DEBUG, "recieved fhandle size %d, local size %d",
+ lckarg->alock.fh.n_len, (int)sizeof(fhandle_t));
+ }
+ memcpy(&newfl->filehandle, lckarg->alock.fh.n_bytes, sizeof(fhandle_t));
+ newfl->addr = (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf;
+ newfl->client.exclusive = lckarg->exclusive;
+ newfl->client.svid = lckarg->alock.svid;
+ newfl->client.oh.n_bytes = malloc(lckarg->alock.oh.n_len);
+ if (newfl->client.oh.n_bytes == NULL) {
+ syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
+ free(newfl);
+ return (flags & LOCK_V4) ?
+ nlm4_denied_nolock : nlm_denied_nolocks;
+ }
+ newfl->client.oh.n_len = lckarg->alock.oh.n_len;
+ memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes,
+ lckarg->alock.oh.n_len);
+ newfl->client.l_offset = lckarg->alock.l_offset;
+ newfl->client.l_len = lckarg->alock.l_len;
+ newfl->client_cookie.n_len = lckarg->cookie.n_len;
+ newfl->client_cookie.n_bytes = malloc(lckarg->cookie.n_len);
+ if (newfl->client_cookie.n_bytes == NULL) {
+ syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
+ free(newfl->client.oh.n_bytes);
+ free(newfl);
+ return (flags & LOCK_V4) ?
+ nlm4_denied_nolock : nlm_denied_nolocks;
+ }
+ memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes,
+ lckarg->cookie.n_len);
+ strncpy(newfl->client_name, lckarg->alock.caller_name, 128);
+ newfl->nsm_status = lckarg->state;
+ newfl->status = 0;
+ newfl->flags = flags;
+ siglock();
+ /* look for a lock rq from this host for this fh */
+ for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
+ fl = LIST_NEXT(fl, lcklst)) {
+ if (memcmp(&newfl->filehandle, &fl->filehandle,
+ sizeof(fhandle_t)) == 0) {
+ if (strcmp(newfl->client_name, fl->client_name) == 0 &&
+ newfl->client.svid == fl->client.svid) {
+ /* already locked by this host ??? */
+ sigunlock();
+ syslog(LOG_NOTICE, "duplicate lock from %s",
+ newfl->client_name);
+ lfree(newfl);
+ switch(fl->status) {
+ case LKST_LOCKED:
+ return (flags & LOCK_V4) ?
+ nlm4_granted : nlm_granted;
+ case LKST_WAITING:
+ case LKST_PROCESSING:
+ return (flags & LOCK_V4) ?
+ nlm4_blocked : nlm_blocked;
+ case LKST_DYING:
+ return (flags & LOCK_V4) ?
+ nlm4_denied : nlm_denied;
+ default:
+ syslog(LOG_NOTICE, "bad status %d",
+ fl->status);
+ return (flags & LOCK_V4) ?
+ nlm4_failed : nlm_denied;
+ }
+ }
+ /*
+ * We already have a lock for this file. Put this one
+ * in waiting state if allowed to block
+ */
+ if (lckarg->block) {
+ syslog(LOG_DEBUG, "lock from %s: already "
+ "locked, waiting",
+ lckarg->alock.caller_name);
+ newfl->status = LKST_WAITING;
+ LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst);
+ do_mon(lckarg->alock.caller_name);
+ sigunlock();
+ return (flags & LOCK_V4) ?
+ nlm4_blocked : nlm_blocked;
+ } else {
+ sigunlock();
+ syslog(LOG_DEBUG, "lock from %s: already "
+ "locked, failed",
+ lckarg->alock.caller_name);
+ lfree(newfl);
+ return (flags & LOCK_V4) ?
+ nlm4_denied : nlm_denied;
+ }
+ }
+ }
+ /* no entry for this file yet; add to list */
+ LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst);
+ /* do the lock */
+ retval = do_lock(newfl, lckarg->block);
+ switch (retval) {
+ case nlm4_granted:
+ /* case nlm_granted: is the same as nlm4_granted */
+ case nlm4_blocked:
+ /* case nlm_blocked: is the same as nlm4_blocked */
+ do_mon(lckarg->alock.caller_name);
+ break;
+ default:
+ lfree(newfl);
+ break;
+ }
+ sigunlock();
+ return retval;
+}
+
+/* unlock a filehandle */
+enum nlm_stats
+unlock(lck, flags)
+ nlm4_lock *lck;
+ int flags;
+{
+ struct file_lock *fl;
+ fhandle_t filehandle;
+ int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+
+ memcpy(&filehandle, lck->fh.n_bytes, sizeof(fhandle_t));
+ siglock();
+ for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
+ fl = LIST_NEXT(fl, lcklst)) {
+ if (strcmp(fl->client_name, lck->caller_name) ||
+ memcmp(&filehandle, &fl->filehandle, sizeof(fhandle_t)) ||
+ fl->client.oh.n_len != lck->oh.n_len ||
+ memcmp(fl->client.oh.n_bytes, lck->oh.n_bytes,
+ fl->client.oh.n_len) != 0 ||
+ fl->client.svid != lck->svid)
+ continue;
+ /* Got it, unlock and remove from the queue */
+ syslog(LOG_DEBUG, "unlock from %s: found struct, status %d",
+ lck->caller_name, fl->status);
+ switch (fl->status) {
+ case LKST_LOCKED:
+ err = do_unlock(fl);
+ break;
+ case LKST_WAITING:
+ /* remove from the list */
+ LIST_REMOVE(fl, lcklst);
+ lfree(fl);
+ break;
+ case LKST_PROCESSING:
+ /*
+ * being handled by a child; will clean up
+ * when the child exits
+ */
+ fl->status = LKST_DYING;
+ break;
+ case LKST_DYING:
+ /* nothing to do */
+ break;
+ default:
+ syslog(LOG_NOTICE, "unknow status %d for %s",
+ fl->status, fl->client_name);
+ }
+ sigunlock();
+ return err;
+ }
+ sigunlock();
+ /* didn't find a matching entry; log anyway */
+ syslog(LOG_NOTICE, "no matching entry for %s",
+ lck->caller_name);
+ return (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+}
+
+void
+lfree(fl)
+ struct file_lock *fl;
+{
+ free(fl->client.oh.n_bytes);
+ free(fl->client_cookie.n_bytes);
+ free(fl);
+}
+
+void
+sigchild_handler(sig)
+ int sig;
+{
+ int status;
+ pid_t pid;
+ struct file_lock *fl;
+
+ while (1) {
+ pid = wait4(-1, &status, WNOHANG, NULL);
+ if (pid == -1) {
+ if (errno != ECHILD)
+ syslog(LOG_NOTICE, "wait failed: %s",
+ strerror(errno));
+ else
+ syslog(LOG_DEBUG, "wait failed: %s",
+ strerror(errno));
+ return;
+ }
+ if (pid == 0) {
+ /* no more child to handle yet */
+ return;
+ }
+ /*
+ * if we're here we have a child that exited
+ * Find the associated file_lock.
+ */
+ for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
+ fl = LIST_NEXT(fl, lcklst)) {
+ if (pid == fl->locker)
+ break;
+ }
+ if (pid != fl->locker) {
+ syslog(LOG_NOTICE, "unknow child %d", pid);
+ } else {
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ syslog(LOG_NOTICE, "child %d failed", pid);
+ /*
+ * can't do much here; we can't reply
+ * anything but OK for blocked locks
+ * Eventually the client will time out
+ * and retry.
+ */
+ do_unlock(fl);
+ return;
+ }
+
+ /* check lock status */
+ syslog(LOG_DEBUG, "processing child %d, status %d",
+ pid, fl->status);
+ switch(fl->status) {
+ case LKST_PROCESSING:
+ fl->status = LKST_LOCKED;
+ send_granted(fl, (fl->flags & LOCK_V4) ?
+ nlm4_granted : nlm_granted);
+ break;
+ case LKST_DYING:
+ do_unlock(fl);
+ break;
+ default:
+ syslog(LOG_NOTICE, "bad lock status (%d) for"
+ " child %d", fl->status, pid);
+ }
+ }
+ }
+}
+
+/*
+ *
+ * try to aquire the lock described by fl. Eventually fock a child to do a
+ * blocking lock if allowed and required.
+ */
+
+enum nlm_stats
+do_lock(fl, block)
+ struct file_lock *fl;
+ int block;
+{
+ int lflags, error;
+ struct stat st;
+
+ fl->fd = fhopen(&fl->filehandle, O_RDWR);
+ if (fl->fd < 0) {
+ switch (errno) {
+ case ESTALE:
+ error = nlm4_stale_fh;
+ break;
+ case EROFS:
+ error = nlm4_rofs;
+ break;
+ default:
+ error = nlm4_failed;
+ }
+ if ((fl->flags & LOCK_V4) == 0)
+ error = nlm_denied;
+ syslog(LOG_NOTICE, "fhopen failed (from %s): %s",
+ fl->client_name, strerror(errno));
+ LIST_REMOVE(fl, lcklst);
+ return error;;
+ }
+ if (fstat(fl->fd, &st) < 0) {
+ syslog(LOG_NOTICE, "fstat failed (from %s): %s",
+ fl->client_name, strerror(errno));
+ }
+ syslog(LOG_DEBUG, "lock from %s for file%s%s: dev %d ino %d (uid %d), "
+ "flags %d",
+ fl->client_name, fl->client.exclusive ? " (exclusive)":"",
+ block ? " (block)":"",
+ st.st_dev, st.st_ino, st.st_uid, fl->flags);
+ lflags = LOCK_NB;
+ if (fl->client.exclusive == 0)
+ lflags |= LOCK_SH;
+ else
+ lflags |= LOCK_EX;
+ error = flock(fl->fd, lflags);
+ if (error != 0 && errno == EAGAIN && block) {
+ switch (fl->locker = fork()) {
+ case -1: /* fork failed */
+ syslog(LOG_NOTICE, "fork failed: %s", strerror(errno));
+ LIST_REMOVE(fl, lcklst);
+ close(fl->fd);
+ return (fl->flags & LOCK_V4) ?
+ nlm4_denied_nolock : nlm_denied_nolocks;
+ case 0:
+ /*
+ * Attempt a blocking lock. Will have to call
+ * NLM_GRANTED later.
+ */
+ setproctitle("%s", fl->client_name);
+ lflags &= ~LOCK_NB;
+ if(flock(fl->fd, lflags) != 0) {
+ syslog(LOG_NOTICE, "flock failed: %s",
+ strerror(errno));
+ exit(-1);
+ }
+ /* lock granted */
+ exit(0);
+ default:
+ syslog(LOG_DEBUG, "lock request from %s: forked %d",
+ fl->client_name, fl->locker);
+ fl->status = LKST_PROCESSING;
+ return (fl->flags & LOCK_V4) ?
+ nlm4_blocked : nlm_blocked;
+ }
+ }
+ /* non block case */
+ if (error != 0) {
+ switch (errno) {
+ case EAGAIN:
+ error = nlm4_denied;
+ break;
+ case ESTALE:
+ error = nlm4_stale_fh;
+ break;
+ case EROFS:
+ error = nlm4_rofs;
+ break;
+ default:
+ error = nlm4_failed;
+ }
+ if ((fl->flags & LOCK_V4) == 0)
+ error = nlm_denied;
+ if (errno != EAGAIN)
+ syslog(LOG_NOTICE, "flock for %s failed: %s",
+ fl->client_name, strerror(errno));
+ else syslog(LOG_DEBUG, "flock for %s failed: %s",
+ fl->client_name, strerror(errno));
+ LIST_REMOVE(fl, lcklst);
+ close(fl->fd);
+ return error;
+ }
+ fl->status = LKST_LOCKED;
+ return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+}
+
+void
+send_granted(fl, opcode)
+ struct file_lock *fl;
+ int opcode;
+{
+ CLIENT *cli;
+ static char dummy;
+ struct timeval timeo;
+ int success;
+ static struct nlm_res retval;
+ static struct nlm4_res retval4;
+
+ cli = get_client(fl->addr,
+ (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS);
+ if (cli == NULL) {
+ syslog(LOG_NOTICE, "failed to get CLIENT for %s",
+ fl->client_name);
+ /*
+ * We fail to notify remote that the lock has been granted.
+ * The client will timeout and retry, the lock will be
+ * granted at this time.
+ */
+ return;
+ }
+ timeo.tv_sec = 0;
+ timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */
+
+ if (fl->flags & LOCK_V4) {
+ static nlm4_testargs res;
+ res.cookie = fl->client_cookie;
+ res.exclusive = fl->client.exclusive;
+ res.alock.caller_name = fl->client_name;
+ res.alock.fh.n_len = sizeof(fhandle_t);
+ res.alock.fh.n_bytes = (char*)&fl->filehandle;
+ res.alock.oh = fl->client.oh;
+ res.alock.svid = fl->client.svid;
+ res.alock.l_offset = fl->client.l_offset;
+ res.alock.l_len = fl->client.l_len;
+ syslog(LOG_DEBUG, "sending v4 reply%s",
+ (fl->flags & LOCK_ASYNC) ? " (async)":"");
+ if (fl->flags & LOCK_ASYNC) {
+ success = clnt_call(cli, NLM4_GRANTED_MSG,
+ xdr_nlm4_testargs, &res, xdr_void, &dummy, timeo);
+ } else {
+ success = clnt_call(cli, NLM4_GRANTED,
+ xdr_nlm4_testargs, &res, xdr_nlm4_res,
+ &retval4, timeo);
+ }
+ } else {
+ static nlm_testargs res;
+
+ res.cookie = fl->client_cookie;
+ res.exclusive = fl->client.exclusive;
+ res.alock.caller_name = fl->client_name;
+ res.alock.fh.n_len = sizeof(fhandle_t);
+ res.alock.fh.n_bytes = (char*)&fl->filehandle;
+ res.alock.oh = fl->client.oh;
+ res.alock.svid = fl->client.svid;
+ res.alock.l_offset = fl->client.l_offset;
+ res.alock.l_len = fl->client.l_len;
+ syslog(LOG_DEBUG, "sending v1 reply%s",
+ (fl->flags & LOCK_ASYNC) ? " (async)":"");
+ if (fl->flags & LOCK_ASYNC) {
+ success = clnt_call(cli, NLM_GRANTED_MSG,
+ xdr_nlm_testargs, &res, xdr_void, &dummy, timeo);
+ } else {
+ success = clnt_call(cli, NLM_GRANTED,
+ xdr_nlm_testargs, &res, xdr_nlm_res,
+ &retval, timeo);
+ }
+ }
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted",
+ success, clnt_sperrno(success));
+
+}
+
+enum nlm_stats
+do_unlock(rfl)
+ struct file_lock *rfl;
+{
+ struct file_lock *fl;
+ int error;
+ int lockst;
+
+ /* unlock the file: closing is enouth ! */
+ if (close(rfl->fd) < 0) {
+ if (errno == ESTALE)
+ error = nlm4_stale_fh;
+ else
+ error = nlm4_failed;
+ if ((fl->flags & LOCK_V4) == 0)
+ error = nlm_denied;
+ syslog(LOG_NOTICE,
+ "close failed (from %s): %s",
+ rfl->client_name, strerror(errno));
+ } else {
+ error = (fl->flags & LOCK_V4) ?
+ nlm4_granted : nlm_granted;
+ }
+ LIST_REMOVE(rfl, lcklst);
+
+ /* process the next LKST_WAITING lock request for this fh */
+ for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
+ fl = LIST_NEXT(fl, lcklst)) {
+ if (fl->status != LKST_WAITING ||
+ memcmp(&rfl->filehandle, &fl->filehandle,
+ sizeof(fhandle_t)) != 0)
+ continue;
+
+ lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */
+ switch (lockst) {
+ case nlm4_granted:
+ /* case nlm_granted: same as nlm4_granted */
+ send_granted(fl, (fl->flags & LOCK_V4) ?
+ nlm4_granted : nlm_granted);
+ break;
+ case nlm4_blocked:
+ /* case nlm_blocked: same as nlm4_blocked */
+ break;
+ default:
+ lfree(fl);
+ break;
+ }
+ break;
+ }
+ return error;
+}
+
+void
+siglock()
+{
+ sigset_t block;
+
+ sigemptyset(&block);
+ sigaddset(&block, SIGCHLD);
+
+ if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) {
+ syslog(LOG_WARNING, "siglock failed: %s", strerror(errno));
+ }
+}
+
+void
+sigunlock()
+{
+ sigset_t block;
+
+ sigemptyset(&block);
+ sigaddset(&block, SIGCHLD);
+
+ if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) {
+ syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno));
+ }
+}
+
+/* monitor a host through rpc.statd, and keep a ref count */
+void
+do_mon(hostname)
+ char *hostname;
+{
+ struct host *hp;
+ struct mon my_mon;
+ struct sm_stat_res res;
+ int retval;
+
+ for (hp = LIST_FIRST(&hostlst_head); hp != NULL;
+ hp = LIST_NEXT(hp, hostlst)) {
+ if (strcmp(hostname, hp->name) == 0) {
+ /* already monitored, just bump refcnt */
+ hp->refcnt++;
+ return;
+ }
+ }
+ /* not found, have to create an entry for it */
+ hp = malloc(sizeof(struct host));
+ strncpy(hp->name, hostname, SM_MAXSTRLEN);
+ hp->refcnt = 1;
+ syslog(LOG_DEBUG, "monitoring host %s",
+ hostname);
+ memset(&my_mon, 0, sizeof(my_mon));
+ my_mon.mon_id.mon_name = hp->name;
+ my_mon.mon_id.my_id.my_name = "localhost";
+ my_mon.mon_id.my_id.my_prog = NLM_PROG;
+ my_mon.mon_id.my_id.my_vers = NLM_SM;
+ my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
+ if ((retval =
+ callrpc("localhost", SM_PROG, SM_VERS, SM_MON, xdr_mon,
+ (char*)&my_mon, xdr_sm_stat_res, (char*)&res)) != 0) {
+ syslog(LOG_WARNING, "rpc to statd failed: %s",
+ clnt_sperrno((enum clnt_stat)retval));
+ free(hp);
+ return;
+ }
+ if (res.res_stat == stat_fail) {
+ syslog(LOG_WARNING, "statd failed");
+ free(hp);
+ return;
+ }
+ LIST_INSERT_HEAD(&hostlst_head, hp, hostlst);
+}
+
+void
+notify(hostname, state)
+ char *hostname;
+ int state;
+{
+ struct file_lock *fl, *next_fl;
+ int err;
+ syslog(LOG_DEBUG, "notify from %s, new state %d", hostname, state);
+ /* search all lock for this host; if status changed, release the lock */
+ siglock();
+ for (fl = LIST_FIRST(&lcklst_head); fl != NULL; fl = next_fl) {
+ next_fl = LIST_NEXT(fl, lcklst);
+ if (strcmp(hostname, fl->client_name) == 0 &&
+ fl->nsm_status != state) {
+ syslog(LOG_DEBUG, "state %d, nsm_state %d, unlocking",
+ fl->status, fl->nsm_status);
+ switch(fl->status) {
+ case LKST_LOCKED:
+ err = do_unlock(fl);
+ if (err != nlm_granted)
+ syslog(LOG_DEBUG,
+ "notify: unlock failed for %s (%d)",
+ hostname, err);
+ break;
+ case LKST_WAITING:
+ LIST_REMOVE(fl, lcklst);
+ lfree(fl);
+ break;
+ case LKST_PROCESSING:
+ fl->status = LKST_DYING;
+ break;
+ case LKST_DYING:
+ break;
+ default:
+ syslog(LOG_NOTICE, "unknow status %d for %s",
+ fl->status, fl->client_name);
+ }
+ }
+ }
+ sigunlock();
+}
diff --git a/usr.sbin/rpc.lockd/lockd_lock.h b/usr.sbin/rpc.lockd/lockd_lock.h
new file mode 100644
index 0000000..5b0232f
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd_lock.h
@@ -0,0 +1,21 @@
+/* $NetBSD: lockd_lock.h,v 1.2 2000/06/09 14:00:54 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/* Headers and function declarations for file-locking utilities */
+
+struct nlm4_holder * testlock __P((struct nlm4_lock *, int));
+
+enum nlm_stats getlock __P((nlm4_lockargs *, struct svc_req *, int));
+enum nlm_stats unlock __P((nlm4_lock *, int));
+void notify __P((char *, int));
+
+/* flags for testlock, getlock & unlock */
+#define LOCK_ASYNC 0x01 /* async version (getlock only) */
+#define LOCK_V4 0x02 /* v4 version */
+#define LOCK_MON 0x04 /* monitored lock (getlock only) */
+#define LOCK_CANCEL 0x08 /* cancel, not unlock request (unlock only) */
+
+/* callbacks from lock_proc.c */
+void transmit_result __P((int, nlm_res *, struct sockaddr *));
+void transmit4_result __P((int, nlm4_res *, struct sockaddr *));
+CLIENT *get_client __P((struct sockaddr *, rpcvers_t));
diff --git a/usr.sbin/rpc.lockd/rpc.lockd.8 b/usr.sbin/rpc.lockd/rpc.lockd.8
index 2438aacf..e87f5bb 100644
--- a/usr.sbin/rpc.lockd/rpc.lockd.8
+++ b/usr.sbin/rpc.lockd/rpc.lockd.8
@@ -1,4 +1,5 @@
-.\" -*- nroff -*-
+.\" $NetBSD: rpc.lockd.8,v 1.5 2000/06/09 18:51:47 cgd Exp $
+.\" $FreeBSD$
.\"
.\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk
.\" All rights reserved.
@@ -31,7 +32,6 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" $FreeBSD$
.\"
.Dd September 24, 1995
.Dt RPC.LOCKD 8
@@ -41,34 +41,64 @@
.Nd NFS file locking daemon
.Sh SYNOPSIS
.Nm
-.Op Fl d Op Ar debug_level
+.Op Fl d Ar debug_level
+.Op Fl g Ar grace period
.Sh DESCRIPTION
-.Nm Rpc.lockd
-is a daemon which provides file- and record-locking services in an NFS
-environment.
+The
+.Nm
+daemon provides monitored and unmonitored file and record locking services
+in an NFS environment.
+To monitor the status of hosts requesting locks,
+the locking daemon typically operates in conjunction
+with
+.Xr rpc.statd 8 .
.Pp
-The following option is available:
+Options and operands available for
+.Nm :
.Bl -tag -width indent
.It Fl d
-Cause debugging information to be written to syslog, recording
-all RPC transactions to the daemon. These messages are logged with level
-LOG_DEBUG and facility LOG_DAEMON. If debug_level is not specified,
-level 1 is assumed, giving one log line per protocol operation. Higher
+The
+.Fl d
+option causes debugging information to be written to syslog, recording
+all RPC transactions to the daemon.
+These messages are logged with level
+.Dv LOG_DEBUG
+and facility
+.Dv LOG_DAEMON .
+Specifying a
+.Ar debug_level
+of 1 results
+in the generation of one log line per protocol operation.
+Higher
debug levels can be specified, causing display of operation arguments
and internal operations of the daemon.
+.It Fl g
+The
+.Fl g
+option allow to specify the
+.Ar grace period ,
+in seconds.
+During the grace period
+.Nm
+only accepts requests from hosts which are reinitialising locks which
+existed before the server restart.
+Default is 30 seconds.
.El
.Pp
Error conditions are logged to syslog, irrespective of the debug level,
-using log level LOG_ERR and facility LOG_DAEMON.
+using log level
+.Dv LOG_ERR
+and facility
+.Dv LOG_DAEMON .
.Pp
The
.Nm
daemon must NOT be invoked by
.Xr inetd 8
because the protocol assumes that the daemon will run from system start time.
-Instead, it should be run from
-.Xr rc 8
-after the network has been started.
+Instead, it should be configured in
+.Xr rc.conf 5
+to run at system startup.
.Sh FILES
.Bl -tag -width /usr/include/rpcsvc/nlm_prot.x -compact
.It Pa /usr/include/rpcsvc/nlm_prot.x
@@ -76,21 +106,32 @@ RPC protocol specification for the network lock manager protocol.
.El
.Sh SEE ALSO
.Xr syslog 3 ,
-.Xr rc 8 ,
+.Xr rc.conf 5 ,
.Xr rpc.statd 8
.Sh BUGS
The current implementation provides only the server side of the protocol
-(ie. clients running other OS types can establish locks on a
+(i.e., clients running other OS types can establish locks on a
.Fx
+/
+.Nx
fileserver,
but there is currently no means for a
.Fx
+/
+.Nx
client to establish locks).
.Pp
-Versions 1, 2 and 3 of the protocol are supported. However, only versions
-2 (Unix systems) and 3 (PC-NFS clients) seem to be in common use - the version
-1 support has not been tested due to the lack of version 1 clients against
-which to test.
+The current implementation serialises locks requests that could be shared.
.Sh STANDARDS
-The implementation is based on the specification in X/Open CAE Specification
-C218, "Protocols for X/Open PC Interworking: XNFS, Issue 4", ISBN 1 872630 66 9
+The implementation is based on the specification in
+.Rs
+.%B "X/Open CAE Specification C218"
+.%T "Protocols for X/Open PC Interworking: XNFS, Issue 4"
+.%O ISBN 1 872630 66 9
+.Re
+.Sh HISTORY
+A version of
+.Nm
+appeared in
+.Tn SunOS
+4.
diff --git a/usr.sbin/rpc.lockd/test.c b/usr.sbin/rpc.lockd/test.c
index 0dd8eae..a751e5c 100644
--- a/usr.sbin/rpc.lockd/test.c
+++ b/usr.sbin/rpc.lockd/test.c
@@ -1,14 +1,17 @@
+/* $NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $ */
+
+#include <sys/cdefs.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nlm_prot.h>
#ifndef lint
#if 0
static char sccsid[] = "from: @(#)nlm_prot.x 1.8 87/09/21 Copyr 1987 Sun Micro";
static char sccsid[] = "from: * @(#)nlm_prot.x 2.1 88/08/01 4.0 RPCSRC";
+#else
+__RCSID("$NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $");
+static const char rcsid[] = "$FreeBSD$";
#endif
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
-
-#include <rpc/rpc.h>
-#include <rpcsvc/nlm_prot.h>
+#endif /* not lint */
/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 0, 0 };
@@ -304,63 +307,59 @@ nlm_free_all_3(argp, clnt)
int main(int argc, char **argv)
{
- CLIENT *cli;
- nlm_res res_block;
- nlm_res *out;
- nlm_lockargs arg;
- struct timeval tim;
-
- printf("Creating client for host %s\n", argv[1]);
- cli = clnt_create(argv[1], NLM_PROG, NLM_VERS, "udp");
- if (!cli)
- {
- printf("Failed to create client\n");
- exit(1);
- }
-
-
- clnt_control(cli, CLGET_TIMEOUT, &tim);
- printf("Default timeout was %d.%d\n", tim.tv_sec, tim.tv_usec);
- tim.tv_usec = -1;
- tim.tv_sec = -1;
- clnt_control(cli, CLSET_TIMEOUT, &tim);
- clnt_control(cli, CLGET_TIMEOUT, &tim);
- printf("timeout now %d.%d\n", tim.tv_sec, tim.tv_usec);
-
-
- arg.cookie.n_len = 4;
- arg.cookie.n_bytes = "hello";
- arg.block = 0;
- arg.exclusive = 0;
- arg.reclaim = 0;
- arg.state = 0x1234;
- arg.alock.caller_name = "localhost";
- arg.alock.fh.n_len = 32;
- arg.alock.fh.n_bytes = "\x04\x04\x02\x00\x01\x00\x00\x00\x0c\x00\x00\x00\xff\xff\xff\xd0\x16\x00\x00\x5b\x7c\xff\xff\xff\xec\x2f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x54\xef\xbf\xd7\x94";
- arg.alock.oh.n_len = 8;
- arg.alock.oh.n_bytes = "\x00\x00\x02\xff\xff\xff\xd3";
- arg.alock.svid = 0x5678;
- arg.alock.l_offset = 0;
- arg.alock.l_len = 100;
-
- res_block.stat.stat = nlm_granted;
- res_block.cookie.n_bytes = "hello";
- res_block.cookie.n_len = 5;
+ CLIENT *cli;
+ nlm_res res_block;
+ nlm_res *out;
+ nlm_lockargs arg;
+ struct timeval tim;
+
+ printf("Creating client for host %s\n", argv[1]);
+ cli = clnt_create(argv[1], NLM_PROG, NLM_VERS, "udp");
+ if (!cli) {
+ errx(1, "Failed to create client\n");
+ /* NOTREACHED */
+ }
+ clnt_control(cli, CLGET_TIMEOUT, &tim);
+ printf("Default timeout was %d.%d\n", tim.tv_sec, tim.tv_usec);
+ tim.tv_usec = -1;
+ tim.tv_sec = -1;
+ clnt_control(cli, CLSET_TIMEOUT, &tim);
+ clnt_control(cli, CLGET_TIMEOUT, &tim);
+ printf("timeout now %d.%d\n", tim.tv_sec, tim.tv_usec);
+
+
+ arg.cookie.n_len = 4;
+ arg.cookie.n_bytes = "hello";
+ arg.block = 0;
+ arg.exclusive = 0;
+ arg.reclaim = 0;
+ arg.state = 0x1234;
+ arg.alock.caller_name = "localhost";
+ arg.alock.fh.n_len = 32;
+ arg.alock.fh.n_bytes = "\x04\x04\x02\x00\x01\x00\x00\x00\x0c\x00\x00\x00\xff\xff\xff\xd0\x16\x00\x00\x5b\x7c\xff\xff\xff\xec\x2f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x54\xef\xbf\xd7\x94";
+ arg.alock.oh.n_len = 8;
+ arg.alock.oh.n_bytes = "\x00\x00\x02\xff\xff\xff\xd3";
+ arg.alock.svid = 0x5678;
+ arg.alock.l_offset = 0;
+ arg.alock.l_len = 100;
+
+ res_block.stat.stat = nlm_granted;
+ res_block.cookie.n_bytes = "hello";
+ res_block.cookie.n_len = 5;
#if 0
- if (nlm_lock_res_1(&res_block, cli)) printf("Success!\n");
- else printf("Fail\n");
+ if (nlm_lock_res_1(&res_block, cli))
+ printf("Success!\n");
+ else
+ printf("Fail\n");
#else
- if (out = nlm_lock_msg_1(&arg, cli))
- {
- printf("Success!\n");
- printf("out->stat = %d", out->stat);
- }
- else
- {
- printf("Fail\n");
- }
+ if (out = nlm_lock_msg_1(&arg, cli)) {
+ printf("Success!\n");
+ printf("out->stat = %d", out->stat);
+ } else {
+ printf("Fail\n");
+ }
#endif
- return 0;
+ return 0;
}
OpenPOWER on IntegriCloud