Home | History | Annotate | Line # | Download | only in rpc.lockd
lockd_lock.c revision 1.9
      1  1.9      wiz /*	$NetBSD: lockd_lock.c,v 1.9 2002/07/10 23:16:34 wiz Exp $	*/
      2  1.1   bouyer 
      3  1.1   bouyer /*
      4  1.1   bouyer  * Copyright (c) 2000 Manuel Bouyer.
      5  1.1   bouyer  *
      6  1.1   bouyer  * Redistribution and use in source and binary forms, with or without
      7  1.1   bouyer  * modification, are permitted provided that the following conditions
      8  1.1   bouyer  * are met:
      9  1.1   bouyer  * 1. Redistributions of source code must retain the above copyright
     10  1.1   bouyer  *    notice, this list of conditions and the following disclaimer.
     11  1.1   bouyer  * 2. Redistributions in binary form must reproduce the above copyright
     12  1.1   bouyer  *    notice, this list of conditions and the following disclaimer in the
     13  1.1   bouyer  *    documentation and/or other materials provided with the distribution.
     14  1.1   bouyer  * 3. All advertising materials mentioning features or use of this software
     15  1.1   bouyer  *    must display the following acknowledgement:
     16  1.1   bouyer  *	This product includes software developed by the University of
     17  1.1   bouyer  *	California, Berkeley and its contributors.
     18  1.1   bouyer  * 4. Neither the name of the University nor the names of its contributors
     19  1.1   bouyer  *    may be used to endorse or promote products derived from this software
     20  1.1   bouyer  *    without specific prior written permission.
     21  1.1   bouyer  *
     22  1.1   bouyer  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  1.1   bouyer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  1.1   bouyer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  1.1   bouyer  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  1.1   bouyer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  1.1   bouyer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  1.1   bouyer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  1.1   bouyer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  1.1   bouyer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  1.1   bouyer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  1.1   bouyer  * SUCH DAMAGE.
     33  1.1   bouyer  *
     34  1.1   bouyer  */
     35  1.1   bouyer 
     36  1.1   bouyer #include <stdio.h>
     37  1.1   bouyer #include <stdlib.h>
     38  1.1   bouyer #include <unistd.h>
     39  1.1   bouyer #include <fcntl.h>
     40  1.1   bouyer #include <syslog.h>
     41  1.1   bouyer #include <errno.h>
     42  1.1   bouyer #include <string.h>
     43  1.1   bouyer #include <signal.h>
     44  1.1   bouyer #include <rpc/rpc.h>
     45  1.1   bouyer #include <sys/socket.h>
     46  1.1   bouyer #include <sys/param.h>
     47  1.1   bouyer #include <sys/mount.h>
     48  1.1   bouyer #include <sys/wait.h>
     49  1.1   bouyer #include <rpcsvc/sm_inter.h>
     50  1.4  thorpej #include <rpcsvc/nlm_prot.h>
     51  1.1   bouyer #include "lockd_lock.h"
     52  1.1   bouyer #include "lockd.h"
     53  1.1   bouyer 
     54  1.1   bouyer /* A set of utilities for managing file locking */
     55  1.1   bouyer LIST_HEAD(lcklst_head, file_lock);
     56  1.1   bouyer struct lcklst_head lcklst_head = LIST_HEAD_INITIALIZER(lcklst_head);
     57  1.1   bouyer 
     58  1.1   bouyer /* struct describing a lock */
     59  1.1   bouyer struct file_lock {
     60  1.1   bouyer 	LIST_ENTRY(file_lock) lcklst;
     61  1.1   bouyer 	fhandle_t filehandle; /* NFS filehandle */
     62  1.1   bouyer 	struct sockaddr *addr;
     63  1.1   bouyer 	struct nlm4_holder client; /* lock holder */
     64  1.1   bouyer 	netobj client_cookie; /* cookie sent by the client */
     65  1.1   bouyer 	char client_name[128];
     66  1.1   bouyer 	int nsm_status; /* status from the remote lock manager */
     67  1.1   bouyer 	int status; /* lock status, see below */
     68  1.1   bouyer 	int flags; /* lock flags, see lockd_lock.h */
     69  1.1   bouyer 	pid_t locker; /* pid of the child process trying to get the lock */
     70  1.1   bouyer 	int fd;	/* file descriptor for this lock */
     71  1.1   bouyer };
     72  1.1   bouyer 
     73  1.1   bouyer /* lock status */
     74  1.1   bouyer #define LKST_LOCKED	1 /* lock is locked */
     75  1.1   bouyer #define LKST_WAITING	2 /* file is already locked by another host */
     76  1.9      wiz #define LKST_PROCESSING	3 /* child is trying to acquire the lock */
     77  1.1   bouyer #define LKST_DYING	4 /* must dies when we get news from the child */
     78  1.1   bouyer 
     79  1.1   bouyer void lfree __P((struct file_lock *));
     80  1.1   bouyer enum nlm_stats do_lock __P((struct file_lock *, int));
     81  1.1   bouyer enum nlm_stats do_unlock __P((struct file_lock *));
     82  1.1   bouyer void send_granted __P((struct file_lock *, int));
     83  1.1   bouyer void siglock __P((void));
     84  1.1   bouyer void sigunlock __P((void));
     85  1.1   bouyer 
     86  1.1   bouyer /* list of hosts we monitor */
     87  1.1   bouyer LIST_HEAD(hostlst_head, host);
     88  1.1   bouyer struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head);
     89  1.1   bouyer 
     90  1.1   bouyer /* struct describing a lock */
     91  1.1   bouyer struct host {
     92  1.1   bouyer 	LIST_ENTRY(host) hostlst;
     93  1.1   bouyer 	char name[SM_MAXSTRLEN];
     94  1.1   bouyer 	int refcnt;
     95  1.1   bouyer };
     96  1.1   bouyer 
     97  1.1   bouyer void do_mon __P((char *));
     98  1.1   bouyer 
     99  1.1   bouyer /*
    100  1.1   bouyer  * testlock(): inform the caller if the requested lock would be granted or not
    101  1.1   bouyer  * returns NULL if lock would granted, or pointer to the current nlm4_holder
    102  1.1   bouyer  * otherwise.
    103  1.1   bouyer  */
    104  1.1   bouyer 
    105  1.1   bouyer struct nlm4_holder *
    106  1.1   bouyer testlock(lock, flags)
    107  1.1   bouyer 	struct nlm4_lock *lock;
    108  1.1   bouyer 	int flags;
    109  1.1   bouyer {
    110  1.1   bouyer 	struct file_lock *fl;
    111  1.1   bouyer 	fhandle_t filehandle;
    112  1.1   bouyer 
    113  1.1   bouyer 	/* convert lock to a local filehandle */
    114  1.1   bouyer 	memcpy(&filehandle, lock->fh.n_bytes, sizeof(filehandle));
    115  1.1   bouyer 
    116  1.1   bouyer 	siglock();
    117  1.1   bouyer 	/* search through the list for lock holder */
    118  1.1   bouyer 	for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
    119  1.1   bouyer 	    fl = LIST_NEXT(fl, lcklst)) {
    120  1.1   bouyer 		if (fl->status != LKST_LOCKED)
    121  1.1   bouyer 			continue;
    122  1.1   bouyer 		if (memcmp(&fl->filehandle, &filehandle, sizeof(filehandle)))
    123  1.1   bouyer 			continue;
    124  1.1   bouyer 		/* got it ! */
    125  1.1   bouyer 		syslog(LOG_DEBUG, "test for %s: found lock held by %s",
    126  1.1   bouyer 		    lock->caller_name, fl->client_name);
    127  1.1   bouyer 		sigunlock();
    128  1.1   bouyer 		return (&fl->client);
    129  1.1   bouyer 	}
    130  1.1   bouyer 	/* not found */
    131  1.1   bouyer 	sigunlock();
    132  1.1   bouyer 	syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name);
    133  1.1   bouyer 	return NULL;
    134  1.1   bouyer }
    135  1.1   bouyer 
    136  1.1   bouyer /*
    137  1.9      wiz  * getlock: try to acquire the lock.
    138  1.1   bouyer  * If file is already locked and we can sleep, put the lock in the list with
    139  1.1   bouyer  * status LKST_WAITING; it'll be processed later.
    140  1.1   bouyer  * Otherwise try to lock. If we're allowed to block, fork a child which
    141  1.1   bouyer  * will do the blocking lock.
    142  1.1   bouyer  */
    143  1.1   bouyer enum nlm_stats
    144  1.1   bouyer getlock(lckarg, rqstp, flags)
    145  1.1   bouyer 	nlm4_lockargs * lckarg;
    146  1.1   bouyer 	struct svc_req *rqstp;
    147  1.1   bouyer 	int flags;
    148  1.1   bouyer {
    149  1.1   bouyer 	struct file_lock *fl, *newfl;
    150  1.1   bouyer 	enum nlm_stats retval;
    151  1.1   bouyer 
    152  1.1   bouyer 	if (grace_expired == 0 && lckarg->reclaim == 0)
    153  1.1   bouyer 		return (flags & LOCK_V4) ?
    154  1.1   bouyer 		    nlm4_denied_grace_period : nlm_denied_grace_period;
    155  1.1   bouyer 
    156  1.1   bouyer 	/* allocate new file_lock for this request */
    157  1.1   bouyer 	newfl = malloc(sizeof(struct file_lock));
    158  1.1   bouyer 	if (newfl == NULL) {
    159  1.1   bouyer 		syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
    160  1.1   bouyer 		/* failed */
    161  1.1   bouyer 		return (flags & LOCK_V4) ?
    162  1.1   bouyer 		    nlm4_denied_nolock : nlm_denied_nolocks;
    163  1.1   bouyer 	}
    164  1.1   bouyer 	if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) {
    165  1.7      wiz 		syslog(LOG_DEBUG, "received fhandle size %d, local size %d",
    166  1.1   bouyer 		    lckarg->alock.fh.n_len, (int)sizeof(fhandle_t));
    167  1.1   bouyer 	}
    168  1.1   bouyer 	memcpy(&newfl->filehandle, lckarg->alock.fh.n_bytes, sizeof(fhandle_t));
    169  1.2     fvdl 	newfl->addr = (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf;
    170  1.1   bouyer 	newfl->client.exclusive = lckarg->exclusive;
    171  1.1   bouyer 	newfl->client.svid = lckarg->alock.svid;
    172  1.1   bouyer 	newfl->client.oh.n_bytes = malloc(lckarg->alock.oh.n_len);
    173  1.1   bouyer 	if (newfl->client.oh.n_bytes == NULL) {
    174  1.1   bouyer 		syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
    175  1.1   bouyer 		free(newfl);
    176  1.1   bouyer 		return (flags & LOCK_V4) ?
    177  1.1   bouyer 		    nlm4_denied_nolock : nlm_denied_nolocks;
    178  1.1   bouyer 	}
    179  1.1   bouyer 	newfl->client.oh.n_len = lckarg->alock.oh.n_len;
    180  1.1   bouyer 	memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes,
    181  1.1   bouyer 	    lckarg->alock.oh.n_len);
    182  1.1   bouyer 	newfl->client.l_offset = lckarg->alock.l_offset;
    183  1.1   bouyer 	newfl->client.l_len = lckarg->alock.l_len;
    184  1.1   bouyer 	newfl->client_cookie.n_len = lckarg->cookie.n_len;
    185  1.1   bouyer 	newfl->client_cookie.n_bytes = malloc(lckarg->cookie.n_len);
    186  1.1   bouyer 	if (newfl->client_cookie.n_bytes == NULL) {
    187  1.1   bouyer 		syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
    188  1.1   bouyer 		free(newfl->client.oh.n_bytes);
    189  1.1   bouyer 		free(newfl);
    190  1.1   bouyer 		return (flags & LOCK_V4) ?
    191  1.1   bouyer 		    nlm4_denied_nolock : nlm_denied_nolocks;
    192  1.1   bouyer 	}
    193  1.1   bouyer 	memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes,
    194  1.1   bouyer 	    lckarg->cookie.n_len);
    195  1.1   bouyer 	strncpy(newfl->client_name, lckarg->alock.caller_name, 128);
    196  1.1   bouyer 	newfl->nsm_status = lckarg->state;
    197  1.1   bouyer 	newfl->status = 0;
    198  1.1   bouyer 	newfl->flags = flags;
    199  1.1   bouyer 	siglock();
    200  1.1   bouyer 	/* look for a lock rq from this host for this fh */
    201  1.1   bouyer 	for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
    202  1.1   bouyer 	    fl = LIST_NEXT(fl, lcklst)) {
    203  1.1   bouyer 		if (memcmp(&newfl->filehandle, &fl->filehandle,
    204  1.1   bouyer 		    sizeof(fhandle_t)) == 0) {
    205  1.1   bouyer 			if (strcmp(newfl->client_name, fl->client_name) == 0 &&
    206  1.1   bouyer 			    newfl->client.svid == fl->client.svid) {
    207  1.1   bouyer 				/* already locked by this host ??? */
    208  1.1   bouyer 				sigunlock();
    209  1.1   bouyer 				syslog(LOG_NOTICE, "duplicate lock from %s",
    210  1.1   bouyer 				    newfl->client_name);
    211  1.1   bouyer 				lfree(newfl);
    212  1.1   bouyer 				switch(fl->status) {
    213  1.1   bouyer 				case LKST_LOCKED:
    214  1.1   bouyer 					return (flags & LOCK_V4) ?
    215  1.1   bouyer 					    nlm4_granted : nlm_granted;
    216  1.1   bouyer 				case LKST_WAITING:
    217  1.1   bouyer 				case LKST_PROCESSING:
    218  1.1   bouyer 					return (flags & LOCK_V4) ?
    219  1.1   bouyer 					    nlm4_blocked : nlm_blocked;
    220  1.1   bouyer 				case LKST_DYING:
    221  1.1   bouyer 					return (flags & LOCK_V4) ?
    222  1.1   bouyer 					    nlm4_denied : nlm_denied;
    223  1.1   bouyer 				default:
    224  1.1   bouyer 					syslog(LOG_NOTICE, "bad status %d",
    225  1.1   bouyer 					    fl->status);
    226  1.1   bouyer 					return (flags & LOCK_V4) ?
    227  1.1   bouyer 					    nlm4_failed : nlm_denied;
    228  1.1   bouyer 				}
    229  1.1   bouyer 			}
    230  1.1   bouyer 			/*
    231  1.1   bouyer 			 * We already have a lock for this file. Put this one
    232  1.1   bouyer 			 * in waiting state if allowed to block
    233  1.1   bouyer 			 */
    234  1.1   bouyer 			if (lckarg->block) {
    235  1.1   bouyer 				syslog(LOG_DEBUG, "lock from %s: already "
    236  1.1   bouyer 				    "locked, waiting",
    237  1.1   bouyer 				    lckarg->alock.caller_name);
    238  1.1   bouyer 				newfl->status = LKST_WAITING;
    239  1.1   bouyer 				LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst);
    240  1.1   bouyer 				do_mon(lckarg->alock.caller_name);
    241  1.1   bouyer 				sigunlock();
    242  1.1   bouyer 				return (flags & LOCK_V4) ?
    243  1.1   bouyer 				    nlm4_blocked : nlm_blocked;
    244  1.1   bouyer 			} else {
    245  1.1   bouyer 				sigunlock();
    246  1.1   bouyer 				syslog(LOG_DEBUG, "lock from %s: already "
    247  1.1   bouyer 				    "locked, failed",
    248  1.1   bouyer 				    lckarg->alock.caller_name);
    249  1.1   bouyer 				lfree(newfl);
    250  1.1   bouyer 				return (flags & LOCK_V4) ?
    251  1.1   bouyer 				    nlm4_denied : nlm_denied;
    252  1.1   bouyer 			}
    253  1.1   bouyer 		}
    254  1.1   bouyer 	}
    255  1.1   bouyer 	/* no entry for this file yet; add to list */
    256  1.1   bouyer 	LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst);
    257  1.1   bouyer 	/* do the lock */
    258  1.1   bouyer 	retval = do_lock(newfl, lckarg->block);
    259  1.1   bouyer 	switch (retval) {
    260  1.1   bouyer 	case nlm4_granted:
    261  1.1   bouyer 	/* case nlm_granted: is the same as nlm4_granted */
    262  1.1   bouyer 	case nlm4_blocked:
    263  1.1   bouyer 	/* case nlm_blocked: is the same as nlm4_blocked */
    264  1.1   bouyer 		do_mon(lckarg->alock.caller_name);
    265  1.1   bouyer 		break;
    266  1.1   bouyer 	default:
    267  1.5    enami 		lfree(newfl);
    268  1.1   bouyer 		break;
    269  1.1   bouyer 	}
    270  1.1   bouyer 	sigunlock();
    271  1.1   bouyer 	return retval;
    272  1.1   bouyer }
    273  1.1   bouyer 
    274  1.1   bouyer /* unlock a filehandle */
    275  1.1   bouyer enum nlm_stats
    276  1.1   bouyer unlock(lck, flags)
    277  1.1   bouyer 	nlm4_lock *lck;
    278  1.1   bouyer 	int flags;
    279  1.1   bouyer {
    280  1.1   bouyer 	struct file_lock *fl;
    281  1.1   bouyer 	fhandle_t filehandle;
    282  1.1   bouyer 	int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
    283  1.1   bouyer 
    284  1.1   bouyer 	memcpy(&filehandle, lck->fh.n_bytes, sizeof(fhandle_t));
    285  1.1   bouyer 	siglock();
    286  1.1   bouyer 	for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
    287  1.1   bouyer 	    fl = LIST_NEXT(fl, lcklst)) {
    288  1.1   bouyer 		if (strcmp(fl->client_name, lck->caller_name) ||
    289  1.1   bouyer 		    memcmp(&filehandle, &fl->filehandle, sizeof(fhandle_t)) ||
    290  1.1   bouyer 		    fl->client.oh.n_len != lck->oh.n_len ||
    291  1.1   bouyer 		    memcmp(fl->client.oh.n_bytes, lck->oh.n_bytes,
    292  1.1   bouyer 			fl->client.oh.n_len) != 0 ||
    293  1.1   bouyer 		    fl->client.svid != lck->svid)
    294  1.1   bouyer 			continue;
    295  1.1   bouyer 		/* Got it, unlock and remove from the queue */
    296  1.1   bouyer 		syslog(LOG_DEBUG, "unlock from %s: found struct, status %d",
    297  1.1   bouyer 		    lck->caller_name, fl->status);
    298  1.1   bouyer 		switch (fl->status) {
    299  1.1   bouyer 		case LKST_LOCKED:
    300  1.1   bouyer 			err = do_unlock(fl);
    301  1.1   bouyer 			break;
    302  1.1   bouyer 		case LKST_WAITING:
    303  1.1   bouyer 			/* remove from the list */
    304  1.1   bouyer 			LIST_REMOVE(fl, lcklst);
    305  1.1   bouyer 			lfree(fl);
    306  1.1   bouyer 			break;
    307  1.1   bouyer 		case LKST_PROCESSING:
    308  1.1   bouyer 			/*
    309  1.1   bouyer 			 * being handled by a child; will clean up
    310  1.1   bouyer 			 * when the child exits
    311  1.1   bouyer 			 */
    312  1.1   bouyer 			fl->status = LKST_DYING;
    313  1.1   bouyer 			break;
    314  1.1   bouyer 		case LKST_DYING:
    315  1.1   bouyer 			/* nothing to do */
    316  1.1   bouyer 			break;
    317  1.1   bouyer 		default:
    318  1.1   bouyer 			syslog(LOG_NOTICE, "unknow status %d for %s",
    319  1.1   bouyer 			    fl->status, fl->client_name);
    320  1.1   bouyer 		}
    321  1.1   bouyer 		sigunlock();
    322  1.1   bouyer 		return err;
    323  1.1   bouyer 	}
    324  1.1   bouyer 	sigunlock();
    325  1.1   bouyer 	/* didn't find a matching entry; log anyway */
    326  1.1   bouyer 	syslog(LOG_NOTICE, "no matching entry for %s",
    327  1.1   bouyer 	    lck->caller_name);
    328  1.1   bouyer 	return (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
    329  1.1   bouyer }
    330  1.1   bouyer 
    331  1.1   bouyer void
    332  1.1   bouyer lfree(fl)
    333  1.1   bouyer 	struct file_lock *fl;
    334  1.1   bouyer {
    335  1.1   bouyer 	free(fl->client.oh.n_bytes);
    336  1.1   bouyer 	free(fl->client_cookie.n_bytes);
    337  1.1   bouyer 	free(fl);
    338  1.1   bouyer }
    339  1.1   bouyer 
    340  1.1   bouyer void
    341  1.1   bouyer sigchild_handler(sig)
    342  1.1   bouyer 	int sig;
    343  1.1   bouyer {
    344  1.1   bouyer 	int status;
    345  1.1   bouyer 	pid_t pid;
    346  1.1   bouyer 	struct file_lock *fl;
    347  1.1   bouyer 
    348  1.1   bouyer 	while (1) {
    349  1.1   bouyer 		pid = wait4(-1, &status, WNOHANG, NULL);
    350  1.1   bouyer 		if (pid == -1) {
    351  1.1   bouyer 			if (errno != ECHILD)
    352  1.1   bouyer 				syslog(LOG_NOTICE, "wait failed: %s",
    353  1.1   bouyer 				    strerror(errno));
    354  1.1   bouyer 			else
    355  1.1   bouyer 				syslog(LOG_DEBUG, "wait failed: %s",
    356  1.1   bouyer 				    strerror(errno));
    357  1.1   bouyer 			return;
    358  1.1   bouyer 		}
    359  1.1   bouyer 		if (pid == 0) {
    360  1.1   bouyer 			/* no more child to handle yet */
    361  1.1   bouyer 			return;
    362  1.1   bouyer 		}
    363  1.1   bouyer 		/*
    364  1.1   bouyer 		 * if we're here we have a child that exited
    365  1.1   bouyer 		 * Find the associated file_lock.
    366  1.1   bouyer 		 */
    367  1.1   bouyer 		for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
    368  1.1   bouyer 		    fl = LIST_NEXT(fl, lcklst)) {
    369  1.1   bouyer 			if (pid == fl->locker)
    370  1.1   bouyer 				break;
    371  1.1   bouyer 		}
    372  1.1   bouyer 		if (pid != fl->locker) {
    373  1.1   bouyer 			syslog(LOG_NOTICE, "unknow child %d", pid);
    374  1.1   bouyer 		} else {
    375  1.1   bouyer 			if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    376  1.1   bouyer 				syslog(LOG_NOTICE, "child %d failed", pid);
    377  1.1   bouyer 				/*
    378  1.1   bouyer 				 * can't do much here; we can't reply
    379  1.1   bouyer 				 * anything but OK for blocked locks
    380  1.1   bouyer 				 * Eventually the client will time out
    381  1.1   bouyer 				 * and retry.
    382  1.1   bouyer 				 */
    383  1.1   bouyer 				do_unlock(fl);
    384  1.1   bouyer 				return;
    385  1.1   bouyer 			}
    386  1.1   bouyer 
    387  1.1   bouyer 			/* check lock status */
    388  1.1   bouyer 			syslog(LOG_DEBUG, "processing child %d, status %d",
    389  1.1   bouyer 			    pid, fl->status);
    390  1.1   bouyer 			switch(fl->status) {
    391  1.1   bouyer 			case LKST_PROCESSING:
    392  1.1   bouyer 				fl->status = LKST_LOCKED;
    393  1.1   bouyer 				send_granted(fl, (fl->flags & LOCK_V4) ?
    394  1.1   bouyer 				    nlm4_granted : nlm_granted);
    395  1.1   bouyer 				break;
    396  1.1   bouyer 			case LKST_DYING:
    397  1.1   bouyer 				do_unlock(fl);
    398  1.1   bouyer 				break;
    399  1.1   bouyer 			default:
    400  1.1   bouyer 				syslog(LOG_NOTICE, "bad lock status (%d) for"
    401  1.1   bouyer 				   " child %d", fl->status, pid);
    402  1.1   bouyer 			}
    403  1.1   bouyer 		}
    404  1.1   bouyer 	}
    405  1.1   bouyer }
    406  1.1   bouyer 
    407  1.1   bouyer /*
    408  1.1   bouyer  *
    409  1.9      wiz  * try to acquire the lock described by fl. Eventually fork a child to do a
    410  1.1   bouyer  * blocking lock if allowed and required.
    411  1.1   bouyer  */
    412  1.1   bouyer 
    413  1.1   bouyer enum nlm_stats
    414  1.1   bouyer do_lock(fl, block)
    415  1.1   bouyer 	struct file_lock *fl;
    416  1.1   bouyer 	int block;
    417  1.1   bouyer {
    418  1.1   bouyer 	int lflags, error;
    419  1.1   bouyer 	struct stat st;
    420  1.1   bouyer 
    421  1.1   bouyer 	fl->fd = fhopen(&fl->filehandle, O_RDWR);
    422  1.1   bouyer 	if (fl->fd < 0) {
    423  1.1   bouyer 		switch (errno) {
    424  1.1   bouyer 		case ESTALE:
    425  1.1   bouyer 			error = nlm4_stale_fh;
    426  1.1   bouyer 			break;
    427  1.1   bouyer 		case EROFS:
    428  1.1   bouyer 			error = nlm4_rofs;
    429  1.1   bouyer 			break;
    430  1.1   bouyer 		default:
    431  1.1   bouyer 			error = nlm4_failed;
    432  1.1   bouyer 		}
    433  1.1   bouyer 		if ((fl->flags & LOCK_V4) == 0)
    434  1.1   bouyer 			error = nlm_denied;
    435  1.1   bouyer 		syslog(LOG_NOTICE, "fhopen failed (from %s): %s",
    436  1.1   bouyer 		    fl->client_name, strerror(errno));
    437  1.1   bouyer 		LIST_REMOVE(fl, lcklst);
    438  1.1   bouyer 		return error;;
    439  1.1   bouyer 	}
    440  1.1   bouyer 	if (fstat(fl->fd, &st) < 0) {
    441  1.1   bouyer 		syslog(LOG_NOTICE, "fstat failed (from %s): %s",
    442  1.1   bouyer 		    fl->client_name, strerror(errno));
    443  1.1   bouyer 	}
    444  1.1   bouyer 	syslog(LOG_DEBUG, "lock from %s for file%s%s: dev %d ino %d (uid %d), "
    445  1.1   bouyer 	    "flags %d",
    446  1.1   bouyer 	    fl->client_name, fl->client.exclusive ? " (exclusive)":"",
    447  1.1   bouyer 	    block ? " (block)":"",
    448  1.1   bouyer 	    st.st_dev, st.st_ino, st.st_uid, fl->flags);
    449  1.1   bouyer 	lflags = LOCK_NB;
    450  1.1   bouyer 	if (fl->client.exclusive == 0)
    451  1.1   bouyer 		lflags |= LOCK_SH;
    452  1.1   bouyer 	else
    453  1.1   bouyer 		lflags |= LOCK_EX;
    454  1.1   bouyer 	error = flock(fl->fd, lflags);
    455  1.1   bouyer 	if (error != 0 && errno == EAGAIN && block) {
    456  1.1   bouyer 		switch (fl->locker = fork()) {
    457  1.1   bouyer 		case -1: /* fork failed */
    458  1.1   bouyer 			syslog(LOG_NOTICE, "fork failed: %s", strerror(errno));
    459  1.1   bouyer 			LIST_REMOVE(fl, lcklst);
    460  1.1   bouyer 			close(fl->fd);
    461  1.1   bouyer 			return (fl->flags & LOCK_V4) ?
    462  1.1   bouyer 			    nlm4_denied_nolock : nlm_denied_nolocks;
    463  1.1   bouyer 		case 0:
    464  1.1   bouyer 			/*
    465  1.1   bouyer 			 * Attempt a blocking lock. Will have to call
    466  1.1   bouyer 			 * NLM_GRANTED later.
    467  1.1   bouyer 			 */
    468  1.3   itojun 			setproctitle("%s", fl->client_name);
    469  1.1   bouyer 			lflags &= ~LOCK_NB;
    470  1.1   bouyer 			if(flock(fl->fd, lflags) != 0) {
    471  1.1   bouyer 				syslog(LOG_NOTICE, "flock failed: %s",
    472  1.1   bouyer 				    strerror(errno));
    473  1.6      wiz 				exit(1);
    474  1.1   bouyer 			}
    475  1.1   bouyer 			/* lock granted */
    476  1.1   bouyer 			exit(0);
    477  1.1   bouyer 		default:
    478  1.1   bouyer 			syslog(LOG_DEBUG, "lock request from %s: forked %d",
    479  1.1   bouyer 			    fl->client_name, fl->locker);
    480  1.1   bouyer 			fl->status = LKST_PROCESSING;
    481  1.1   bouyer 			return (fl->flags & LOCK_V4) ?
    482  1.1   bouyer 			    nlm4_blocked : nlm_blocked;
    483  1.1   bouyer 		}
    484  1.1   bouyer 	}
    485  1.1   bouyer 	/* non block case */
    486  1.1   bouyer 	if (error != 0) {
    487  1.1   bouyer 		switch (errno) {
    488  1.1   bouyer 		case EAGAIN:
    489  1.1   bouyer 			error = nlm4_denied;
    490  1.1   bouyer 			break;
    491  1.1   bouyer 		case ESTALE:
    492  1.1   bouyer 			error = nlm4_stale_fh;
    493  1.1   bouyer 			break;
    494  1.1   bouyer 		case EROFS:
    495  1.1   bouyer 			error = nlm4_rofs;
    496  1.1   bouyer 			break;
    497  1.1   bouyer 		default:
    498  1.1   bouyer 			error = nlm4_failed;
    499  1.1   bouyer 		}
    500  1.1   bouyer 		if ((fl->flags & LOCK_V4) == 0)
    501  1.1   bouyer 			error = nlm_denied;
    502  1.1   bouyer 		if (errno != EAGAIN)
    503  1.1   bouyer 			syslog(LOG_NOTICE, "flock for %s failed: %s",
    504  1.1   bouyer 			    fl->client_name, strerror(errno));
    505  1.1   bouyer 		else syslog(LOG_DEBUG, "flock for %s failed: %s",
    506  1.1   bouyer 			    fl->client_name, strerror(errno));
    507  1.1   bouyer 		LIST_REMOVE(fl, lcklst);
    508  1.1   bouyer 		close(fl->fd);
    509  1.1   bouyer 		return error;
    510  1.1   bouyer 	}
    511  1.1   bouyer 	fl->status = LKST_LOCKED;
    512  1.1   bouyer 	return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
    513  1.1   bouyer }
    514  1.1   bouyer 
    515  1.1   bouyer void
    516  1.1   bouyer send_granted(fl, opcode)
    517  1.1   bouyer 	struct file_lock *fl;
    518  1.1   bouyer 	int opcode;
    519  1.1   bouyer {
    520  1.1   bouyer 	CLIENT *cli;
    521  1.1   bouyer 	static char dummy;
    522  1.1   bouyer 	struct timeval timeo;
    523  1.1   bouyer 	int success;
    524  1.1   bouyer 	static struct nlm_res retval;
    525  1.1   bouyer 	static struct nlm4_res retval4;
    526  1.1   bouyer 
    527  1.2     fvdl 	cli = get_client(fl->addr,
    528  1.1   bouyer 	    (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS);
    529  1.1   bouyer 	if (cli == NULL) {
    530  1.1   bouyer 		syslog(LOG_NOTICE, "failed to get CLIENT for %s",
    531  1.1   bouyer 		    fl->client_name);
    532  1.1   bouyer 		/*
    533  1.1   bouyer 		 * We fail to notify remote that the lock has been granted.
    534  1.1   bouyer 		 * The client will timeout and retry, the lock will be
    535  1.1   bouyer 		 * granted at this time.
    536  1.1   bouyer 		 */
    537  1.1   bouyer 		return;
    538  1.1   bouyer 	}
    539  1.1   bouyer 	timeo.tv_sec = 0;
    540  1.1   bouyer 	timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */
    541  1.1   bouyer 
    542  1.1   bouyer 	if (fl->flags & LOCK_V4) {
    543  1.1   bouyer 		static nlm4_testargs res;
    544  1.1   bouyer 		res.cookie = fl->client_cookie;
    545  1.1   bouyer 		res.exclusive = fl->client.exclusive;
    546  1.1   bouyer 		res.alock.caller_name = fl->client_name;
    547  1.1   bouyer 		res.alock.fh.n_len = sizeof(fhandle_t);
    548  1.1   bouyer 		res.alock.fh.n_bytes = (char*)&fl->filehandle;
    549  1.1   bouyer 		res.alock.oh = fl->client.oh;
    550  1.1   bouyer 		res.alock.svid = fl->client.svid;
    551  1.1   bouyer 		res.alock.l_offset = fl->client.l_offset;
    552  1.1   bouyer 		res.alock.l_len = fl->client.l_len;
    553  1.1   bouyer 		syslog(LOG_DEBUG, "sending v4 reply%s",
    554  1.1   bouyer 		    (fl->flags & LOCK_ASYNC) ? " (async)":"");
    555  1.1   bouyer 		if (fl->flags & LOCK_ASYNC) {
    556  1.1   bouyer 			success = clnt_call(cli, NLM4_GRANTED_MSG,
    557  1.1   bouyer 			    xdr_nlm4_testargs, &res, xdr_void, &dummy, timeo);
    558  1.1   bouyer 		} else {
    559  1.1   bouyer 			success = clnt_call(cli, NLM4_GRANTED,
    560  1.1   bouyer 			    xdr_nlm4_testargs, &res, xdr_nlm4_res,
    561  1.1   bouyer 			    &retval4, timeo);
    562  1.1   bouyer 		}
    563  1.1   bouyer 	} else {
    564  1.1   bouyer 		static nlm_testargs res;
    565  1.1   bouyer 
    566  1.1   bouyer 		res.cookie = fl->client_cookie;
    567  1.1   bouyer 		res.exclusive = fl->client.exclusive;
    568  1.1   bouyer 		res.alock.caller_name = fl->client_name;
    569  1.1   bouyer 		res.alock.fh.n_len = sizeof(fhandle_t);
    570  1.1   bouyer 		res.alock.fh.n_bytes = (char*)&fl->filehandle;
    571  1.1   bouyer 		res.alock.oh = fl->client.oh;
    572  1.1   bouyer 		res.alock.svid = fl->client.svid;
    573  1.1   bouyer 		res.alock.l_offset = fl->client.l_offset;
    574  1.1   bouyer 		res.alock.l_len = fl->client.l_len;
    575  1.1   bouyer 		syslog(LOG_DEBUG, "sending v1 reply%s",
    576  1.1   bouyer 		    (fl->flags & LOCK_ASYNC) ? " (async)":"");
    577  1.1   bouyer 		if (fl->flags & LOCK_ASYNC) {
    578  1.1   bouyer 			success = clnt_call(cli, NLM_GRANTED_MSG,
    579  1.1   bouyer 			    xdr_nlm_testargs, &res, xdr_void, &dummy, timeo);
    580  1.1   bouyer 		} else {
    581  1.1   bouyer 			success = clnt_call(cli, NLM_GRANTED,
    582  1.1   bouyer 			    xdr_nlm_testargs, &res, xdr_nlm_res,
    583  1.1   bouyer 			    &retval, timeo);
    584  1.1   bouyer 		}
    585  1.1   bouyer 	}
    586  1.1   bouyer 	if (debug_level > 2)
    587  1.1   bouyer 		syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted",
    588  1.1   bouyer 		    success, clnt_sperrno(success));
    589  1.1   bouyer 
    590  1.1   bouyer }
    591  1.1   bouyer 
    592  1.1   bouyer enum nlm_stats
    593  1.1   bouyer do_unlock(rfl)
    594  1.1   bouyer 	struct file_lock *rfl;
    595  1.1   bouyer {
    596  1.1   bouyer 	struct file_lock *fl;
    597  1.1   bouyer 	int error;
    598  1.1   bouyer 	int lockst;
    599  1.1   bouyer 
    600  1.1   bouyer 	/* unlock the file: closing is enouth ! */
    601  1.1   bouyer 	if (close(rfl->fd) < 0) {
    602  1.1   bouyer 		if (errno == ESTALE)
    603  1.1   bouyer 			error = nlm4_stale_fh;
    604  1.1   bouyer 		else
    605  1.1   bouyer 			error = nlm4_failed;
    606  1.1   bouyer 		if ((fl->flags & LOCK_V4) == 0)
    607  1.1   bouyer 			error = nlm_denied;
    608  1.1   bouyer 		syslog(LOG_NOTICE,
    609  1.1   bouyer 		    "close failed (from %s): %s",
    610  1.1   bouyer 		    rfl->client_name, strerror(errno));
    611  1.1   bouyer 	} else {
    612  1.1   bouyer 		error = (fl->flags & LOCK_V4) ?
    613  1.1   bouyer 		    nlm4_granted : nlm_granted;
    614  1.1   bouyer 	}
    615  1.1   bouyer 	LIST_REMOVE(rfl, lcklst);
    616  1.1   bouyer 
    617  1.1   bouyer 	/* process the next LKST_WAITING lock request for this fh */
    618  1.1   bouyer 	for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
    619  1.1   bouyer 	     fl = LIST_NEXT(fl, lcklst)) {
    620  1.1   bouyer 		if (fl->status != LKST_WAITING ||
    621  1.1   bouyer 		    memcmp(&rfl->filehandle, &fl->filehandle,
    622  1.1   bouyer 		    sizeof(fhandle_t)) != 0)
    623  1.1   bouyer 			continue;
    624  1.1   bouyer 
    625  1.1   bouyer 		lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */
    626  1.1   bouyer 		switch (lockst) {
    627  1.1   bouyer 		case nlm4_granted:
    628  1.1   bouyer 		/* case nlm_granted: same as nlm4_granted */
    629  1.1   bouyer 			send_granted(fl, (fl->flags & LOCK_V4) ?
    630  1.1   bouyer 			    nlm4_granted : nlm_granted);
    631  1.1   bouyer 			break;
    632  1.1   bouyer 		case nlm4_blocked:
    633  1.1   bouyer 		/* case nlm_blocked: same as nlm4_blocked */
    634  1.1   bouyer 			break;
    635  1.1   bouyer 		default:
    636  1.1   bouyer 			lfree(fl);
    637  1.1   bouyer 			break;
    638  1.1   bouyer 		}
    639  1.1   bouyer 		break;
    640  1.1   bouyer 	}
    641  1.8    oster 	lfree(rfl);
    642  1.1   bouyer 	return error;
    643  1.1   bouyer }
    644  1.1   bouyer 
    645  1.1   bouyer void
    646  1.1   bouyer siglock()
    647  1.1   bouyer {
    648  1.1   bouyer 	sigset_t block;
    649  1.1   bouyer 
    650  1.1   bouyer 	sigemptyset(&block);
    651  1.1   bouyer 	sigaddset(&block, SIGCHLD);
    652  1.1   bouyer 
    653  1.1   bouyer 	if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) {
    654  1.1   bouyer 		syslog(LOG_WARNING, "siglock failed: %s", strerror(errno));
    655  1.1   bouyer 	}
    656  1.1   bouyer }
    657  1.1   bouyer 
    658  1.1   bouyer void
    659  1.1   bouyer sigunlock()
    660  1.1   bouyer {
    661  1.1   bouyer 	sigset_t block;
    662  1.1   bouyer 
    663  1.1   bouyer 	sigemptyset(&block);
    664  1.1   bouyer 	sigaddset(&block, SIGCHLD);
    665  1.1   bouyer 
    666  1.1   bouyer 	if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) {
    667  1.1   bouyer 		syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno));
    668  1.1   bouyer 	}
    669  1.1   bouyer }
    670  1.1   bouyer 
    671  1.1   bouyer /* monitor a host through rpc.statd, and keep a ref count */
    672  1.1   bouyer void
    673  1.1   bouyer do_mon(hostname)
    674  1.1   bouyer 	char *hostname;
    675  1.1   bouyer {
    676  1.1   bouyer 	struct host *hp;
    677  1.1   bouyer 	struct mon my_mon;
    678  1.1   bouyer 	struct sm_stat_res res;
    679  1.1   bouyer 	int retval;
    680  1.1   bouyer 
    681  1.1   bouyer 	for (hp = LIST_FIRST(&hostlst_head); hp != NULL;
    682  1.1   bouyer 	    hp = LIST_NEXT(hp, hostlst)) {
    683  1.1   bouyer 		if (strcmp(hostname, hp->name) == 0) {
    684  1.1   bouyer 			/* already monitored, just bump refcnt */
    685  1.1   bouyer 			hp->refcnt++;
    686  1.1   bouyer 			return;
    687  1.1   bouyer 		}
    688  1.1   bouyer 	}
    689  1.1   bouyer 	/* not found, have to create an entry for it */
    690  1.1   bouyer 	hp = malloc(sizeof(struct host));
    691  1.1   bouyer 	strncpy(hp->name, hostname, SM_MAXSTRLEN);
    692  1.1   bouyer 	hp->refcnt = 1;
    693  1.1   bouyer 	syslog(LOG_DEBUG, "monitoring host %s",
    694  1.1   bouyer 	    hostname);
    695  1.1   bouyer 	memset(&my_mon, 0, sizeof(my_mon));
    696  1.1   bouyer 	my_mon.mon_id.mon_name = hp->name;
    697  1.1   bouyer 	my_mon.mon_id.my_id.my_name = "localhost";
    698  1.1   bouyer 	my_mon.mon_id.my_id.my_prog = NLM_PROG;
    699  1.1   bouyer 	my_mon.mon_id.my_id.my_vers = NLM_SM;
    700  1.1   bouyer 	my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
    701  1.1   bouyer 	if ((retval =
    702  1.1   bouyer 	    callrpc("localhost", SM_PROG, SM_VERS, SM_MON, xdr_mon,
    703  1.1   bouyer 	    (char*)&my_mon, xdr_sm_stat_res, (char*)&res)) != 0) {
    704  1.1   bouyer 		syslog(LOG_WARNING, "rpc to statd failed: %s",
    705  1.1   bouyer 		    clnt_sperrno((enum clnt_stat)retval));
    706  1.1   bouyer 		free(hp);
    707  1.1   bouyer 		return;
    708  1.1   bouyer 	}
    709  1.1   bouyer 	if (res.res_stat == stat_fail) {
    710  1.1   bouyer 		syslog(LOG_WARNING, "statd failed");
    711  1.1   bouyer 		free(hp);
    712  1.1   bouyer 		return;
    713  1.1   bouyer 	}
    714  1.1   bouyer 	LIST_INSERT_HEAD(&hostlst_head, hp, hostlst);
    715  1.1   bouyer }
    716  1.1   bouyer 
    717  1.1   bouyer void
    718  1.1   bouyer notify(hostname, state)
    719  1.1   bouyer 	char *hostname;
    720  1.1   bouyer 	int state;
    721  1.1   bouyer {
    722  1.1   bouyer 	struct file_lock *fl, *next_fl;
    723  1.1   bouyer 	int err;
    724  1.1   bouyer 	syslog(LOG_DEBUG, "notify from %s, new state %d", hostname, state);
    725  1.1   bouyer 	/* search all lock for this host; if status changed, release the lock */
    726  1.1   bouyer 	siglock();
    727  1.1   bouyer 	for (fl = LIST_FIRST(&lcklst_head); fl != NULL; fl = next_fl) {
    728  1.1   bouyer 		next_fl = LIST_NEXT(fl, lcklst);
    729  1.1   bouyer 		if (strcmp(hostname, fl->client_name) == 0 &&
    730  1.1   bouyer 		    fl->nsm_status != state) {
    731  1.1   bouyer 			syslog(LOG_DEBUG, "state %d, nsm_state %d, unlocking",
    732  1.1   bouyer 			    fl->status, fl->nsm_status);
    733  1.1   bouyer 			switch(fl->status) {
    734  1.1   bouyer 			case LKST_LOCKED:
    735  1.1   bouyer 				err = do_unlock(fl);
    736  1.1   bouyer 				if (err != nlm_granted)
    737  1.1   bouyer 					syslog(LOG_DEBUG,
    738  1.1   bouyer 					    "notify: unlock failed for %s (%d)",
    739  1.1   bouyer 			    		    hostname, err);
    740  1.1   bouyer 				break;
    741  1.1   bouyer 			case LKST_WAITING:
    742  1.1   bouyer 				LIST_REMOVE(fl, lcklst);
    743  1.1   bouyer 				lfree(fl);
    744  1.1   bouyer 				break;
    745  1.1   bouyer 			case LKST_PROCESSING:
    746  1.1   bouyer 				fl->status = LKST_DYING;
    747  1.1   bouyer 				break;
    748  1.1   bouyer 			case LKST_DYING:
    749  1.1   bouyer 				break;
    750  1.1   bouyer 			default:
    751  1.1   bouyer 				syslog(LOG_NOTICE, "unknow status %d for %s",
    752  1.1   bouyer 				    fl->status, fl->client_name);
    753  1.1   bouyer 			}
    754  1.1   bouyer 		}
    755  1.1   bouyer 	}
    756  1.1   bouyer 	sigunlock();
    757  1.1   bouyer }
    758