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