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