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