Import Jan 15 version of Andrew Gordon <andrew.gordon@net-tel.co.uk>'s

stub lockd.

This implements just the protocol, but does not interact with the kernel.
It says "Yes!" to all requests.  This is useful if you have people using
tools that do locking for no reason (eg: some PC NFS systems running some
Microsoft products) and will happily report they couldn't lock the file
and merrily proceed anyway.  Running this will not change the reliability of
sharing files, it'll just keep it out of everybody's face.
This commit is contained in:
Peter Wemm 1996-02-17 15:11:29 +00:00
parent add3bbdaef
commit 503d2aa8a2
14 changed files with 2962 additions and 0 deletions

14
sbin/rpc.lockd/Makefile Normal file
View File

@ -0,0 +1,14 @@
# $Id$
PROG = rpc.lockd
SRCS = lockd.c procs.c
MAN8 = rpc.lockd.8
DPADD= ${LIBRPCSVC}
LDADD= -lrpcsvc
.include <bsd.prog.mk>
test: test.c
cc -o test test.c -lrpcsvc

58
sbin/rpc.lockd/handles.c Normal file
View File

@ -0,0 +1,58 @@
/*
* 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 "lockd.h"
/* ------------------------------------------------------------------------- */
/*
* need to find all fds in use by a host and free them when host crashes
(need not be efficient)
* need to find fd corresponding to <inode, device>
*/
typedef struct fdinfo
{
int fd; /* The file descriptor itself */
int ref_count; /* Count of hosts using the fd - fd is */
/* closed when this reaches zero */
ino_t inode_no; /* The inode number of this file. */
dev_t device; /* device on which the file lives. */
struct fdinfo *next; /* Chain of FdInfo structures */
struct fdinfo *prev;
} FdInfo;

325
sbin/rpc.lockd/lockd.c Normal file
View File

