msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
from_addr((struct sockaddr *)&msg->lm_addr));
+ monitor_lock_host_by_addr((struct sockaddr *)&msg->lm_addr);
+
if (msg->lm_flags & LOCKD_MSG_NFSV3) {
arg4.cookie.n_bytes = (char *)&msg->lm_xid;
arg4.cookie.n_len = sizeof(msg->lm_xid);
#include <stdio.h>
#include <string.h>
#include <syslog.h>
+#include <stdlib.h>
+#include <sys/queue.h>
#include <rpc/rpc.h>
#include <rpcsvc/sm_inter.h>
static void log_from_addr(const char *, struct svc_req *);
static void log_netobj(netobj *obj);
-static int addrcmp(struct sockaddr *, struct sockaddr *);
/* log_from_addr ----------------------------------------------------------- */
/*
static rpcvers_t clnt_cache_vers[CLIENT_CACHE_SIZE];
static int clnt_cache_next_to_use = 0;
-static int
-addrcmp(sa1, sa2)
- struct sockaddr *sa1;
- struct sockaddr *sa2;
+/*
+ * Because lockd is single-threaded, slow/unresponsive portmappers on
+ * clients can cause serious performance issues. So, we keep a list of
+ * these bad hosts, and limit how often we try to get_client() for those hosts.
+ */
+struct badhost {
+ TAILQ_ENTRY(badhost) list;
+ struct sockaddr_storage addr; /* host address */
+ int count; /* # of occurences */
+ time_t timelast; /* last attempted */
+ time_t timenext; /* next allowed */
+};
+TAILQ_HEAD(badhostlist_head, badhost);
+static struct badhostlist_head badhostlist_head = TAILQ_HEAD_INITIALIZER(badhostlist_head);
+#define BADHOST_CLIENT_TOOK_TOO_LONG 5 /* In seconds */
+#define BADHOST_INITIAL_DELAY 120 /* In seconds */
+#define BADHOST_MAXIMUM_DELAY 3600 /* In seconds */
+#define BADHOST_DELAY_INCREMENT 300 /* In seconds */
+
+int
+addrcmp(const struct sockaddr *sa1, const struct sockaddr *sa2)
{
int len;
void *p1, *p2;
struct sockaddr *host_addr;
rpcvers_t vers;
{
- CLIENT *client;
+ CLIENT *client, *cached_client;
struct timeval retry_time, time_now;
int i;
int sock_no;
+ time_t time_start, cache_time = 0;
+ struct badhost *badhost, *nextbadhost;
gettimeofday(&time_now, NULL);
/*
- * Search for the given client in the cache, zapping any expired
- * entries that we happen to notice in passing.
+ * Search for the given client in the cache.
*/
+ cached_client = NULL;
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) && clnt_cache_vers[i] == vers) {
- /* Found it! */
+ if (!client)
+ continue;
+ if (clnt_cache_vers[i] != vers)
+ continue;
+ if (addrcmp((struct sockaddr *)&clnt_cache_addr[i], host_addr))
+ continue;
+ /* Found it! */
+ if (((clnt_cache_time[i] + CLIENT_CACHE_LIFETIME) > time_now.tv_sec)) {
if (debug_level > 3)
syslog(LOG_DEBUG, "Found CLIENT* in cache");
return (client);
}
+ syslog(LOG_DEBUG, "Found expired CLIENT* in cache");
+ cached_client = client;
+ /* if we end up reusing this guy, make sure we keep the same timestamp */
+ cache_time = clnt_cache_time[i];
+ clnt_cache_time[i] = 0L;
+ clnt_cache_ptr[i] = NULL;
+ client = NULL;
+ break;
}
- if (debug_level > 3)
- syslog(LOG_DEBUG, "CLIENT* not found in cache, creating");
+ /*
+ * Search for the given client in the badhost list.
+ */
+ badhost = TAILQ_FIRST(&badhostlist_head);
+ while (badhost) {
+ nextbadhost = TAILQ_NEXT(badhost, list);
+ if (!addrcmp(host_addr, (struct sockaddr *)&badhost->addr))
+ break;
+ if ((badhost->timelast + BADHOST_MAXIMUM_DELAY) < time_now.tv_sec) {
+ /* cleanup entries we haven't heard from in a while */
+ TAILQ_REMOVE(&badhostlist_head, badhost, list);
+ free(badhost);
+ }
+ badhost = nextbadhost;
+ }
- /* Not found in cache. Free the next entry if it is in use. */
+ if (badhost && (time_now.tv_sec < badhost->timenext)) {
+ /*
+ * We've got a badhost, and we don't want to try
+ * consulting it again yet. If we've got a stale
+ * cached CLIENT*, go ahead and try to use that.
+ */
+ if (cached_client) {
+ syslog(LOG_DEBUG, "badhost delayed: stale CLIENT* 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;
+ }
+ client = cached_client;
+ goto update_cache_entry;
+ }
+ syslog(LOG_DEBUG, "badhost delayed: valid CLIENT* not found in cache");
+ return NULL;
+ }
+
+ if (debug_level > 3) {
+ if (!cached_client)
+ syslog(LOG_DEBUG, "CLIENT* not found in cache, creating");
+ else
+ syslog(LOG_DEBUG, "stale CLIENT* found in cache, updating");
+ }
+
+ /* 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 */
+ time_start = time_now.tv_sec;
sock_no = RPC_ANYSOCK;
retry_time.tv_sec = 5;
retry_time.tv_usec = 0;
((struct sockaddr_in *)host_addr)->sin_port = 0; /* Force consultation with portmapper */
client = clntudp_create((struct sockaddr_in *)host_addr, NLM_PROG, vers, retry_time, &sock_no);
+
+ gettimeofday(&time_now, NULL);
+ if (time_now.tv_sec - time_start >= BADHOST_CLIENT_TOOK_TOO_LONG) {
+ /*
+ * The client create took a long time! (slow/unresponsive portmapper?)
+ * Add/update an entry in the badhost list.
+ */
+ if (!badhost && (badhost = malloc(sizeof(struct badhost)))) {
+ /* allocate new badhost */
+ memcpy(&badhost->addr, host_addr, host_addr->sa_len);
+ badhost->count = 0;
+ TAILQ_INSERT_TAIL(&badhostlist_head, badhost, list);
+ }
+ if (badhost) {
+ /* update count and times */
+ badhost->count++;
+ badhost->timelast = time_now.tv_sec;
+ if (badhost->count == 1) {
+ /* first timers get a shorter initial delay */
+ badhost->timenext = time_now.tv_sec + BADHOST_INITIAL_DELAY;
+ } else {
+ /* multiple offenders get an increasingly larger delay */
+ int delay = (badhost->count - 1) * BADHOST_DELAY_INCREMENT;
+ if (delay > BADHOST_MAXIMUM_DELAY)
+ delay = BADHOST_MAXIMUM_DELAY;
+ badhost->timenext = time_now.tv_sec + delay;
+ }
+ /* move to end of list */
+ TAILQ_REMOVE(&badhostlist_head, badhost, list);
+ TAILQ_INSERT_TAIL(&badhostlist_head, badhost, list);
+ }
+ } else if (badhost) {
+ /* host seems good now, remove it from list */
+ TAILQ_REMOVE(&badhostlist_head, badhost, list);
+ free(badhost);
+ badhost = NULL;
+ }
+
if (!client) {
- syslog(LOG_DEBUG, "%s", clnt_spcreateerror("clntudp_create"));
- syslog(LOG_DEBUG, "Unable to contact %s",
- inet_ntoa(((struct sockaddr_in *)host_addr)->sin_addr));
- return NULL;
+ /* We couldn't get a new CLIENT* */
+ if (!cached_client) {
+ syslog(LOG_WARNING, "Unable to contact %s: %s",
+ inet_ntoa(((struct sockaddr_in *)host_addr)->sin_addr),
+ clnt_spcreateerror("clntudp_create"));
+ return NULL;
+ }
+ /*
+ * We couldn't get updated info from portmapper, but we did
+ * still have the stale cached data. So we might as well try
+ * to use it.
+ */
+ client = cached_client;
+ syslog(LOG_WARNING, "Unable to update contact info for %s: %s",
+ inet_ntoa(((struct sockaddr_in *)host_addr)->sin_addr),
+ clnt_spcreateerror("clntudp_create"));
+ } else {
+ /*
+ * We've got a new/updated CLIENT* for this host.
+ * So, destroy any previously cached CLIENT*.
+ */
+ if (cached_client)
+ clnt_destroy(cached_client);
+
+ /*
+ * 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",
+ inet_ntoa(((struct sockaddr_in *)host_addr)->sin_addr));
+
+ /* make sure the new entry gets the current timestamp */
+ cache_time = time_now.tv_sec;
}
- /* Success - update the cache entry */
+update_cache_entry:
+ /* Success (of some sort) - 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_vers[clnt_cache_next_to_use] = vers;
- clnt_cache_time[clnt_cache_next_to_use] = time_now.tv_sec;
- if (++clnt_cache_next_to_use > CLIENT_CACHE_SIZE)
+ clnt_cache_time[clnt_cache_next_to_use] = cache_time;
+ 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",
- inet_ntoa(((struct sockaddr_in *)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
+ * Returns: success (0) or failure (-1) at sending the datagram
* 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
+int
transmit_result(opcode, result, addr)
int opcode;
nlm_res *result;
if (debug_level > 2)
syslog(LOG_DEBUG, "clnt_call returns %d(%s)",
success, clnt_sperrno(success));
+ return (0);
}
+ return (-1);
}
/* transmit4_result --------------------------------------------------------- */
/*
* Purpose: Transmit result for nlm4_xxx_msg pseudo-RPCs
- * Returns: Nothing - we have no idea if the datagram got there
+ * Returns: success (0) or failure (-1) at sending the datagram
* 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
+int
transmit4_result(opcode, result, addr)
int opcode;
nlm4_res *result;
if (debug_level > 2)
syslog(LOG_DEBUG, "clnt_call returns %d(%s)",
success, clnt_sperrno(success));
+ return (0);
}
+ return (-1);
}
/*
struct nlm_lock *arg;
struct nlm4_lock *arg4;
{
- memcpy(arg4, arg, sizeof(nlm_lock));
+ arg4->caller_name = arg->caller_name;
+ arg4->fh = arg->fh;
+ arg4->oh = arg->oh;
+ arg4->svid = arg->svid;
arg4->l_offset = arg->l_offset;
arg4->l_len = arg->l_len;
}
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));
+ if (transmit_result(NLM_LOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)) < 0) {
+ /* if res.stat.stat was success/blocked, then unlock/cancel */
+ if (res.stat.stat == nlm_granted)
+ unlock(&arg4.alock, LOCK_V4);
+ else if (res.stat.stat == nlm_blocked) {
+ nlm4_cancargs carg;
+ carg.cookie = arg4.cookie;
+ carg.block = arg4.block;
+ carg.exclusive = arg4.exclusive;
+ carg.alock = arg4.alock;
+ cancellock(&carg, 0);
+ }
+ }
return (NULL);
}
res.cookie = arg->cookie;
res.stat.stat = cancellock(&arg4, 0);
- transmit_result(NLM_CANCEL_RES, &res,
- (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ if (transmit_result(NLM_CANCEL_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)) < 0) {
+ /* XXX do we need to (un)do anything if this fails? */
+ }
return (NULL);
}
res.stat.stat = unlock(&arg4, 0);
res.cookie = arg->cookie;
- transmit_result(NLM_UNLOCK_RES, &res,
- (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ if (transmit_result(NLM_UNLOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)) < 0) {
+ /* XXX do we need to (un)do anything if this fails? */
+ }
return (NULL);
}
res.cookie = arg->cookie;
- transmit_result(NLM_GRANTED_RES, &res,
- (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ if (transmit_result(NLM_GRANTED_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)) < 0) {
+ /* XXX do we need to (un)do anything if this fails? */
+ }
return (NULL);
}
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));
+ if (transmit4_result(NLM4_LOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)) < 0) {
+ /* if res.stat.stat was success/blocked, then unlock/cancel */
+ if (res.stat.stat == nlm4_granted)
+ unlock(&arg->alock, LOCK_V4);
+ else if (res.stat.stat == nlm4_blocked) {
+ nlm4_cancargs carg;
+ carg.cookie = arg->cookie;
+ carg.block = arg->block;
+ carg.exclusive = arg->exclusive;
+ carg.alock = arg->alock;
+ cancellock(&carg, LOCK_V4);
+ }
+ }
return (NULL);
}
res.cookie = arg->cookie;
res.stat.stat = cancellock(arg, LOCK_V4);
- transmit4_result(NLM4_CANCEL_RES, &res,
- (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ if (transmit4_result(NLM4_CANCEL_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)) < 0) {
+ /* XXX do we need to (un)do anything if this fails? */
+ }
return (NULL);
}
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));
+ if (transmit4_result(NLM4_UNLOCK_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)) < 0) {
+ /* XXX do we need to (un)do anything if this fails? */
+ }
return (NULL);
}
res.cookie = arg->cookie;
- transmit4_result(NLM4_GRANTED_RES, &res,
- (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ if (transmit4_result(NLM4_GRANTED_RES, &res,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)) < 0) {
+ /* XXX do we need to (un)do anything if this fails? */
+ }
return (NULL);
}
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
+#include <sys/resource.h>
#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
int ch;
struct sigaction sigalarm;
int grace_period = 30;
+ struct rlimit rlp;
while ((ch = getopt(argc, argv, "d:g:wx:")) != (-1)) {
switch (ch) {
* Note that it is NOT sensible to run this program from inetd - the
* protocol assumes that it will run immediately at boot time.
*/
- if (debug_level != 99 && daemon(0, 0)) {
+ if (debug_level != 99 && daemon(0, debug_level > 0)) {
err(1, "cannot fork");
/* NOTREACHED */
}
signal(SIGHUP, handle_sig_cleanup);
signal(SIGQUIT, handle_sig_cleanup);
- if (claim_pid_file("/var/run/lockd.pid", 0) < 0)
- errx(1, "cannot claim pid file");
+ openlog("rpc.lockd", debug_level == 99 ? LOG_PERROR : 0, LOG_DAEMON);
+
+ if (claim_pid_file("/var/run/lockd.pid", 0) < 0) {
+ syslog(LOG_ERR, "cannot claim pid file");
+ exit(1);
+ }
if (waitkern) {
struct timespec ts;
nanosleep(&ts, NULL);
}
- openlog("rpc.lockd", debug_level == 99 ? LOG_PERROR : 0, LOG_DAEMON);
if (debug_level)
syslog(LOG_INFO, "Starting, debug level %d", debug_level);
else
(void)pmap_unset(NLM_PROG, NLM_VERS4);
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)");
- if (!svc_register(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, IPPROTO_UDP))
- errx(1, "unable to register (NLM_PROG, NLM_VERS4, udp)");
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service");
+ exit(1);
+ }
+ if (!svc_register(transp, NLM_PROG, NLM_SM, nlm_prog_0, IPPROTO_UDP)) {
+ syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_SM, udp)");
+ exit(1);
+ }
+ if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_UDP)) {
+ syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS, udp)");
+ exit(1);
+ }
+ if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_UDP)) {
+ syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERSX, udp)");
+ exit(1);
+ }
+ if (!svc_register(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, IPPROTO_UDP)) {
+ syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS4, udp)");
+ exit(1);
+ }
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)");
- if (!svc_register(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, IPPROTO_TCP))
- errx(1, "unable to register (NLM_PROG, NLM_VERS4, tcp)");
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create tcp service");
+ exit(1);
+ }
+ if (!svc_register(transp, NLM_PROG, NLM_SM, nlm_prog_0, IPPROTO_TCP)) {
+ syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_SM, tcp)");
+ exit(1);
+ }
+ if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_TCP)) {
+ syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS, tcp)");
+ exit(1);
+ }
+ if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_TCP)) {
+ syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERSX, tcp)");
+ exit(1);
+ }
+ if (!svc_register(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, IPPROTO_TCP)) {
+ syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS4, tcp)");
+ exit(1);
+ }
sigalarm.sa_handler = (sig_t) sigalarm_handler;
sigemptyset(&sigalarm.sa_mask);
client_pid = client_request();
+ /* raise our resource limits as far as they can go */
+ if (getrlimit(RLIMIT_NOFILE, &rlp)) {
+ syslog(LOG_WARNING, "getrlimit(RLIMIT_NOFILE) failed: %s",
+ strerror(errno));
+ } else {
+ rlp.rlim_cur = rlp.rlim_max;
+ if (setrlimit(RLIMIT_NOFILE, &rlp)) {
+ syslog(LOG_WARNING, "setrlimit(RLIMIT_NOFILE) failed: %s",
+ strerror(errno));
+ }
+ }
+
my_svc_run(); /* Should never return */
exit(1);
}
ret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON_ALL,
xdr_my_id, &id, xdr_sm_stat, &stat);
if (ret) {
- warnx("%lu %s", SM_PROG, clnt_sperrno(ret));
+ syslog(LOG_WARNING, "%lu %s", SM_PROG, clnt_sperrno(ret));
if (++attempt < 20) {
sleep(attempt);
continue;
} while (1);
if (ret != 0) {
- errx(1, "%lu %s", SM_PROG, clnt_sperrno(ret));
+ syslog(LOG_ERR, "%lu %s", SM_PROG, clnt_sperrno(ret));
+ exit(1);
}
nsm_state = stat.state;
#define MAXOBJECTSIZE 64
#define MAXBUFFERSIZE 1024
-/*
- * SM_MAXSTRLEN is usually 1024. This means that lock requests and
- * host name monitoring entries are *MUCH* larger than they should be
- */
-
/*
* A set of utilities for managing file locking
*
struct sockaddr *addr;
struct nlm4_holder client; /* lock holder */
u_int64_t granted_cookie;
- char client_name[SM_MAXSTRLEN];
int nsm_status; /* status from the remote lock manager */
int status; /* lock status, see below */
int flags; /* lock flags, see lockd_lock.h */
int blocking; /* blocking lock or not */
- pid_t locker; /* pid of the child process trying to get the lock */
- int fd; /* file descriptor for this lock */
+ char client_name[SM_MAXSTRLEN]; /* client_name is really variable length and must be last! */
};
LIST_HEAD(nfslocklist_head, file_lock);
struct file_share {
LIST_ENTRY(file_share) nfssharelist;
netobj oh; /* share holder */
- char client_name[SM_MAXSTRLEN];
short mode;
short access;
+ char client_name[SM_MAXSTRLEN]; /* name is really variable length and must be last! */
};
LIST_HEAD(nfssharelist_head, file_share);
/* struct describing a monitored host */
struct host {
TAILQ_ENTRY(host) hostlst;
- char name[SM_MAXSTRLEN];
int refcnt;
time_t lastuse;
+ struct sockaddr addr;
+ char name[SM_MAXSTRLEN]; /* name is really variable length and must be last! */
};
/* list of hosts we monitor */
TAILQ_HEAD(hostlst_head, host);
void siglock(void);
void sigunlock(void);
void destroy_lock_host(struct host *ihp);
-void monitor_lock_host(const char *hostname);
-void unmonitor_lock_host(const char *hostname);
+static void monitor_lock_host(const char *hostname, const struct sockaddr *addr);
void copy_nlm4_lock_to_nlm4_holder(const struct nlm4_lock *src,
const bool_t exclusive, struct nlm4_holder *dest);
struct file_lock * allocate_file_lock(const netobj *lockowner,
- const netobj *filehandle);
+ const netobj *filehandle, const struct sockaddr *addr,
+ const char *caller_name);
void deallocate_file_lock(struct file_lock *fl);
void fill_file_lock(struct file_lock *fl,
- struct sockaddr *addr, const bool_t exclusive, const int32_t svid,
- const u_int64_t offset, const u_int64_t len, const char *caller_name,
+ const bool_t exclusive, const int32_t svid,
+ const u_int64_t offset, const u_int64_t len,
const int state, const int status, const int flags, const int blocking);
int regions_overlap(const u_int64_t start1, const u_int64_t len1,
const u_int64_t start2, const u_int64_t len2);;
}
+size_t
+strnlen(const char *s, size_t len)
+{
+ size_t n;
+
+ for (n = 0; s[n] != 0 && n < len; n++)
+ ;
+ return n;
+}
+
/*
* allocate_file_lock: Create a lock with the given parameters
*/
struct file_lock *
-allocate_file_lock(const netobj *lockowner, const netobj *filehandle)
+allocate_file_lock(const netobj *lockowner, const netobj *filehandle,
+ const struct sockaddr *addr, const char *caller_name)
{
struct file_lock *newfl;
+ size_t n;
+
+ /* Beware of rubbish input! */
+ n = strnlen(caller_name, SM_MAXSTRLEN);
+ if (n == SM_MAXSTRLEN) {
+ return NULL;
+ }
- newfl = malloc(sizeof(struct file_lock));
+ newfl = malloc(sizeof(*newfl) - sizeof(newfl->client_name) + n + 1);
if (newfl == NULL) {
return NULL;
}
- bzero(newfl, sizeof(newfl));
+ bzero(newfl, sizeof(*newfl) - sizeof(newfl->client_name));
+ memcpy(newfl->client_name, caller_name, n);
+ newfl->client_name[n] = 0;
newfl->client.oh.n_bytes = malloc(lockowner->n_len);
if (newfl->client.oh.n_bytes == NULL) {
newfl->filehandle.n_len = filehandle->n_len;
bcopy(filehandle->n_bytes, newfl->filehandle.n_bytes, filehandle->n_len);
+ newfl->addr = malloc(addr->sa_len);
+ if (newfl->addr == NULL) {
+ free(newfl->client.oh.n_bytes);
+ free(newfl);
+ return NULL;
+ }
+ memcpy(newfl->addr, addr, addr->sa_len);
+
return newfl;
}
*/
void
fill_file_lock(struct file_lock *fl,
- struct sockaddr *addr, const bool_t exclusive, const int32_t svid,
- const u_int64_t offset, const u_int64_t len, const char *caller_name,
+ const bool_t exclusive, const int32_t svid,
+ const u_int64_t offset, const u_int64_t len,
const int state, const int status, const int flags, const int blocking)
{
- fl->addr = addr;
-
fl->client.exclusive = exclusive;
fl->client.svid = svid;
fl->client.l_offset = offset;
fl->client.l_len = len;
- strncpy(fl->client_name, caller_name, SM_MAXSTRLEN);
-
fl->nsm_status = state;
fl->status = status;
fl->flags = flags;
void
deallocate_file_lock(struct file_lock *fl)
{
+ free(fl->addr);
free(fl->client.oh.n_bytes);
free(fl->filehandle.n_bytes);
free(fl);
&start1, &len1, &start2, &len2);
if ((spstatus & SPL_LOCK1) != 0) {
- *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle);
+ *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle, exist_lock->addr, exist_lock->client_name);
if (*left_lock == NULL) {
debuglog("Unable to allocate resource for split 1\n");
return SPL_RESERR;
}
fill_file_lock(*left_lock,
- exist_lock->addr,
exist_lock->client.exclusive, exist_lock->client.svid,
start1, len1,
- exist_lock->client_name, exist_lock->nsm_status,
+ exist_lock->nsm_status,
exist_lock->status, exist_lock->flags, exist_lock->blocking);
}
if ((spstatus & SPL_LOCK2) != 0) {
- *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle);
+ *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->filehandle, exist_lock->addr, exist_lock->client_name);
if (*right_lock == NULL) {
debuglog("Unable to allocate resource for split 1\n");
if (*left_lock != NULL) {
}
fill_file_lock(*right_lock,
- exist_lock->addr,
exist_lock->client.exclusive, exist_lock->client.svid,
start2, len2,
- exist_lock->client_name, exist_lock->nsm_status,
+ exist_lock->nsm_status,
exist_lock->status, exist_lock->flags, exist_lock->blocking);
}
retval = NFS_DENIED_NOLOCK;
- printf("Attempting to match lock...\n");
+ debuglog("Attempting to match lock...\n");
mfl = get_lock_matching_unlock(fl);
if (mfl != NULL) {
return (HW_RESERR);
}
nmf->filehandle.n_bytes = malloc(fl->filehandle.n_len);
- if (nmf == NULL) {
+ if (nmf->filehandle.n_bytes == NULL) {
debuglog("hwlock resource allocation failure\n");
free(nmf);
return (HW_RESERR);
if (nmf->fd < 0) {
debuglog("fhopen failed (from %16s): %32s\n",
fl->client_name, strerror(errno));
+ free(nmf->filehandle.n_bytes);
free(nmf);
switch (errno) {
case ESTALE:
debuglog("flock failed (from %16s): %32s\n",
fl->client_name, strerror(errno));
close(nmf->fd);
+ free(nmf->filehandle.n_bytes);
free(nmf);
switch (errno) {
case EAGAIN:
if (imf->refcount <= 0) {
close(imf->fd);
LIST_REMOVE(imf, monfilelist);
+ free(imf->filehandle.n_bytes);
free(imf);
}
debuglog("Exiting unlock_hwlock (HW_GRANTED)\n");
retval = PFL_GRANTED;
}
if (fl->flags & LOCK_MON)
- monitor_lock_host(fl->client_name);
+ monitor_lock_host_by_name(fl->client_name);
break;
case HW_RESERR:
debuglog("HW RESERR\n");
if (retval == PFL_NFSDENIED || retval == PFL_HWDENIED) {
/* Once last chance to check the lock */
if (fl->blocking == 1) {
- /* Queue the lock */
- debuglog("BLOCKING LOCK RECEIVED\n");
- retval = (retval == PFL_NFSDENIED ?
- PFL_NFSBLOCKED : PFL_HWBLOCKED);
- add_blockingfilelock(fl);
- dump_filelock(fl);
+ if (retval == PFL_NFSDENIED) {
+ /* Queue the lock */
+ debuglog("BLOCKING LOCK RECEIVED\n");
+ retval = PFL_NFSBLOCKED;
+ add_blockingfilelock(fl);
+ dump_filelock(fl);
+ } else {
+ /* retval is okay as PFL_HWDENIED */
+ debuglog("BLOCKING LOCK DENIED IN HARDWARE\n");
+ dump_filelock(fl);
+ }
} else {
/* Leave retval alone, it's already correct */
debuglog("Lock denied. Non-blocking failure\n");
debuglog("HW duplicate lock failure for left split\n");
}
if (lfl->flags & LOCK_MON)
- monitor_lock_host(lfl->client_name);
+ monitor_lock_host_by_name(lfl->client_name);
}
if (rfl != NULL) {
debuglog("HW duplicate lock failure for right split\n");
}
if (rfl->flags & LOCK_MON)
- monitor_lock_host(rfl->client_name);
+ monitor_lock_host_by_name(rfl->client_name);
}
switch (unlstatus) {
nlm4_denied_grace_period : nlm_denied_grace_period;
/* allocate new file_lock for this request */
- newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->alock.fh);
+ newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->alock.fh,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt),
+ lckarg->alock.caller_name);
if (newfl == NULL) {
syslog(LOG_NOTICE, "lock allocate failed: %s", strerror(errno));
/* failed */
}
fill_file_lock(newfl,
- (struct sockaddr *)svc_getcaller(rqstp->rq_xprt),
lckarg->exclusive, lckarg->alock.svid, lckarg->alock.l_offset,
lckarg->alock.l_len,
- lckarg->alock.caller_name, lckarg->state, 0, flags, lckarg->block);
+ lckarg->state, 0, flags, lckarg->block);
/*
* newfl is now fully constructed and deallocate_file_lock
/*
* XXX: The following monitor/unmonitor routines
* have not been extensively tested (ie. no regression
- * script exists like for the locking sections
+ * script exists like for the locking sections)
*/
/*
* enqueue it at the front of the "in use" queue.
*/
struct host *
-get_lock_host(struct hostlst_head *hd, const char *hostname)
+get_lock_host(struct hostlst_head *hd, const char *hostname, const struct sockaddr *saddr)
{
struct host *ihp;
- debuglog("get_lock_host %s\n", hostname);
+ if (!hostname && !saddr)
+ return (NULL);
+
+ debuglog("get_lock_host %s\n", hostname ? hostname : "addr");
TAILQ_FOREACH(ihp, hd, hostlst) {
- if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) {
- TAILQ_REMOVE(hd, ihp, hostlst);
- /* Host is already monitored, bump refcount */
+ if (hostname && (strncmp(hostname, ihp->name, SM_MAXSTRLEN) != 0))
+ continue;
+ if (saddr && addrcmp(saddr, &ihp->addr))
+ continue;
+ TAILQ_REMOVE(hd, ihp, hostlst);
+ /*
+ * Host is already monitored, so just bump the
+ * reference count. But don't bump the reference
+ * count if we're adding additional client-side
+ * references. Client-side monitors are done by
+ * address, are never unmonitored, and should only
+ * take one refcount. Otherwise, repeated calls
+ * could cause the refcount to wrap.
+ */
+ if (!saddr || !ihp->addr.sa_len)
++ihp->refcnt;
- ihp->lastuse = currsec;
- /* Host should only be in the monitor list once */
- TAILQ_INSERT_HEAD(&hostlst_head, ihp, hostlst);
- break;
- }
+ ihp->lastuse = currsec;
+ /* Host should only be in the monitor list once */
+ TAILQ_INSERT_HEAD(&hostlst_head, ihp, hostlst);
+ break;
}
debuglog("get_lock_host %s %s\n",
- ihp == NULL ? "did not find" : "found", hostname);
+ ihp == NULL ? "did not find" : "found", hostname ? hostname : "addr");
return (ihp);
}
* inform statd
*/
void
-monitor_lock_host(const char *hostname)
+monitor_lock_host_by_name(const char *hostname)
{
- struct host *ihp, *nhp;
- struct mon smon;
- struct sm_stat_res sres;
- int rpcret, statflag;
-
- rpcret = 0;
- statflag = 0;
+ struct host *ihp;
debuglog("monitor_lock_host: %s\n", hostname);
- ihp = get_lock_host(&hostlst_head, hostname);
+ ihp = get_lock_host(&hostlst_head, hostname, NULL);
if (ihp == NULL)
- ihp = get_lock_host(&hostlst_unref, hostname);
+ ihp = get_lock_host(&hostlst_unref, hostname, NULL);
if (ihp != NULL) {
debuglog("Monitor_lock_host: %s (cached)\n", hostname);
return;
}
- debuglog("Monitor_lock_host: %s (not found, creating)\n", hostname);
- /* Host is not yet monitored, add it */
- nhp = malloc(sizeof(struct host));
+ monitor_lock_host(hostname, NULL);
+}
+
+void
+monitor_lock_host_by_addr(const struct sockaddr *saddr)
+{
+ struct host *ihp;
+ struct hostent *hp;
+ char hostaddr[SM_MAXSTRLEN];
+ struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
+ if (getnameinfo(saddr, saddr->sa_len, hostaddr, sizeof(hostaddr),
+ NULL, 0, NI_NUMERICHOST)) {
+ debuglog("monitor_lock_host: bad address\n");
+ return;
+ }
+ debuglog("monitor_lock_host: %s\n", hostaddr);
+ ihp = get_lock_host(&hostlst_head, NULL, saddr);
+ if (ihp == NULL)
+ ihp = get_lock_host(&hostlst_unref, NULL, saddr);
+ if (ihp != NULL) {
+ debuglog("Monitor_lock_host: %s (cached)\n", ihp->name);
+ return;
+ }
+
+ hp = gethostbyaddr((char*)&sin->sin_addr, sizeof(sin->sin_addr), AF_INET);
+ if (hp) {
+ monitor_lock_host(hp->h_name, saddr);
+ } else {
+ // herror(hostaddr);
+ monitor_lock_host(hostaddr, saddr);
+ }
+}
+
+static void
+monitor_lock_host(const char *hostname, const struct sockaddr *saddr)
+{
+ struct host *nhp;
+ struct mon smon;
+ struct sm_stat_res sres;
+ int rpcret, statflag;
+ size_t n;
+
+ rpcret = 0;
+ statflag = 0;
+
+ /* Host is not yet monitored, add it */
+ debuglog("Monitor_lock_host: %s (creating)\n", hostname);
+ n = strnlen(hostname, SM_MAXSTRLEN);
+ if (n == SM_MAXSTRLEN) {
+ debuglog("monitor_lock_host: hostname too long\n");
+ return;
+ }
+ nhp = malloc(sizeof(*nhp) - sizeof(nhp->name) + n + 1);
if (nhp == NULL) {
debuglog("Unable to allocate entry for statd mon\n");
return;
}
/* Allocated new host entry, now fill the fields */
- strncpy(nhp->name, hostname, SM_MAXSTRLEN);
+ memcpy(nhp->name, hostname, n);
+ nhp->name[n] = 0;
nhp->refcnt = 1;
nhp->lastuse = currsec;
- debuglog("Locally Monitoring host %16s\n",hostname);
+ if (saddr) {
+ bcopy(saddr, &nhp->addr, saddr->sa_len);
+ } else {
+ nhp->addr.sa_len = 0;
+ }
+ debuglog("Locally Monitoring host '%s'\n", hostname);
debuglog("Attempting to tell statd\n");
TAILQ_FOREACH(ihp, &hostlst_head, hostlst) {
if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) {
- /* Host is monitored, bump refcount */
+ /* Host is unmonitored, drop refcount */
--ihp->refcnt;
/* Host should only be in the monitor list once */
break;
{
struct sharefile *shrfile;
struct file_share *sh;
+ size_t n;
debuglog("Entering getshare...\n");
}
/* create/init new share */
- sh = malloc(sizeof(struct file_share));
+ n = strnlen(shrarg->share.caller_name, SM_MAXSTRLEN);
+ if (n < SM_MAXSTRLEN) {
+ sh = malloc(sizeof(*sh) - sizeof(sh->client_name) + n + 1);
+ } else {
+ debuglog("getshare failed: hostname too long\n");
+ sh = NULL;
+ }
if (!sh) {
debuglog("getshare failed: can't allocate share\n");
if (!shrfile->refcount) {
}
return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
}
+ bzero(sh, sizeof(*sh) - sizeof(sh->client_name));
sh->oh.n_len = shrarg->share.oh.n_len;
sh->oh.n_bytes = malloc(sh->oh.n_len);
if (!sh->oh.n_bytes) {
}
return (flags & LOCK_V4) ? nlm4_failed : nlm_denied;
}
- strncpy(sh->client_name, shrarg->share.caller_name, SM_MAXSTRLEN);
+ memcpy(sh->client_name, shrarg->share.caller_name, n);
+ sh->client_name[n] = 0;
sh->mode = shrarg->share.mode;
sh->access = shrarg->share.access;
void notify(const char *hostname, const int state);
+void monitor_lock_host_by_name(const char *hostname);
+void monitor_lock_host_by_addr(const struct sockaddr *addr);
+void unmonitor_lock_host(const char *hostname);
+
/* flags for testlock, getlock & unlock */
#define LOCK_ASYNC 0x01 /* async version (getlock only) */
#define LOCK_V4 0x02 /* v4 version */
#define LOCK_ANSWER_LOCK_EXCL 0x0004 /* lock is exclusive */
/* callbacks from lock_proc.c */
-void transmit_result(int, nlm_res *, struct sockaddr *);
-void transmit4_result(int, nlm4_res *, struct sockaddr *);
+int transmit_result(int, nlm_res *, struct sockaddr *);
+int transmit4_result(int, nlm4_res *, struct sockaddr *);
CLIENT *get_client(struct sockaddr *, rpcvers_t);
+int addrcmp(const struct sockaddr *, const struct sockaddr *);
extern time_t currsec;
hp = find_host(arg->mon_name, FALSE);
if (!hp)
{
- /* Never heard of this host - why is it notifying us? */
- syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
- return (&dummy);
+ /*
+ * Hmmm... We've never heard of this host.
+ * It's possible the host just didn't give us the right hostname.
+ * Let's try the IP address the request came from and any hostnames it has.
+ */
+ struct sockaddr_in *claddr;
+ if ((claddr = svc_getcaller(req->rq_xprt))) {
+ struct hostent *he;
+ he = gethostbyaddr((char*)&claddr->sin_addr, sizeof(claddr->sin_addr), AF_INET);
+ if (he) {
+ char **np = he->h_aliases;
+ hp = find_host(he->h_name, FALSE);
+ while (!hp && *np) {
+ hp = find_host(*np, FALSE);
+ if (!hp)
+ np++;
+ }
+ }
+ if (hp)
+ syslog(LOG_DEBUG, "Notification from host %s found as %s",
+ arg->mon_name, hp->hostname);
+ }
+ if (!hp) {
+ /* Never heard of this host - why is it notifying us? */
+ syslog(LOG_DEBUG, "Unsolicited notification from host %s", arg->mon_name);
+ return (&dummy);
+ }
}
lp = hp->monList;
if (!lp) return (&dummy); /* We know this host, but have no */
while (lp)
{
- tx_arg.mon_name = arg->mon_name;
+ tx_arg.mon_name = hp->hostname;
tx_arg.state = arg->state;
memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");