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