@ -0,0 +1,325 @@
/*
* 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.
*
*/
/* 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 "lockd.h"
int debug_level = 0; /* Zero means no debugging syslog() calls */
static void nlm_prog_1();
static void nlm_prog_3();
main(int argc, char **argv)
{
SVCXPRT *transp;
if (argc > 1)
{
if (strncmp(argv[1], "-d", 2))
{
fprintf(stderr, "Usage: rpc.lockd [-d [<debuglevel>]]\n");
exit(1);
}
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)
{
(void)fprintf(stderr, "cannot create udp service.\n");
exit(1);
}
if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_UDP))
{
(void)fprintf(stderr, "unable to register (NLM_PROG, NLM_VERS, udp).\n");
exit(1);
}
if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_UDP))
{
(void)fprintf(stderr, "unable to register (NLM_PROG, NLM_VERSX, udp).\n");
exit(1);
}
transp = svctcp_create(RPC_ANYSOCK, 0, 0);
if (transp == NULL)
{
(void)fprintf(stderr, "cannot create tcp service.\n");
exit(1);
}
if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_TCP))
{
(void)fprintf(stderr, "unable to register (NLM_PROG, NLM_VERS, tcp).\n");
exit(1);
}
if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_TCP))
{
(void)fprintf(stderr, "unable to register (NLM_PROG, NLM_VERSX, tcp).\n");
exit(1);
}
/* Note that it is NOT sensible to run this program from inetd - the */
/* protocol assumes that it will run immediately at boot time. */
daemon(0, 0);
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);
}
static void
nlm_prog_1(struct svc_req *rqstp, SVCXPRT *transp)
{
union
{
struct nlm_testargs nlm_test_1_arg;
struct nlm_lockargs nlm_lock_1_arg;
struct nlm_cancargs nlm_cancel_1_arg;
struct nlm_unlockargs nlm_unlock_1_arg;
struct nlm_testargs nlm_granted_1_arg;
struct nlm_testargs nlm_test_msg_1_arg;
struct nlm_lockargs nlm_lock_msg_1_arg;
struct nlm_cancargs nlm_cancel_msg_1_arg;
struct nlm_unlockargs nlm_unlock_msg_1_arg;
struct nlm_testargs nlm_granted_msg_1_arg;
nlm_testres nlm_test_res_1_arg;
nlm_res nlm_lock_res_1_arg;
nlm_res nlm_cancel_res_1_arg;
nlm_res nlm_unlock_res_1_arg;
nlm_res nlm_granted_res_1_arg;
} argument;
char *result;
bool_t (*xdr_argument)(), (*xdr_result)();
char *(*local)();
switch (rqstp->rq_proc)
{
case NULLPROC:
(void)svc_sendreply(transp, xdr_void, (char *)NULL);
return;
case NLM_TEST:
xdr_argument = xdr_nlm_testargs;
xdr_result = xdr_nlm_testres;
local = (char *(*)()) nlm_test_1;
break;
case NLM_LOCK:
xdr_argument = xdr_nlm_lockargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_lock_1;
break;
case NLM_CANCEL:
xdr_argument = xdr_nlm_cancargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_cancel_1;
break;
case NLM_UNLOCK:
xdr_argument = xdr_nlm_unlockargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_unlock_1;
break;
case NLM_GRANTED:
xdr_argument = xdr_nlm_testargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_granted_1;
break;
case NLM_TEST_MSG:
xdr_argument = xdr_nlm_testargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_test_msg_1;
break;
case NLM_LOCK_MSG:
xdr_argument = xdr_nlm_lockargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_lock_msg_1;
break;
case NLM_CANCEL_MSG:
xdr_argument = xdr_nlm_cancargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_cancel_msg_1;
break;
case NLM_UNLOCK_MSG:
xdr_argument = xdr_nlm_unlockargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_unlock_msg_1;
break;
case NLM_GRANTED_MSG:
xdr_argument = xdr_nlm_testargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_granted_msg_1;
break;
case NLM_TEST_RES:
xdr_argument = xdr_nlm_testres;
xdr_result = xdr_void;
local = (char *(*)()) nlm_test_res_1;
break;
case NLM_LOCK_RES:
xdr_argument = xdr_nlm_res;
xdr_result = xdr_void;
local = (char *(*)()) nlm_lock_res_1;
break;
case NLM_CANCEL_RES:
xdr_argument = xdr_nlm_res;
xdr_result = xdr_void;
local = (char *(*)()) nlm_cancel_res_1;
break;
case NLM_UNLOCK_RES:
xdr_argument = xdr_nlm_res;
xdr_result = xdr_void;
local = (char *(*)()) nlm_unlock_res_1;
break;
case NLM_GRANTED_RES:
xdr_argument = xdr_nlm_res;
xdr_result = xdr_void;
local = (char *(*)()) nlm_granted_res_1;
break;
default:
svcerr_noproc(transp);
return;
}
bzero((char *)&argument, sizeof(argument));
if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument))
{
syslog(LOG_ERR, "RPC received with invalid arguments");
svcerr_decode(transp);
return;
}
result = (*local)(&argument, rqstp);
if (result != NULL && !svc_sendreply(transp, xdr_result, result))
{
svcerr_systemerr(transp);
}
if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument))
{
syslog(LOG_ERR, "unable to free arguments");
}
}
static void
nlm_prog_3(struct svc_req *rqstp, SVCXPRT *transp)
{
union
{
nlm_shareargs nlm_share_3_arg;
nlm_shareargs nlm_unshare_3_arg;
nlm_lockargs nlm_nm_lock_3_arg;
nlm_notify nlm_free_all_3_arg;
} argument;
char *result;
bool_t (*xdr_argument)(), (*xdr_result)();
char *(*local)();
switch (rqstp->rq_proc)
{
case NULLPROC:
(void)svc_sendreply(transp, xdr_void, (char *)NULL);
return;
case NLM_SHARE:
xdr_argument = xdr_nlm_shareargs;
xdr_result = xdr_nlm_shareres;
local = (char *(*)()) nlm_share_3;
break;
case NLM_UNSHARE:
xdr_argument = xdr_nlm_shareargs;
xdr_result = xdr_nlm_shareres;
local = (char *(*)()) nlm_unshare_3;
break;
case NLM_NM_LOCK:
xdr_argument = xdr_nlm_lockargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_nm_lock_3;
break;
case NLM_FREE_ALL:
xdr_argument = xdr_nlm_notify;
xdr_result = xdr_void;
local = (char *(*)()) nlm_free_all_3;
break;
default:
svcerr_noproc(transp);
return;
}
bzero((char *)&argument, sizeof(argument));
if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument))
{
syslog(LOG_ERR, "RPC received with invalid arguments");
svcerr_decode(transp);
return;
}
result = (*local)(&argument, rqstp);
if (result != NULL && !svc_sendreply(transp, xdr_result, result))
{
svcerr_systemerr(transp);
}
if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument))
{
syslog(LOG_ERR, "unable to free arguments");
}
}

44
sbin/rpc.lockd/lockd.h Normal file
View File

@ -0,0 +1,44 @@
/*
* 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 <stdio.h>
#include <rpc/rpc.h>
#include <syslog.h>
#include <rpcsvc/sm_inter.h> /* protocol to talk to rpc.statd */
#include <rpcsvc/nlm_prot.h> /* The protocol we are implementing */
/* global variables ------------------------------------------------------- */
extern int debug_level;

588
sbin/rpc.lockd/procs.c Normal file
View File

