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