diff options
| author | Alfred Perlstein <alfred@FreeBSD.org> | 2001-03-19 12:50:13 +0000 |
|---|---|---|
| committer | Alfred Perlstein <alfred@FreeBSD.org> | 2001-03-19 12:50:13 +0000 |
| commit | 8360efbd6c932013ffdb2f83d2f2de4278febb5e (patch) | |
| tree | b842b4bf2665ef953be005b10013a2f3daf323c3 /usr.sbin/rpc.lockd | |
| parent | 1ac2b9fe972a0c73729565f8310fa6eba55718c4 (diff) | |
Notes
Diffstat (limited to 'usr.sbin/rpc.lockd')
| -rw-r--r-- | usr.sbin/rpc.lockd/Makefile | 22 | ||||
| -rw-r--r-- | usr.sbin/rpc.lockd/lock_proc.c | 1294 | ||||
| -rw-r--r-- | usr.sbin/rpc.lockd/lockd.c | 219 | ||||
| -rw-r--r-- | usr.sbin/rpc.lockd/lockd.h | 17 | ||||
| -rw-r--r-- | usr.sbin/rpc.lockd/lockd_lock.c | 759 | ||||
| -rw-r--r-- | usr.sbin/rpc.lockd/lockd_lock.h | 21 | ||||
| -rw-r--r-- | usr.sbin/rpc.lockd/rpc.lockd.8 | 87 | ||||
| -rw-r--r-- | usr.sbin/rpc.lockd/test.c | 111 |
8 files changed, 2380 insertions, 150 deletions
diff --git a/usr.sbin/rpc.lockd/Makefile b/usr.sbin/rpc.lockd/Makefile index 19a6565a4048..ba6b3c77dbc1 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 000000000000..8b08fd0f49eb --- /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 ac328569126e..e0ac5cf52994 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; -void nlm_prog_1 __P((struct svc_req *, SVCXPRT *)); -void nlm_prog_3 __P((struct svc_req *, SVCXPRT *)); -static void usage __P((void)); +int grace_expired; -int debug_level = 0; /* Zero means no debugging syslog() calls */ +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)); + +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; + 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); - 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; - } + /* + * Check if IPv6 support is present. + */ + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) + maxindex = 2; + else { + close(s); + maxindex = 4; + } - (void)pmap_unset(NLM_PROG, NLM_VERS); - (void)pmap_unset(NLM_PROG, NLM_VERSX); + for (i = 0; i < maxindex; i++) { + nconf = getnetconfigent(transports[i]); + if (nconf == NULL) + errx(1, "cannot get udp netconf."); - 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 = 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); + } - 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, "cannot fork"); + /* NOTREACHED */ + } - /* 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"); + 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); + 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; +{ + 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 39586bfd77ae..42f14f1f2956 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 000000000000..998a2009e82c --- /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 000000000000..5b0232f69d13 --- /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 2438aacf85d0..e87f5bb92c00 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 0dd8eaefa09e..a751e5c6f4e4 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); - } + 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); - 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; + 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; + 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; } |