@ -0,0 +1,588 @@
/*
* 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 "lockd.h"
#include <sys/param.h> /* for MAXHOSTNAMELEN */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define CLIENT_CACHE_SIZE 64 /* No. of client sockets cached */
#define CLIENT_CACHE_LIFETIME 120 /* In seconds */
/* 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(char *fun_name, struct svc_req *req)
{
struct sockaddr_in *addr;
struct hostent *host;
char hostname_buf[40];
addr = svc_getcaller(req->rq_xprt);
host = gethostbyaddr((char *)&(addr->sin_addr), addr->sin_len, AF_INET);
if (host)
{
strncpy(hostname_buf, host->h_name, sizeof(hostname_buf));
hostname_buf[sizeof(hostname_buf) -1] = '\0';
}
else /* No hostname available - print raw address */
{
strcpy(hostname_buf, inet_ntoa(addr->sin_addr));
}
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 in_addr clnt_cache_addr[CLIENT_CACHE_SIZE];
static int clnt_cache_next_to_use = 0;
static CLIENT *get_client(struct sockaddr_in *host_addr)
{
CLIENT *client;
int sock_no;
struct timeval retry_time, time_now;
int i;
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 && !memcmp(&clnt_cache_addr[i], &host_addr->sin_addr,
sizeof(struct in_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;
}
/* Create the new client handle */
sock_no = RPC_ANYSOCK;
retry_time.tv_sec = 5;
retry_time.tv_usec = 0;
host_addr->sin_port = 0; /* Force consultation with portmapper */
client = clntudp_create(host_addr, NLM_PROG, NLM_VERS, retry_time, &sock_no);
if (!client)
{
syslog(LOG_ERR, clnt_spcreateerror("clntudp_create"));
syslog(LOG_ERR, "Unable to return result to %s",
inet_ntoa(host_addr->sin_addr));
return NULL;
}
/* Success - update the cache entry */
clnt_cache_ptr[clnt_cache_next_to_use] = client;
clnt_cache_addr[clnt_cache_next_to_use] = host_addr->sin_addr;
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, &retry_time);
if (debug_level > 3) syslog(LOG_DEBUG, "Created CLIENT* for %s",
inet_ntoa(host_addr->sin_addr));
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
*/
static void transmit_result(int opcode, nlm_res *result, struct svc_req *req)
{
static char dummy;
struct sockaddr_in *addr;
CLIENT *cli;
int success;
struct timeval timeo;
addr = svc_getcaller(req->rq_xprt);
if (cli = get_client(addr))
{
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\n", success);
}
}
/* ------------------------------------------------------------------------- */
/*
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(nlm_testargs *arg, struct svc_req *rqstp)
{
static nlm_testres res;
if (debug_level) log_from_addr("nlm_test", rqstp);
/* 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;
res.stat.stat = nlm_granted;
return (&res);
}
void *nlm_test_msg_1(nlm_testargs *arg, struct svc_req *rqstp)
{
nlm_testres res;
static char dummy;
struct sockaddr_in *addr;
CLIENT *cli;
int success;
struct timeval timeo;
if (debug_level) log_from_addr("nlm_test_msg", rqstp);
res.cookie = arg->cookie;
res.stat.stat = nlm_granted;
/* nlm_test has different result type to the other operations, so */
/* can't use transmit_result() in this case */
addr = svc_getcaller(rqstp->rq_xprt);
if (cli = get_client(addr))
{
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\n", success);
}
return (NULL);
}
/* nlm_lock ---------------------------------------------------------------- */
/*
Purposes: Establish a lock
Returns: granted, denied or blocked
Notes: *** grace period support missing
*/
nlm_res *nlm_lock_1(nlm_lockargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
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 = nlm_granted;
return (&res);
}
void *nlm_lock_msg_1(nlm_lockargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
if (debug_level) log_from_addr("nlm_lock_msg", rqstp);
res.cookie = arg->cookie;
res.stat.stat = nlm_granted;
transmit_result(NLM_LOCK_RES, &res, rqstp);
return (NULL);
}
/* nlm_cancel -------------------------------------------------------------- */
/*
Purpose: Cancel a blocked lock request
Returns: granted or denied
Notes:
*/
nlm_res *nlm_cancel_1(nlm_cancargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
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 = nlm_denied;
return (&res);
}
void *nlm_cancel_msg_1(nlm_cancargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
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 = nlm_denied;
transmit_result(NLM_CANCEL_RES, &res, rqstp);
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(nlm_unlockargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
if (debug_level) log_from_addr("nlm_unlock", rqstp);
res.stat.stat= nlm_granted;
res.cookie = arg->cookie;
return (&res);
}
void *nlm_unlock_msg_1(nlm_unlockargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
if (debug_level) log_from_addr("nlm_unlock_msg", rqstp);
res.stat.stat = nlm_granted;
res.cookie = arg->cookie;
transmit_result(NLM_UNLOCK_RES, &res, rqstp);
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(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(nlm_testargs *arg, struct svc_req *rqstp)
{
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, rqstp);
return (NULL);
}
/* nlm_test_res ------------------------------------------------------------ */
/*
Purpose: Accept result from earlier nlm_test_msg() call
Returns: Nothing
*/
void *nlm_test_res_1(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(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(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(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(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(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(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(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(nlm_notify *arg, struct svc_req *rqstp)
{
static char dummy;
if (debug_level) log_from_addr("nlm_free_all", rqstp);
return (&dummy);
}

View File

@ -0,0 +1,94 @@
.\" -*- nroff -*-
.\"
.\" 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 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 AUTHOR 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.
.\"
.\"
.Dd September 24, 1995
.Dt RPC.LOCK 8
.Os
.Sh NAME
.Nm rpc.lock
.Nd NFS file locking daemon
.Sh SYNOPSIS
.Nm /sbin/rpc.lockd
.Op Fl d Op Ar debug_level
.Sh DESCRIPTION
.Nm rpc.rstatd
is a daemon which provides file- and record-locking services in an NFS
environment.
.Pp
Options and operands available for
.Nm rpc.lockd :
.Bl -tag -width Ds
.It Fl d
The
.Fl d
option causes debugging information to be written to syslog, recording
all RPC transations 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
debug levels can be specified, causing display of operation arguments
and internal operations of the daemon.
.El
.Pp
Error conditions are logged to syslog, irrespecive of the debug level,
using log level LOG_ERR and facility LOG_DAEMON.
.Pp
The
.Nm rpc.lockd
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.
.Sh FILES
.Bl -tag -width /usr/include/rpcsvc/nlm_prot.x -compact
.It Pa /usr/include/rpcsvc/nlm_prot.x
RPC protocol specification for the network lock manager protocol.
.El
.Sh SEE ALSO
.Xr rpc.statd 8 ,
.Xr rc 8 ,
.Xr syslog 3
.Sh BUGS
The current implementation provides only the server side of the protocol
(ie. clients running other OS types can establish locks on a FreeBSD fileserver,
but there is currently no means for a FreeBSD 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.
.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

358
sbin/rpc.lockd/test.c Normal file
View File

@ -0,0 +1,358 @@
#include <rpc/rpc.h>
#include <rpcsvc/nlm_prot.h>
#ifndef lint
/*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";*/
static char rcsid[] = "nlm_prot.x,v 1.1 1994/08/04 19:01:48 wollman Exp";
#endif /* not lint */
/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 0, 0 };
nlm_testres *
nlm_test_1(argp, clnt)
struct nlm_testargs *argp;
CLIENT *clnt;
{
static nlm_testres res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_TEST, xdr_nlm_testargs, argp, xdr_nlm_testres, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_res *
nlm_lock_1(argp, clnt)
struct nlm_lockargs *argp;
CLIENT *clnt;
{
enum clnt_stat st;
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (st = clnt_call(clnt, NLM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
printf("clnt_call returns %d\n", st);
clnt_perror(clnt, "humbug");
return (NULL);
}
return (&res);
}
nlm_res *
nlm_cancel_1(argp, clnt)
struct nlm_cancargs *argp;
CLIENT *clnt;
{
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_CANCEL, xdr_nlm_cancargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_res *
nlm_unlock_1(argp, clnt)
struct nlm_unlockargs *argp;
CLIENT *clnt;
{
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_UNLOCK, xdr_nlm_unlockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_res *
nlm_granted_1(argp, clnt)
struct nlm_testargs *argp;
CLIENT *clnt;
{
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_GRANTED, xdr_nlm_testargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
void *
nlm_test_msg_1(argp, clnt)
struct nlm_testargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_TEST_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_lock_msg_1(argp, clnt)
struct nlm_lockargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_LOCK_MSG, xdr_nlm_lockargs, argp, xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
clnt_perror(clnt, "nlm_lock_msg_1");
return (NULL);
}
return ((void *)&res);
}
void *
nlm_cancel_msg_1(argp, clnt)
struct nlm_cancargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_CANCEL_MSG, xdr_nlm_cancargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_unlock_msg_1(argp, clnt)
struct nlm_unlockargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_UNLOCK_MSG, xdr_nlm_unlockargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_granted_msg_1(argp, clnt)
struct nlm_testargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_GRANTED_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_test_res_1(argp, clnt)
nlm_testres *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_TEST_RES, xdr_nlm_testres, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_lock_res_1(argp, clnt)
nlm_res *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_LOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_cancel_res_1(argp, clnt)
nlm_res *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_CANCEL_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_unlock_res_1(argp, clnt)
nlm_res *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_UNLOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_granted_res_1(argp, clnt)
nlm_res *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_GRANTED_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
nlm_shareres *
nlm_share_3(argp, clnt)
nlm_shareargs *argp;
CLIENT *clnt;
{
static nlm_shareres res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_SHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_shareres *
nlm_unshare_3(argp, clnt)
nlm_shareargs *argp;
CLIENT *clnt;
{
static nlm_shareres res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_UNSHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_res *
nlm_nm_lock_3(argp, clnt)
nlm_lockargs *argp;
CLIENT *clnt;
{
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_NM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
void *
nlm_free_all_3(argp, clnt)
nlm_notify *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_FREE_ALL, xdr_nlm_notify, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
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);
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");
#else
if (out = nlm_lock_msg_1(&arg, cli))
{
printf("Success!\n");
printf("out->stat = %d", out->stat);
}
else
{
printf("Fail\n");
}
#endif
return 0;
}

View File

@ -0,0 +1,14 @@
# $Id$
PROG = rpc.lockd
SRCS = lockd.c procs.c
MAN8 = rpc.lockd.8
DPADD= ${LIBRPCSVC}
LDADD= -lrpcsvc
.include <bsd.prog.mk>
test: test.c
cc -o test test.c -lrpcsvc

View File

@ -0,0 +1,58 @@
/*
* 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 "lockd.h"
/* ------------------------------------------------------------------------- */
/*
* need to find all fds in use by a host and free them when host crashes
(need not be efficient)
* need to find fd corresponding to <inode, device>
*/
typedef struct fdinfo
{
int fd; /* The file descriptor itself */
int ref_count; /* Count of hosts using the fd - fd is */
/* closed when this reaches zero */
ino_t inode_no; /* The inode number of this file. */
dev_t device; /* device on which the file lives. */
struct fdinfo *next; /* Chain of FdInfo structures */
struct fdinfo *prev;
} FdInfo;

325
usr.sbin/rpc.lockd/lockd.c Normal file
View File

@ -0,0 +1,325 @@
/*
* 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.
*
*/
/* 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 "lockd.h"
int debug_level = 0; /* Zero means no debugging syslog() calls */
static void nlm_prog_1();
static void nlm_prog_3();
main(int argc, char **argv)
{
SVCXPRT *transp;
if (argc > 1)
{
if (strncmp(argv[1], "-d", 2))
{
fprintf(stderr, "Usage: rpc.lockd [-d [<debuglevel>]]\n");
exit(1);
}
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)
{
(void)fprintf(stderr, "cannot create udp service.\n");
exit(1);
}
if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_UDP))
{
(void)fprintf(stderr, "unable to register (NLM_PROG, NLM_VERS, udp).\n");
exit(1);
}
if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_UDP))
{
(void)fprintf(stderr, "unable to register (NLM_PROG, NLM_VERSX, udp).\n");
exit(1);
}
transp = svctcp_create(RPC_ANYSOCK, 0, 0);
if (transp == NULL)
{
(void)fprintf(stderr, "cannot create tcp service.\n");
exit(1);
}
if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_TCP))
{
(void)fprintf(stderr, "unable to register (NLM_PROG, NLM_VERS, tcp).\n");
exit(1);
}
if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_TCP))
{
(void)fprintf(stderr, "unable to register (NLM_PROG, NLM_VERSX, tcp).\n");
exit(1);
}
/* Note that it is NOT sensible to run this program from inetd - the */
/* protocol assumes that it will run immediately at boot time. */
daemon(0, 0);
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);
}
static void
nlm_prog_1(struct svc_req *rqstp, SVCXPRT *transp)
{
union
{
struct nlm_testargs nlm_test_1_arg;
struct nlm_lockargs nlm_lock_1_arg;
struct nlm_cancargs nlm_cancel_1_arg;
struct nlm_unlockargs nlm_unlock_1_arg;
struct nlm_testargs nlm_granted_1_arg;
struct nlm_testargs nlm_test_msg_1_arg;
struct nlm_lockargs nlm_lock_msg_1_arg;
struct nlm_cancargs nlm_cancel_msg_1_arg;
struct nlm_unlockargs nlm_unlock_msg_1_arg;
struct nlm_testargs nlm_granted_msg_1_arg;
nlm_testres nlm_test_res_1_arg;
nlm_res nlm_lock_res_1_arg;
nlm_res nlm_cancel_res_1_arg;
nlm_res nlm_unlock_res_1_arg;
nlm_res nlm_granted_res_1_arg;
} argument;
char *result;
bool_t (*xdr_argument)(), (*xdr_result)();
char *(*local)();
switch (rqstp->rq_proc)
{
case NULLPROC:
(void)svc_sendreply(transp, xdr_void, (char *)NULL);
return;
case NLM_TEST:
xdr_argument = xdr_nlm_testargs;
xdr_result = xdr_nlm_testres;
local = (char *(*)()) nlm_test_1;
break;
case NLM_LOCK:
xdr_argument = xdr_nlm_lockargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_lock_1;
break;
case NLM_CANCEL:
xdr_argument = xdr_nlm_cancargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_cancel_1;
break;
case NLM_UNLOCK:
xdr_argument = xdr_nlm_unlockargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_unlock_1;
break;
case NLM_GRANTED:
xdr_argument = xdr_nlm_testargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_granted_1;
break;
case NLM_TEST_MSG:
xdr_argument = xdr_nlm_testargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_test_msg_1;
break;
case NLM_LOCK_MSG:
xdr_argument = xdr_nlm_lockargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_lock_msg_1;
break;
case NLM_CANCEL_MSG:
xdr_argument = xdr_nlm_cancargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_cancel_msg_1;
break;
case NLM_UNLOCK_MSG:
xdr_argument = xdr_nlm_unlockargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_unlock_msg_1;
break;
case NLM_GRANTED_MSG:
xdr_argument = xdr_nlm_testargs;
xdr_result = xdr_void;
local = (char *(*)()) nlm_granted_msg_1;
break;
case NLM_TEST_RES:
xdr_argument = xdr_nlm_testres;
xdr_result = xdr_void;
local = (char *(*)()) nlm_test_res_1;
break;
case NLM_LOCK_RES:
xdr_argument = xdr_nlm_res;
xdr_result = xdr_void;
local = (char *(*)()) nlm_lock_res_1;
break;
case NLM_CANCEL_RES:
xdr_argument = xdr_nlm_res;
xdr_result = xdr_void;
local = (char *(*)()) nlm_cancel_res_1;
break;
case NLM_UNLOCK_RES:
xdr_argument = xdr_nlm_res;
xdr_result = xdr_void;
local = (char *(*)()) nlm_unlock_res_1;
break;
case NLM_GRANTED_RES:
xdr_argument = xdr_nlm_res;
xdr_result = xdr_void;
local = (char *(*)()) nlm_granted_res_1;
break;
default:
svcerr_noproc(transp);
return;
}
bzero((char *)&argument, sizeof(argument));
if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument))
{
syslog(LOG_ERR, "RPC received with invalid arguments");
svcerr_decode(transp);
return;
}
result = (*local)(&argument, rqstp);
if (result != NULL && !svc_sendreply(transp, xdr_result, result))
{
svcerr_systemerr(transp);
}
if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument))
{
syslog(LOG_ERR, "unable to free arguments");
}
}
static void
nlm_prog_3(struct svc_req *rqstp, SVCXPRT *transp)
{
union
{
nlm_shareargs nlm_share_3_arg;
nlm_shareargs nlm_unshare_3_arg;
nlm_lockargs nlm_nm_lock_3_arg;
nlm_notify nlm_free_all_3_arg;
} argument;
char *result;
bool_t (*xdr_argument)(), (*xdr_result)();
char *(*local)();
switch (rqstp->rq_proc)
{
case NULLPROC:
(void)svc_sendreply(transp, xdr_void, (char *)NULL);
return;
case NLM_SHARE:
xdr_argument = xdr_nlm_shareargs;
xdr_result = xdr_nlm_shareres;
local = (char *(*)()) nlm_share_3;
break;
case NLM_UNSHARE:
xdr_argument = xdr_nlm_shareargs;
xdr_result = xdr_nlm_shareres;
local = (char *(*)()) nlm_unshare_3;
break;
case NLM_NM_LOCK:
xdr_argument = xdr_nlm_lockargs;
xdr_result = xdr_nlm_res;
local = (char *(*)()) nlm_nm_lock_3;
break;
case NLM_FREE_ALL:
xdr_argument = xdr_nlm_notify;
xdr_result = xdr_void;
local = (char *(*)()) nlm_free_all_3;
break;
default:
svcerr_noproc(transp);
return;
}
bzero((char *)&argument, sizeof(argument));
if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument))
{
syslog(LOG_ERR, "RPC received with invalid arguments");
svcerr_decode(transp);
return;
}
result = (*local)(&argument, rqstp);
if (result != NULL && !svc_sendreply(transp, xdr_result, result))
{
svcerr_systemerr(transp);
}
if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument))
{
syslog(LOG_ERR, "unable to free arguments");
}
}

