2007-11-05 02:12:50 +03:00
|
|
|
/* $NetBSD: lockd_lock.c,v 1.28 2007/11/04 23:12:50 christos Exp $ */
|
2000-06-07 18:34:40 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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:
|
2003-10-05 21:48:49 +04:00
|
|
|
* 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.
|
2000-06-07 18:34:40 +04:00
|
|
|
*
|
|
|
|
* 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/socket.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <rpcsvc/sm_inter.h>
|
2000-08-12 22:08:43 +04:00
|
|
|
#include <rpcsvc/nlm_prot.h>
|
2000-06-07 18:34:40 +04:00
|
|
|
#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);
|
|
|
|
|
2006-08-08 17:10:05 +04:00
|
|
|
#define FHANDLE_SIZE_MAX 1024 /* arbitrary big enough value */
|
|
|
|
typedef struct {
|
|
|
|
size_t fhsize;
|
|
|
|
char *fhdata;
|
2006-07-13 16:00:24 +04:00
|
|
|
} nfs_fhandle_t;
|
|
|
|
|
2006-08-08 17:10:05 +04:00
|
|
|
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,
|
2006-08-09 18:12:47 +04:00
|
|
|
"received fhandle size %zd, max supported size %d",
|
2006-08-08 17:10:05 +04:00
|
|
|
sz, FHANDLE_SIZE_MAX);
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fh->fhdata = malloc(sz);
|
|
|
|
if (fh->fhdata == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fh->fhsize = sz;
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)memcpy(fh->fhdata, rfh->n_bytes, sz);
|
2006-08-08 17:10:05 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fhfree(nfs_fhandle_t *fh)
|
|
|
|
{
|
|
|
|
|
|
|
|
free(fh->fhdata);
|
|
|
|
}
|
|
|
|
|
2000-06-07 18:34:40 +04:00
|
|
|
/* struct describing a lock */
|
|
|
|
struct file_lock {
|
|
|
|
LIST_ENTRY(file_lock) lcklst;
|
2006-07-13 16:00:24 +04:00
|
|
|
nfs_fhandle_t filehandle; /* NFS filehandle */
|
2000-06-07 18:34:40 +04:00
|
|
|
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 */
|
2002-07-11 03:16:32 +04:00
|
|
|
#define LKST_PROCESSING 3 /* child is trying to acquire the lock */
|
2000-06-07 18:34:40 +04:00
|
|
|
#define LKST_DYING 4 /* must dies when we get news from the child */
|
|
|
|
|
2006-08-08 17:10:05 +04:00
|
|
|
static struct file_lock *lalloc(void);
|
2007-11-05 02:12:50 +03:00
|
|
|
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);
|
2000-06-07 18:34:40 +04:00
|
|
|
|
|
|
|
/* 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;
|
2003-03-14 17:08:44 +03:00
|
|
|
char name[SM_MAXSTRLEN+1];
|
2000-06-07 18:34:40 +04:00
|
|
|
int refcnt;
|
|
|
|
};
|
|
|
|
|
2007-11-05 02:12:50 +03:00
|
|
|
void do_mon(const char *);
|
2000-06-07 18:34:40 +04:00
|
|
|
|
2003-03-16 12:05:56 +03:00
|
|
|
#define LL_FH 0x01
|
|
|
|
#define LL_NAME 0x02
|
|
|
|
#define LL_SVID 0x04
|
|
|
|
|
2007-11-05 02:12:50 +03:00
|
|
|
static struct file_lock *lock_lookup(struct file_lock *, int);
|
2003-03-16 12:05:56 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* lock_lookup: lookup a matching lock.
|
|
|
|
* called with siglock held.
|
|
|
|
*/
|
|
|
|
static struct file_lock *
|
2007-11-05 02:12:50 +03:00
|
|
|
lock_lookup(struct file_lock *newfl, int flags)
|
2003-03-16 12:05:56 +03:00
|
|
|
{
|
|
|
|
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 &&
|
2006-08-08 17:10:05 +04:00
|
|
|
fhcmp(&newfl->filehandle, &fl->filehandle) != 0)
|
2003-03-16 12:05:56 +03:00
|
|
|
continue;
|
|
|
|
/* found */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fl;
|
|
|
|
}
|
|
|
|
|
2000-06-07 18:34:40 +04:00
|
|
|
/*
|
|
|
|
* 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 *
|
2007-11-05 02:12:50 +03:00
|
|
|
/*ARGSUSED*/
|
|
|
|
testlock(struct nlm4_lock *lock, int flags)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
|
|
|
struct file_lock *fl;
|
2006-07-13 16:00:24 +04:00
|
|
|
nfs_fhandle_t filehandle;
|
2000-06-07 18:34:40 +04:00
|
|
|
|
|
|
|
/* convert lock to a local filehandle */
|
2006-08-08 17:10:05 +04:00
|
|
|
if (fhconv(&filehandle, &lock->fh)) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "fhconv failed (%m)");
|
2006-08-08 17:10:05 +04:00
|
|
|
return NULL; /* XXX */
|
|
|
|
}
|
2000-06-07 18:34:40 +04:00
|
|
|
|
|
|
|
siglock();
|
|
|
|
/* search through the list for lock holder */
|
2003-03-14 17:03:00 +03:00
|
|
|
LIST_FOREACH(fl, &lcklst_head, lcklst) {
|
2000-06-07 18:34:40 +04:00
|
|
|
if (fl->status != LKST_LOCKED)
|
|
|
|
continue;
|
2006-08-08 17:10:05 +04:00
|
|
|
if (fhcmp(&fl->filehandle, &filehandle) != 0)
|
2000-06-07 18:34:40 +04:00
|
|
|
continue;
|
|
|
|
/* got it ! */
|
|
|
|
syslog(LOG_DEBUG, "test for %s: found lock held by %s",
|
|
|
|
lock->caller_name, fl->client_name);
|
|
|
|
sigunlock();
|
2006-08-08 17:10:05 +04:00
|
|
|
fhfree(&filehandle);
|
2000-06-07 18:34:40 +04:00
|
|
|
return (&fl->client);
|
|
|
|
}
|
|
|
|
/* not found */
|
|
|
|
sigunlock();
|
2006-08-08 17:10:05 +04:00
|
|
|
fhfree(&filehandle);
|
2000-06-07 18:34:40 +04:00
|
|
|
syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2002-07-11 03:16:32 +04:00
|
|
|
* getlock: try to acquire the lock.
|
2000-06-07 18:34:40 +04:00
|
|
|
* 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
|
2007-11-05 02:12:50 +03:00
|
|
|
getlock(nlm4_lockargs * lckarg, struct svc_req *rqstp, int flags)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
|
|
|
struct file_lock *fl, *newfl;
|
|
|
|
enum nlm_stats retval;
|
2003-06-19 15:13:06 +04:00
|
|
|
struct sockaddr *addr;
|
2000-06-07 18:34:40 +04:00
|
|
|
|
|
|
|
if (grace_expired == 0 && lckarg->reclaim == 0)
|
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_denied_grace_period : nlm_denied_grace_period;
|
2000-06-07 18:34:40 +04:00
|
|
|
|
|
|
|
/* allocate new file_lock for this request */
|
2006-08-08 17:10:05 +04:00
|
|
|
newfl = lalloc();
|
2000-06-07 18:34:40 +04:00
|
|
|
if (newfl == NULL) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "malloc failed (%m)");
|
2000-06-07 18:34:40 +04:00
|
|
|
/* failed */
|
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_denied_nolock : nlm_denied_nolocks;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
2006-08-08 17:10:05 +04:00
|
|
|
if (fhconv(&newfl->filehandle, &lckarg->alock.fh)) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "fhconv failed (%m)");
|
2006-08-08 17:10:05 +04:00
|
|
|
lfree(newfl);
|
|
|
|
/* failed */
|
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_denied_nolock : nlm_denied_nolocks;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
2003-06-19 15:13:06 +04:00
|
|
|
addr = (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf;
|
2007-11-05 02:12:50 +03:00
|
|
|
newfl->addr = malloc((size_t)addr->sa_len);
|
2003-06-19 15:13:06 +04:00
|
|
|
if (newfl->addr == NULL) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "malloc failed (%m)");
|
2006-08-08 17:10:05 +04:00
|
|
|
lfree(newfl);
|
2003-06-19 15:13:06 +04:00
|
|
|
/* failed */
|
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_denied_nolock : nlm_denied_nolocks;
|
2003-06-19 15:13:06 +04:00
|
|
|
}
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)memcpy(newfl->addr, addr, (size_t)addr->sa_len);
|
2000-06-07 18:34:40 +04:00
|
|
|
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) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "malloc failed (%m)");
|
2006-08-08 17:10:05 +04:00
|
|
|
lfree(newfl);
|
2000-06-07 18:34:40 +04:00
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_denied_nolock : nlm_denied_nolocks;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
|
|
|
newfl->client.oh.n_len = lckarg->alock.oh.n_len;
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes,
|
2000-06-07 18:34:40 +04:00
|
|
|
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) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "malloc failed (%m)");
|
2006-08-08 17:10:05 +04:00
|
|
|
lfree(newfl);
|
2000-06-07 18:34:40 +04:00
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_denied_nolock : nlm_denied_nolocks;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes,
|
2000-06-07 18:34:40 +04:00
|
|
|
lckarg->cookie.n_len);
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)strlcpy(newfl->client_name, lckarg->alock.caller_name,
|
2003-03-14 17:10:00 +03:00
|
|
|
sizeof(newfl->client_name));
|
2000-06-07 18:34:40 +04:00
|
|
|
newfl->nsm_status = lckarg->state;
|
|
|
|
newfl->status = 0;
|
|
|
|
newfl->flags = flags;
|
|
|
|
siglock();
|
|
|
|
/* look for a lock rq from this host for this fh */
|
2003-03-16 12:05:56 +03:00
|
|
|
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) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_granted : nlm_granted;
|
2003-03-16 12:05:56 +03:00
|
|
|
case LKST_WAITING:
|
|
|
|
case LKST_PROCESSING:
|
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_blocked : nlm_blocked;
|
2003-03-16 12:05:56 +03:00
|
|
|
case LKST_DYING:
|
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_denied : nlm_denied;
|
2003-03-16 12:05:56 +03:00
|
|
|
default:
|
|
|
|
syslog(LOG_NOTICE, "bad status %d",
|
|
|
|
fl->status);
|
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_failed : nlm_denied;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
2003-03-16 12:05:56 +03:00
|
|
|
/* NOTREACHED */
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
2003-03-16 12:05:56 +03:00
|
|
|
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);
|
|
|
|
do_mon(lckarg->alock.caller_name);
|
|
|
|
sigunlock();
|
|
|
|
return (flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_blocked : nlm_blocked;
|
2003-03-16 12:05:56 +03:00
|
|
|
} 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) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_denied : nlm_denied;
|
2003-03-16 12:05:56 +03:00
|
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
2000-06-07 18:34:40 +04:00
|
|
|
/* 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:
|
2000-11-21 06:47:41 +03:00
|
|
|
lfree(newfl);
|
2000-06-07 18:34:40 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
sigunlock();
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unlock a filehandle */
|
|
|
|
enum nlm_stats
|
2007-11-05 02:12:50 +03:00
|
|
|
unlock(nlm4_lock *lck, int flags)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
|
|
|
struct file_lock *fl;
|
2006-07-13 16:00:24 +04:00
|
|
|
nfs_fhandle_t filehandle;
|
2007-11-05 02:12:50 +03:00
|
|
|
int err = (flags & LOCK_V4) ? (enum nlm_stats)nlm4_granted : nlm_granted;
|
2000-06-07 18:34:40 +04:00
|
|
|
|
2006-08-08 17:10:05 +04:00
|
|
|
if (fhconv(&filehandle, &lck->fh)) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "fhconv failed (%m)");
|
|
|
|
return (flags & LOCK_V4) ? (enum nlm_stats)nlm4_denied : nlm_denied;
|
2006-08-08 17:10:05 +04:00
|
|
|
}
|
2000-06-07 18:34:40 +04:00
|
|
|
siglock();
|
2003-03-14 17:03:00 +03:00
|
|
|
LIST_FOREACH(fl, &lcklst_head, lcklst) {
|
2000-06-07 18:34:40 +04:00
|
|
|
if (strcmp(fl->client_name, lck->caller_name) ||
|
2006-08-08 17:10:05 +04:00
|
|
|
fhcmp(&filehandle, &fl->filehandle) != 0 ||
|
2000-06-07 18:34:40 +04:00
|
|
|
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 */
|
2003-03-16 09:55:47 +03:00
|
|
|
syslog(LOG_DEBUG, "unlock from %s.%" PRIu32 ": found struct, "
|
|
|
|
"status %d", lck->caller_name, lck->svid, fl->status);
|
2000-06-07 18:34:40 +04:00
|
|
|
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();
|
2006-08-08 17:10:05 +04:00
|
|
|
fhfree(&filehandle);
|
2000-06-07 18:34:40 +04:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
sigunlock();
|
|
|
|
/* didn't find a matching entry; log anyway */
|
|
|
|
syslog(LOG_NOTICE, "no matching entry for %s",
|
|
|
|
lck->caller_name);
|
2006-08-08 17:10:05 +04:00
|
|
|
fhfree(&filehandle);
|
2007-11-05 02:12:50 +03:00
|
|
|
return (flags & LOCK_V4) ? (enum nlm_stats)nlm4_granted : nlm_granted;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
|
|
|
|
2006-08-08 17:10:05 +04:00
|
|
|
static struct file_lock *
|
|
|
|
lalloc(void)
|
|
|
|
{
|
2007-10-27 22:41:54 +04:00
|
|
|
return calloc(1, sizeof(struct file_lock));
|
2006-08-08 17:10:05 +04:00
|
|
|
}
|
|
|
|
|
2000-06-07 18:34:40 +04:00
|
|
|
void
|
2007-11-05 02:12:50 +03:00
|
|
|
lfree(struct file_lock *fl)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
2003-06-19 15:13:06 +04:00
|
|
|
free(fl->addr);
|
2000-06-07 18:34:40 +04:00
|
|
|
free(fl->client.oh.n_bytes);
|
|
|
|
free(fl->client_cookie.n_bytes);
|
2006-08-08 17:10:05 +04:00
|
|
|
fhfree(&fl->filehandle);
|
2000-06-07 18:34:40 +04:00
|
|
|
free(fl);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-11-05 02:12:50 +03:00
|
|
|
/*ARGSUSED*/
|
|
|
|
sigchild_handler(int sig)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
2007-11-05 02:12:50 +03:00
|
|
|
int sstatus;
|
2000-06-07 18:34:40 +04:00
|
|
|
pid_t pid;
|
|
|
|
struct file_lock *fl;
|
|
|
|
|
2007-11-05 02:12:50 +03:00
|
|
|
for (;;) {
|
|
|
|
pid = wait4(-1, &sstatus, WNOHANG, NULL);
|
2000-06-07 18:34:40 +04:00
|
|
|
if (pid == -1) {
|
|
|
|
if (errno != ECHILD)
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "wait failed (%m)");
|
2000-06-07 18:34:40 +04:00
|
|
|
else
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_DEBUG, "wait failed (%m)");
|
2000-06-07 18:34:40 +04:00
|
|
|
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.
|
|
|
|
*/
|
2003-03-14 17:03:00 +03:00
|
|
|
LIST_FOREACH(fl, &lcklst_head, lcklst) {
|
2000-06-07 18:34:40 +04:00
|
|
|
if (pid == fl->locker)
|
|
|
|
break;
|
|
|
|
}
|
2003-03-14 16:46:23 +03:00
|
|
|
if (fl == NULL) {
|
2000-06-07 18:34:40 +04:00
|
|
|
syslog(LOG_NOTICE, "unknow child %d", pid);
|
|
|
|
} else {
|
2003-03-14 16:46:23 +03:00
|
|
|
/*
|
|
|
|
* protect from pid reusing.
|
|
|
|
*/
|
|
|
|
fl->locker = 0;
|
2007-11-05 02:12:50 +03:00
|
|
|
if (!WIFEXITED(sstatus) || WEXITSTATUS(sstatus) != 0) {
|
2000-06-07 18:34:40 +04:00
|
|
|
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.
|
|
|
|
*/
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)do_unlock(fl);
|
2000-06-07 18:34:40 +04:00
|
|
|
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) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_granted : nlm_granted);
|
2000-06-07 18:34:40 +04:00
|
|
|
break;
|
|
|
|
case LKST_DYING:
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)do_unlock(fl);
|
2000-06-07 18:34:40 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
syslog(LOG_NOTICE, "bad lock status (%d) for"
|
|
|
|
" child %d", fl->status, pid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
2002-07-11 03:16:32 +04:00
|
|
|
* try to acquire the lock described by fl. Eventually fork a child to do a
|
2000-06-07 18:34:40 +04:00
|
|
|
* blocking lock if allowed and required.
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum nlm_stats
|
2007-11-05 02:12:50 +03:00
|
|
|
do_lock(struct file_lock *fl, int block)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
|
|
|
int lflags, error;
|
|
|
|
struct stat st;
|
|
|
|
|
2006-08-08 17:10:05 +04:00
|
|
|
fl->fd = fhopen(fl->filehandle.fhdata, fl->filehandle.fhsize, O_RDWR);
|
2000-06-07 18:34:40 +04:00
|
|
|
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;
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "fhopen failed (from %s) (%m)",
|
|
|
|
fl->client_name);
|
2000-06-07 18:34:40 +04:00
|
|
|
LIST_REMOVE(fl, lcklst);
|
2003-01-20 08:29:53 +03:00
|
|
|
return error;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
|
|
|
if (fstat(fl->fd, &st) < 0) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "fstat failed (from %s) (%m)",
|
|
|
|
fl->client_name);
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
2003-03-16 09:55:47 +03:00
|
|
|
syslog(LOG_DEBUG, "lock from %s.%" PRIu32 " for file%s%s: "
|
2005-08-19 06:07:18 +04:00
|
|
|
"dev %u ino %llu (uid %d), flags %d",
|
2003-03-16 09:55:47 +03:00
|
|
|
fl->client_name, fl->client.svid,
|
|
|
|
fl->client.exclusive ? " (exclusive)":"", block ? " (block)":"",
|
2005-08-19 06:07:18 +04:00
|
|
|
st.st_dev, (unsigned long long)st.st_ino, st.st_uid, fl->flags);
|
2000-06-07 18:34:40 +04:00
|
|
|
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 */
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "fork failed (%m)");
|
2000-06-07 18:34:40 +04:00
|
|
|
LIST_REMOVE(fl, lcklst);
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)close(fl->fd);
|
2000-06-07 18:34:40 +04:00
|
|
|
return (fl->flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_denied_nolock : nlm_denied_nolocks;
|
2000-06-07 18:34:40 +04:00
|
|
|
case 0:
|
|
|
|
/*
|
|
|
|
* Attempt a blocking lock. Will have to call
|
|
|
|
* NLM_GRANTED later.
|
|
|
|
*/
|
2003-03-16 09:55:47 +03:00
|
|
|
setproctitle("%s.%" PRIu32,
|
|
|
|
fl->client_name, fl->client.svid);
|
2000-06-07 18:34:40 +04:00
|
|
|
lflags &= ~LOCK_NB;
|
|
|
|
if(flock(fl->fd, lflags) != 0) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "flock failed (%m)");
|
2003-03-14 16:53:08 +03:00
|
|
|
_exit(1);
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
|
|
|
/* lock granted */
|
2003-03-14 16:53:08 +03:00
|
|
|
_exit(0);
|
2007-11-05 02:12:50 +03:00
|
|
|
/*NOTREACHED*/
|
2000-06-07 18:34:40 +04:00
|
|
|
default:
|
2003-03-16 09:55:47 +03:00
|
|
|
syslog(LOG_DEBUG, "lock request from %s.%" PRIu32 ": "
|
|
|
|
"forked %d",
|
|
|
|
fl->client_name, fl->client.svid, fl->locker);
|
2000-06-07 18:34:40 +04:00
|
|
|
fl->status = LKST_PROCESSING;
|
|
|
|
return (fl->flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_blocked : nlm_blocked;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* 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)
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "flock for %s failed (%m)",
|
|
|
|
fl->client_name);
|
|
|
|
else syslog(LOG_DEBUG, "flock for %s failed (%m)",
|
|
|
|
fl->client_name);
|
2000-06-07 18:34:40 +04:00
|
|
|
LIST_REMOVE(fl, lcklst);
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)close(fl->fd);
|
2000-06-07 18:34:40 +04:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
fl->status = LKST_LOCKED;
|
2007-11-05 02:12:50 +03:00
|
|
|
return (fl->flags & LOCK_V4) ? (enum nlm_stats)nlm4_granted : nlm_granted;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-11-05 02:12:50 +03:00
|
|
|
/*ARGSUSED*/
|
|
|
|
send_granted(struct file_lock *fl, int opcode)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
|
|
|
CLIENT *cli;
|
|
|
|
static char dummy;
|
|
|
|
struct timeval timeo;
|
|
|
|
int success;
|
|
|
|
static struct nlm_res retval;
|
|
|
|
static struct nlm4_res retval4;
|
|
|
|
|
2007-11-05 02:12:50 +03:00
|
|
|
cli = get_client(fl->addr, (rpcvers_t)
|
|
|
|
((fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS));
|
2000-06-07 18:34:40 +04:00
|
|
|
if (cli == NULL) {
|
2003-03-16 09:55:47 +03:00
|
|
|
syslog(LOG_NOTICE, "failed to get CLIENT for %s.%" PRIu32,
|
|
|
|
fl->client_name, fl->client.svid);
|
2000-06-07 18:34:40 +04:00
|
|
|
/*
|
|
|
|
* 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) {
|
2007-11-05 02:12:50 +03:00
|
|
|
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;
|
2000-06-07 18:34:40 +04:00
|
|
|
syslog(LOG_DEBUG, "sending v4 reply%s",
|
|
|
|
(fl->flags & LOCK_ASYNC) ? " (async)":"");
|
|
|
|
if (fl->flags & LOCK_ASYNC) {
|
|
|
|
success = clnt_call(cli, NLM4_GRANTED_MSG,
|
2007-11-05 02:12:50 +03:00
|
|
|
xdr_nlm4_testargs, &result, xdr_void, &dummy, timeo);
|
2000-06-07 18:34:40 +04:00
|
|
|
} else {
|
|
|
|
success = clnt_call(cli, NLM4_GRANTED,
|
2007-11-05 02:12:50 +03:00
|
|
|
xdr_nlm4_testargs, &result, xdr_nlm4_res,
|
2000-06-07 18:34:40 +04:00
|
|
|
&retval4, timeo);
|
|
|
|
}
|
|
|
|
} else {
|
2007-11-05 02:12:50 +03:00
|
|
|
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;
|
2000-06-07 18:34:40 +04:00
|
|
|
syslog(LOG_DEBUG, "sending v1 reply%s",
|
|
|
|
(fl->flags & LOCK_ASYNC) ? " (async)":"");
|
|
|
|
if (fl->flags & LOCK_ASYNC) {
|
|
|
|
success = clnt_call(cli, NLM_GRANTED_MSG,
|
2007-11-05 02:12:50 +03:00
|
|
|
xdr_nlm_testargs, &result, xdr_void, &dummy, timeo);
|
2000-06-07 18:34:40 +04:00
|
|
|
} else {
|
|
|
|
success = clnt_call(cli, NLM_GRANTED,
|
2007-11-05 02:12:50 +03:00
|
|
|
xdr_nlm_testargs, &result, xdr_nlm_res,
|
2000-06-07 18:34:40 +04:00
|
|
|
&retval, timeo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (debug_level > 2)
|
|
|
|
syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted",
|
|
|
|
success, clnt_sperrno(success));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
enum nlm_stats
|
2007-11-05 02:12:50 +03:00
|
|
|
do_unlock(struct file_lock *rfl)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
|
|
|
struct file_lock *fl;
|
|
|
|
int error;
|
|
|
|
int lockst;
|
|
|
|
|
2002-07-26 05:00:39 +04:00
|
|
|
/* unlock the file: closing is enough ! */
|
2007-11-05 02:12:50 +03:00
|
|
|
if (close(rfl->fd) == -1) {
|
2000-06-07 18:34:40 +04:00
|
|
|
if (errno == ESTALE)
|
|
|
|
error = nlm4_stale_fh;
|
|
|
|
else
|
|
|
|
error = nlm4_failed;
|
2005-06-02 13:30:27 +04:00
|
|
|
if ((rfl->flags & LOCK_V4) == 0)
|
2000-06-07 18:34:40 +04:00
|
|
|
error = nlm_denied;
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_NOTICE, "close failed (from %s) (%m)",
|
|
|
|
rfl->client_name);
|
2000-06-07 18:34:40 +04:00
|
|
|
} else {
|
2005-06-02 13:30:27 +04:00
|
|
|
error = (rfl->flags & LOCK_V4) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_granted : nlm_granted;
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
|
|
|
LIST_REMOVE(rfl, lcklst);
|
|
|
|
|
|
|
|
/* process the next LKST_WAITING lock request for this fh */
|
2003-03-14 17:03:00 +03:00
|
|
|
LIST_FOREACH(fl, &lcklst_head, lcklst) {
|
2000-06-07 18:34:40 +04:00
|
|
|
if (fl->status != LKST_WAITING ||
|
2006-08-08 17:10:05 +04:00
|
|
|
fhcmp(&rfl->filehandle, &fl->filehandle) != 0)
|
2000-06-07 18:34:40 +04:00
|
|
|
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) ?
|
2007-11-05 02:12:50 +03:00
|
|
|
(enum nlm_stats)nlm4_granted : nlm_granted);
|
2000-06-07 18:34:40 +04:00
|
|
|
break;
|
|
|
|
case nlm4_blocked:
|
|
|
|
/* case nlm_blocked: same as nlm4_blocked */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
lfree(fl);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2001-10-23 23:54:53 +04:00
|
|
|
lfree(rfl);
|
2000-06-07 18:34:40 +04:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-11-05 02:12:50 +03:00
|
|
|
siglock(void)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
|
|
|
sigset_t block;
|
|
|
|
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)sigemptyset(&block);
|
|
|
|
(void)sigaddset(&block, SIGCHLD);
|
2000-06-07 18:34:40 +04:00
|
|
|
|
|
|
|
if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_WARNING, "siglock failed (%m)");
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-11-05 02:12:50 +03:00
|
|
|
sigunlock(void)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
|
|
|
sigset_t block;
|
|
|
|
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)sigemptyset(&block);
|
|
|
|
(void)sigaddset(&block, SIGCHLD);
|
2000-06-07 18:34:40 +04:00
|
|
|
|
|
|
|
if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_WARNING, "sigunlock failed (%m)");
|
2000-06-07 18:34:40 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* monitor a host through rpc.statd, and keep a ref count */
|
|
|
|
void
|
2007-11-05 02:12:50 +03:00
|
|
|
do_mon(const char *hostname)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
2007-11-05 02:12:50 +03:00
|
|
|
static char localhost[] = "localhost";
|
2000-06-07 18:34:40 +04:00
|
|
|
struct host *hp;
|
|
|
|
struct mon my_mon;
|
2007-11-05 02:12:50 +03:00
|
|
|
struct sm_stat_res result;
|
2000-06-07 18:34:40 +04:00
|
|
|
int retval;
|
|
|
|
|
2003-03-14 17:03:00 +03:00
|
|
|
LIST_FOREACH(hp, &hostlst_head, hostlst) {
|
2000-06-07 18:34:40 +04:00
|
|
|
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));
|
2003-06-19 15:13:06 +04:00
|
|
|
if (hp == NULL) {
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_WARNING, "can't monitor host %s (%m)",
|
2003-06-19 15:13:06 +04:00
|
|
|
hostname);
|
|
|
|
return;
|
|
|
|
}
|
2007-11-05 02:12:50 +03:00
|
|
|
(void)strlcpy(hp->name, hostname, sizeof(hp->name));
|
2000-06-07 18:34:40 +04:00
|
|
|
hp->refcnt = 1;
|
2007-11-05 02:12:50 +03:00
|
|
|
syslog(LOG_DEBUG, "monitoring host %s", hostname);
|
|
|
|
(void)memset(&my_mon, 0, sizeof(my_mon));
|
2000-06-07 18:34:40 +04:00
|
|
|
my_mon.mon_id.mon_name = hp->name;
|
2007-11-05 02:12:50 +03:00
|
|
|
my_mon.mon_id.my_id.my_name = localhost;
|
2000-06-07 18:34:40 +04:00
|
|
|
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;
|
2007-11-05 02:12:50 +03:00
|
|
|
if ((retval = callrpc(localhost, SM_PROG, SM_VERS, SM_MON, xdr_mon,
|
|
|
|
(void *)&my_mon, xdr_sm_stat_res, (void *)&result)) != 0) {
|
|
|
|
syslog(LOG_WARNING, "rpc to statd failed (%s)",
|
2000-06-07 18:34:40 +04:00
|
|
|
clnt_sperrno((enum clnt_stat)retval));
|
|
|
|
free(hp);
|
|
|
|
return;
|
|
|
|
}
|
2007-11-05 02:12:50 +03:00
|
|
|
if (result.res_stat == stat_fail) {
|
2000-06-07 18:34:40 +04:00
|
|
|
syslog(LOG_WARNING, "statd failed");
|
|
|
|
free(hp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LIST_INSERT_HEAD(&hostlst_head, hp, hostlst);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-11-05 02:12:50 +03:00
|
|
|
notify(const char *hostname, int state)
|
2000-06-07 18:34:40 +04:00
|
|
|
{
|
|
|
|
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();
|
|
|
|
}
|