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