View File

@ -0,0 +1,44 @@
/*
* 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 <stdio.h>
#include <rpc/rpc.h>
#include <syslog.h>
#include <rpcsvc/sm_inter.h> /* protocol to talk to rpc.statd */
#include <rpcsvc/nlm_prot.h> /* The protocol we are implementing */
/* global variables ------------------------------------------------------- */
extern int debug_level;

588
usr.sbin/rpc.lockd/procs.c Normal file
View File

@ -0,0 +1,588 @@
/*
* 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 "lockd.h"
#include <sys/param.h> /* for MAXHOSTNAMELEN */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define CLIENT_CACHE_SIZE 64 /* No. of client sockets cached */
#define CLIENT_CACHE_LIFETIME 120 /* In seconds */
/* 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(char *fun_name, struct svc_req *req)
{
struct sockaddr_in *addr;
struct hostent *host;
char hostname_buf[40];
addr = svc_getcaller(req->rq_xprt);
host = gethostbyaddr((char *)&(addr->sin_addr), addr->sin_len, AF_INET);
if (host)
{
strncpy(hostname_buf, host->h_name, sizeof(hostname_buf));
hostname_buf[sizeof(hostname_buf) -1] = '\0';
}
else /* No hostname available - print raw address */
{
strcpy(hostname_buf, inet_ntoa(addr->sin_addr));
}
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 in_addr clnt_cache_addr[CLIENT_CACHE_SIZE];
static int clnt_cache_next_to_use = 0;
static CLIENT *get_client(struct sockaddr_in *host_addr)
{
CLIENT *client;
int sock_no;
struct timeval retry_time, time_now;
int i;
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 && !memcmp(&clnt_cache_addr[i], &host_addr->sin_addr,
sizeof(struct in_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;
}
/* Create the new client handle */
sock_no = RPC_ANYSOCK;
retry_time.tv_sec = 5;
retry_time.tv_usec = 0;
host_addr->sin_port = 0; /* Force consultation with portmapper */
client = clntudp_create(host_addr, NLM_PROG, NLM_VERS, retry_time, &sock_no);
if (!client)
{
syslog(LOG_ERR, clnt_spcreateerror("clntudp_create"));
syslog(LOG_ERR, "Unable to return result to %s",
inet_ntoa(host_addr->sin_addr));
return NULL;
}
/* Success - update the cache entry */
clnt_cache_ptr[clnt_cache_next_to_use] = client;
clnt_cache_addr[clnt_cache_next_to_use] = host_addr->sin_addr;
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, &retry_time);
if (debug_level > 3) syslog(LOG_DEBUG, "Created CLIENT* for %s",
inet_ntoa(host_addr->sin_addr));
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
*/
static void transmit_result(int opcode, nlm_res *result, struct svc_req *req)
{
static char dummy;
struct sockaddr_in *addr;
CLIENT *cli;
int success;
struct timeval timeo;
addr = svc_getcaller(req->rq_xprt);
if (cli = get_client(addr))
{
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\n", success);
}
}
/* ------------------------------------------------------------------------- */
/*
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(nlm_testargs *arg, struct svc_req *rqstp)
{
static nlm_testres res;
if (debug_level) log_from_addr("nlm_test", rqstp);
/* 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;
res.stat.stat = nlm_granted;
return (&res);
}
void *nlm_test_msg_1(nlm_testargs *arg, struct svc_req *rqstp)
{
nlm_testres res;
static char dummy;
struct sockaddr_in *addr;
CLIENT *cli;
int success;
struct timeval timeo;
if (debug_level) log_from_addr("nlm_test_msg", rqstp);
res.cookie = arg->cookie;
res.stat.stat = nlm_granted;
/* nlm_test has different result type to the other operations, so */
/* can't use transmit_result() in this case */
addr = svc_getcaller(rqstp->rq_xprt);
if (cli = get_client(addr))
{
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\n", success);
}
return (NULL);
}
/* nlm_lock ---------------------------------------------------------------- */
/*
Purposes: Establish a lock
Returns: granted, denied or blocked
Notes: *** grace period support missing
*/
nlm_res *nlm_lock_1(nlm_lockargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
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 = nlm_granted;
return (&res);
}
void *nlm_lock_msg_1(nlm_lockargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
if (debug_level) log_from_addr("nlm_lock_msg", rqstp);
res.cookie = arg->cookie;
res.stat.stat = nlm_granted;
transmit_result(NLM_LOCK_RES, &res, rqstp);
return (NULL);
}
/* nlm_cancel -------------------------------------------------------------- */
/*
Purpose: Cancel a blocked lock request
Returns: granted or denied
Notes:
*/
nlm_res *nlm_cancel_1(nlm_cancargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
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 = nlm_denied;
return (&res);
}
void *nlm_cancel_msg_1(nlm_cancargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
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 = nlm_denied;
transmit_result(NLM_CANCEL_RES, &res, rqstp);
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(nlm_unlockargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
if (debug_level) log_from_addr("nlm_unlock", rqstp);
res.stat.stat= nlm_granted;
res.cookie = arg->cookie;
return (&res);
}
void *nlm_unlock_msg_1(nlm_unlockargs *arg, struct svc_req *rqstp)
{
static nlm_res res;
if (debug_level) log_from_addr("nlm_unlock_msg", rqstp);
res.stat.stat = nlm_granted;
res.cookie = arg->cookie;
transmit_result(NLM_UNLOCK_RES, &res, rqstp);
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(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(nlm_testargs *arg, struct svc_req *rqstp)
{
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, rqstp);
return (NULL);
}
/* nlm_test_res ------------------------------------------------------------ */
/*
Purpose: Accept result from earlier nlm_test_msg() call
Returns: Nothing
*/
void *nlm_test_res_1(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(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(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(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(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(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(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(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(nlm_notify *arg, struct svc_req *rqstp)
{
static char dummy;
if (debug_level) log_from_addr("nlm_free_all", rqstp);
return (&dummy);
}

View File

@ -0,0 +1,94 @@
.\" -*- nroff -*-
.\"
.\" 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 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 AUTHOR 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.
.\"
.\"
.Dd September 24, 1995
.Dt RPC.LOCK 8
.Os
.Sh NAME
.Nm rpc.lock
.Nd NFS file locking daemon
.Sh SYNOPSIS
.Nm /sbin/rpc.lockd
.Op Fl d Op Ar debug_level
.Sh DESCRIPTION
.Nm rpc.rstatd
is a daemon which provides file- and record-locking services in an NFS
environment.
.Pp
Options and operands available for
.Nm rpc.lockd :
.Bl -tag -width Ds
.It Fl d
The
.Fl d
option causes debugging information to be written to syslog, recording
all RPC transations 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
debug levels can be specified, causing display of operation arguments
and internal operations of the daemon.
.El
.Pp
Error conditions are logged to syslog, irrespecive of the debug level,
using log level LOG_ERR and facility LOG_DAEMON.
.Pp
The
.Nm rpc.lockd
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.
.Sh FILES
.Bl -tag -width /usr/include/rpcsvc/nlm_prot.x -compact
.It Pa /usr/include/rpcsvc/nlm_prot.x
RPC protocol specification for the network lock manager protocol.
.El
.Sh SEE ALSO
.Xr rpc.statd 8 ,
.Xr rc 8 ,
.Xr syslog 3
.Sh BUGS
The current implementation provides only the server side of the protocol
(ie. clients running other OS types can establish locks on a FreeBSD fileserver,
but there is currently no means for a FreeBSD 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.
.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

358
usr.sbin/rpc.lockd/test.c Normal file
View File

@ -0,0 +1,358 @@
#include <rpc/rpc.h>
#include <rpcsvc/nlm_prot.h>
#ifndef lint
/*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";*/
static char rcsid[] = "nlm_prot.x,v 1.1 1994/08/04 19:01:48 wollman Exp";
#endif /* not lint */
/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 0, 0 };
nlm_testres *
nlm_test_1(argp, clnt)
struct nlm_testargs *argp;
CLIENT *clnt;
{
static nlm_testres res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_TEST, xdr_nlm_testargs, argp, xdr_nlm_testres, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_res *
nlm_lock_1(argp, clnt)
struct nlm_lockargs *argp;
CLIENT *clnt;
{
enum clnt_stat st;
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (st = clnt_call(clnt, NLM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
printf("clnt_call returns %d\n", st);
clnt_perror(clnt, "humbug");
return (NULL);
}
return (&res);
}
nlm_res *
nlm_cancel_1(argp, clnt)
struct nlm_cancargs *argp;
CLIENT *clnt;
{
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_CANCEL, xdr_nlm_cancargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_res *
nlm_unlock_1(argp, clnt)
struct nlm_unlockargs *argp;
CLIENT *clnt;
{
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_UNLOCK, xdr_nlm_unlockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_res *
nlm_granted_1(argp, clnt)
struct nlm_testargs *argp;
CLIENT *clnt;
{
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_GRANTED, xdr_nlm_testargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
void *
nlm_test_msg_1(argp, clnt)
struct nlm_testargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_TEST_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_lock_msg_1(argp, clnt)
struct nlm_lockargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_LOCK_MSG, xdr_nlm_lockargs, argp, xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
clnt_perror(clnt, "nlm_lock_msg_1");
return (NULL);
}
return ((void *)&res);
}
void *
nlm_cancel_msg_1(argp, clnt)
struct nlm_cancargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_CANCEL_MSG, xdr_nlm_cancargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_unlock_msg_1(argp, clnt)
struct nlm_unlockargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_UNLOCK_MSG, xdr_nlm_unlockargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_granted_msg_1(argp, clnt)
struct nlm_testargs *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_GRANTED_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_test_res_1(argp, clnt)
nlm_testres *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_TEST_RES, xdr_nlm_testres, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_lock_res_1(argp, clnt)
nlm_res *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_LOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_cancel_res_1(argp, clnt)
nlm_res *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_CANCEL_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_unlock_res_1(argp, clnt)
nlm_res *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_UNLOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
void *
nlm_granted_res_1(argp, clnt)
nlm_res *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_GRANTED_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
nlm_shareres *
nlm_share_3(argp, clnt)
nlm_shareargs *argp;
CLIENT *clnt;
{
static nlm_shareres res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_SHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_shareres *
nlm_unshare_3(argp, clnt)
nlm_shareargs *argp;
CLIENT *clnt;
{
static nlm_shareres res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_UNSHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
nlm_res *
nlm_nm_lock_3(argp, clnt)
nlm_lockargs *argp;
CLIENT *clnt;
{
static nlm_res res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_NM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return (&res);
}
void *
nlm_free_all_3(argp, clnt)
nlm_notify *argp;
CLIENT *clnt;
{
static char res;
bzero((char *)&res, sizeof(res));
if (clnt_call(clnt, NLM_FREE_ALL, xdr_nlm_notify, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
return (NULL);
}
return ((void *)&res);
}
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);
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");
#else
if (out = nlm_lock_msg_1(&arg, cli))
{
printf("Success!\n");
printf("out->stat = %d", out->stat);
}
else
{
printf("Fail\n");
}
#endif
return 0;
}