share/examples - Fix cdev warnings
[dragonfly.git] / usr.sbin / rpc.lockd / lockd_lock.c
1 /*
2  * Copyright (c) 2000 Manuel Bouyer.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. 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.
15  *
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.
27  *
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 $
30  */
31
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"
52
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);
56
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 */
70 };
71
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 */
77
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);
85
86 /* list of hosts we monitor */
87 LIST_HEAD(hostlst_head, host);
88 struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head);
89
90 /* struct describing a lock */
91 struct host {
92         LIST_ENTRY(host) hostlst;
93         char name[SM_MAXSTRLEN];
94         int refcnt;
95 };
96
97 void    do_mon(char *);
98
99 /*
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.
103  */
104
105 struct nlm4_holder *
106 testlock(struct nlm4_lock *lock, int flags)
107 {
108         struct file_lock *fl;
109         fhandle_t filehandle;
110
111         /* convert lock to a local filehandle */
112         memcpy(&filehandle, lock->fh.n_bytes, sizeof(filehandle));
113
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);
127         }
128         /* not found */
129         sigunlock();
130         syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name);
131         return NULL;
132 }
133
134 /*
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.
140  */
141 enum nlm_stats
142 getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, int flags)
143 {
144         struct file_lock *fl, *newfl;
145         enum nlm_stats retval;
146
147         if (grace_expired == 0 && lckarg->reclaim == 0)
148                 return (flags & LOCK_V4) ?
149                     nlm4_denied_grace_period : nlm_denied_grace_period;
150
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;
158         }
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));
162         }
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;
173         }
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;
187         }
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;
223                                 }
224                         }
225                         /*
226                          * We already have a lock for this file. Put this one
227                          * in waiting state if allowed to block
228                          */
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;
247                         }
248                 }
249         }
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;
264         }
265         sigunlock();
266         return retval;
267 }
268
269 /* unlock a filehandle */
270 enum nlm_stats
271 unlock(nlm4_lock *lck, int flags)
272 {
273         struct file_lock *fl;
274         fhandle_t filehandle;
275         int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted;
276
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:
301                         /*
302                          * being handled by a child; will clean up
303                          * when the child exits
304                          */
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);
313                 }
314                 sigunlock();
315                 return err;
316         }
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;
322 }
323
324 void
325 lfree(struct file_lock *fl)
326 {
327         free(fl->client.oh.n_bytes);
328         free(fl->client_cookie.n_bytes);
329         free(fl);
330 }
331
332 void
333 sigchild_handler(int sig)
334 {
335         int status;
336         pid_t pid;
337         struct file_lock *fl;
338
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;
349                 }
350                 if (pid == 0) {
351                         /* no more child to handle yet */
352                         return;
353                 }
354                 /*
355                  * if we're here we have a child that exited
356                  * Find the associated file_lock.
357                  */
358                 for (fl = LIST_FIRST(&lcklst_head); fl != NULL;
359                     fl = LIST_NEXT(fl, lcklst)) {
360                         if (pid == fl->locker)
361                                 break;
362                 }
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);
368                                 /*
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.
373                                  */
374                                 do_unlock(fl);
375                                 return;
376                         }
377
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);
393                         }
394                 }
395         }
396 }
397
398 /*
399  *
400  * try to acquire the lock described by fl. Eventually fock a child to do a
401  * blocking lock if allowed and required.
402  */
403
404 enum nlm_stats
405 do_lock(struct file_lock *fl, int block)
406 {
407         int lflags, error;
408         struct stat st;
409
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;
421                 }
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;
428         }
429         if (fstat(fl->fd, &st) < 0) {
430                 syslog(LOG_NOTICE, "fstat failed (from %s): %s",
431                     fl->client_name, strerror(errno));
432         }
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:
453                         /*
454                          * Attempt a blocking lock. Will have to call
455                          * NLM_GRANTED later.
456                          */
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);
463                         }
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;
472                 }
473         }
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;
488                 }
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;
499         }
500         fl->status = LKST_LOCKED;
501         return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
502 }
503
504 void
505 send_granted(struct file_lock *fl, int opcode)
506 {
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;
513
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);
519                 /*
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.
523                  */
524                 return;
525         }
526         timeo.tv_sec = 0;
527         timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */
528
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);
550                 }
551         } else {
552                 static nlm_testargs res;
553
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);
573                 }
574         }
575         if (debug_level > 2)
576                 syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted",
577                     success, clnt_sperrno(success));
578
579 }
580
581 enum nlm_stats
582 do_unlock(struct file_lock *rfl)
583 {
584         struct file_lock *fl;
585         int error;
586         int lockst;
587
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;
602         }
603         LIST_REMOVE(rfl, lcklst);
604
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;
612
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;
626                 }
627                 break;
628         }
629         return error;
630 }
631
632 void
633 siglock(void)
634 {
635         sigset_t block;
636
637         sigemptyset(&block);
638         sigaddset(&block, SIGCHLD);
639
640         if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) {
641                 syslog(LOG_WARNING, "siglock failed: %s", strerror(errno));
642         }
643 }
644
645 void
646 sigunlock(void)
647 {
648         sigset_t block;
649
650         sigemptyset(&block);
651         sigaddset(&block, SIGCHLD);
652
653         if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) {
654                 syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno));
655         }
656 }
657
658 /* monitor a host through rpc.statd, and keep a ref count */
659 void
660 do_mon(char *hostname)
661 {
662         struct host *hp;
663         struct mon my_mon;
664         struct sm_stat_res res;
665         int retval;
666
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;
673                 }
674         }
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;
694         }
695         if (res.res_stat == stat_fail) {
696                 syslog(LOG_WARNING, "statd failed");
697                 free(hp);
698                 return;
699         }
700         LIST_INSERT_HEAD(&hostlst_head, hp, hostlst);
701 }
702
703 void
704 notify(const char *hostname, int state)
705 {
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);
737                         }
738                 }
739         }
740         sigunlock();
741 }