xref: /netbsd-src/usr.sbin/rpc.lockd/lockd_lock.c (revision ce2c90c7c172d95d2402a5b3d96d8f8e6d138a21)
1 /*	$NetBSD: lockd_lock.c,v 1.26 2006/08/09 14:12:47 martin 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 	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 __P((struct file_lock *));
124 enum nlm_stats do_lock __P((struct file_lock *, int));
125 enum nlm_stats do_unlock __P((struct file_lock *));
126 void send_granted __P((struct file_lock *, int));
127 void siglock __P((void));
128 void sigunlock __P((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 __P((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 __P((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(newfl, flags)
155 	struct file_lock *newfl;
156 	int flags;
157 {
158 	struct file_lock *fl;
159 
160 	LIST_FOREACH(fl, &lcklst_head, lcklst) {
161 		if ((flags & LL_SVID) != 0 &&
162 		    newfl->client.svid != fl->client.svid)
163 			continue;
164 		if ((flags & LL_NAME) != 0 &&
165 		    strcmp(newfl->client_name, fl->client_name) != 0)
166 			continue;
167 		if ((flags & LL_FH) != 0 &&
168 		    fhcmp(&newfl->filehandle, &fl->filehandle) != 0)
169 			continue;
170 		/* found */
171 		break;
172 	}
173 
174 	return fl;
175 }
176 
177 /*
178  * testlock(): inform the caller if the requested lock would be granted or not
179  * returns NULL if lock would granted, or pointer to the current nlm4_holder
180  * otherwise.
181  */
182 
183 struct nlm4_holder *
184 testlock(lock, flags)
185 	struct nlm4_lock *lock;
186 	int flags;
187 {
188 	struct file_lock *fl;
189 	nfs_fhandle_t filehandle;
190 
191 	/* convert lock to a local filehandle */
192 	if (fhconv(&filehandle, &lock->fh)) {
193 		syslog(LOG_NOTICE, "fhconv failed: %s", strerror(errno));
194 		return NULL; /* XXX */
195 	}
196 
197 	siglock();
198 	/* search through the list for lock holder */
199 	LIST_FOREACH(fl, &lcklst_head, lcklst) {
200 		if (fl->status != LKST_LOCKED)
201 			continue;
202 		if (fhcmp(&fl->filehandle, &filehandle) != 0)
203 			continue;
204 		/* got it ! */
205 		syslog(LOG_DEBUG, "test for %s: found lock held by %s",
206 		    lock->caller_name, fl->client_name);
207 		sigunlock();
208 		fhfree(&filehandle);
209 		return (&fl->client);
210 	}
211 	/* not found */
212 	sigunlock();
213 	fhfree(&filehandle);
214 	syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name);
215 	return NULL;
216 }
217 
218 /*
219  * getlock: try to acquire the lock.
220  * If file is already locked and we can sleep, put the lock in the list with
221  * status LKST_WAITING; it'll be processed later.
222  * Otherwise try to lock. If we're allowed to block, fork a child which
223  * will do the blocking lock.
224  */
225 enum nlm_stats
226 getlock(lckarg, rqstp, flags)
227 	nlm4_lockargs * lckarg;
228 	struct svc_req *rqstp;
229 	int flags;
230 {
231 	struct file_lock *fl, *newfl;
232 	enum nlm_stats retval;
233 	struct sockaddr *addr;
234 
235 	if (grace_expired == 0 && lckarg->reclaim == 0)
236 		return (flags & LOCK_V4) ?
237 		    nlm4_denied_grace_period : nlm_denied_grace_period;
238 
239 	/* allocate new file_lock for this request */
240 	newfl = lalloc();
241 	if (newfl == NULL) {
242 		syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
243 		/* failed */
244 		return (flags & LOCK_V4) ?
245 		    nlm4_denied_nolock : nlm_denied_nolocks;
246 	}
247 	if (fhconv(&newfl->filehandle, &lckarg->alock.fh)) {
248 		syslog(LOG_NOTICE, "fhconv failed: %s", strerror(errno));
249 		lfree(newfl);
250 		/* failed */
251 		return (flags & LOCK_V4) ?
252 		    nlm4_denied_nolock : nlm_denied_nolocks;
253 	}
254 	addr = (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf;
255 	newfl->addr = malloc(addr->sa_len);
256 	if (newfl->addr == NULL) {
257 		syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
258 		lfree(newfl);
259 		/* failed */
260 		return (flags & LOCK_V4) ?
261 		    nlm4_denied_nolock : nlm_denied_nolocks;
262 	}
263 	memcpy(newfl->addr, addr, addr->sa_len);
264 	newfl->client.exclusive = lckarg->exclusive;
265 	newfl->client.svid = lckarg->alock.svid;
266 	newfl->client.oh.n_bytes = malloc(lckarg->alock.oh.n_len);
267 	if (newfl->client.oh.n_bytes == NULL) {
268 		syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
269 		lfree(newfl);
270 		return (flags & LOCK_V4) ?
271 		    nlm4_denied_nolock : nlm_denied_nolocks;
272 	}
273 	newfl->client.oh.n_len = lckarg->alock.oh.n_len;
274 	memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes,
275 	    lckarg->alock.oh.n_len);
276 	newfl->client.l_offset = lckarg->alock.l_offset;
277 	newfl->client.l_len = lckarg->alock.l_len;
278 	newfl->client_cookie.n_len = lckarg->cookie.n_len;
279 	newfl->client_cookie.n_bytes = malloc(lckarg->cookie.n_len);
280 	if (newfl->client_cookie.n_bytes == NULL) {
281 		syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
282 		lfree(newfl);
283 		return (flags & LOCK_V4) ?
284 		    nlm4_denied_nolock : nlm_denied_nolocks;
285 	}
286 	memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes,
287 	    lckarg->cookie.n_len);
288 	strlcpy(newfl->client_name, lckarg->alock.caller_name,
289 	    sizeof(newfl->client_name));
290 	newfl->nsm_status = lckarg->state;
291 	newfl->status = 0;
292 	newfl->flags = flags;
293 	siglock();
294 	/* look for a lock rq from this host for this fh */
295 	fl = lock_lookup(newfl, LL_FH|LL_NAME|LL_SVID);
296 	if (fl) {
297 		/* already locked by this host ??? */
298 		sigunlock();
299 		syslog(LOG_NOTICE, "duplicate lock from %s.%"
300 		    PRIu32,
301 		    newfl->client_name, newfl->client.svid);
302 		lfree(newfl);
303 		switch(fl->status) {
304 		case LKST_LOCKED:
305 			return (flags & LOCK_V4) ?
306 			    nlm4_granted : nlm_granted;
307 		case LKST_WAITING:
308 		case LKST_PROCESSING:
309 			return (flags & LOCK_V4) ?
310 			    nlm4_blocked : nlm_blocked;
311 		case LKST_DYING:
312 			return (flags & LOCK_V4) ?
313 			    nlm4_denied : nlm_denied;
314 		default:
315 			syslog(LOG_NOTICE, "bad status %d",
316 			    fl->status);
317 			return (flags & LOCK_V4) ?
318 			    nlm4_failed : nlm_denied;
319 		}
320 		/* NOTREACHED */
321 	}
322 	fl = lock_lookup(newfl, LL_FH);
323 	if (fl) {
324 		/*
325 		 * We already have a lock for this file.
326 		 * Put this one in waiting state if allowed to block
327 		 */
328 		if (lckarg->block) {
329 			syslog(LOG_DEBUG, "lock from %s.%" PRIu32 ": "
330 			    "already locked, waiting",
331 			    lckarg->alock.caller_name,
332 			    lckarg->alock.svid);
333 			newfl->status = LKST_WAITING;
334 			LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst);
335 			do_mon(lckarg->alock.caller_name);
336 			sigunlock();
337 			return (flags & LOCK_V4) ?
338 			    nlm4_blocked : nlm_blocked;
339 		} else {
340 			sigunlock();
341 			syslog(LOG_DEBUG, "lock from %s.%" PRIu32 ": "
342 			    "already locked, failed",
343 			    lckarg->alock.caller_name,
344 			    lckarg->alock.svid);
345 			lfree(newfl);
346 			return (flags & LOCK_V4) ?
347 			    nlm4_denied : nlm_denied;
348 		}
349 		/* NOTREACHED */
350 	}
351 
352 	/* no entry for this file yet; add to list */
353 	LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst);
354 	/* do the lock */
355 	retval = do_lock(newfl, lckarg->block);
356 	switch (retval) {
357 	case nlm4_granted:
358 	/* case nlm_granted: is the same as nlm4_granted */
359 	case nlm4_blocked:
360 	/* case nlm_blocked: is the same as nlm4_blocked */
361 		do_mon(lckarg->alock.caller_name);
362 		break;
363 	default:
364 		lfree(newfl);
365 		break;
366 	}
367 	sigunlock();
368 	return retval;
369 }
370 
371 /* unlock a filehandle */
372 enum nlm_stats
373 unlock(lck, flags)
374 	nlm4_lock *lck;
375 	int flags;
376 {
377 	struct file_lock *fl;
378 	nfs_fhandle_t filehandle;
379 	int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
380 
381 	if (fhconv(&filehandle, &lck->fh)) {
382 		syslog(LOG_NOTICE, "fhconv failed: %s", strerror(errno));
383 		return (flags & LOCK_V4) ? nlm4_denied : nlm_denied;
384 	}
385 	siglock();
386 	LIST_FOREACH(fl, &lcklst_head, lcklst) {
387 		if (strcmp(fl->client_name, lck->caller_name) ||
388 		    fhcmp(&filehandle, &fl->filehandle) != 0 ||
389 		    fl->client.oh.n_len != lck->oh.n_len ||
390 		    memcmp(fl->client.oh.n_bytes, lck->oh.n_bytes,
391 			fl->client.oh.n_len) != 0 ||
392 		    fl->client.svid != lck->svid)
393 			continue;
394 		/* Got it, unlock and remove from the queue */
395 		syslog(LOG_DEBUG, "unlock from %s.%" PRIu32 ": found struct, "
396 		    "status %d", lck->caller_name, lck->svid, fl->status);
397 		switch (fl->status) {
398 		case LKST_LOCKED:
399 			err = do_unlock(fl);
400 			break;
401 		case LKST_WAITING:
402 			/* remove from the list */
403 			LIST_REMOVE(fl, lcklst);
404 			lfree(fl);
405 			break;
406 		case LKST_PROCESSING:
407 			/*
408 			 * being handled by a child; will clean up
409 			 * when the child exits
410 			 */
411 			fl->status = LKST_DYING;
412 			break;
413 		case LKST_DYING:
414 			/* nothing to do */
415 			break;
416 		default:
417 			syslog(LOG_NOTICE, "unknow status %d for %s",
418 			    fl->status, fl->client_name);
419 		}
420 		sigunlock();
421 		fhfree(&filehandle);
422 		return err;
423 	}
424 	sigunlock();
425 	/* didn't find a matching entry; log anyway */
426 	syslog(LOG_NOTICE, "no matching entry for %s",
427 	    lck->caller_name);
428 	fhfree(&filehandle);
429 	return (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
430 }
431 
432 static struct file_lock *
433 lalloc(void)
434 {
435 	struct file_lock *fl;
436 
437 	fl = malloc(sizeof(*fl));
438 	if (fl != NULL) {
439 		fl->addr = NULL;
440 		fl->client.oh.n_bytes = NULL;
441 		fl->client_cookie.n_bytes = NULL;
442 		fl->filehandle.fhdata = NULL;
443 	}
444 	return fl;
445 }
446 
447 void
448 lfree(fl)
449 	struct file_lock *fl;
450 {
451 	free(fl->addr);
452 	free(fl->client.oh.n_bytes);
453 	free(fl->client_cookie.n_bytes);
454 	fhfree(&fl->filehandle);
455 	free(fl);
456 }
457 
458 void
459 sigchild_handler(sig)
460 	int sig;
461 {
462 	int status;
463 	pid_t pid;
464 	struct file_lock *fl;
465 
466 	while (1) {
467 		pid = wait4(-1, &status, WNOHANG, NULL);
468 		if (pid == -1) {
469 			if (errno != ECHILD)
470 				syslog(LOG_NOTICE, "wait failed: %s",
471 				    strerror(errno));
472 			else
473 				syslog(LOG_DEBUG, "wait failed: %s",
474 				    strerror(errno));
475 			return;
476 		}
477 		if (pid == 0) {
478 			/* no more child to handle yet */
479 			return;
480 		}
481 		/*
482 		 * if we're here we have a child that exited
483 		 * Find the associated file_lock.
484 		 */
485 		LIST_FOREACH(fl, &lcklst_head, lcklst) {
486 			if (pid == fl->locker)
487 				break;
488 		}
489 		if (fl == NULL) {
490 			syslog(LOG_NOTICE, "unknow child %d", pid);
491 		} else {
492 			/*
493 			 * protect from pid reusing.
494 			 */
495 			fl->locker = 0;
496 			if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
497 				syslog(LOG_NOTICE, "child %d failed", pid);
498 				/*
499 				 * can't do much here; we can't reply
500 				 * anything but OK for blocked locks
501 				 * Eventually the client will time out
502 				 * and retry.
503 				 */
504 				do_unlock(fl);
505 				return;
506 			}
507 
508 			/* check lock status */
509 			syslog(LOG_DEBUG, "processing child %d, status %d",
510 			    pid, fl->status);
511 			switch(fl->status) {
512 			case LKST_PROCESSING:
513 				fl->status = LKST_LOCKED;
514 				send_granted(fl, (fl->flags & LOCK_V4) ?
515 				    nlm4_granted : nlm_granted);
516 				break;
517 			case LKST_DYING:
518 				do_unlock(fl);
519 				break;
520 			default:
521 				syslog(LOG_NOTICE, "bad lock status (%d) for"
522 				   " child %d", fl->status, pid);
523 			}
524 		}
525 	}
526 }
527 
528 /*
529  *
530  * try to acquire the lock described by fl. Eventually fork a child to do a
531  * blocking lock if allowed and required.
532  */
533 
534 enum nlm_stats
535 do_lock(fl, block)
536 	struct file_lock *fl;
537 	int block;
538 {
539 	int lflags, error;
540 	struct stat st;
541 
542 	fl->fd = fhopen(fl->filehandle.fhdata, fl->filehandle.fhsize, O_RDWR);
543 	if (fl->fd < 0) {
544 		switch (errno) {
545 		case ESTALE:
546 			error = nlm4_stale_fh;
547 			break;
548 		case EROFS:
549 			error = nlm4_rofs;
550 			break;
551 		default:
552 			error = nlm4_failed;
553 		}
554 		if ((fl->flags & LOCK_V4) == 0)
555 			error = nlm_denied;
556 		syslog(LOG_NOTICE, "fhopen failed (from %s): %s",
557 		    fl->client_name, strerror(errno));
558 		LIST_REMOVE(fl, lcklst);
559 		return error;
560 	}
561 	if (fstat(fl->fd, &st) < 0) {
562 		syslog(LOG_NOTICE, "fstat failed (from %s): %s",
563 		    fl->client_name, strerror(errno));
564 	}
565 	syslog(LOG_DEBUG, "lock from %s.%" PRIu32 " for file%s%s: "
566 	    "dev %u ino %llu (uid %d), flags %d",
567 	    fl->client_name, fl->client.svid,
568 	    fl->client.exclusive ? " (exclusive)":"", block ? " (block)":"",
569 	    st.st_dev, (unsigned long long)st.st_ino, st.st_uid, fl->flags);
570 	lflags = LOCK_NB;
571 	if (fl->client.exclusive == 0)
572 		lflags |= LOCK_SH;
573 	else
574 		lflags |= LOCK_EX;
575 	error = flock(fl->fd, lflags);
576 	if (error != 0 && errno == EAGAIN && block) {
577 		switch (fl->locker = fork()) {
578 		case -1: /* fork failed */
579 			syslog(LOG_NOTICE, "fork failed: %s", strerror(errno));
580 			LIST_REMOVE(fl, lcklst);
581 			close(fl->fd);
582 			return (fl->flags & LOCK_V4) ?
583 			    nlm4_denied_nolock : nlm_denied_nolocks;
584 		case 0:
585 			/*
586 			 * Attempt a blocking lock. Will have to call
587 			 * NLM_GRANTED later.
588 			 */
589 			setproctitle("%s.%" PRIu32,
590 			    fl->client_name, fl->client.svid);
591 			lflags &= ~LOCK_NB;
592 			if(flock(fl->fd, lflags) != 0) {
593 				syslog(LOG_NOTICE, "flock failed: %s",
594 				    strerror(errno));
595 				_exit(1);
596 			}
597 			/* lock granted */
598 			_exit(0);
599 		default:
600 			syslog(LOG_DEBUG, "lock request from %s.%" PRIu32 ": "
601 			    "forked %d",
602 			    fl->client_name, fl->client.svid, fl->locker);
603 			fl->status = LKST_PROCESSING;
604 			return (fl->flags & LOCK_V4) ?
605 			    nlm4_blocked : nlm_blocked;
606 		}
607 	}
608 	/* non block case */
609 	if (error != 0) {
610 		switch (errno) {
611 		case EAGAIN:
612 			error = nlm4_denied;
613 			break;
614 		case ESTALE:
615 			error = nlm4_stale_fh;
616 			break;
617 		case EROFS:
618 			error = nlm4_rofs;
619 			break;
620 		default:
621 			error = nlm4_failed;
622 		}
623 		if ((fl->flags & LOCK_V4) == 0)
624 			error = nlm_denied;
625 		if (errno != EAGAIN)
626 			syslog(LOG_NOTICE, "flock for %s failed: %s",
627 			    fl->client_name, strerror(errno));
628 		else syslog(LOG_DEBUG, "flock for %s failed: %s",
629 			    fl->client_name, strerror(errno));
630 		LIST_REMOVE(fl, lcklst);
631 		close(fl->fd);
632 		return error;
633 	}
634 	fl->status = LKST_LOCKED;
635 	return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
636 }
637 
638 void
639 send_granted(fl, opcode)
640 	struct file_lock *fl;
641 	int opcode;
642 {
643 	CLIENT *cli;
644 	static char dummy;
645 	struct timeval timeo;
646 	int success;
647 	static struct nlm_res retval;
648 	static struct nlm4_res retval4;
649 
650 	cli = get_client(fl->addr,
651 	    (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS);
652 	if (cli == NULL) {
653 		syslog(LOG_NOTICE, "failed to get CLIENT for %s.%" PRIu32,
654 		    fl->client_name, fl->client.svid);
655 		/*
656 		 * We fail to notify remote that the lock has been granted.
657 		 * The client will timeout and retry, the lock will be
658 		 * granted at this time.
659 		 */
660 		return;
661 	}
662 	timeo.tv_sec = 0;
663 	timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */
664 
665 	if (fl->flags & LOCK_V4) {
666 		static nlm4_testargs res;
667 		res.cookie = fl->client_cookie;
668 		res.exclusive = fl->client.exclusive;
669 		res.alock.caller_name = fl->client_name;
670 		res.alock.fh.n_len = fl->filehandle.fhsize;
671 		res.alock.fh.n_bytes = fl->filehandle.fhdata;
672 		res.alock.oh = fl->client.oh;
673 		res.alock.svid = fl->client.svid;
674 		res.alock.l_offset = fl->client.l_offset;
675 		res.alock.l_len = fl->client.l_len;
676 		syslog(LOG_DEBUG, "sending v4 reply%s",
677 		    (fl->flags & LOCK_ASYNC) ? " (async)":"");
678 		if (fl->flags & LOCK_ASYNC) {
679 			success = clnt_call(cli, NLM4_GRANTED_MSG,
680 			    xdr_nlm4_testargs, &res, xdr_void, &dummy, timeo);
681 		} else {
682 			success = clnt_call(cli, NLM4_GRANTED,
683 			    xdr_nlm4_testargs, &res, xdr_nlm4_res,
684 			    &retval4, timeo);
685 		}
686 	} else {
687 		static nlm_testargs res;
688 
689 		res.cookie = fl->client_cookie;
690 		res.exclusive = fl->client.exclusive;
691 		res.alock.caller_name = fl->client_name;
692 		res.alock.fh.n_len = fl->filehandle.fhsize;
693 		res.alock.fh.n_bytes = fl->filehandle.fhdata;
694 		res.alock.oh = fl->client.oh;
695 		res.alock.svid = fl->client.svid;
696 		res.alock.l_offset = fl->client.l_offset;
697 		res.alock.l_len = fl->client.l_len;
698 		syslog(LOG_DEBUG, "sending v1 reply%s",
699 		    (fl->flags & LOCK_ASYNC) ? " (async)":"");
700 		if (fl->flags & LOCK_ASYNC) {
701 			success = clnt_call(cli, NLM_GRANTED_MSG,
702 			    xdr_nlm_testargs, &res, xdr_void, &dummy, timeo);
703 		} else {
704 			success = clnt_call(cli, NLM_GRANTED,
705 			    xdr_nlm_testargs, &res, xdr_nlm_res,
706 			    &retval, timeo);
707 		}
708 	}
709 	if (debug_level > 2)
710 		syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted",
711 		    success, clnt_sperrno(success));
712 
713 }
714 
715 enum nlm_stats
716 do_unlock(rfl)
717 	struct file_lock *rfl;
718 {
719 	struct file_lock *fl;
720 	int error;
721 	int lockst;
722 
723 	/* unlock the file: closing is enough ! */
724 	if (close(rfl->fd) < 0) {
725 		if (errno == ESTALE)
726 			error = nlm4_stale_fh;
727 		else
728 			error = nlm4_failed;
729 		if ((rfl->flags & LOCK_V4) == 0)
730 			error = nlm_denied;
731 		syslog(LOG_NOTICE,
732 		    "close failed (from %s): %s",
733 		    rfl->client_name, strerror(errno));
734 	} else {
735 		error = (rfl->flags & LOCK_V4) ?
736 		    nlm4_granted : nlm_granted;
737 	}
738 	LIST_REMOVE(rfl, lcklst);
739 
740 	/* process the next LKST_WAITING lock request for this fh */
741 	LIST_FOREACH(fl, &lcklst_head, lcklst) {
742 		if (fl->status != LKST_WAITING ||
743 		    fhcmp(&rfl->filehandle, &fl->filehandle) != 0)
744 			continue;
745 
746 		lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */
747 		switch (lockst) {
748 		case nlm4_granted:
749 		/* case nlm_granted: same as nlm4_granted */
750 			send_granted(fl, (fl->flags & LOCK_V4) ?
751 			    nlm4_granted : nlm_granted);
752 			break;
753 		case nlm4_blocked:
754 		/* case nlm_blocked: same as nlm4_blocked */
755 			break;
756 		default:
757 			lfree(fl);
758 			break;
759 		}
760 		break;
761 	}
762 	lfree(rfl);
763 	return error;
764 }
765 
766 void
767 siglock()
768 {
769 	sigset_t block;
770 
771 	sigemptyset(&block);
772 	sigaddset(&block, SIGCHLD);
773 
774 	if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) {
775 		syslog(LOG_WARNING, "siglock failed: %s", strerror(errno));
776 	}
777 }
778 
779 void
780 sigunlock()
781 {
782 	sigset_t block;
783 
784 	sigemptyset(&block);
785 	sigaddset(&block, SIGCHLD);
786 
787 	if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) {
788 		syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno));
789 	}
790 }
791 
792 /* monitor a host through rpc.statd, and keep a ref count */
793 void
794 do_mon(hostname)
795 	char *hostname;
796 {
797 	struct host *hp;
798 	struct mon my_mon;
799 	struct sm_stat_res res;
800 	int retval;
801 
802 	LIST_FOREACH(hp, &hostlst_head, hostlst) {
803 		if (strcmp(hostname, hp->name) == 0) {
804 			/* already monitored, just bump refcnt */
805 			hp->refcnt++;
806 			return;
807 		}
808 	}
809 	/* not found, have to create an entry for it */
810 	hp = malloc(sizeof(struct host));
811  	if (hp == NULL) {
812  		syslog(LOG_WARNING, "can't monitor host %s: malloc failed\n",
813  		    hostname);
814  		return;
815 	}
816 	strlcpy(hp->name, hostname, sizeof(hp->name));
817 	hp->refcnt = 1;
818 	syslog(LOG_DEBUG, "monitoring host %s",
819 	    hostname);
820 	memset(&my_mon, 0, sizeof(my_mon));
821 	my_mon.mon_id.mon_name = hp->name;
822 	my_mon.mon_id.my_id.my_name = "localhost";
823 	my_mon.mon_id.my_id.my_prog = NLM_PROG;
824 	my_mon.mon_id.my_id.my_vers = NLM_SM;
825 	my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
826 	if ((retval =
827 	    callrpc("localhost", SM_PROG, SM_VERS, SM_MON, xdr_mon,
828 	    (char*)&my_mon, xdr_sm_stat_res, (char*)&res)) != 0) {
829 		syslog(LOG_WARNING, "rpc to statd failed: %s",
830 		    clnt_sperrno((enum clnt_stat)retval));
831 		free(hp);
832 		return;
833 	}
834 	if (res.res_stat == stat_fail) {
835 		syslog(LOG_WARNING, "statd failed");
836 		free(hp);
837 		return;
838 	}
839 	LIST_INSERT_HEAD(&hostlst_head, hp, hostlst);
840 }
841 
842 void
843 notify(hostname, state)
844 	char *hostname;
845 	int state;
846 {
847 	struct file_lock *fl, *next_fl;
848 	int err;
849 	syslog(LOG_DEBUG, "notify from %s, new state %d", hostname, state);
850 	/* search all lock for this host; if status changed, release the lock */
851 	siglock();
852 	for (fl = LIST_FIRST(&lcklst_head); fl != NULL; fl = next_fl) {
853 		next_fl = LIST_NEXT(fl, lcklst);
854 		if (strcmp(hostname, fl->client_name) == 0 &&
855 		    fl->nsm_status != state) {
856 			syslog(LOG_DEBUG, "state %d, nsm_state %d, unlocking",
857 			    fl->status, fl->nsm_status);
858 			switch(fl->status) {
859 			case LKST_LOCKED:
860 				err = do_unlock(fl);
861 				if (err != nlm_granted)
862 					syslog(LOG_DEBUG,
863 					    "notify: unlock failed for %s (%d)",
864 			    		    hostname, err);
865 				break;
866 			case LKST_WAITING:
867 				LIST_REMOVE(fl, lcklst);
868 				lfree(fl);
869 				break;
870 			case LKST_PROCESSING:
871 				fl->status = LKST_DYING;
872 				break;
873 			case LKST_DYING:
874 				break;
875 			default:
876 				syslog(LOG_NOTICE, "unknow status %d for %s",
877 				    fl->status, fl->client_name);
878 			}
879 		}
880 	}
881 	sigunlock();
882 }
883