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