diff options
Diffstat (limited to 'usr.sbin/rpc.lockd/lockd_lock.c')
-rw-r--r-- | usr.sbin/rpc.lockd/lockd_lock.c | 759 |
1 files changed, 759 insertions, 0 deletions
diff --git a/usr.sbin/rpc.lockd/lockd_lock.c b/usr.sbin/rpc.lockd/lockd_lock.c new file mode 100644 index 0000000..998a200 --- /dev/null +++ b/usr.sbin/rpc.lockd/lockd_lock.c @@ -0,0 +1,759 @@ +/* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2000 Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <syslog.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <rpc/rpc.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/wait.h> +#include <rpcsvc/sm_inter.h> +#include <rpcsvc/nlm_prot.h> +#include "lockd_lock.h" +#include "lockd.h" + +/* A set of utilities for managing file locking */ +LIST_HEAD(lcklst_head, file_lock); +struct lcklst_head lcklst_head = LIST_HEAD_INITIALIZER(lcklst_head); + +/* struct describing a lock */ +struct file_lock { + LIST_ENTRY(file_lock) lcklst; + fhandle_t filehandle; /* NFS filehandle */ + struct sockaddr *addr; + struct nlm4_holder client; /* lock holder */ + netobj client_cookie; /* cookie sent by the client */ + char client_name[128]; + int nsm_status; /* status from the remote lock manager */ + int status; /* lock status, see below */ + int flags; /* lock flags, see lockd_lock.h */ + pid_t locker; /* pid of the child process trying to get the lock */ + int fd; /* file descriptor for this lock */ +}; + +/* lock status */ +#define LKST_LOCKED 1 /* lock is locked */ +#define LKST_WAITING 2 /* file is already locked by another host */ +#define LKST_PROCESSING 3 /* child is trying to aquire the lock */ +#define LKST_DYING 4 /* must dies when we get news from the child */ + +void lfree __P((struct file_lock *)); +enum nlm_stats do_lock __P((struct file_lock *, int)); +enum nlm_stats do_unlock __P((struct file_lock *)); +void send_granted __P((struct file_lock *, int)); +void siglock __P((void)); +void sigunlock __P((void)); + +/* list of hosts we monitor */ +LIST_HEAD(hostlst_head, host); +struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head); + +/* struct describing a lock */ +struct host { + LIST_ENTRY(host) hostlst; + char name[SM_MAXSTRLEN]; + int refcnt; +}; + +void do_mon __P((char *)); + +/* + * testlock(): inform the caller if the requested lock would be granted or not + * returns NULL if lock would granted, or pointer to the current nlm4_holder + * otherwise. + */ + +struct nlm4_holder * +testlock(lock, flags) + struct nlm4_lock *lock; + int flags; +{ + struct file_lock *fl; + fhandle_t filehandle; + + /* convert lock to a local filehandle */ + memcpy(&filehandle, lock->fh.n_bytes, sizeof(filehandle)); + + siglock(); + /* search through the list for lock holder */ + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (fl->status != LKST_LOCKED) + continue; + if (memcmp(&fl->filehandle, &filehandle, sizeof(filehandle))) + continue; + /* got it ! */ + syslog(LOG_DEBUG, "test for %s: found lock held by %s", + lock->caller_name, fl->client_name); + sigunlock(); + return (&fl->client); + } + /* not found */ + sigunlock(); + syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name); + return NULL; +} + +/* + * getlock: try to aquire the lock. + * If file is already locked and we can sleep, put the lock in the list with + * status LKST_WAITING; it'll be processed later. + * Otherwise try to lock. If we're allowed to block, fork a child which + * will do the blocking lock. + */ +enum nlm_stats +getlock(lckarg, rqstp, flags) + nlm4_lockargs * lckarg; + struct svc_req *rqstp; + int flags; +{ + struct file_lock *fl, *newfl; + enum nlm_stats retval; + + if (grace_expired == 0 && lckarg->reclaim == 0) + return (flags & LOCK_V4) ? + nlm4_denied_grace_period : nlm_denied_grace_period; + + /* allocate new file_lock for this request */ + newfl = malloc(sizeof(struct file_lock)); + if (newfl == NULL) { + syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno)); + /* failed */ + return (flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + } + if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) { + syslog(LOG_DEBUG, "recieved fhandle size %d, local size %d", + lckarg->alock.fh.n_len, (int)sizeof(fhandle_t)); + } + memcpy(&newfl->filehandle, lckarg->alock.fh.n_bytes, sizeof(fhandle_t)); + newfl->addr = (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf; + newfl->client.exclusive = lckarg->exclusive; + newfl->client.svid = lckarg->alock.svid; + newfl->client.oh.n_bytes = malloc(lckarg->alock.oh.n_len); + if (newfl->client.oh.n_bytes == NULL) { + syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno)); + free(newfl); + return (flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + } + newfl->client.oh.n_len = lckarg->alock.oh.n_len; + memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes, + lckarg->alock.oh.n_len); + newfl->client.l_offset = lckarg->alock.l_offset; + newfl->client.l_len = lckarg->alock.l_len; + newfl->client_cookie.n_len = lckarg->cookie.n_len; + newfl->client_cookie.n_bytes = malloc(lckarg->cookie.n_len); + if (newfl->client_cookie.n_bytes == NULL) { + syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno)); + free(newfl->client.oh.n_bytes); + free(newfl); + return (flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + } + memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes, + lckarg->cookie.n_len); + strncpy(newfl->client_name, lckarg->alock.caller_name, 128); + newfl->nsm_status = lckarg->state; + newfl->status = 0; + newfl->flags = flags; + siglock(); + /* look for a lock rq from this host for this fh */ + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (memcmp(&newfl->filehandle, &fl->filehandle, + sizeof(fhandle_t)) == 0) { + if (strcmp(newfl->client_name, fl->client_name) == 0 && + newfl->client.svid == fl->client.svid) { + /* already locked by this host ??? */ + sigunlock(); + syslog(LOG_NOTICE, "duplicate lock from %s", + newfl->client_name); + lfree(newfl); + switch(fl->status) { + case LKST_LOCKED: + return (flags & LOCK_V4) ? + nlm4_granted : nlm_granted; + case LKST_WAITING: + case LKST_PROCESSING: + return (flags & LOCK_V4) ? + nlm4_blocked : nlm_blocked; + case LKST_DYING: + return (flags & LOCK_V4) ? + nlm4_denied : nlm_denied; + default: + syslog(LOG_NOTICE, "bad status %d", + fl->status); + return (flags & LOCK_V4) ? + nlm4_failed : nlm_denied; + } + } + /* + * We already have a lock for this file. Put this one + * in waiting state if allowed to block + */ + if (lckarg->block) { + syslog(LOG_DEBUG, "lock from %s: already " + "locked, waiting", + lckarg->alock.caller_name); + newfl->status = LKST_WAITING; + LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst); + do_mon(lckarg->alock.caller_name); + sigunlock(); + return (flags & LOCK_V4) ? + nlm4_blocked : nlm_blocked; + } else { + sigunlock(); + syslog(LOG_DEBUG, "lock from %s: already " + "locked, failed", + lckarg->alock.caller_name); + lfree(newfl); + return (flags & LOCK_V4) ? + nlm4_denied : nlm_denied; + } + } + } + /* no entry for this file yet; add to list */ + LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst); + /* do the lock */ + retval = do_lock(newfl, lckarg->block); + switch (retval) { + case nlm4_granted: + /* case nlm_granted: is the same as nlm4_granted */ + case nlm4_blocked: + /* case nlm_blocked: is the same as nlm4_blocked */ + do_mon(lckarg->alock.caller_name); + break; + default: + lfree(newfl); + break; + } + sigunlock(); + return retval; +} + +/* unlock a filehandle */ +enum nlm_stats +unlock(lck, flags) + nlm4_lock *lck; + int flags; +{ + struct file_lock *fl; + fhandle_t filehandle; + int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted; + + memcpy(&filehandle, lck->fh.n_bytes, sizeof(fhandle_t)); + siglock(); + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (strcmp(fl->client_name, lck->caller_name) || + memcmp(&filehandle, &fl->filehandle, sizeof(fhandle_t)) || + fl->client.oh.n_len != lck->oh.n_len || + memcmp(fl->client.oh.n_bytes, lck->oh.n_bytes, + fl->client.oh.n_len) != 0 || + fl->client.svid != lck->svid) + continue; + /* Got it, unlock and remove from the queue */ + syslog(LOG_DEBUG, "unlock from %s: found struct, status %d", + lck->caller_name, fl->status); + switch (fl->status) { + case LKST_LOCKED: + err = do_unlock(fl); + break; + case LKST_WAITING: + /* remove from the list */ + LIST_REMOVE(fl, lcklst); + lfree(fl); + break; + case LKST_PROCESSING: + /* + * being handled by a child; will clean up + * when the child exits + */ + fl->status = LKST_DYING; + break; + case LKST_DYING: + /* nothing to do */ + break; + default: + syslog(LOG_NOTICE, "unknow status %d for %s", + fl->status, fl->client_name); + } + sigunlock(); + return err; + } + sigunlock(); + /* didn't find a matching entry; log anyway */ + syslog(LOG_NOTICE, "no matching entry for %s", + lck->caller_name); + return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; +} + +void +lfree(fl) + struct file_lock *fl; +{ + free(fl->client.oh.n_bytes); + free(fl->client_cookie.n_bytes); + free(fl); +} + +void +sigchild_handler(sig) + int sig; +{ + int status; + pid_t pid; + struct file_lock *fl; + + while (1) { + pid = wait4(-1, &status, WNOHANG, NULL); + if (pid == -1) { + if (errno != ECHILD) + syslog(LOG_NOTICE, "wait failed: %s", + strerror(errno)); + else + syslog(LOG_DEBUG, "wait failed: %s", + strerror(errno)); + return; + } + if (pid == 0) { + /* no more child to handle yet */ + return; + } + /* + * if we're here we have a child that exited + * Find the associated file_lock. + */ + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (pid == fl->locker) + break; + } + if (pid != fl->locker) { + syslog(LOG_NOTICE, "unknow child %d", pid); + } else { + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + syslog(LOG_NOTICE, "child %d failed", pid); + /* + * can't do much here; we can't reply + * anything but OK for blocked locks + * Eventually the client will time out + * and retry. + */ + do_unlock(fl); + return; + } + + /* check lock status */ + syslog(LOG_DEBUG, "processing child %d, status %d", + pid, fl->status); + switch(fl->status) { + case LKST_PROCESSING: + fl->status = LKST_LOCKED; + send_granted(fl, (fl->flags & LOCK_V4) ? + nlm4_granted : nlm_granted); + break; + case LKST_DYING: + do_unlock(fl); + break; + default: + syslog(LOG_NOTICE, "bad lock status (%d) for" + " child %d", fl->status, pid); + } + } + } +} + +/* + * + * try to aquire the lock described by fl. Eventually fock a child to do a + * blocking lock if allowed and required. + */ + +enum nlm_stats +do_lock(fl, block) + struct file_lock *fl; + int block; +{ + int lflags, error; + struct stat st; + + fl->fd = fhopen(&fl->filehandle, O_RDWR); + if (fl->fd < 0) { + switch (errno) { + case ESTALE: + error = nlm4_stale_fh; + break; + case EROFS: + error = nlm4_rofs; + break; + default: + error = nlm4_failed; + } + if ((fl->flags & LOCK_V4) == 0) + error = nlm_denied; + syslog(LOG_NOTICE, "fhopen failed (from %s): %s", + fl->client_name, strerror(errno)); + LIST_REMOVE(fl, lcklst); + return error;; + } + if (fstat(fl->fd, &st) < 0) { + syslog(LOG_NOTICE, "fstat failed (from %s): %s", + fl->client_name, strerror(errno)); + } + syslog(LOG_DEBUG, "lock from %s for file%s%s: dev %d ino %d (uid %d), " + "flags %d", + fl->client_name, fl->client.exclusive ? " (exclusive)":"", + block ? " (block)":"", + st.st_dev, st.st_ino, st.st_uid, fl->flags); + lflags = LOCK_NB; + if (fl->client.exclusive == 0) + lflags |= LOCK_SH; + else + lflags |= LOCK_EX; + error = flock(fl->fd, lflags); + if (error != 0 && errno == EAGAIN && block) { + switch (fl->locker = fork()) { + case -1: /* fork failed */ + syslog(LOG_NOTICE, "fork failed: %s", strerror(errno)); + LIST_REMOVE(fl, lcklst); + close(fl->fd); + return (fl->flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + case 0: + /* + * Attempt a blocking lock. Will have to call + * NLM_GRANTED later. + */ + setproctitle("%s", fl->client_name); + lflags &= ~LOCK_NB; + if(flock(fl->fd, lflags) != 0) { + syslog(LOG_NOTICE, "flock failed: %s", + strerror(errno)); + exit(-1); + } + /* lock granted */ + exit(0); + default: + syslog(LOG_DEBUG, "lock request from %s: forked %d", + fl->client_name, fl->locker); + fl->status = LKST_PROCESSING; + return (fl->flags & LOCK_V4) ? + nlm4_blocked : nlm_blocked; + } + } + /* non block case */ + if (error != 0) { + switch (errno) { + case EAGAIN: + error = nlm4_denied; + break; + case ESTALE: + error = nlm4_stale_fh; + break; + case EROFS: + error = nlm4_rofs; + break; + default: + error = nlm4_failed; + } + if ((fl->flags & LOCK_V4) == 0) + error = nlm_denied; + if (errno != EAGAIN) + syslog(LOG_NOTICE, "flock for %s failed: %s", + fl->client_name, strerror(errno)); + else syslog(LOG_DEBUG, "flock for %s failed: %s", + fl->client_name, strerror(errno)); + LIST_REMOVE(fl, lcklst); + close(fl->fd); + return error; + } + fl->status = LKST_LOCKED; + return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; +} + +void +send_granted(fl, opcode) + struct file_lock *fl; + int opcode; +{ + CLIENT *cli; + static char dummy; + struct timeval timeo; + int success; + static struct nlm_res retval; + static struct nlm4_res retval4; + + cli = get_client(fl->addr, + (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS); + if (cli == NULL) { + syslog(LOG_NOTICE, "failed to get CLIENT for %s", + fl->client_name); + /* + * We fail to notify remote that the lock has been granted. + * The client will timeout and retry, the lock will be + * granted at this time. + */ + return; + } + timeo.tv_sec = 0; + timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */ + + if (fl->flags & LOCK_V4) { + static nlm4_testargs res; + res.cookie = fl->client_cookie; + res.exclusive = fl->client.exclusive; + res.alock.caller_name = fl->client_name; + res.alock.fh.n_len = sizeof(fhandle_t); + res.alock.fh.n_bytes = (char*)&fl->filehandle; + res.alock.oh = fl->client.oh; + res.alock.svid = fl->client.svid; + res.alock.l_offset = fl->client.l_offset; + res.alock.l_len = fl->client.l_len; + syslog(LOG_DEBUG, "sending v4 reply%s", + (fl->flags & LOCK_ASYNC) ? " (async)":""); + if (fl->flags & LOCK_ASYNC) { + success = clnt_call(cli, NLM4_GRANTED_MSG, + xdr_nlm4_testargs, &res, xdr_void, &dummy, timeo); + } else { + success = clnt_call(cli, NLM4_GRANTED, + xdr_nlm4_testargs, &res, xdr_nlm4_res, + &retval4, timeo); + } + } else { + static nlm_testargs res; + + res.cookie = fl->client_cookie; + res.exclusive = fl->client.exclusive; + res.alock.caller_name = fl->client_name; + res.alock.fh.n_len = sizeof(fhandle_t); + res.alock.fh.n_bytes = (char*)&fl->filehandle; + res.alock.oh = fl->client.oh; + res.alock.svid = fl->client.svid; + res.alock.l_offset = fl->client.l_offset; + res.alock.l_len = fl->client.l_len; + syslog(LOG_DEBUG, "sending v1 reply%s", + (fl->flags & LOCK_ASYNC) ? " (async)":""); + if (fl->flags & LOCK_ASYNC) { + success = clnt_call(cli, NLM_GRANTED_MSG, + xdr_nlm_testargs, &res, xdr_void, &dummy, timeo); + } else { + success = clnt_call(cli, NLM_GRANTED, + xdr_nlm_testargs, &res, xdr_nlm_res, + &retval, timeo); + } + } + if (debug_level > 2) + syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted", + success, clnt_sperrno(success)); + +} + +enum nlm_stats +do_unlock(rfl) + struct file_lock *rfl; +{ + struct file_lock *fl; + int error; + int lockst; + + /* unlock the file: closing is enouth ! */ + if (close(rfl->fd) < 0) { + if (errno == ESTALE) + error = nlm4_stale_fh; + else + error = nlm4_failed; + if ((fl->flags & LOCK_V4) == 0) + error = nlm_denied; + syslog(LOG_NOTICE, + "close failed (from %s): %s", + rfl->client_name, strerror(errno)); + } else { + error = (fl->flags & LOCK_V4) ? + nlm4_granted : nlm_granted; + } + LIST_REMOVE(rfl, lcklst); + + /* process the next LKST_WAITING lock request for this fh */ + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (fl->status != LKST_WAITING || + memcmp(&rfl->filehandle, &fl->filehandle, + sizeof(fhandle_t)) != 0) + continue; + + lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */ + switch (lockst) { + case nlm4_granted: + /* case nlm_granted: same as nlm4_granted */ + send_granted(fl, (fl->flags & LOCK_V4) ? + nlm4_granted : nlm_granted); + break; + case nlm4_blocked: + /* case nlm_blocked: same as nlm4_blocked */ + break; + default: + lfree(fl); + break; + } + break; + } + return error; +} + +void +siglock() +{ + sigset_t block; + + sigemptyset(&block); + sigaddset(&block, SIGCHLD); + + if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) { + syslog(LOG_WARNING, "siglock failed: %s", strerror(errno)); + } +} + +void +sigunlock() +{ + sigset_t block; + + sigemptyset(&block); + sigaddset(&block, SIGCHLD); + + if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) { + syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno)); + } +} + +/* monitor a host through rpc.statd, and keep a ref count */ +void +do_mon(hostname) + char *hostname; +{ + struct host *hp; + struct mon my_mon; + struct sm_stat_res res; + int retval; + + for (hp = LIST_FIRST(&hostlst_head); hp != NULL; + hp = LIST_NEXT(hp, hostlst)) { + if (strcmp(hostname, hp->name) == 0) { + /* already monitored, just bump refcnt */ + hp->refcnt++; + return; + } + } + /* not found, have to create an entry for it */ + hp = malloc(sizeof(struct host)); + strncpy(hp->name, hostname, SM_MAXSTRLEN); + hp->refcnt = 1; + syslog(LOG_DEBUG, "monitoring host %s", + hostname); + memset(&my_mon, 0, sizeof(my_mon)); + my_mon.mon_id.mon_name = hp->name; + my_mon.mon_id.my_id.my_name = "localhost"; + my_mon.mon_id.my_id.my_prog = NLM_PROG; + my_mon.mon_id.my_id.my_vers = NLM_SM; + my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; + if ((retval = + callrpc("localhost", SM_PROG, SM_VERS, SM_MON, xdr_mon, + (char*)&my_mon, xdr_sm_stat_res, (char*)&res)) != 0) { + syslog(LOG_WARNING, "rpc to statd failed: %s", + clnt_sperrno((enum clnt_stat)retval)); + free(hp); + return; + } + if (res.res_stat == stat_fail) { + syslog(LOG_WARNING, "statd failed"); + free(hp); + return; + } + LIST_INSERT_HEAD(&hostlst_head, hp, hostlst); +} + +void +notify(hostname, state) + char *hostname; + int state; +{ + struct file_lock *fl, *next_fl; + int err; + syslog(LOG_DEBUG, "notify from %s, new state %d", hostname, state); + /* search all lock for this host; if status changed, release the lock */ + siglock(); + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; fl = next_fl) { + next_fl = LIST_NEXT(fl, lcklst); + if (strcmp(hostname, fl->client_name) == 0 && + fl->nsm_status != state) { + syslog(LOG_DEBUG, "state %d, nsm_state %d, unlocking", + fl->status, fl->nsm_status); + switch(fl->status) { + case LKST_LOCKED: + err = do_unlock(fl); + if (err != nlm_granted) + syslog(LOG_DEBUG, + "notify: unlock failed for %s (%d)", + hostname, err); + break; + case LKST_WAITING: + LIST_REMOVE(fl, lcklst); + lfree(fl); + break; + case LKST_PROCESSING: + fl->status = LKST_DYING; + break; + case LKST_DYING: + break; + default: + syslog(LOG_NOTICE, "unknow status %d for %s", + fl->status, fl->client_name); + } + } + } + sigunlock(); +} |