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