diff options
Diffstat (limited to 'usr.sbin/rpc.lockd/lockd_lock.c')
-rw-r--r-- | usr.sbin/rpc.lockd/lockd_lock.c | 792 |
1 files changed, 792 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 00000000000..52d8c96d5b6 --- /dev/null +++ b/usr.sbin/rpc.lockd/lockd_lock.c @@ -0,0 +1,792 @@ +/* $NetBSD: lockd_lock.c,v 1.2 2000/06/09 14:00:53 fvdl Exp $ */ + +/* + * 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 Manuel Bouyer. + * 4. The name of the author may not 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 <inttypes.h> +#include <syslog.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <rpc/rpc.h> +#include <sys/socket.h> +#include <sys/stat.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); + +#define FHANDLE_SIZE_MAX 1024 /* arbitrary big enough value */ +typedef struct { + size_t fhsize; + char *fhdata; +} nfs_fhandle_t; + +static int +fhcmp(const nfs_fhandle_t *fh1, const nfs_fhandle_t *fh2) +{ + + if (fh1->fhsize != fh2->fhsize) { + return 1; + } + return memcmp(fh1->fhdata, fh2->fhdata, fh1->fhsize); +} + +static int +fhconv(nfs_fhandle_t *fh, const netobj *rfh) +{ + size_t sz; + + sz = rfh->n_len; + if (sz > FHANDLE_SIZE_MAX) { + syslog(LOG_DEBUG, + "received fhandle size %zd, max supported size %d", + sz, FHANDLE_SIZE_MAX); + errno = EINVAL; + return -1; + } + fh->fhdata = malloc(sz); + if (fh->fhdata == NULL) { + return -1; + } + fh->fhsize = sz; + memcpy(fh->fhdata, rfh->n_bytes, sz); + return 0; +} + +static void +fhfree(nfs_fhandle_t *fh) +{ + + free(fh->fhdata); +} + +/* struct describing a lock */ +struct file_lock { + LIST_ENTRY(file_lock) lcklst; + nfs_fhandle_t filehandle; /* NFS filehandle */ + struct sockaddr_in *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 acquire the lock */ +#define LKST_DYING 4 /* must dies when we get news from the child */ + +static struct file_lock *lalloc(void); +void lfree(struct file_lock *); +enum nlm_stats do_lock(struct file_lock *, int); +enum nlm_stats do_unlock(struct file_lock *); +void send_granted(struct file_lock *, int); +void siglock(void); +void sigunlock(void); + +#define LL_FH 0x01 +#define LL_NAME 0x02 +#define LL_SVID 0x04 + +static struct file_lock *lock_lookup(struct file_lock *, int); + +/* + * lock_lookup: lookup a matching lock. + * called with siglock held. + */ +static struct file_lock * +lock_lookup(struct file_lock *newfl, int flags) +{ + struct file_lock *fl; + + LIST_FOREACH(fl, &lcklst_head, lcklst) { + if ((flags & LL_SVID) != 0 && + newfl->client.svid != fl->client.svid) + continue; + if ((flags & LL_NAME) != 0 && + strcmp(newfl->client_name, fl->client_name) != 0) + continue; + if ((flags & LL_FH) != 0 && + fhcmp(&newfl->filehandle, &fl->filehandle) != 0) + continue; + /* found */ + break; + } + + return fl; +} + +/* + * 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 * +/*ARGSUSED*/ +testlock(struct nlm4_lock *lock, int flags) +{ + struct file_lock *fl; + nfs_fhandle_t filehandle; + + /* convert lock to a local filehandle */ + if (fhconv(&filehandle, &lock->fh)) { + syslog(LOG_NOTICE, "fhconv failed (%m)"); + return NULL; /* XXX */ + } + + siglock(); + /* search through the list for lock holder */ + LIST_FOREACH(fl, &lcklst_head, lcklst) { + if (fl->status != LKST_LOCKED) + continue; + if (fhcmp(&fl->filehandle, &filehandle) != 0) + continue; + /* got it ! */ + syslog(LOG_DEBUG, "test for %s: found lock held by %s", + lock->caller_name, fl->client_name); + sigunlock(); + fhfree(&filehandle); + return (&fl->client); + } + /* not found */ + sigunlock(); + fhfree(&filehandle); + syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name); + return NULL; +} + +/* + * getlock: try to acquire 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(nlm4_lockargs * lckarg, struct svc_req *rqstp, int flags) +{ + struct file_lock *fl, *newfl; + enum nlm_stats retval; + struct sockaddr_in *addr; + + 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 = lalloc(); + if (newfl == NULL) { + syslog(LOG_NOTICE, "malloc failed (%m)"); + /* failed */ + return (flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + } + if (fhconv(&newfl->filehandle, &lckarg->alock.fh)) { + syslog(LOG_NOTICE, "fhconv failed (%m)"); + lfree(newfl); + /* failed */ + return (flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + } + addr = svc_getcaller(rqstp->rq_xprt); + newfl->addr = malloc(addr->sin_len); + if (newfl->addr == NULL) { + syslog(LOG_NOTICE, "malloc failed (%m)"); + lfree(newfl); + /* failed */ + return (flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + } + memcpy(newfl->addr, addr, addr->sin_len); + 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 (%m)"); + lfree(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 (%m)"); + lfree(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); + strlcpy(newfl->client_name, lckarg->alock.caller_name, + sizeof(newfl->client_name)); + newfl->nsm_status = lckarg->state; + newfl->status = 0; + newfl->flags = flags; + siglock(); + /* look for a lock rq from this host for this fh */ + fl = lock_lookup(newfl, LL_FH|LL_NAME|LL_SVID); + if (fl) { + /* already locked by this host ??? */ + sigunlock(); + syslog(LOG_NOTICE, "duplicate lock from %s.%" + PRIu32, + newfl->client_name, newfl->client.svid); + 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; + } + /* NOTREACHED */ + } + fl = lock_lookup(newfl, LL_FH); + if (fl) { + /* + * 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.%" PRIu32 ": " + "already locked, waiting", + lckarg->alock.caller_name, + lckarg->alock.svid); + newfl->status = LKST_WAITING; + LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst); + sigunlock(); + return (flags & LOCK_V4) ? + nlm4_blocked : nlm_blocked; + } else { + sigunlock(); + syslog(LOG_DEBUG, "lock from %s.%" PRIu32 ": " + "already locked, failed", + lckarg->alock.caller_name, + lckarg->alock.svid); + lfree(newfl); + return (flags & LOCK_V4) ? + nlm4_denied : nlm_denied; + } + /* NOTREACHED */ + } + + /* 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 */ + break; + default: + lfree(newfl); + break; + } + sigunlock(); + return retval; +} + +/* unlock a filehandle */ +enum nlm_stats +unlock(nlm4_lock *lck, int flags) +{ + struct file_lock *fl; + nfs_fhandle_t filehandle; + int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted; + + if (fhconv(&filehandle, &lck->fh)) { + syslog(LOG_NOTICE, "fhconv failed (%m)"); + return (flags & LOCK_V4) ? nlm4_denied : nlm_denied; + } + siglock(); + LIST_FOREACH(fl, &lcklst_head, lcklst) { + if (strcmp(fl->client_name, lck->caller_name) || + fhcmp(&filehandle, &fl->filehandle) != 0 || + 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.%" PRIu32 ": found struct, " + "status %d", lck->caller_name, lck->svid, 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(); + fhfree(&filehandle); + return err; + } + sigunlock(); + /* didn't find a matching entry; log anyway */ + syslog(LOG_NOTICE, "no matching entry for %s", + lck->caller_name); + fhfree(&filehandle); + return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; +} + +static struct file_lock * +lalloc(void) +{ + return calloc(1, sizeof(struct file_lock)); +} + +void +lfree(struct file_lock *fl) +{ + free(fl->addr); + free(fl->client.oh.n_bytes); + free(fl->client_cookie.n_bytes); + fhfree(&fl->filehandle); + free(fl); +} + +void +/*ARGSUSED*/ +sigchild_handler(int sig) +{ + int sstatus; + pid_t pid; + struct file_lock *fl; + + for (;;) { + pid = wait4(-1, &sstatus, WNOHANG, NULL); + if (pid == -1) { + if (errno != ECHILD) + syslog(LOG_NOTICE, "wait failed (%m)"); + else + syslog(LOG_DEBUG, "wait failed (%m)"); + 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. + */ + LIST_FOREACH(fl, &lcklst_head, lcklst) { + if (pid == fl->locker) + break; + } + if (fl == NULL) { + syslog(LOG_NOTICE, "unknown child %d", pid); + } else { + /* protect from pid reusing. */ + fl->locker = 0; + if (!WIFEXITED(sstatus) || WEXITSTATUS(sstatus) != 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 acquire the lock described by fl. Eventually fock a child to do a + * blocking lock if allowed and required. + */ + +enum nlm_stats +do_lock(struct file_lock *fl, int block) +{ + int lflags, error; + struct stat st; + + fl->fd = fhopen((fhandle_t *)fl->filehandle.fhdata, 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) (%m)", + fl->client_name); + LIST_REMOVE(fl, lcklst); + return error; + } + if (fstat(fl->fd, &st) < 0) { + syslog(LOG_NOTICE, "fstat failed (from %s) (%m)", + fl->client_name); + } + syslog(LOG_DEBUG, "lock from %s.%" PRIu32 " for file%s%s: " + "dev %u ino %d (uid %d), flags %d", + fl->client_name, fl->client.svid, + 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 (%m)"); + 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.%" PRIu32, + fl->client_name, fl->client.svid); + lflags &= ~LOCK_NB; + if(flock(fl->fd, lflags) != 0) { + syslog(LOG_NOTICE, "flock failed (%m)"); + _exit(1); + } + /* lock granted */ + _exit(0); + /*NOTREACHED*/ + default: + syslog(LOG_DEBUG, "lock request from %s.%" PRIu32 ": " + "forked %d", + fl->client_name, fl->client.svid, 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 (%m)", + fl->client_name); + else syslog(LOG_DEBUG, "flock for %s failed (%m)", + fl->client_name); + LIST_REMOVE(fl, lcklst); + close(fl->fd); + return error; + } + fl->status = LKST_LOCKED; + return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; +} + +void +/*ARGSUSED*/ +send_granted(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.%" PRIu32, + fl->client_name, fl->client.svid); + /* + * 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 result; + result.cookie = fl->client_cookie; + result.exclusive = fl->client.exclusive; + result.alock.caller_name = fl->client_name; + result.alock.fh.n_len = fl->filehandle.fhsize; + result.alock.fh.n_bytes = fl->filehandle.fhdata; + result.alock.oh = fl->client.oh; + result.alock.svid = fl->client.svid; + result.alock.l_offset = fl->client.l_offset; + result.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, &result, xdr_void, &dummy, timeo); + } else { + success = clnt_call(cli, NLM4_GRANTED, + xdr_nlm4_testargs, &result, xdr_nlm4_res, + &retval4, timeo); + } + } else { + static nlm_testargs result; + + result.cookie = fl->client_cookie; + result.exclusive = fl->client.exclusive; + result.alock.caller_name = fl->client_name; + result.alock.fh.n_len = fl->filehandle.fhsize; + result.alock.fh.n_bytes = fl->filehandle.fhdata; + result.alock.oh = fl->client.oh; + result.alock.svid = fl->client.svid; + result.alock.l_offset = + (unsigned int)fl->client.l_offset; + result.alock.l_len = + (unsigned int)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, &result, xdr_void, &dummy, timeo); + } else { + success = clnt_call(cli, NLM_GRANTED, + xdr_nlm_testargs, &result, 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(struct file_lock *rfl) +{ + struct file_lock *fl; + int error; + int lockst; + + /* unlock the file: closing is enough ! */ + if (close(rfl->fd) == -1) { + if (errno == ESTALE) + error = nlm4_stale_fh; + else + error = nlm4_failed; + if ((rfl->flags & LOCK_V4) == 0) + error = nlm_denied; + syslog(LOG_NOTICE, "close failed (from %s) (%m)", + rfl->client_name); + } else { + error = (rfl->flags & LOCK_V4) ? + nlm4_granted : nlm_granted; + } + LIST_REMOVE(rfl, lcklst); + + /* process the next LKST_WAITING lock request for this fh */ + LIST_FOREACH(fl, &lcklst_head, lcklst) { + if (fl->status != LKST_WAITING || + fhcmp(&rfl->filehandle, &fl->filehandle) != 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; + } + lfree(rfl); + return error; +} + +void +siglock(void) +{ + sigset_t block; + + sigemptyset(&block); + sigaddset(&block, SIGCHLD); + + if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) { + syslog(LOG_WARNING, "siglock failed (%m)"); + } +} + +void +sigunlock(void) +{ + sigset_t block; + + sigemptyset(&block); + sigaddset(&block, SIGCHLD); + + if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) { + syslog(LOG_WARNING, "sigunlock failed (%m)"); + } +} + +void +notify(const 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(); +} |