1*5b133f3fSguenther /* $OpenBSD: lockd_lock.c,v 1.12 2023/03/08 04:43:15 guenther Exp $ */
25507b659Ssturm
35507b659Ssturm /*
45507b659Ssturm * Copyright (c) 2000 Manuel Bouyer.
55507b659Ssturm *
65507b659Ssturm * Redistribution and use in source and binary forms, with or without
75507b659Ssturm * modification, are permitted provided that the following conditions
85507b659Ssturm * are met:
95507b659Ssturm * 1. Redistributions of source code must retain the above copyright
105507b659Ssturm * notice, this list of conditions and the following disclaimer.
115507b659Ssturm * 2. Redistributions in binary form must reproduce the above copyright
125507b659Ssturm * notice, this list of conditions and the following disclaimer in the
135507b659Ssturm * documentation and/or other materials provided with the distribution.
145507b659Ssturm *
155507b659Ssturm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
165507b659Ssturm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
175507b659Ssturm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
185507b659Ssturm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
195507b659Ssturm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
205507b659Ssturm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
215507b659Ssturm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
225507b659Ssturm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
235507b659Ssturm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
245507b659Ssturm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
255507b659Ssturm * SUCH DAMAGE.
265507b659Ssturm *
275507b659Ssturm */
285507b659Ssturm
29b9fc9a72Sderaadt #include <sys/socket.h>
30b9fc9a72Sderaadt #include <sys/stat.h>
31b9fc9a72Sderaadt #include <sys/mount.h>
32b9fc9a72Sderaadt #include <sys/wait.h>
335507b659Ssturm #include <stdio.h>
345507b659Ssturm #include <stdlib.h>
355507b659Ssturm #include <unistd.h>
365507b659Ssturm #include <fcntl.h>
375507b659Ssturm #include <inttypes.h>
385507b659Ssturm #include <syslog.h>
395507b659Ssturm #include <errno.h>
405507b659Ssturm #include <string.h>
415507b659Ssturm #include <signal.h>
425507b659Ssturm #include <rpc/rpc.h>
435507b659Ssturm #include <rpcsvc/sm_inter.h>
445507b659Ssturm #include <rpcsvc/nlm_prot.h>
455507b659Ssturm #include "lockd_lock.h"
465507b659Ssturm #include "lockd.h"
475507b659Ssturm
485507b659Ssturm /* A set of utilities for managing file locking */
495507b659Ssturm LIST_HEAD(lcklst_head, file_lock);
505507b659Ssturm struct lcklst_head lcklst_head = LIST_HEAD_INITIALIZER(lcklst_head);
515507b659Ssturm
525507b659Ssturm #define FHANDLE_SIZE_MAX 1024 /* arbitrary big enough value */
535507b659Ssturm typedef struct {
545507b659Ssturm size_t fhsize;
555507b659Ssturm char *fhdata;
565507b659Ssturm } nfs_fhandle_t;
575507b659Ssturm
585507b659Ssturm static int
fhcmp(const nfs_fhandle_t * fh1,const nfs_fhandle_t * fh2)595507b659Ssturm fhcmp(const nfs_fhandle_t *fh1, const nfs_fhandle_t *fh2)
605507b659Ssturm {
61d2a74e6aSsturm return memcmp(fh1->fhdata, fh2->fhdata, sizeof(fhandle_t));
625507b659Ssturm }
635507b659Ssturm
645507b659Ssturm static int
fhconv(nfs_fhandle_t * fh,const netobj * rfh)655507b659Ssturm fhconv(nfs_fhandle_t *fh, const netobj *rfh)
665507b659Ssturm {
675507b659Ssturm size_t sz;
685507b659Ssturm
695507b659Ssturm sz = rfh->n_len;
705507b659Ssturm if (sz > FHANDLE_SIZE_MAX) {
715507b659Ssturm syslog(LOG_DEBUG,
725507b659Ssturm "received fhandle size %zd, max supported size %d",
735507b659Ssturm sz, FHANDLE_SIZE_MAX);
745507b659Ssturm errno = EINVAL;
755507b659Ssturm return -1;
765507b659Ssturm }
775507b659Ssturm fh->fhdata = malloc(sz);
785507b659Ssturm if (fh->fhdata == NULL) {
795507b659Ssturm return -1;
805507b659Ssturm }
815507b659Ssturm fh->fhsize = sz;
825507b659Ssturm memcpy(fh->fhdata, rfh->n_bytes, sz);
835507b659Ssturm return 0;
845507b659Ssturm }
855507b659Ssturm
865507b659Ssturm static void
fhfree(nfs_fhandle_t * fh)875507b659Ssturm fhfree(nfs_fhandle_t *fh)
885507b659Ssturm {
895507b659Ssturm
905507b659Ssturm free(fh->fhdata);
915507b659Ssturm }
925507b659Ssturm
935507b659Ssturm /* struct describing a lock */
945507b659Ssturm struct file_lock {
955507b659Ssturm LIST_ENTRY(file_lock) lcklst;
965507b659Ssturm nfs_fhandle_t filehandle; /* NFS filehandle */
975507b659Ssturm struct sockaddr_in *addr;
985507b659Ssturm struct nlm4_holder client; /* lock holder */
995507b659Ssturm netobj client_cookie; /* cookie sent by the client */
1005507b659Ssturm char client_name[128];
1015507b659Ssturm int nsm_status; /* status from the remote lock manager */
1025507b659Ssturm int status; /* lock status, see below */
1035507b659Ssturm int flags; /* lock flags, see lockd_lock.h */
1045507b659Ssturm pid_t locker; /* pid of the child process trying to get the lock */
1055507b659Ssturm int fd; /* file descriptor for this lock */
1065507b659Ssturm };
1075507b659Ssturm
1085507b659Ssturm /* lock status */
1095507b659Ssturm #define LKST_LOCKED 1 /* lock is locked */
1105507b659Ssturm #define LKST_WAITING 2 /* file is already locked by another host */
1115507b659Ssturm #define LKST_PROCESSING 3 /* child is trying to acquire the lock */
112d595756bSjmc #define LKST_DYING 4 /* must die when we get news from the child */
1135507b659Ssturm
1145507b659Ssturm static struct file_lock *lalloc(void);
1155507b659Ssturm void lfree(struct file_lock *);
1165507b659Ssturm enum nlm_stats do_lock(struct file_lock *, int);
1175507b659Ssturm enum nlm_stats do_unlock(struct file_lock *);
1185507b659Ssturm void send_granted(struct file_lock *, int);
1195507b659Ssturm void siglock(void);
1205507b659Ssturm void sigunlock(void);
1215507b659Ssturm
12275a10426Ssturm /* list of hosts we monitor */
12375a10426Ssturm LIST_HEAD(hostlst_head, host);
12475a10426Ssturm struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head);
12575a10426Ssturm
12675a10426Ssturm /* struct describing a lock */
12775a10426Ssturm struct host {
12875a10426Ssturm LIST_ENTRY(host) hostlst;
12975a10426Ssturm char name[SM_MAXSTRLEN+1];
13075a10426Ssturm int refcnt;
13175a10426Ssturm };
13275a10426Ssturm
13375a10426Ssturm void do_mon(const char *);
13475a10426Ssturm
1355507b659Ssturm #define LL_FH 0x01
1365507b659Ssturm #define LL_NAME 0x02
1375507b659Ssturm #define LL_SVID 0x04
1385507b659Ssturm
1395507b659Ssturm static struct file_lock *lock_lookup(struct file_lock *, int);
1405507b659Ssturm
1415507b659Ssturm /*
1425507b659Ssturm * lock_lookup: lookup a matching lock.
1435507b659Ssturm * called with siglock held.
1445507b659Ssturm */
1455507b659Ssturm static struct file_lock *
lock_lookup(struct file_lock * newfl,int flags)1465507b659Ssturm lock_lookup(struct file_lock *newfl, int flags)
1475507b659Ssturm {
1485507b659Ssturm struct file_lock *fl;
1495507b659Ssturm
1505507b659Ssturm LIST_FOREACH(fl, &lcklst_head, lcklst) {
1515507b659Ssturm if ((flags & LL_SVID) != 0 &&
1525507b659Ssturm newfl->client.svid != fl->client.svid)
1535507b659Ssturm continue;
1545507b659Ssturm if ((flags & LL_NAME) != 0 &&
1555507b659Ssturm strcmp(newfl->client_name, fl->client_name) != 0)
1565507b659Ssturm continue;
1575507b659Ssturm if ((flags & LL_FH) != 0 &&
1585507b659Ssturm fhcmp(&newfl->filehandle, &fl->filehandle) != 0)
1595507b659Ssturm continue;
1605507b659Ssturm /* found */
1615507b659Ssturm break;
1625507b659Ssturm }
1635507b659Ssturm
1645507b659Ssturm return fl;
1655507b659Ssturm }
1665507b659Ssturm
1675507b659Ssturm /*
1685507b659Ssturm * testlock(): inform the caller if the requested lock would be granted or not
1695507b659Ssturm * returns NULL if lock would granted, or pointer to the current nlm4_holder
1705507b659Ssturm * otherwise.
1715507b659Ssturm */
1725507b659Ssturm
1735507b659Ssturm struct nlm4_holder *
testlock(struct nlm4_lock * lock,int flags)1745507b659Ssturm testlock(struct nlm4_lock *lock, int flags)
1755507b659Ssturm {
1765507b659Ssturm struct file_lock *fl;
1775507b659Ssturm nfs_fhandle_t filehandle;
1785507b659Ssturm
1795507b659Ssturm /* convert lock to a local filehandle */
1805507b659Ssturm if (fhconv(&filehandle, &lock->fh)) {
1815507b659Ssturm syslog(LOG_NOTICE, "fhconv failed (%m)");
1825507b659Ssturm return NULL; /* XXX */
1835507b659Ssturm }
1845507b659Ssturm
1855507b659Ssturm siglock();
1865507b659Ssturm /* search through the list for lock holder */
1875507b659Ssturm LIST_FOREACH(fl, &lcklst_head, lcklst) {
1885507b659Ssturm if (fl->status != LKST_LOCKED)
1895507b659Ssturm continue;
1905507b659Ssturm if (fhcmp(&fl->filehandle, &filehandle) != 0)
1915507b659Ssturm continue;
1925507b659Ssturm /* got it ! */
1935507b659Ssturm syslog(LOG_DEBUG, "test for %s: found lock held by %s",
1945507b659Ssturm lock->caller_name, fl->client_name);
1955507b659Ssturm sigunlock();
1965507b659Ssturm fhfree(&filehandle);
1975507b659Ssturm return (&fl->client);
1985507b659Ssturm }
1995507b659Ssturm /* not found */
2005507b659Ssturm sigunlock();
2015507b659Ssturm fhfree(&filehandle);
2025507b659Ssturm syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name);
2035507b659Ssturm return NULL;
2045507b659Ssturm }
2055507b659Ssturm
2065507b659Ssturm /*
2075507b659Ssturm * getlock: try to acquire the lock.
2085507b659Ssturm * If file is already locked and we can sleep, put the lock in the list with
2095507b659Ssturm * status LKST_WAITING; it'll be processed later.
2105507b659Ssturm * Otherwise try to lock. If we're allowed to block, fork a child which
2115507b659Ssturm * will do the blocking lock.
2125507b659Ssturm */
2135507b659Ssturm enum nlm_stats
getlock(nlm4_lockargs * lckarg,struct svc_req * rqstp,int flags)2145507b659Ssturm getlock(nlm4_lockargs * lckarg, struct svc_req *rqstp, int flags)
2155507b659Ssturm {
2165507b659Ssturm struct file_lock *fl, *newfl;
2175507b659Ssturm enum nlm_stats retval;
2185507b659Ssturm struct sockaddr_in *addr;
2195507b659Ssturm
2205507b659Ssturm if (grace_expired == 0 && lckarg->reclaim == 0)
2215507b659Ssturm return (flags & LOCK_V4) ?
2225507b659Ssturm nlm4_denied_grace_period : nlm_denied_grace_period;
2235507b659Ssturm
2245507b659Ssturm /* allocate new file_lock for this request */
2255507b659Ssturm newfl = lalloc();
2265507b659Ssturm if (newfl == NULL) {
2275507b659Ssturm syslog(LOG_NOTICE, "malloc failed (%m)");
2285507b659Ssturm /* failed */
2295507b659Ssturm return (flags & LOCK_V4) ?
2305507b659Ssturm nlm4_denied_nolock : nlm_denied_nolocks;
2315507b659Ssturm }
2325507b659Ssturm if (fhconv(&newfl->filehandle, &lckarg->alock.fh)) {
2335507b659Ssturm syslog(LOG_NOTICE, "fhconv failed (%m)");
2345507b659Ssturm lfree(newfl);
2355507b659Ssturm /* failed */
2365507b659Ssturm return (flags & LOCK_V4) ?
2375507b659Ssturm nlm4_denied_nolock : nlm_denied_nolocks;
2385507b659Ssturm }
2395507b659Ssturm addr = svc_getcaller(rqstp->rq_xprt);
2405507b659Ssturm newfl->addr = malloc(addr->sin_len);
2415507b659Ssturm if (newfl->addr == NULL) {
2425507b659Ssturm syslog(LOG_NOTICE, "malloc failed (%m)");
2435507b659Ssturm lfree(newfl);
2445507b659Ssturm /* failed */
2455507b659Ssturm return (flags & LOCK_V4) ?
2465507b659Ssturm nlm4_denied_nolock : nlm_denied_nolocks;
2475507b659Ssturm }
2485507b659Ssturm memcpy(newfl->addr, addr, addr->sin_len);
2495507b659Ssturm newfl->client.exclusive = lckarg->exclusive;
2505507b659Ssturm newfl->client.svid = lckarg->alock.svid;
2515507b659Ssturm newfl->client.oh.n_bytes = malloc(lckarg->alock.oh.n_len);
2525507b659Ssturm if (newfl->client.oh.n_bytes == NULL) {
2535507b659Ssturm syslog(LOG_NOTICE, "malloc failed (%m)");
2545507b659Ssturm lfree(newfl);
2555507b659Ssturm return (flags & LOCK_V4) ?
2565507b659Ssturm nlm4_denied_nolock : nlm_denied_nolocks;
2575507b659Ssturm }
2585507b659Ssturm newfl->client.oh.n_len = lckarg->alock.oh.n_len;
2595507b659Ssturm memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes,
2605507b659Ssturm lckarg->alock.oh.n_len);
2615507b659Ssturm newfl->client.l_offset = lckarg->alock.l_offset;
2625507b659Ssturm newfl->client.l_len = lckarg->alock.l_len;
2635507b659Ssturm newfl->client_cookie.n_len = lckarg->cookie.n_len;
2645507b659Ssturm newfl->client_cookie.n_bytes = malloc(lckarg->cookie.n_len);
2655507b659Ssturm if (newfl->client_cookie.n_bytes == NULL) {
2665507b659Ssturm syslog(LOG_NOTICE, "malloc failed (%m)");
2675507b659Ssturm lfree(newfl);
2685507b659Ssturm return (flags & LOCK_V4) ?
2695507b659Ssturm nlm4_denied_nolock : nlm_denied_nolocks;
2705507b659Ssturm }
2715507b659Ssturm memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes,
2725507b659Ssturm lckarg->cookie.n_len);
2735507b659Ssturm strlcpy(newfl->client_name, lckarg->alock.caller_name,
2745507b659Ssturm sizeof(newfl->client_name));
2755507b659Ssturm newfl->nsm_status = lckarg->state;
2765507b659Ssturm newfl->status = 0;
2775507b659Ssturm newfl->flags = flags;
2785507b659Ssturm siglock();
2795507b659Ssturm /* look for a lock rq from this host for this fh */
2805507b659Ssturm fl = lock_lookup(newfl, LL_FH|LL_NAME|LL_SVID);
2815507b659Ssturm if (fl) {
2825507b659Ssturm /* already locked by this host ??? */
2835507b659Ssturm sigunlock();
2845507b659Ssturm syslog(LOG_NOTICE, "duplicate lock from %s.%"
2855507b659Ssturm PRIu32,
2865507b659Ssturm newfl->client_name, newfl->client.svid);
2875507b659Ssturm lfree(newfl);
2885507b659Ssturm switch(fl->status) {
2895507b659Ssturm case LKST_LOCKED:
2905507b659Ssturm return (flags & LOCK_V4) ?
2915507b659Ssturm nlm4_granted : nlm_granted;
2925507b659Ssturm case LKST_WAITING:
2935507b659Ssturm case LKST_PROCESSING:
2945507b659Ssturm return (flags & LOCK_V4) ?
2955507b659Ssturm nlm4_blocked : nlm_blocked;
2965507b659Ssturm case LKST_DYING:
2975507b659Ssturm return (flags & LOCK_V4) ?
2985507b659Ssturm nlm4_denied : nlm_denied;
2995507b659Ssturm default:
3005507b659Ssturm syslog(LOG_NOTICE, "bad status %d",
3015507b659Ssturm fl->status);
3025507b659Ssturm return (flags & LOCK_V4) ?
3035507b659Ssturm nlm4_failed : nlm_denied;
3045507b659Ssturm }
3055507b659Ssturm /* NOTREACHED */
3065507b659Ssturm }
3075507b659Ssturm fl = lock_lookup(newfl, LL_FH);
3085507b659Ssturm if (fl) {
3095507b659Ssturm /*
3105507b659Ssturm * We already have a lock for this file.
3115507b659Ssturm * Put this one in waiting state if allowed to block
3125507b659Ssturm */
3135507b659Ssturm if (lckarg->block) {
3145507b659Ssturm syslog(LOG_DEBUG, "lock from %s.%" PRIu32 ": "
3155507b659Ssturm "already locked, waiting",
3165507b659Ssturm lckarg->alock.caller_name,
3175507b659Ssturm lckarg->alock.svid);
3185507b659Ssturm newfl->status = LKST_WAITING;
3195507b659Ssturm LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst);
32075a10426Ssturm do_mon(lckarg->alock.caller_name);
3215507b659Ssturm sigunlock();
3225507b659Ssturm return (flags & LOCK_V4) ?
3235507b659Ssturm nlm4_blocked : nlm_blocked;
3245507b659Ssturm } else {
3255507b659Ssturm sigunlock();
3265507b659Ssturm syslog(LOG_DEBUG, "lock from %s.%" PRIu32 ": "
3275507b659Ssturm "already locked, failed",
3285507b659Ssturm lckarg->alock.caller_name,
3295507b659Ssturm lckarg->alock.svid);
3305507b659Ssturm lfree(newfl);
3315507b659Ssturm return (flags & LOCK_V4) ?
3325507b659Ssturm nlm4_denied : nlm_denied;
3335507b659Ssturm }
3345507b659Ssturm /* NOTREACHED */
3355507b659Ssturm }
3365507b659Ssturm
3375507b659Ssturm /* no entry for this file yet; add to list */
3385507b659Ssturm LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst);
3395507b659Ssturm /* do the lock */
3405507b659Ssturm retval = do_lock(newfl, lckarg->block);
3415507b659Ssturm switch (retval) {
3425507b659Ssturm case nlm4_granted:
3435507b659Ssturm /* case nlm_granted: is the same as nlm4_granted */
3445507b659Ssturm case nlm4_blocked:
3455507b659Ssturm /* case nlm_blocked: is the same as nlm4_blocked */
34675a10426Ssturm do_mon(lckarg->alock.caller_name);
3475507b659Ssturm break;
3485507b659Ssturm default:
3495507b659Ssturm lfree(newfl);
3505507b659Ssturm break;
3515507b659Ssturm }
3525507b659Ssturm sigunlock();
3535507b659Ssturm return retval;
3545507b659Ssturm }
3555507b659Ssturm
3565507b659Ssturm /* unlock a filehandle */
3575507b659Ssturm enum nlm_stats
unlock(nlm4_lock * lck,int flags)3585507b659Ssturm unlock(nlm4_lock *lck, int flags)
3595507b659Ssturm {
3605507b659Ssturm struct file_lock *fl;
3615507b659Ssturm nfs_fhandle_t filehandle;
3625507b659Ssturm int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
3635507b659Ssturm
3645507b659Ssturm if (fhconv(&filehandle, &lck->fh)) {
3655507b659Ssturm syslog(LOG_NOTICE, "fhconv failed (%m)");
3665507b659Ssturm return (flags & LOCK_V4) ? nlm4_denied : nlm_denied;
3675507b659Ssturm }
3685507b659Ssturm siglock();
3695507b659Ssturm LIST_FOREACH(fl, &lcklst_head, lcklst) {
3705507b659Ssturm if (strcmp(fl->client_name, lck->caller_name) ||
3715507b659Ssturm fhcmp(&filehandle, &fl->filehandle) != 0 ||
3725507b659Ssturm fl->client.oh.n_len != lck->oh.n_len ||
3735507b659Ssturm memcmp(fl->client.oh.n_bytes, lck->oh.n_bytes,
3745507b659Ssturm fl->client.oh.n_len) != 0 ||
3755507b659Ssturm fl->client.svid != lck->svid)
3765507b659Ssturm continue;
3775507b659Ssturm /* Got it, unlock and remove from the queue */
3785507b659Ssturm syslog(LOG_DEBUG, "unlock from %s.%" PRIu32 ": found struct, "
3795507b659Ssturm "status %d", lck->caller_name, lck->svid, fl->status);
3805507b659Ssturm switch (fl->status) {
3815507b659Ssturm case LKST_LOCKED:
3825507b659Ssturm err = do_unlock(fl);
3835507b659Ssturm break;
3845507b659Ssturm case LKST_WAITING:
3855507b659Ssturm /* remove from the list */
3865507b659Ssturm LIST_REMOVE(fl, lcklst);
3875507b659Ssturm lfree(fl);
3885507b659Ssturm break;
3895507b659Ssturm case LKST_PROCESSING:
3905507b659Ssturm /*
3915507b659Ssturm * being handled by a child; will clean up
3925507b659Ssturm * when the child exits
3935507b659Ssturm */
3945507b659Ssturm fl->status = LKST_DYING;
3955507b659Ssturm break;
3965507b659Ssturm case LKST_DYING:
3975507b659Ssturm /* nothing to do */
3985507b659Ssturm break;
3995507b659Ssturm default:
4003a50f0a9Sjmc syslog(LOG_NOTICE, "unknown status %d for %s",
4015507b659Ssturm fl->status, fl->client_name);
4025507b659Ssturm }
4035507b659Ssturm sigunlock();
4045507b659Ssturm fhfree(&filehandle);
4055507b659Ssturm return err;
4065507b659Ssturm }
4075507b659Ssturm sigunlock();
4085507b659Ssturm /* didn't find a matching entry; log anyway */
4095507b659Ssturm syslog(LOG_NOTICE, "no matching entry for %s",
4105507b659Ssturm lck->caller_name);
4115507b659Ssturm fhfree(&filehandle);
4125507b659Ssturm return (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
4135507b659Ssturm }
4145507b659Ssturm
4155507b659Ssturm static struct file_lock *
lalloc(void)4165507b659Ssturm lalloc(void)
4175507b659Ssturm {
4185507b659Ssturm return calloc(1, sizeof(struct file_lock));
4195507b659Ssturm }
4205507b659Ssturm
4215507b659Ssturm void
lfree(struct file_lock * fl)4225507b659Ssturm lfree(struct file_lock *fl)
4235507b659Ssturm {
4245507b659Ssturm free(fl->addr);
4255507b659Ssturm free(fl->client.oh.n_bytes);
4265507b659Ssturm free(fl->client_cookie.n_bytes);
4275507b659Ssturm fhfree(&fl->filehandle);
4285507b659Ssturm free(fl);
4295507b659Ssturm }
4305507b659Ssturm
4315507b659Ssturm void
sigchild_handler(int sig)4325507b659Ssturm sigchild_handler(int sig)
4335507b659Ssturm {
4345507b659Ssturm int sstatus;
4355507b659Ssturm pid_t pid;
4365507b659Ssturm struct file_lock *fl;
4375507b659Ssturm
4385507b659Ssturm for (;;) {
4395507b659Ssturm pid = wait4(-1, &sstatus, WNOHANG, NULL);
4405507b659Ssturm if (pid == -1) {
4415507b659Ssturm if (errno != ECHILD)
4425507b659Ssturm syslog(LOG_NOTICE, "wait failed (%m)");
4435507b659Ssturm else
4445507b659Ssturm syslog(LOG_DEBUG, "wait failed (%m)");
4455507b659Ssturm return;
4465507b659Ssturm }
4475507b659Ssturm if (pid == 0) {
4485507b659Ssturm /* no more child to handle yet */
4495507b659Ssturm return;
4505507b659Ssturm }
4515507b659Ssturm /*
4525507b659Ssturm * if we're here we have a child that exited
4535507b659Ssturm * Find the associated file_lock.
4545507b659Ssturm */
4555507b659Ssturm LIST_FOREACH(fl, &lcklst_head, lcklst) {
4565507b659Ssturm if (pid == fl->locker)
4575507b659Ssturm break;
4585507b659Ssturm }
4595507b659Ssturm if (fl == NULL) {
4605507b659Ssturm syslog(LOG_NOTICE, "unknown child %d", pid);
4615507b659Ssturm } else {
4625507b659Ssturm /* protect from pid reusing. */
4635507b659Ssturm fl->locker = 0;
4645507b659Ssturm if (!WIFEXITED(sstatus) || WEXITSTATUS(sstatus) != 0) {
4655507b659Ssturm syslog(LOG_NOTICE, "child %d failed", pid);
4665507b659Ssturm /*
4675507b659Ssturm * can't do much here; we can't reply
4685507b659Ssturm * anything but OK for blocked locks
4695507b659Ssturm * Eventually the client will time out
4705507b659Ssturm * and retry.
4715507b659Ssturm */
4725507b659Ssturm do_unlock(fl);
4735507b659Ssturm return;
4745507b659Ssturm }
4755507b659Ssturm
4765507b659Ssturm /* check lock status */
4775507b659Ssturm syslog(LOG_DEBUG, "processing child %d, status %d",
4785507b659Ssturm pid, fl->status);
4795507b659Ssturm switch(fl->status) {
4805507b659Ssturm case LKST_PROCESSING:
4815507b659Ssturm fl->status = LKST_LOCKED;
4825507b659Ssturm send_granted(fl, (fl->flags & LOCK_V4) ?
4835507b659Ssturm nlm4_granted : nlm_granted);
4845507b659Ssturm break;
4855507b659Ssturm case LKST_DYING:
4865507b659Ssturm do_unlock(fl);
4875507b659Ssturm break;
4885507b659Ssturm default:
4895507b659Ssturm syslog(LOG_NOTICE, "bad lock status (%d) for"
4905507b659Ssturm " child %d", fl->status, pid);
4915507b659Ssturm }
4925507b659Ssturm }
4935507b659Ssturm }
4945507b659Ssturm }
4955507b659Ssturm
4965507b659Ssturm /*
4975507b659Ssturm *
498d5a3b0c4Sjmc * try to acquire the lock described by fl. Eventually fork a child to do a
4995507b659Ssturm * blocking lock if allowed and required.
5005507b659Ssturm */
5015507b659Ssturm
5025507b659Ssturm enum nlm_stats
do_lock(struct file_lock * fl,int block)5035507b659Ssturm do_lock(struct file_lock *fl, int block)
5045507b659Ssturm {
5055507b659Ssturm int lflags, error;
5065507b659Ssturm struct stat st;
5075507b659Ssturm
5085507b659Ssturm fl->fd = fhopen((fhandle_t *)fl->filehandle.fhdata, O_RDWR);
509df69c215Sderaadt if (fl->fd == -1) {
5105507b659Ssturm switch (errno) {
5115507b659Ssturm case ESTALE:
5125507b659Ssturm error = nlm4_stale_fh;
5135507b659Ssturm break;
5145507b659Ssturm case EROFS:
5155507b659Ssturm error = nlm4_rofs;
5165507b659Ssturm break;
5175507b659Ssturm default:
5185507b659Ssturm error = nlm4_failed;
5195507b659Ssturm }
5205507b659Ssturm if ((fl->flags & LOCK_V4) == 0)
5215507b659Ssturm error = nlm_denied;
5225507b659Ssturm syslog(LOG_NOTICE, "fhopen failed (from %s) (%m)",
5235507b659Ssturm fl->client_name);
5245507b659Ssturm LIST_REMOVE(fl, lcklst);
5255507b659Ssturm return error;
5265507b659Ssturm }
527df69c215Sderaadt if (fstat(fl->fd, &st) == -1) {
5285507b659Ssturm syslog(LOG_NOTICE, "fstat failed (from %s) (%m)",
5295507b659Ssturm fl->client_name);
5305507b659Ssturm }
5315507b659Ssturm syslog(LOG_DEBUG, "lock from %s.%" PRIu32 " for file%s%s: "
53279d31f91Sderaadt "dev %u ino %llu (uid %d), flags %d",
5335507b659Ssturm fl->client_name, fl->client.svid,
5345507b659Ssturm fl->client.exclusive ? " (exclusive)":"", block ? " (block)":"",
53579d31f91Sderaadt st.st_dev, (unsigned long long)st.st_ino, st.st_uid, fl->flags);
5365507b659Ssturm lflags = LOCK_NB;
5375507b659Ssturm if (fl->client.exclusive == 0)
5385507b659Ssturm lflags |= LOCK_SH;
5395507b659Ssturm else
5405507b659Ssturm lflags |= LOCK_EX;
5415507b659Ssturm error = flock(fl->fd, lflags);
5425507b659Ssturm if (error != 0 && errno == EAGAIN && block) {
5435507b659Ssturm switch (fl->locker = fork()) {
5445507b659Ssturm case -1: /* fork failed */
5455507b659Ssturm syslog(LOG_NOTICE, "fork failed (%m)");
5465507b659Ssturm LIST_REMOVE(fl, lcklst);
5475507b659Ssturm close(fl->fd);
5485507b659Ssturm return (fl->flags & LOCK_V4) ?
5495507b659Ssturm nlm4_denied_nolock : nlm_denied_nolocks;
5505507b659Ssturm case 0:
5515507b659Ssturm /*
5525507b659Ssturm * Attempt a blocking lock. Will have to call
5535507b659Ssturm * NLM_GRANTED later.
5545507b659Ssturm */
5555507b659Ssturm setproctitle("%s.%" PRIu32,
5565507b659Ssturm fl->client_name, fl->client.svid);
5575507b659Ssturm lflags &= ~LOCK_NB;
5585507b659Ssturm if(flock(fl->fd, lflags) != 0) {
5595507b659Ssturm syslog(LOG_NOTICE, "flock failed (%m)");
5605507b659Ssturm _exit(1);
5615507b659Ssturm }
5625507b659Ssturm /* lock granted */
5635507b659Ssturm _exit(0);
5645507b659Ssturm /*NOTREACHED*/
5655507b659Ssturm default:
5665507b659Ssturm syslog(LOG_DEBUG, "lock request from %s.%" PRIu32 ": "
5675507b659Ssturm "forked %d",
5685507b659Ssturm fl->client_name, fl->client.svid, fl->locker);
5695507b659Ssturm fl->status = LKST_PROCESSING;
5705507b659Ssturm return (fl->flags & LOCK_V4) ?
5715507b659Ssturm nlm4_blocked : nlm_blocked;
5725507b659Ssturm }
5735507b659Ssturm }
5745507b659Ssturm /* non block case */
5755507b659Ssturm if (error != 0) {
5765507b659Ssturm switch (errno) {
5775507b659Ssturm case EAGAIN:
5785507b659Ssturm error = nlm4_denied;
5795507b659Ssturm break;
5805507b659Ssturm case ESTALE:
5815507b659Ssturm error = nlm4_stale_fh;
5825507b659Ssturm break;
5835507b659Ssturm case EROFS:
5845507b659Ssturm error = nlm4_rofs;
5855507b659Ssturm break;
5865507b659Ssturm default:
5875507b659Ssturm error = nlm4_failed;
5885507b659Ssturm }
5895507b659Ssturm if ((fl->flags & LOCK_V4) == 0)
5905507b659Ssturm error = nlm_denied;
5915507b659Ssturm if (errno != EAGAIN)
5925507b659Ssturm syslog(LOG_NOTICE, "flock for %s failed (%m)",
5935507b659Ssturm fl->client_name);
5945507b659Ssturm else syslog(LOG_DEBUG, "flock for %s failed (%m)",
5955507b659Ssturm fl->client_name);
5965507b659Ssturm LIST_REMOVE(fl, lcklst);
5975507b659Ssturm close(fl->fd);
5985507b659Ssturm return error;
5995507b659Ssturm }
6005507b659Ssturm fl->status = LKST_LOCKED;
6015507b659Ssturm return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
6025507b659Ssturm }
6035507b659Ssturm
6045507b659Ssturm void
send_granted(struct file_lock * fl,int opcode)6055507b659Ssturm send_granted(struct file_lock *fl, int opcode)
6065507b659Ssturm {
6075507b659Ssturm CLIENT *cli;
6085507b659Ssturm static char dummy;
6095507b659Ssturm struct timeval timeo;
6105507b659Ssturm int success;
6115507b659Ssturm static struct nlm_res retval;
6125507b659Ssturm static struct nlm4_res retval4;
6135507b659Ssturm
6145507b659Ssturm cli = get_client(fl->addr,
6155507b659Ssturm (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS);
6165507b659Ssturm if (cli == NULL) {
6175507b659Ssturm syslog(LOG_NOTICE, "failed to get CLIENT for %s.%" PRIu32,
6185507b659Ssturm fl->client_name, fl->client.svid);
6195507b659Ssturm /*
6205507b659Ssturm * We fail to notify remote that the lock has been granted.
6215507b659Ssturm * The client will timeout and retry, the lock will be
6225507b659Ssturm * granted at this time.
6235507b659Ssturm */
6245507b659Ssturm return;
6255507b659Ssturm }
6265507b659Ssturm timeo.tv_sec = 0;
6275507b659Ssturm timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */
6285507b659Ssturm
6295507b659Ssturm if (fl->flags & LOCK_V4) {
6305507b659Ssturm static nlm4_testargs result;
6315507b659Ssturm result.cookie = fl->client_cookie;
6325507b659Ssturm result.exclusive = fl->client.exclusive;
6335507b659Ssturm result.alock.caller_name = fl->client_name;
6345507b659Ssturm result.alock.fh.n_len = fl->filehandle.fhsize;
6355507b659Ssturm result.alock.fh.n_bytes = fl->filehandle.fhdata;
6365507b659Ssturm result.alock.oh = fl->client.oh;
6375507b659Ssturm result.alock.svid = fl->client.svid;
6385507b659Ssturm result.alock.l_offset = fl->client.l_offset;
6395507b659Ssturm result.alock.l_len = fl->client.l_len;
6405507b659Ssturm syslog(LOG_DEBUG, "sending v4 reply%s",
6415507b659Ssturm (fl->flags & LOCK_ASYNC) ? " (async)":"");
6425507b659Ssturm if (fl->flags & LOCK_ASYNC) {
6435507b659Ssturm success = clnt_call(cli, NLM4_GRANTED_MSG,
6445507b659Ssturm xdr_nlm4_testargs, &result, xdr_void, &dummy, timeo);
6455507b659Ssturm } else {
6465507b659Ssturm success = clnt_call(cli, NLM4_GRANTED,
6475507b659Ssturm xdr_nlm4_testargs, &result, xdr_nlm4_res,
6485507b659Ssturm &retval4, timeo);
6495507b659Ssturm }
6505507b659Ssturm } else {
6515507b659Ssturm static nlm_testargs result;
6525507b659Ssturm
6535507b659Ssturm result.cookie = fl->client_cookie;
6545507b659Ssturm result.exclusive = fl->client.exclusive;
6555507b659Ssturm result.alock.caller_name = fl->client_name;
6565507b659Ssturm result.alock.fh.n_len = fl->filehandle.fhsize;
6575507b659Ssturm result.alock.fh.n_bytes = fl->filehandle.fhdata;
6585507b659Ssturm result.alock.oh = fl->client.oh;
6595507b659Ssturm result.alock.svid = fl->client.svid;
6605507b659Ssturm result.alock.l_offset =
6615507b659Ssturm (unsigned int)fl->client.l_offset;
6625507b659Ssturm result.alock.l_len =
6635507b659Ssturm (unsigned int)fl->client.l_len;
6645507b659Ssturm syslog(LOG_DEBUG, "sending v1 reply%s",
6655507b659Ssturm (fl->flags & LOCK_ASYNC) ? " (async)":"");
6665507b659Ssturm if (fl->flags & LOCK_ASYNC) {
6675507b659Ssturm success = clnt_call(cli, NLM_GRANTED_MSG,
6685507b659Ssturm xdr_nlm_testargs, &result, xdr_void, &dummy, timeo);
6695507b659Ssturm } else {
6705507b659Ssturm success = clnt_call(cli, NLM_GRANTED,
6715507b659Ssturm xdr_nlm_testargs, &result, xdr_nlm_res,
6725507b659Ssturm &retval, timeo);
6735507b659Ssturm }
6745507b659Ssturm }
6755507b659Ssturm if (debug_level > 2)
6765507b659Ssturm syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted",
6775507b659Ssturm success, clnt_sperrno(success));
6785507b659Ssturm
6795507b659Ssturm }
6805507b659Ssturm
6815507b659Ssturm enum nlm_stats
do_unlock(struct file_lock * rfl)6825507b659Ssturm do_unlock(struct file_lock *rfl)
6835507b659Ssturm {
6845507b659Ssturm struct file_lock *fl;
6855507b659Ssturm int error;
6865507b659Ssturm int lockst;
6875507b659Ssturm
6885507b659Ssturm /* unlock the file: closing is enough ! */
6895507b659Ssturm if (close(rfl->fd) == -1) {
6905507b659Ssturm if (errno == ESTALE)
6915507b659Ssturm error = nlm4_stale_fh;
6925507b659Ssturm else
6935507b659Ssturm error = nlm4_failed;
6945507b659Ssturm if ((rfl->flags & LOCK_V4) == 0)
6955507b659Ssturm error = nlm_denied;
6965507b659Ssturm syslog(LOG_NOTICE, "close failed (from %s) (%m)",
6975507b659Ssturm rfl->client_name);
6985507b659Ssturm } else {
6995507b659Ssturm error = (rfl->flags & LOCK_V4) ?
7005507b659Ssturm nlm4_granted : nlm_granted;
7015507b659Ssturm }
7025507b659Ssturm LIST_REMOVE(rfl, lcklst);
7035507b659Ssturm
7045507b659Ssturm /* process the next LKST_WAITING lock request for this fh */
7055507b659Ssturm LIST_FOREACH(fl, &lcklst_head, lcklst) {
7065507b659Ssturm if (fl->status != LKST_WAITING ||
7075507b659Ssturm fhcmp(&rfl->filehandle, &fl->filehandle) != 0)
7085507b659Ssturm continue;
7095507b659Ssturm
7105507b659Ssturm lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */
7115507b659Ssturm switch (lockst) {
7125507b659Ssturm case nlm4_granted:
7135507b659Ssturm /* case nlm_granted: same as nlm4_granted */
7145507b659Ssturm send_granted(fl, (fl->flags & LOCK_V4) ?
7155507b659Ssturm nlm4_granted : nlm_granted);
7165507b659Ssturm break;
7175507b659Ssturm case nlm4_blocked:
7185507b659Ssturm /* case nlm_blocked: same as nlm4_blocked */
7195507b659Ssturm break;
7205507b659Ssturm default:
7215507b659Ssturm lfree(fl);
7225507b659Ssturm break;
7235507b659Ssturm }
7245507b659Ssturm break;
7255507b659Ssturm }
7265507b659Ssturm lfree(rfl);
7275507b659Ssturm return error;
7285507b659Ssturm }
7295507b659Ssturm
7305507b659Ssturm void
siglock(void)7315507b659Ssturm siglock(void)
7325507b659Ssturm {
7335507b659Ssturm sigset_t block;
7345507b659Ssturm
7355507b659Ssturm sigemptyset(&block);
7365507b659Ssturm sigaddset(&block, SIGCHLD);
7375507b659Ssturm
738df69c215Sderaadt if (sigprocmask(SIG_BLOCK, &block, NULL) == -1) {
7395507b659Ssturm syslog(LOG_WARNING, "siglock failed (%m)");
7405507b659Ssturm }
7415507b659Ssturm }
7425507b659Ssturm
7435507b659Ssturm void
sigunlock(void)7445507b659Ssturm sigunlock(void)
7455507b659Ssturm {
7465507b659Ssturm sigset_t block;
7475507b659Ssturm
7485507b659Ssturm sigemptyset(&block);
7495507b659Ssturm sigaddset(&block, SIGCHLD);
7505507b659Ssturm
751df69c215Sderaadt if (sigprocmask(SIG_UNBLOCK, &block, NULL) == -1) {
7525507b659Ssturm syslog(LOG_WARNING, "sigunlock failed (%m)");
7535507b659Ssturm }
7545507b659Ssturm }
7555507b659Ssturm
75675a10426Ssturm /* monitor a host through rpc.statd, and keep a ref count */
75775a10426Ssturm void
do_mon(const char * hostname)75875a10426Ssturm do_mon(const char *hostname)
75975a10426Ssturm {
76075a10426Ssturm static char localhost[] = "localhost";
76175a10426Ssturm struct host *hp;
76275a10426Ssturm struct mon my_mon;
76375a10426Ssturm struct sm_stat_res result;
76475a10426Ssturm int retval;
76575a10426Ssturm
76675a10426Ssturm LIST_FOREACH(hp, &hostlst_head, hostlst) {
76775a10426Ssturm if (strcmp(hostname, hp->name) == 0) {
76875a10426Ssturm /* already monitored, just bump refcnt */
76975a10426Ssturm hp->refcnt++;
77075a10426Ssturm return;
77175a10426Ssturm }
77275a10426Ssturm }
77375a10426Ssturm /* not found, have to create an entry for it */
77475a10426Ssturm hp = malloc(sizeof(struct host));
77575a10426Ssturm if (hp == NULL) {
77675a10426Ssturm syslog(LOG_WARNING, "can't monitor host %s (%m)", hostname);
77775a10426Ssturm return;
77875a10426Ssturm }
77975a10426Ssturm strlcpy(hp->name, hostname, sizeof(hp->name));
78075a10426Ssturm hp->refcnt = 1;
78175a10426Ssturm syslog(LOG_DEBUG, "monitoring host %s", hostname);
78275a10426Ssturm memset(&my_mon, 0, sizeof(my_mon));
78375a10426Ssturm my_mon.mon_id.mon_name = hp->name;
78475a10426Ssturm my_mon.mon_id.my_id.my_name = localhost;
78575a10426Ssturm my_mon.mon_id.my_id.my_prog = NLM_PROG;
78675a10426Ssturm my_mon.mon_id.my_id.my_vers = NLM_SM;
78775a10426Ssturm my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
78875a10426Ssturm if ((retval = callrpc(localhost, SM_PROG, SM_VERS, SM_MON, xdr_mon,
78975a10426Ssturm (void *)&my_mon, xdr_sm_stat_res, (void *)&result)) != 0) {
79075a10426Ssturm syslog(LOG_WARNING, "rpc to statd failed (%s)",
79175a10426Ssturm clnt_sperrno((enum clnt_stat)retval));
79275a10426Ssturm free(hp);
79375a10426Ssturm return;
79475a10426Ssturm }
79575a10426Ssturm if (result.res_stat == stat_fail) {
79675a10426Ssturm syslog(LOG_WARNING, "statd failed");
79775a10426Ssturm free(hp);
79875a10426Ssturm return;
79975a10426Ssturm }
80075a10426Ssturm LIST_INSERT_HEAD(&hostlst_head, hp, hostlst);
80175a10426Ssturm }
80275a10426Ssturm
8035507b659Ssturm void
notify(const char * hostname,int state)8045507b659Ssturm notify(const char *hostname, int state)
8055507b659Ssturm {
8065507b659Ssturm struct file_lock *fl, *next_fl;
8075507b659Ssturm int err;
8085507b659Ssturm syslog(LOG_DEBUG, "notify from %s, new state %d", hostname, state);
8095507b659Ssturm /* search all lock for this host; if status changed, release the lock */
8105507b659Ssturm siglock();
8115507b659Ssturm for (fl = LIST_FIRST(&lcklst_head); fl != NULL; fl = next_fl) {
8125507b659Ssturm next_fl = LIST_NEXT(fl, lcklst);
8135507b659Ssturm if (strcmp(hostname, fl->client_name) == 0 &&
8145507b659Ssturm fl->nsm_status != state) {
8155507b659Ssturm syslog(LOG_DEBUG, "state %d, nsm_state %d, unlocking",
8165507b659Ssturm fl->status, fl->nsm_status);
8175507b659Ssturm switch(fl->status) {
8185507b659Ssturm case LKST_LOCKED:
8195507b659Ssturm err = do_unlock(fl);
8205507b659Ssturm if (err != nlm_granted)
8215507b659Ssturm syslog(LOG_DEBUG,
8225507b659Ssturm "notify: unlock failed for %s (%d)",
8235507b659Ssturm hostname, err);
8245507b659Ssturm break;
8255507b659Ssturm case LKST_WAITING:
8265507b659Ssturm LIST_REMOVE(fl, lcklst);
8275507b659Ssturm lfree(fl);
8285507b659Ssturm break;
8295507b659Ssturm case LKST_PROCESSING:
8305507b659Ssturm fl->status = LKST_DYING;
8315507b659Ssturm break;
8325507b659Ssturm case LKST_DYING:
8335507b659Ssturm break;
8345507b659Ssturm default:
8353a50f0a9Sjmc syslog(LOG_NOTICE, "unknown status %d for %s",
8365507b659Ssturm fl->status, fl->client_name);
8375507b659Ssturm }
8385507b659Ssturm }
8395507b659Ssturm }
8405507b659Ssturm sigunlock();
8415507b659Ssturm }
842