lockd_lock.c revision 84923
1254889Smarkj/* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */ 2254889Smarkj/* $FreeBSD: head/usr.sbin/rpc.lockd/lockd_lock.c 84923 2001-10-14 18:36:35Z alfred $ */ 3254889Smarkj 4254889Smarkj/* 5254889Smarkj * Copyright (c) 2000 Manuel Bouyer. 6254889Smarkj * 7254889Smarkj * Redistribution and use in source and binary forms, with or without 8254889Smarkj * modification, are permitted provided that the following conditions 9254889Smarkj * are met: 10254889Smarkj * 1. Redistributions of source code must retain the above copyright 11254889Smarkj * notice, this list of conditions and the following disclaimer. 12254889Smarkj * 2. Redistributions in binary form must reproduce the above copyright 13254889Smarkj * notice, this list of conditions and the following disclaimer in the 14254889Smarkj * documentation and/or other materials provided with the distribution. 15254889Smarkj * 3. All advertising materials mentioning features or use of this software 16254889Smarkj * must display the following acknowledgement: 17254889Smarkj * This product includes software developed by the University of 18254889Smarkj * California, Berkeley and its contributors. 19254889Smarkj * 4. Neither the name of the University nor the names of its contributors 20254889Smarkj * may be used to endorse or promote products derived from this software 21254889Smarkj * without specific prior written permission. 22254889Smarkj * 23254889Smarkj * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24254889Smarkj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25254889Smarkj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26254889Smarkj * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27254889Smarkj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28254889Smarkj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29254889Smarkj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30254889Smarkj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31254889Smarkj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32254889Smarkj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33254889Smarkj * SUCH DAMAGE. 34254889Smarkj * 35254889Smarkj */ 36254889Smarkj 37254889Smarkj#include <stdio.h> 38254889Smarkj#include <stdlib.h> 39254889Smarkj#include <unistd.h> 40254889Smarkj#include <fcntl.h> 41254889Smarkj#include <syslog.h> 42254889Smarkj#include <errno.h> 43254889Smarkj#include <string.h> 44254889Smarkj#include <signal.h> 45254889Smarkj#include <rpc/rpc.h> 46254889Smarkj#include <sys/types.h> 47254889Smarkj#include <sys/stat.h> 48254889Smarkj#include <sys/socket.h> 49254889Smarkj#include <sys/param.h> 50254889Smarkj#include <sys/mount.h> 51254889Smarkj#include <sys/wait.h> 52254889Smarkj#include <rpcsvc/sm_inter.h> 53254889Smarkj#include <rpcsvc/nlm_prot.h> 54254889Smarkj#include "lockd_lock.h" 55254889Smarkj#include "lockd.h" 56254889Smarkj 57254889Smarkj/* 58254889Smarkj * A set of utilities for managing file locking 59254889Smarkj * 60254889Smarkj * XXX: All locks are in a linked list, a better structure should be used 61254889Smarkj * to improve search/access effeciency. 62254889Smarkj */ 63254889SmarkjLIST_HEAD(lcklst_head, file_lock); 64254889Smarkjstruct lcklst_head lcklst_head = LIST_HEAD_INITIALIZER(lcklst_head); 65254889Smarkj 66254889Smarkj/* struct describing a lock */ 67254889Smarkjstruct file_lock { 68254889Smarkj LIST_ENTRY(file_lock) lcklst; 69254889Smarkj fhandle_t filehandle; /* NFS filehandle */ 70254889Smarkj struct sockaddr *addr; 71254889Smarkj struct nlm4_holder client; /* lock holder */ 72254889Smarkj netobj client_cookie; /* cookie sent by the client */ 73254889Smarkj char client_name[128]; 74254889Smarkj int nsm_status; /* status from the remote lock manager */ 75254889Smarkj int status; /* lock status, see below */ 76254889Smarkj int flags; /* lock flags, see lockd_lock.h */ 77254889Smarkj pid_t locker; /* pid of the child process trying to get the lock */ 78254889Smarkj int fd; /* file descriptor for this lock */ 79254889Smarkj}; 80254889Smarkj 81254889Smarkj/* lock status */ 82254889Smarkj#define LKST_LOCKED 1 /* lock is locked */ 83254889Smarkj/* XXX: Is this flag file specific or lock specific? */ 84254889Smarkj#define LKST_WAITING 2 /* file is already locked by another host */ 85254889Smarkj#define LKST_PROCESSING 3 /* child is trying to aquire the lock */ 86254889Smarkj#define LKST_DYING 4 /* must dies when we get news from the child */ 87254889Smarkj 88254889Smarkjvoid lfree __P((struct file_lock *)); 89254889Smarkjenum nlm_stats do_lock __P((struct file_lock *, int)); 90254889Smarkjenum nlm_stats do_unlock __P((struct file_lock *)); 91254889Smarkjvoid send_granted __P((struct file_lock *, int)); 92254889Smarkjvoid siglock __P((void)); 93254889Smarkjvoid sigunlock __P((void)); 94254889Smarkjint regions_overlap __P((u_int64_t start1, u_int64_t len1, u_int64_t start2, 95254889Smarkj u_int64_t len2)); 96254889Smarkj 97254889Smarkj/* list of hosts we monitor */ 98254889SmarkjLIST_HEAD(hostlst_head, host); 99254889Smarkjstruct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head); 100254889Smarkj 101254889Smarkj/* struct describing a lock */ 102254889Smarkjstruct host { 103254889Smarkj LIST_ENTRY(host) hostlst; 104254889Smarkj char name[SM_MAXSTRLEN]; 105254889Smarkj int refcnt; 106254889Smarkj}; 107254889Smarkj 108254889Smarkjvoid do_mon __P((char *)); 109254889Smarkj 110254889Smarkj/* 111254889Smarkj * regions_overlap(): This function examines the two provided regions for overlap. 112254889Smarkj * It is non-trivial because start+len *CAN* overflow a 64-bit unsigned integer 113254889Smarkj * and NFS semantics are unspecified on this account. 114254889Smarkj */ 115254889Smarkjint 116254889Smarkjregions_overlap(start1, len1, start2, len2) 117254889Smarkj u_int64_t start1, len1, start2, len2; 118254889Smarkj{ 119254889Smarkj int result; 120254889Smarkj 121254889Smarkj /* XXX: Need to adjust checks to account for integer overflow */ 122254889Smarkj if (len1 == 0 && len2 == 0) { 123254889Smarkj /* Regions *must* overlap if they both extend to the end */ 124254889Smarkj result = TRUE; 125254889Smarkj } else if (len1 == 0 && start2+len2 < start1) { 126254889Smarkj /* Region 2 is completely to the left of Region 1 */ 127254889Smarkj result = FALSE; 128254889Smarkj } else if (start1+len1 < start2 && len2 == 0) { 129254889Smarkj /* Region 1 is completely to the left of region 2 */ 130254889Smarkj result = FALSE; 131254889Smarkj } else if (start1 + len1 <= start2 || start2+len2 <= start1) { 132254889Smarkj /* 1 is completely left of 2 or 2 is completely left of 1 */ 133254889Smarkj result = FALSE; 134254889Smarkj } else { 135254889Smarkj result = TRUE; 136254889Smarkj } 137254889Smarkj return (result); 138254889Smarkj} 139254889Smarkj/* 140254889Smarkj * testlock(): inform the caller if the requested lock would be granted or not 141254889Smarkj * returns NULL if lock would granted, or pointer to the current nlm4_holder 142254889Smarkj * otherwise. 143254889Smarkj */ 144255993Smarkj 145255993Smarkjstruct nlm4_holder * 146255993Smarkjtestlock(lock, exclusive, flags) 147255993Smarkj struct nlm4_lock *lock; 148255993Smarkj bool_t exclusive; 149255993Smarkj int flags; 150255993Smarkj{ 151255993Smarkj struct file_lock *fl; 152255993Smarkj fhandle_t filehandle; 153255993Smarkj 154255993Smarkj /* convert lock to a local filehandle */ 155255993Smarkj memcpy(&filehandle, lock->fh.n_bytes, sizeof(filehandle)); 156255993Smarkj 157255993Smarkj siglock(); 158255993Smarkj /* search through the list for lock holder */ 159255993Smarkj for (fl = LIST_FIRST(&lcklst_head); fl != NULL; 160255993Smarkj fl = LIST_NEXT(fl, lcklst)) { 161255993Smarkj if (fl->status != LKST_LOCKED) 162255993Smarkj continue; 163254889Smarkj /* 164254889Smarkj * XXX: Could we possibly have identical filehandles 165254889Smarkj * on different systems? 166255604Smarkj * ie. Do we need to check more than just the filehandle? 167254889Smarkj * ie. Could someone artificially create requests which are 168254889Smarkj * security violations? 169254889Smarkj */ 170254889Smarkj if (memcmp(&fl->filehandle, &filehandle, sizeof(filehandle))) 171254889Smarkj continue; 172254889Smarkj /* File handles match, look for lock region overlap */ 173254889Smarkj if (regions_overlap(lock->l_offset, lock->l_len, 174254889Smarkj fl->client.l_offset, fl->client.l_len)) { 175254889Smarkj syslog(LOG_DEBUG, 176254889Smarkj "Region overlap found %llu : %llu -- %llu : %llu\n", 177254889Smarkj lock->l_offset, lock->l_len, 178254889Smarkj fl->client.l_offset,fl->client.l_len); 179254889Smarkj /* Regions overlap. Now check for exclusivity. */ 180254889Smarkj if (exclusive || fl->client.exclusive) { 181254889Smarkj /* Lock test must fail, regions are exclusive */ 182254889Smarkj break; 183254889Smarkj } 184254889Smarkj } 185254889Smarkj /* Continue looping through all locks */ 186254889Smarkj } 187254889Smarkj sigunlock(); 188254889Smarkj if (fl == NULL) { 189254889Smarkj syslog(LOG_DEBUG, "test for %s: no lock found", 190254889Smarkj lock->caller_name); 191254889Smarkj return NULL; 192254889Smarkj } else { 193254889Smarkj syslog(LOG_DEBUG, "test for %s: found lock held by %s", 194254889Smarkj lock->caller_name, fl->client_name); 195254889Smarkj return (&fl->client); 196254889Smarkj } 197254889Smarkj} 198254889Smarkj 199254889Smarkj/* 200254889Smarkj * getlock: try to aquire the lock. 201254889Smarkj * If file is already locked and we can sleep, put the lock in the list with 202255993Smarkj * status LKST_WAITING; it'll be processed later. 203254889Smarkj * Otherwise try to lock. If we're allowed to block, fork a child which 204254889Smarkj * will do the blocking lock. 205254889Smarkj */ 206254889Smarkjenum nlm_stats 207254889Smarkjgetlock(lckarg, rqstp, flags) 208254889Smarkj nlm4_lockargs * lckarg; 209254889Smarkj struct svc_req *rqstp; 210254889Smarkj int flags; 211254889Smarkj{ 212254889Smarkj struct file_lock *fl, *newfl; 213254889Smarkj enum nlm_stats retval; 214254889Smarkj 215254889Smarkj if (grace_expired == 0 && lckarg->reclaim == 0) 216254889Smarkj return (flags & LOCK_V4) ? 217254889Smarkj nlm4_denied_grace_period : nlm_denied_grace_period; 218254889Smarkj 219255993Smarkj /* allocate new file_lock for this request */ 220255993Smarkj newfl = malloc(sizeof(struct file_lock)); 221255993Smarkj if (newfl == NULL) { 222255993Smarkj syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno)); 223255993Smarkj /* failed */ 224254889Smarkj return (flags & LOCK_V4) ? 225255993Smarkj nlm4_denied_nolocks : nlm_denied_nolocks; 226255993Smarkj } 227255993Smarkj if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) { 228255993Smarkj syslog(LOG_DEBUG, "recieved fhandle size %d, local size %d", 229255993Smarkj lckarg->alock.fh.n_len, (int)sizeof(fhandle_t)); 230255993Smarkj } 231255993Smarkj memcpy(&newfl->filehandle, lckarg->alock.fh.n_bytes, sizeof(fhandle_t)); 232255993Smarkj newfl->addr = (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf; 233255993Smarkj newfl->client.exclusive = lckarg->exclusive; 234255993Smarkj newfl->client.svid = lckarg->alock.svid; 235255993Smarkj newfl->client.oh.n_bytes = malloc(lckarg->alock.oh.n_len); 236255993Smarkj if (newfl->client.oh.n_bytes == NULL) { 237255993Smarkj syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno)); 238255993Smarkj free(newfl); 239254889Smarkj return (flags & LOCK_V4) ? 240254889Smarkj nlm4_denied_nolocks : nlm_denied_nolocks; 241254889Smarkj } 242 newfl->client.oh.n_len = lckarg->alock.oh.n_len; 243 memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes, 244 lckarg->alock.oh.n_len); 245 newfl->client.l_offset = lckarg->alock.l_offset; 246 newfl->client.l_len = lckarg->alock.l_len; 247 newfl->client_cookie.n_len = lckarg->cookie.n_len; 248 newfl->client_cookie.n_bytes = malloc(lckarg->cookie.n_len); 249 if (newfl->client_cookie.n_bytes == NULL) { 250 syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno)); 251 free(newfl->client.oh.n_bytes); 252 free(newfl); 253 return (flags & LOCK_V4) ? 254 nlm4_denied_nolocks : nlm_denied_nolocks; 255 } 256 memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes, 257 lckarg->cookie.n_len); 258 strncpy(newfl->client_name, lckarg->alock.caller_name, 128); 259 newfl->nsm_status = lckarg->state; 260 newfl->status = 0; 261 newfl->flags = flags; 262 siglock(); 263 /* look for a lock rq from this host for this fh */ 264 for (fl = LIST_FIRST(&lcklst_head); fl != NULL; 265 fl = LIST_NEXT(fl, lcklst)) { 266 if (memcmp(&newfl->filehandle, &fl->filehandle, 267 sizeof(fhandle_t)) == 0) { 268 if (strcmp(newfl->client_name, fl->client_name) == 0 && 269 newfl->client.svid == fl->client.svid) { 270 /* already locked by this host ??? */ 271 sigunlock(); 272 syslog(LOG_NOTICE, "duplicate lock from %s", 273 newfl->client_name); 274 lfree(newfl); 275 switch(fl->status) { 276 case LKST_LOCKED: 277 return (flags & LOCK_V4) ? 278 nlm4_granted : nlm_granted; 279 case LKST_WAITING: 280 case LKST_PROCESSING: 281 return (flags & LOCK_V4) ? 282 nlm4_blocked : nlm_blocked; 283 case LKST_DYING: 284 return (flags & LOCK_V4) ? 285 nlm4_denied : nlm_denied; 286 default: 287 syslog(LOG_NOTICE, "bad status %d", 288 fl->status); 289 return (flags & LOCK_V4) ? 290 nlm4_failed : nlm_denied; 291 } 292 } 293 /* 294 * We already have a lock for this file. Put this one 295 * in waiting state if allowed to block 296 */ 297 if (lckarg->block) { 298 syslog(LOG_DEBUG, "lock from %s: already " 299 "locked, waiting", 300 lckarg->alock.caller_name); 301 newfl->status = LKST_WAITING; 302 LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst); 303 do_mon(lckarg->alock.caller_name); 304 sigunlock(); 305 return (flags & LOCK_V4) ? 306 nlm4_blocked : nlm_blocked; 307 } else { 308 sigunlock(); 309 syslog(LOG_DEBUG, "lock from %s: already " 310 "locked, failed", 311 lckarg->alock.caller_name); 312 lfree(newfl); 313 return (flags & LOCK_V4) ? 314 nlm4_denied : nlm_denied; 315 } 316 } 317 } 318 /* no entry for this file yet; add to list */ 319 LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst); 320 /* do the lock */ 321 retval = do_lock(newfl, lckarg->block); 322 switch (retval) { 323 case nlm4_granted: 324 /* case nlm_granted: is the same as nlm4_granted */ 325 case nlm4_blocked: 326 /* case nlm_blocked: is the same as nlm4_blocked */ 327 do_mon(lckarg->alock.caller_name); 328 break; 329 default: 330 lfree(newfl); 331 break; 332 } 333 sigunlock(); 334 return retval; 335} 336 337/* unlock a filehandle */ 338enum nlm_stats 339unlock(lck, flags) 340 nlm4_lock *lck; 341 int flags; 342{ 343 struct file_lock *fl; 344 fhandle_t filehandle; 345 int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted; 346 347 memcpy(&filehandle, lck->fh.n_bytes, sizeof(fhandle_t)); 348 siglock(); 349 for (fl = LIST_FIRST(&lcklst_head); fl != NULL; 350 fl = LIST_NEXT(fl, lcklst)) { 351 if (strcmp(fl->client_name, lck->caller_name) || 352 memcmp(&filehandle, &fl->filehandle, sizeof(fhandle_t)) || 353 fl->client.oh.n_len != lck->oh.n_len || 354 memcmp(fl->client.oh.n_bytes, lck->oh.n_bytes, 355 fl->client.oh.n_len) != 0 || 356 fl->client.svid != lck->svid) 357 continue; 358 /* Got it, unlock and remove from the queue */ 359 syslog(LOG_DEBUG, "unlock from %s: found struct, status %d", 360 lck->caller_name, fl->status); 361 switch (fl->status) { 362 case LKST_LOCKED: 363 err = do_unlock(fl); 364 break; 365 case LKST_WAITING: 366 /* remove from the list */ 367 LIST_REMOVE(fl, lcklst); 368 lfree(fl); 369 break; 370 case LKST_PROCESSING: 371 /* 372 * being handled by a child; will clean up 373 * when the child exits 374 */ 375 fl->status = LKST_DYING; 376 break; 377 case LKST_DYING: 378 /* nothing to do */ 379 break; 380 default: 381 syslog(LOG_NOTICE, "unknow status %d for %s", 382 fl->status, fl->client_name); 383 } 384 sigunlock(); 385 return err; 386 } 387 sigunlock(); 388 /* didn't find a matching entry; log anyway */ 389 syslog(LOG_NOTICE, "no matching entry for %s", 390 lck->caller_name); 391 return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; 392} 393 394void 395lfree(fl) 396 struct file_lock *fl; 397{ 398 free(fl->client.oh.n_bytes); 399 free(fl->client_cookie.n_bytes); 400 free(fl); 401} 402 403void 404sigchild_handler(sig) 405 int sig; 406{ 407 int status; 408 pid_t pid; 409 struct file_lock *fl; 410 411 while (1) { 412 pid = wait4(-1, &status, WNOHANG, NULL); 413 if (pid == -1) { 414 if (errno != ECHILD) 415 syslog(LOG_NOTICE, "wait failed: %s", 416 strerror(errno)); 417 else 418 syslog(LOG_DEBUG, "wait failed: %s", 419 strerror(errno)); 420 return; 421 } 422 if (pid == 0) { 423 /* no more child to handle yet */ 424 return; 425 } 426 /* 427 * if we're here we have a child that exited 428 * Find the associated file_lock. 429 */ 430 for (fl = LIST_FIRST(&lcklst_head); fl != NULL; 431 fl = LIST_NEXT(fl, lcklst)) { 432 if (pid == fl->locker) 433 break; 434 } 435 if (pid != fl->locker) { 436 syslog(LOG_NOTICE, "unknow child %d", pid); 437 } else { 438 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 439 syslog(LOG_NOTICE, "child %d failed", pid); 440 /* 441 * can't do much here; we can't reply 442 * anything but OK for blocked locks 443 * Eventually the client will time out 444 * and retry. 445 */ 446 do_unlock(fl); 447 return; 448 } 449 450 /* check lock status */ 451 syslog(LOG_DEBUG, "processing child %d, status %d", 452 pid, fl->status); 453 switch(fl->status) { 454 case LKST_PROCESSING: 455 fl->status = LKST_LOCKED; 456 send_granted(fl, (fl->flags & LOCK_V4) ? 457 nlm4_granted : nlm_granted); 458 break; 459 case LKST_DYING: 460 do_unlock(fl); 461 break; 462 default: 463 syslog(LOG_NOTICE, "bad lock status (%d) for" 464 " child %d", fl->status, pid); 465 } 466 } 467 } 468} 469 470/* 471 * 472 * try to aquire the lock described by fl. Eventually fock a child to do a 473 * blocking lock if allowed and required. 474 */ 475 476enum nlm_stats 477do_lock(fl, block) 478 struct file_lock *fl; 479 int block; 480{ 481 int lflags, error; 482 struct stat st; 483 484 fl->fd = fhopen(&fl->filehandle, O_RDWR); 485 if (fl->fd < 0) { 486 switch (errno) { 487 case ESTALE: 488 error = nlm4_stale_fh; 489 break; 490 case EROFS: 491 error = nlm4_rofs; 492 break; 493 default: 494 error = nlm4_failed; 495 } 496 if ((fl->flags & LOCK_V4) == 0) 497 error = nlm_denied; 498 syslog(LOG_NOTICE, "fhopen failed (from %s): %s", 499 fl->client_name, strerror(errno)); 500 LIST_REMOVE(fl, lcklst); 501 return error;; 502 } 503 if (fstat(fl->fd, &st) < 0) { 504 syslog(LOG_NOTICE, "fstat failed (from %s): %s", 505 fl->client_name, strerror(errno)); 506 } 507 syslog(LOG_DEBUG, "lock from %s for file%s%s: dev %d ino %d (uid %d), " 508 "flags %d", 509 fl->client_name, fl->client.exclusive ? " (exclusive)":"", 510 block ? " (block)":"", 511 st.st_dev, st.st_ino, st.st_uid, fl->flags); 512 lflags = LOCK_NB; 513 if (fl->client.exclusive == 0) 514 lflags |= LOCK_SH; 515 else 516 lflags |= LOCK_EX; 517 error = flock(fl->fd, lflags); 518 if (error != 0 && errno == EAGAIN && block) { 519 switch (fl->locker = fork()) { 520 case -1: /* fork failed */ 521 syslog(LOG_NOTICE, "fork failed: %s", strerror(errno)); 522 LIST_REMOVE(fl, lcklst); 523 close(fl->fd); 524 return (fl->flags & LOCK_V4) ? 525 nlm4_denied_nolocks : nlm_denied_nolocks; 526 case 0: 527 /* 528 * Attempt a blocking lock. Will have to call 529 * NLM_GRANTED later. 530 */ 531 setproctitle("%s", fl->client_name); 532 lflags &= ~LOCK_NB; 533 if(flock(fl->fd, lflags) != 0) { 534 syslog(LOG_NOTICE, "flock failed: %s", 535 strerror(errno)); 536 exit(-1); 537 } 538 /* lock granted */ 539 exit(0); 540 default: 541 syslog(LOG_DEBUG, "lock request from %s: forked %d", 542 fl->client_name, fl->locker); 543 fl->status = LKST_PROCESSING; 544 return (fl->flags & LOCK_V4) ? 545 nlm4_blocked : nlm_blocked; 546 } 547 } 548 /* non block case */ 549 if (error != 0) { 550 switch (errno) { 551 case EAGAIN: 552 error = nlm4_denied; 553 break; 554 case ESTALE: 555 error = nlm4_stale_fh; 556 break; 557 case EROFS: 558 error = nlm4_rofs; 559 break; 560 default: 561 error = nlm4_failed; 562 } 563 if ((fl->flags & LOCK_V4) == 0) 564 error = nlm_denied; 565 if (errno != EAGAIN) 566 syslog(LOG_NOTICE, "flock for %s failed: %s", 567 fl->client_name, strerror(errno)); 568 else syslog(LOG_DEBUG, "flock for %s failed: %s", 569 fl->client_name, strerror(errno)); 570 LIST_REMOVE(fl, lcklst); 571 close(fl->fd); 572 return error; 573 } 574 fl->status = LKST_LOCKED; 575 return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; 576} 577 578void 579send_granted(fl, opcode) 580 struct file_lock *fl; 581 int opcode; 582{ 583 CLIENT *cli; 584 static char dummy; 585 struct timeval timeo; 586 int success; 587 static struct nlm_res retval; 588 static struct nlm4_res retval4; 589 590 cli = get_client(fl->addr, 591 (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS); 592 if (cli == NULL) { 593 syslog(LOG_NOTICE, "failed to get CLIENT for %s", 594 fl->client_name); 595 /* 596 * We fail to notify remote that the lock has been granted. 597 * The client will timeout and retry, the lock will be 598 * granted at this time. 599 */ 600 return; 601 } 602 timeo.tv_sec = 0; 603 timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */ 604 605 if (fl->flags & LOCK_V4) { 606 static nlm4_testargs res; 607 res.cookie = fl->client_cookie; 608 res.exclusive = fl->client.exclusive; 609 res.alock.caller_name = fl->client_name; 610 res.alock.fh.n_len = sizeof(fhandle_t); 611 res.alock.fh.n_bytes = (char*)&fl->filehandle; 612 res.alock.oh = fl->client.oh; 613 res.alock.svid = fl->client.svid; 614 res.alock.l_offset = fl->client.l_offset; 615 res.alock.l_len = fl->client.l_len; 616 syslog(LOG_DEBUG, "sending v4 reply%s", 617 (fl->flags & LOCK_ASYNC) ? " (async)":""); 618 if (fl->flags & LOCK_ASYNC) { 619 success = clnt_call(cli, NLM4_GRANTED_MSG, 620 xdr_nlm4_testargs, &res, xdr_void, &dummy, timeo); 621 } else { 622 success = clnt_call(cli, NLM4_GRANTED, 623 xdr_nlm4_testargs, &res, xdr_nlm4_res, 624 &retval4, timeo); 625 } 626 } else { 627 static nlm_testargs res; 628 629 res.cookie = fl->client_cookie; 630 res.exclusive = fl->client.exclusive; 631 res.alock.caller_name = fl->client_name; 632 res.alock.fh.n_len = sizeof(fhandle_t); 633 res.alock.fh.n_bytes = (char*)&fl->filehandle; 634 res.alock.oh = fl->client.oh; 635 res.alock.svid = fl->client.svid; 636 res.alock.l_offset = fl->client.l_offset; 637 res.alock.l_len = fl->client.l_len; 638 syslog(LOG_DEBUG, "sending v1 reply%s", 639 (fl->flags & LOCK_ASYNC) ? " (async)":""); 640 if (fl->flags & LOCK_ASYNC) { 641 success = clnt_call(cli, NLM_GRANTED_MSG, 642 xdr_nlm_testargs, &res, xdr_void, &dummy, timeo); 643 } else { 644 success = clnt_call(cli, NLM_GRANTED, 645 xdr_nlm_testargs, &res, xdr_nlm_res, 646 &retval, timeo); 647 } 648 } 649 if (debug_level > 2) 650 syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted", 651 success, clnt_sperrno(success)); 652 653} 654 655enum nlm_stats 656do_unlock(rfl) 657 struct file_lock *rfl; 658{ 659 struct file_lock *fl; 660 int error; 661 int lockst; 662 663 /* unlock the file: closing is enouth ! */ 664 if (close(rfl->fd) < 0) { 665 if (errno == ESTALE) 666 error = nlm4_stale_fh; 667 else 668 error = nlm4_failed; 669 if ((fl->flags & LOCK_V4) == 0) 670 error = nlm_denied; 671 syslog(LOG_NOTICE, 672 "close failed (from %s): %s", 673 rfl->client_name, strerror(errno)); 674 } else { 675 error = (fl->flags & LOCK_V4) ? 676 nlm4_granted : nlm_granted; 677 } 678 LIST_REMOVE(rfl, lcklst); 679 680 /* process the next LKST_WAITING lock request for this fh */ 681 for (fl = LIST_FIRST(&lcklst_head); fl != NULL; 682 fl = LIST_NEXT(fl, lcklst)) { 683 if (fl->status != LKST_WAITING || 684 memcmp(&rfl->filehandle, &fl->filehandle, 685 sizeof(fhandle_t)) != 0) 686 continue; 687 688 lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */ 689 switch (lockst) { 690 case nlm4_granted: 691 /* case nlm_granted: same as nlm4_granted */ 692 send_granted(fl, (fl->flags & LOCK_V4) ? 693 nlm4_granted : nlm_granted); 694 break; 695 case nlm4_blocked: 696 /* case nlm_blocked: same as nlm4_blocked */ 697 break; 698 default: 699 lfree(fl); 700 break; 701 } 702 break; 703 } 704 return error; 705} 706 707void 708siglock() 709{ 710 sigset_t block; 711 712 sigemptyset(&block); 713 sigaddset(&block, SIGCHLD); 714 715 if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) { 716 syslog(LOG_WARNING, "siglock failed: %s", strerror(errno)); 717 } 718} 719 720void 721sigunlock() 722{ 723 sigset_t block; 724 725 sigemptyset(&block); 726 sigaddset(&block, SIGCHLD); 727 728 if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) { 729 syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno)); 730 } 731} 732 733/* monitor a host through rpc.statd, and keep a ref count */ 734void 735do_mon(hostname) 736 char *hostname; 737{ 738 struct host *hp; 739 struct mon my_mon; 740 struct sm_stat_res res; 741 int retval; 742 743 for (hp = LIST_FIRST(&hostlst_head); hp != NULL; 744 hp = LIST_NEXT(hp, hostlst)) { 745 if (strcmp(hostname, hp->name) == 0) { 746 /* already monitored, just bump refcnt */ 747 hp->refcnt++; 748 return; 749 } 750 } 751 /* not found, have to create an entry for it */ 752 hp = malloc(sizeof(struct host)); 753 strncpy(hp->name, hostname, SM_MAXSTRLEN); 754 hp->refcnt = 1; 755 syslog(LOG_DEBUG, "monitoring host %s", 756 hostname); 757 memset(&my_mon, 0, sizeof(my_mon)); 758 my_mon.mon_id.mon_name = hp->name; 759 my_mon.mon_id.my_id.my_name = "localhost"; 760 my_mon.mon_id.my_id.my_prog = NLM_PROG; 761 my_mon.mon_id.my_id.my_vers = NLM_SM; 762 my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; 763 if ((retval = 764 callrpc("localhost", SM_PROG, SM_VERS, SM_MON, xdr_mon, 765 (char*)&my_mon, xdr_sm_stat_res, (char*)&res)) != 0) { 766 syslog(LOG_WARNING, "rpc to statd failed: %s", 767 clnt_sperrno((enum clnt_stat)retval)); 768 free(hp); 769 return; 770 } 771 if (res.res_stat == stat_fail) { 772 syslog(LOG_WARNING, "statd failed"); 773 free(hp); 774 return; 775 } 776 LIST_INSERT_HEAD(&hostlst_head, hp, hostlst); 777} 778 779void 780notify(hostname, state) 781 char *hostname; 782 int state; 783{ 784 struct file_lock *fl, *next_fl; 785 int err; 786 syslog(LOG_DEBUG, "notify from %s, new state %d", hostname, state); 787 /* search all lock for this host; if status changed, release the lock */ 788 siglock(); 789 for (fl = LIST_FIRST(&lcklst_head); fl != NULL; fl = next_fl) { 790 next_fl = LIST_NEXT(fl, lcklst); 791 if (strcmp(hostname, fl->client_name) == 0 && 792 fl->nsm_status != state) { 793 syslog(LOG_DEBUG, "state %d, nsm_state %d, unlocking", 794 fl->status, fl->nsm_status); 795 switch(fl->status) { 796 case LKST_LOCKED: 797 err = do_unlock(fl); 798 if (err != nlm_granted) 799 syslog(LOG_DEBUG, 800 "notify: unlock failed for %s (%d)", 801 hostname, err); 802 break; 803 case LKST_WAITING: 804 LIST_REMOVE(fl, lcklst); 805 lfree(fl); 806 break; 807 case LKST_PROCESSING: 808 fl->status = LKST_DYING; 809 break; 810 case LKST_DYING: 811 break; 812 default: 813 syslog(LOG_NOTICE, "unknow status %d for %s", 814 fl->status, fl->client_name); 815 } 816 } 817 } 818 sigunlock(); 819} 820