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