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