xref: /dflybsd-src/usr.sbin/rpc.lockd/lockd_lock.c (revision 5aa057b64f642f8017fd3a1ffeb8e0fe897714b3)
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 			    (xdrproc_t)xdr_nlm4_testargs, &res,
548 			    (xdrproc_t)xdr_void, &dummy, timeo);
549 		} else {
550 			success = clnt_call(cli, NLM4_GRANTED,
551 			    (xdrproc_t)xdr_nlm4_testargs, &res,
552 			    (xdrproc_t)xdr_nlm4_res, &retval4, timeo);
553 		}
554 	} else {
555 		static nlm_testargs res;
556 
557 		res.cookie = fl->client_cookie;
558 		res.exclusive = fl->client.exclusive;
559 		res.alock.caller_name = fl->client_name;
560 		res.alock.fh.n_len = sizeof(fhandle_t);
561 		res.alock.fh.n_bytes = (char*)&fl->filehandle;
562 		res.alock.oh = fl->client.oh;
563 		res.alock.svid = fl->client.svid;
564 		res.alock.l_offset = fl->client.l_offset;
565 		res.alock.l_len = fl->client.l_len;
566 		syslog(LOG_DEBUG, "sending v1 reply%s",
567 		    (fl->flags & LOCK_ASYNC) ? " (async)":"");
568 		if (fl->flags & LOCK_ASYNC) {
569 			success = clnt_call(cli, NLM_GRANTED_MSG,
570 			    (xdrproc_t)xdr_nlm_testargs, &res,
571 			    (xdrproc_t)xdr_void, &dummy, timeo);
572 		} else {
573 			success = clnt_call(cli, NLM_GRANTED,
574 			    (xdrproc_t)xdr_nlm_testargs, &res,
575 			    (xdrproc_t)xdr_nlm_res, &retval, timeo);
576 		}
577 	}
578 	if (debug_level > 2)
579 		syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted",
580 		    success, clnt_sperrno(success));
581 
582 }
583 
584 enum nlm_stats
585 do_unlock(struct file_lock *rfl)
586 {
587 	struct file_lock *fl;
588 	int error;
589 	int lockst;
590 
591 	/* unlock the file: closing is enouth ! */
592 	if (close(rfl->fd) < 0) {
593 		if (errno == ESTALE)
594 			error = nlm4_stale_fh;
595 		else
596 			error = nlm4_failed;
597 		if ((fl->flags & LOCK_V4) == 0)
598 			error = nlm_denied;
599 		syslog(LOG_NOTICE,
600 		    "close failed (from %s): %s",
601 		    rfl->client_name, strerror(errno));
602 	} else {
603 		error = (fl->flags & LOCK_V4) ?
604 		    nlm4_granted : nlm_granted;
605 	}
606 	LIST_REMOVE(rfl, lcklst);
607 
608 	/* process the next LKST_WAITING lock request for this fh */
609 	for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
610 	     fl = LIST_NEXT(fl, lcklst)) {
611 		if (fl->status != LKST_WAITING ||
612 		    memcmp(&rfl->filehandle, &fl->filehandle,
613 		    sizeof(fhandle_t)) != 0)
614 			continue;
615 
616 		lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */
617 		switch (lockst) {
618 		case nlm4_granted:
619 		/* case nlm_granted: same as nlm4_granted */
620 			send_granted(fl, (fl->flags & LOCK_V4) ?
621 			    nlm4_granted : nlm_granted);
622 			break;
623 		case nlm4_blocked:
624 		/* case nlm_blocked: same as nlm4_blocked */
625 			break;
626 		default:
627 			lfree(fl);
628 			break;
629 		}
630 		break;
631 	}
632 	return error;
633 }
634 
635 void
636 siglock(void)
637 {
638 	sigset_t block;
639 
640 	sigemptyset(&block);
641 	sigaddset(&block, SIGCHLD);
642 
643 	if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) {
644 		syslog(LOG_WARNING, "siglock failed: %s", strerror(errno));
645 	}
646 }
647 
648 void
649 sigunlock(void)
650 {
651 	sigset_t block;
652 
653 	sigemptyset(&block);
654 	sigaddset(&block, SIGCHLD);
655 
656 	if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) {
657 		syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno));
658 	}
659 }
660 
661 /* monitor a host through rpc.statd, and keep a ref count */
662 void
663 do_mon(char *hostname)
664 {
665 	struct host *hp;
666 	struct mon my_mon;
667 	struct sm_stat_res res;
668 	int retval;
669 
670 	for (hp = LIST_FIRST(&hostlst_head); hp != NULL;
671 	    hp = LIST_NEXT(hp, hostlst)) {
672 		if (strcmp(hostname, hp->name) == 0) {
673 			/* already monitored, just bump refcnt */
674 			hp->refcnt++;
675 			return;
676 		}
677 	}
678 	/* not found, have to create an entry for it */
679 	hp = malloc(sizeof(struct host));
680 	strncpy(hp->name, hostname, SM_MAXSTRLEN);
681 	hp->refcnt = 1;
682 	syslog(LOG_DEBUG, "monitoring host %s",
683 	    hostname);
684 	memset(&my_mon, 0, sizeof(my_mon));
685 	my_mon.mon_id.mon_name = hp->name;
686 	my_mon.mon_id.my_id.my_name = "localhost";
687 	my_mon.mon_id.my_id.my_prog = NLM_PROG;
688 	my_mon.mon_id.my_id.my_vers = NLM_SM;
689 	my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
690 	if ((retval =
691 	    callrpc("localhost", SM_PROG, SM_VERS, SM_MON, (xdrproc_t)xdr_mon,
692 	    (char*)&my_mon, (xdrproc_t)xdr_sm_stat_res, (char*)&res)) != 0) {
693 		syslog(LOG_WARNING, "rpc to statd failed: %s",
694 		    clnt_sperrno((enum clnt_stat)retval));
695 		free(hp);
696 		return;
697 	}
698 	if (res.res_stat == stat_fail) {
699 		syslog(LOG_WARNING, "statd failed");
700 		free(hp);
701 		return;
702 	}
703 	LIST_INSERT_HEAD(&hostlst_head, hp, hostlst);
704 }
705 
706 void
707 notify(const char *hostname, int state)
708 {
709 	struct file_lock *fl, *next_fl;
710 	int err;
711 	syslog(LOG_DEBUG, "notify from %s, new state %d", hostname, state);
712 	/* search all lock for this host; if status changed, release the lock */
713 	siglock();
714 	for (fl = LIST_FIRST(&lcklst_head); fl != NULL; fl = next_fl) {
715 		next_fl = LIST_NEXT(fl, lcklst);
716 		if (strcmp(hostname, fl->client_name) == 0 &&
717 		    fl->nsm_status != state) {
718 			syslog(LOG_DEBUG, "state %d, nsm_state %d, unlocking",
719 			    fl->status, fl->nsm_status);
720 			switch(fl->status) {
721 			case LKST_LOCKED:
722 				err = do_unlock(fl);
723 				if (err != nlm_granted)
724 					syslog(LOG_DEBUG,
725 					    "notify: unlock failed for %s (%d)",
726 					    hostname, err);
727 				break;
728 			case LKST_WAITING:
729 				LIST_REMOVE(fl, lcklst);
730 				lfree(fl);
731 				break;
732 			case LKST_PROCESSING:
733 				fl->status = LKST_DYING;
734 				break;
735 			case LKST_DYING:
736 				break;
737 			default:
738 				syslog(LOG_NOTICE, "unknown status %d for %s",
739 				    fl->status, fl->client_name);
740 			}
741 		}
742 	}
743 	sigunlock();
744 }
745