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