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