1/* 2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3 * 4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5 * and others. 6 * 7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8 * Portions Copyright (C) 1989-1992, Brian Berliner 9 * 10 * You may distribute under the terms of the GNU General Public License as 11 * specified in the README file that comes with the CVS source distribution. 12 * 13 * Set Lock 14 * 15 * Lock file support for CVS. 16 * 17 * $FreeBSD$ 18 */ 19 20/* The node Concurrency in doc/cvs.texinfo has a brief introduction to 21 how CVS locks function, and some of the user-visible consequences of 22 their existence. Here is a summary of why they exist (and therefore, 23 the consequences of hacking CVS to read a repository without creating 24 locks): 25 26 There are two uses. One is the ability to prevent there from being 27 two writers at the same time. This is necessary for any number of 28 reasons (fileattr code, probably others). Commit needs to lock the 29 whole tree so that nothing happens between the up-to-date check and 30 the actual checkin. 31 32 The second use is the ability to ensure that there is not a writer 33 and a reader at the same time (several readers are allowed). Reasons 34 for this are: 35 36 * Readlocks ensure that once CVS has found a collection of rcs 37 files using Find_Names, the files will still exist when it reads 38 them (they may have moved in or out of the attic). 39 40 * Readlocks provide some modicum of consistency, although this is 41 kind of limited--see the node Concurrency in cvs.texinfo. 42 43 * Readlocks ensure that the RCS file does not change between 44 RCS_parse and RCS_reparsercsfile time. This one strikes me as 45 important, although I haven't thought up what bad scenarios might 46 be. 47 48 * Readlocks ensure that we won't find the file in the state in 49 which it is in between the calls to add_rcs_file and RCS_checkin in 50 commit.c (when a file is being added). This state is a state in 51 which the RCS file parsing routines in rcs.c cannot parse the file. 52 53 * Readlocks ensure that a reader won't try to look at a 54 half-written fileattr file (fileattr is not updated atomically). 55 56 (see also the description of anonymous read-only access in 57 "Password authentication security" node in doc/cvs.texinfo). 58 59 While I'm here, I'll try to summarize a few random suggestions 60 which periodically get made about how locks might be different: 61 62 1. Check for EROFS. Maybe useful, although in the presence of NFS 63 EROFS does *not* mean that the file system is unchanging. 64 65 2. Provide an option to disable locks for operations which only 66 read (see above for some of the consequences). 67 68 3. Have a server internally do the locking. Probably a good 69 long-term solution, and many people have been working hard on code 70 changes which would eventually make it possible to have a server 71 which can handle various connections in one process, but there is 72 much, much work still to be done before this is feasible. */ 73 74#include "cvs.h" 75#include <assert.h> 76 77#ifdef HAVE_NANOSLEEP 78# include "xtime.h" 79#else /* HAVE_NANOSLEEP */ 80# if !defined HAVE_USLEEP && defined HAVE_SELECT 81 /* use select as a workaround */ 82# include "xselect.h" 83# endif /* !defined HAVE_USLEEP && defined HAVE_SELECT */ 84#endif /* !HAVE_NANOSLEEP */ 85 86 87struct lock { 88 /* This is the directory in which we may have a lock named by the 89 readlock variable, a lock named by the writelock variable, and/or 90 a lock named CVSLCK. The storage is not allocated along with the 91 struct lock; it is allocated by the Reader_Lock caller or in the 92 case of writelocks, it is just a pointer to the storage allocated 93 for the ->key field. */ 94 char *repository; 95 96 /* The name of the master lock dir. Usually CVSLCK. */ 97 const char *lockdirname; 98 99 /* The full path to the lock dir, if we are currently holding it. 100 * 101 * This will be LOCKDIRNAME catted onto REPOSITORY. We waste a little 102 * space by storing it, but save a later malloc/free. 103 */ 104 char *lockdir; 105 106 /* Note there is no way of knowing whether the readlock and writelock 107 exist. The code which sets the locks doesn't use SIG_beginCrSect 108 to set a flag like we do for CVSLCK. */ 109}; 110 111static void remove_locks PROTO((void)); 112static int readers_exist PROTO((char *repository)); 113static int set_lock PROTO ((struct lock *lock, int will_wait)); 114static void clear_lock PROTO ((struct lock *lock)); 115static void set_lockers_name PROTO((struct stat *statp)); 116static int set_writelock_proc PROTO((Node * p, void *closure)); 117static int unlock_proc PROTO((Node * p, void *closure)); 118static int write_lock PROTO ((struct lock *lock)); 119static void lock_simple_remove PROTO ((struct lock *lock)); 120static void lock_wait PROTO((char *repository)); 121static void lock_obtained PROTO((char *repository)); 122 123/* Malloc'd array containing the username of the whoever has the lock. 124 Will always be non-NULL in the cases where it is needed. */ 125static char *lockers_name; 126/* Malloc'd array specifying name of a readlock within a directory. 127 Or NULL if none. */ 128static char *readlock; 129/* Malloc'd array specifying name of a writelock within a directory. 130 Or NULL if none. */ 131static char *writelock; 132/* Malloc'd array specifying the name of a CVSLCK file (absolute pathname). 133 Will always be non-NULL in the cases where it is used. */ 134static List *locklist; 135 136#define L_OK 0 /* success */ 137#define L_ERROR 1 /* error condition */ 138#define L_LOCKED 2 /* lock owned by someone else */ 139 140/* This is the (single) readlock which is set by Reader_Lock. The 141 repository field is NULL if there is no such lock. */ 142static struct lock global_readlock = {NULL, CVSLCK, NULL}; 143 144static struct lock global_history_lock = {NULL, CVSHISTORYLCK, NULL}; 145static struct lock global_val_tags_lock = {NULL, CVSVALTAGSLCK, NULL}; 146 147/* List of locks set by lock_tree_for_write. This is redundant 148 with locklist, sort of. */ 149static List *lock_tree_list; 150 151/* If we set locks with lock_dir_for_write, then locked_dir contains 152 the malloc'd name of the repository directory which we have locked. 153 locked_list is the same thing packaged into a list and is redundant 154 with locklist the same way that lock_tree_list is. */ 155static char *locked_dir; 156static List *locked_list; 157 158/* LockDir from CVSROOT/config. */ 159char *lock_dir; 160 161static char *lock_name PROTO ((const char *repository, const char *name)); 162 163/* Return a newly malloc'd string containing the name of the lock for the 164 repository REPOSITORY and the lock file name within that directory 165 NAME. Also create the directories in which to put the lock file 166 if needed (if we need to, could save system call(s) by doing 167 that only if the actual operation fails. But for now we'll keep 168 things simple). */ 169static char * 170lock_name (repository, name) 171 const char *repository; 172 const char *name; 173{ 174 char *retval; 175 const char *p; 176 char *q; 177 const char *short_repos; 178 mode_t save_umask = 0; 179 int saved_umask = 0; 180 181 if (lock_dir == NULL) 182 { 183 /* This is the easy case. Because the lock files go directly 184 in the repository, no need to create directories or anything. */ 185 retval = xmalloc (strlen (repository) + strlen (name) + 10); 186 (void) sprintf (retval, "%s/%s", repository, name); 187 } 188 else 189 { 190 struct stat sb; 191 mode_t new_mode = 0; 192 193 /* The interesting part of the repository is the part relative 194 to CVSROOT. */ 195 assert (current_parsed_root != NULL); 196 assert (current_parsed_root->directory != NULL); 197 assert (strncmp (repository, current_parsed_root->directory, 198 strlen (current_parsed_root->directory)) == 0); 199 short_repos = repository + strlen (current_parsed_root->directory) + 1; 200 201 if (strcmp (repository, current_parsed_root->directory) == 0) 202 short_repos = "."; 203 else 204 assert (short_repos[-1] == '/'); 205 206 retval = xmalloc (strlen (lock_dir) 207 + strlen (short_repos) 208 + strlen (name) 209 + 10); 210 strcpy (retval, lock_dir); 211 q = retval + strlen (retval); 212 *q++ = '/'; 213 214 strcpy (q, short_repos); 215 216 /* In the common case, where the directory already exists, let's 217 keep it to one system call. */ 218 if (CVS_STAT (retval, &sb) < 0) 219 { 220 /* If we need to be creating more than one directory, we'll 221 get the existence_error here. */ 222 if (!existence_error (errno)) 223 error (1, errno, "cannot stat directory %s", retval); 224 } 225 else 226 { 227 if (S_ISDIR (sb.st_mode)) 228 goto created; 229 else 230 error (1, 0, "%s is not a directory", retval); 231 } 232 233 /* Now add the directories one at a time, so we can create 234 them if needed. 235 236 The idea behind the new_mode stuff is that the directory we 237 end up creating will inherit permissions from its parent 238 directory (we re-set new_mode with each EEXIST). CVSUMASK 239 isn't right, because typically the reason for LockDir is to 240 use a different set of permissions. We probably want to 241 inherit group ownership also (but we don't try to deal with 242 that, some systems do it for us either always or when g+s is on). 243 244 We don't try to do anything about the permissions on the lock 245 files themselves. The permissions don't really matter so much 246 because the locks will generally be removed by the process 247 which created them. */ 248 249 if (CVS_STAT (lock_dir, &sb) < 0) 250 error (1, errno, "cannot stat %s", lock_dir); 251 new_mode = sb.st_mode; 252 save_umask = umask (0000); 253 saved_umask = 1; 254 255 p = short_repos; 256 while (1) 257 { 258 while (!ISDIRSEP (*p) && *p != '\0') 259 ++p; 260 if (ISDIRSEP (*p)) 261 { 262 strncpy (q, short_repos, p - short_repos); 263 q[p - short_repos] = '\0'; 264 if (!ISDIRSEP (q[p - short_repos - 1]) 265 && CVS_MKDIR (retval, new_mode) < 0) 266 { 267 int saved_errno = errno; 268 if (saved_errno != EEXIST) 269 error (1, errno, "cannot make directory %s", retval); 270 else 271 { 272 if (CVS_STAT (retval, &sb) < 0) 273 error (1, errno, "cannot stat %s", retval); 274 new_mode = sb.st_mode; 275 } 276 } 277 ++p; 278 } 279 else 280 { 281 strcpy (q, short_repos); 282 if (CVS_MKDIR (retval, new_mode) < 0 283 && errno != EEXIST) 284 error (1, errno, "cannot make directory %s", retval); 285 goto created; 286 } 287 } 288 created:; 289 290 strcat (retval, "/"); 291 strcat (retval, name); 292 293 if (saved_umask) 294 { 295 assert (umask (save_umask) == 0000); 296 saved_umask = 0; 297 } 298 } 299 return retval; 300} 301 302/* 303 * Clean up all outstanding locks 304 */ 305void 306Lock_Cleanup () 307{ 308 /* FIXME: error handling here is kind of bogus; we sometimes will call 309 error, which in turn can call us again. For the moment work around 310 this by refusing to reenter this function (this is a kludge). */ 311 /* FIXME-reentrancy: the workaround isn't reentrant. */ 312 static int in_lock_cleanup = 0; 313 314 if (trace) 315 (void) fprintf (stderr, "%s-> Lock_Cleanup()\n", CLIENT_SERVER_STR); 316 317 if (in_lock_cleanup) 318 return; 319 in_lock_cleanup = 1; 320 321 remove_locks (); 322 323 dellist (&lock_tree_list); 324 325 if (locked_dir != NULL) 326 { 327 dellist (&locked_list); 328 free (locked_dir); 329 locked_dir = NULL; 330 locked_list = NULL; 331 } 332 333 if (global_history_lock.repository) clear_history_lock (); 334 if (global_val_tags_lock.repository) clear_val_tags_lock (); 335 336 in_lock_cleanup = 0; 337} 338 339/* 340 * Remove locks without discarding the lock information 341 */ 342static void 343remove_locks () 344{ 345 /* clean up simple locks (if any) */ 346 if (global_readlock.repository != NULL) 347 { 348 lock_simple_remove (&global_readlock); 349 global_readlock.repository = NULL; 350 } 351 352 /* clean up multiple locks (if any) */ 353 if (locklist != (List *) NULL) 354 { 355 (void) walklist (locklist, unlock_proc, NULL); 356 locklist = (List *) NULL; 357 } 358} 359 360/* 361 * walklist proc for removing a list of locks 362 */ 363static int 364unlock_proc (p, closure) 365 Node *p; 366 void *closure; 367{ 368 lock_simple_remove (p->data); 369 return (0); 370} 371 372 373 374/* Remove the lock files. */ 375static void 376lock_simple_remove (lock) 377 struct lock *lock; 378{ 379 char *tmp; 380 381 /* If readlock is set, the lock directory *might* have been created, but 382 since Reader_Lock doesn't use SIG_beginCrSect the way that set_lock 383 does, we don't know that. That is why we need to check for 384 existence_error here. */ 385 if (readlock != NULL) 386 { 387 tmp = lock_name (lock->repository, readlock); 388 if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) 389 error (0, errno, "failed to remove lock %s", tmp); 390 free (tmp); 391 } 392 393 /* If writelock is set, the lock directory *might* have been created, but 394 since write_lock doesn't use SIG_beginCrSect the way that set_lock 395 does, we don't know that. That is why we need to check for 396 existence_error here. */ 397 if (writelock != NULL) 398 { 399 tmp = lock_name (lock->repository, writelock); 400 if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) 401 error (0, errno, "failed to remove lock %s", tmp); 402 free (tmp); 403 } 404 405 clear_lock (lock); 406} 407 408 409 410/* 411 * Create a lock file for readers 412 */ 413int 414Reader_Lock (xrepository) 415 char *xrepository; 416{ 417 int err = 0; 418 FILE *fp; 419 char *tmp; 420 421 if (trace) 422 (void) fprintf (stderr, "%s-> Reader_Lock(%s)\n", CLIENT_SERVER_STR, 423 xrepository); 424 425 if (noexec || readonlyfs) 426 return 0; 427 428 /* we only do one directory at a time for read locks! */ 429 if (global_readlock.repository != NULL) 430 { 431 error (0, 0, "Reader_Lock called while read locks set - Help!"); 432 return 1; 433 } 434 435 if (readlock == NULL) 436 { 437 readlock = xmalloc (strlen (hostname) + sizeof (CVSRFL) + 40); 438 (void) sprintf (readlock, 439#ifdef HAVE_LONG_FILE_NAMES 440 "%s.%s.%ld", CVSRFL, hostname, 441#else 442 "%s.%ld", CVSRFL, 443#endif 444 (long) getpid ()); 445 } 446 447 /* remember what we're locking (for Lock_Cleanup) */ 448 global_readlock.repository = xrepository; 449 450 /* get the lock dir for our own */ 451 if (set_lock (&global_readlock, 1) != L_OK) 452 { 453 error (0, 0, "failed to obtain dir lock in repository `%s'", 454 xrepository); 455 if (readlock != NULL) 456 free (readlock); 457 readlock = NULL; 458 /* We don't set global_readlock.repository to NULL. I think this 459 only works because recurse.c will give a fatal error if we return 460 a nonzero value. */ 461 return 1; 462 } 463 464 /* write a read-lock */ 465 tmp = lock_name (xrepository, readlock); 466 if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF) 467 { 468 error (0, errno, "cannot create read lock in repository `%s'", 469 xrepository); 470 if (readlock != NULL) 471 free (readlock); 472 readlock = NULL; 473 err = 1; 474 } 475 free (tmp); 476 477 /* free the lock dir */ 478 clear_lock (&global_readlock); 479 480 return err; 481} 482 483 484 485/* 486 * Lock a list of directories for writing 487 */ 488static char *lock_error_repos; 489static int lock_error; 490 491static int Writer_Lock PROTO ((List * list)); 492 493static int 494Writer_Lock (list) 495 List *list; 496{ 497 char *wait_repos; 498 499 if (noexec) 500 return 0; 501 502 if (readonlyfs) { 503 error (0, 0, "write lock failed - read-only repository"); 504 return (1); 505 } 506 507 /* We only know how to do one list at a time */ 508 if (locklist != (List *) NULL) 509 { 510 error (0, 0, "Writer_Lock called while write locks set - Help!"); 511 return 1; 512 } 513 514 wait_repos = NULL; 515 for (;;) 516 { 517 /* try to lock everything on the list */ 518 lock_error = L_OK; /* init for set_writelock_proc */ 519 lock_error_repos = (char *) NULL; /* init for set_writelock_proc */ 520 locklist = list; /* init for Lock_Cleanup */ 521 if (lockers_name != NULL) 522 free (lockers_name); 523 lockers_name = xstrdup ("unknown"); 524 525 (void) walklist (list, set_writelock_proc, NULL); 526 527 switch (lock_error) 528 { 529 case L_ERROR: /* Real Error */ 530 if (wait_repos != NULL) 531 free (wait_repos); 532 Lock_Cleanup (); /* clean up any locks we set */ 533 error (0, 0, "lock failed - giving up"); 534 return 1; 535 536 case L_LOCKED: /* Someone already had a lock */ 537 remove_locks (); /* clean up any locks we set */ 538 lock_wait (lock_error_repos); /* sleep a while and try again */ 539 wait_repos = xstrdup (lock_error_repos); 540 continue; 541 542 case L_OK: /* we got the locks set */ 543 if (wait_repos != NULL) 544 { 545 lock_obtained (wait_repos); 546 free (wait_repos); 547 } 548 return 0; 549 550 default: 551 if (wait_repos != NULL) 552 free (wait_repos); 553 error (0, 0, "unknown lock status %d in Writer_Lock", 554 lock_error); 555 return 1; 556 } 557 } 558} 559 560 561 562/* 563 * walklist proc for setting write locks 564 */ 565static int 566set_writelock_proc (p, closure) 567 Node *p; 568 void *closure; 569{ 570 /* if some lock was not OK, just skip this one */ 571 if (lock_error != L_OK) 572 return 0; 573 574 /* apply the write lock */ 575 lock_error_repos = p->key; 576 lock_error = write_lock (p->data); 577 return 0; 578} 579 580 581 582/* 583 * Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if 584 * lock held by someone else or L_ERROR if an error occurred 585 */ 586static int 587write_lock (lock) 588 struct lock *lock; 589{ 590 int status; 591 FILE *fp; 592 char *tmp; 593 594 if (trace) 595 (void) fprintf (stderr, "%s-> write_lock(%s)\n", 596 CLIENT_SERVER_STR, lock->repository); 597 598 if (writelock == NULL) 599 { 600 writelock = xmalloc (strlen (hostname) + sizeof (CVSWFL) + 40); 601 (void) sprintf (writelock, 602#ifdef HAVE_LONG_FILE_NAMES 603 "%s.%s.%ld", CVSWFL, hostname, 604#else 605 "%s.%ld", CVSWFL, 606#endif 607 (long) getpid()); 608 } 609 610 /* make sure the lock dir is ours (not necessarily unique to us!) */ 611 status = set_lock (lock, 0); 612 if (status == L_OK) 613 { 614 /* we now own a writer - make sure there are no readers */ 615 if (readers_exist (lock->repository)) 616 { 617 /* clean up the lock dir if we created it */ 618 if (status == L_OK) 619 { 620 clear_lock (lock); 621 } 622 623 /* indicate we failed due to read locks instead of error */ 624 return L_LOCKED; 625 } 626 627 /* write the write-lock file */ 628 tmp = lock_name (lock->repository, writelock); 629 if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF) 630 { 631 int xerrno = errno; 632 633 if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) 634 error (0, errno, "failed to remove lock %s", tmp); 635 636 /* free the lock dir if we created it */ 637 if (status == L_OK) 638 { 639 clear_lock (lock); 640 } 641 642 /* return the error */ 643 error (0, xerrno, "cannot create write lock in repository `%s'", 644 lock->repository); 645 free (tmp); 646 return L_ERROR; 647 } 648 free (tmp); 649 return L_OK; 650 } 651 else 652 return status; 653} 654 655 656 657/* 658 * readers_exist() returns 0 if there are no reader lock files remaining in 659 * the repository; else 1 is returned, to indicate that the caller should 660 * sleep a while and try again. 661 */ 662static int 663readers_exist (repository) 664 char *repository; 665{ 666 char *lockdir; 667 char *line; 668 DIR *dirp; 669 struct dirent *dp; 670 struct stat sb; 671 int ret; 672#ifdef CVS_FUDGELOCKS 673 time_t now; 674 (void)time (&now); 675#endif 676 677 lockdir = lock_name (repository, ""); 678 679 assert (lockdir != NULL); 680 681 lockdir[strlen (lockdir) - 1] = '\0'; /* remove trailing slash */ 682 683 do { 684 if ((dirp = CVS_OPENDIR (lockdir)) == NULL) 685 error (1, 0, "cannot open directory %s", lockdir); 686 687 ret = 0; 688 errno = 0; 689 while ((dp = CVS_READDIR (dirp)) != NULL) 690 { 691 if (CVS_FNMATCH (CVSRFLPAT, dp->d_name, 0) == 0) 692 { 693 line = xmalloc (strlen (lockdir) + 1 + strlen (dp->d_name) + 1); 694 (void)sprintf (line, "%s/%s", lockdir, dp->d_name); 695 if (CVS_STAT (line, &sb) != -1) 696 { 697#ifdef CVS_FUDGELOCKS 698 /* 699 * If the create time of the file is more than CVSLCKAGE 700 * seconds ago, try to clean-up the lock file, and if 701 * successful, re-open the directory and try again. 702 */ 703 if (now >= (sb.st_ctime + CVSLCKAGE) && 704 CVS_UNLINK (line) != -1) 705 { 706 free (line); 707 ret = -1; 708 break; 709 } 710#endif 711 set_lockers_name (&sb); 712 } 713 else 714 { 715 /* If the file doesn't exist, it just means that it 716 * disappeared between the time we did the readdir and the 717 * time we did the stat. 718 */ 719 if (!existence_error (errno)) 720 error (0, errno, "cannot stat %s", line); 721 } 722 errno = 0; 723 free (line); 724 ret = 1; 725 break; 726 } 727 errno = 0; 728 } 729 if (errno != 0) 730 error (0, errno, "error reading directory %s", repository); 731 732 CVS_CLOSEDIR (dirp); 733 } while (ret < 0); 734 735 if (lockdir != NULL) 736 free (lockdir); 737 return ret; 738} 739 740 741 742/* 743 * Set the static variable lockers_name appropriately, based on the stat 744 * structure passed in. 745 */ 746static void 747set_lockers_name (statp) 748 struct stat *statp; 749{ 750 struct passwd *pw; 751 752 if (lockers_name != NULL) 753 free (lockers_name); 754 if ((pw = (struct passwd *)getpwuid (statp->st_uid)) != 755 (struct passwd *)NULL) 756 { 757 lockers_name = xstrdup (pw->pw_name); 758 } 759 else 760 { 761 lockers_name = xmalloc (20); 762 (void)sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid); 763 } 764} 765 766 767 768/* 769 * Persistently tries to make the directory "lckdir", which serves as a 770 * lock. 771 * 772 * #ifdef CVS_FUDGELOCKS 773 * If the create time on the directory is greater than CVSLCKAGE 774 * seconds old, just try to remove the directory. 775 * #endif 776 * 777 */ 778static int 779set_lock (lock, will_wait) 780 struct lock *lock; 781 int will_wait; 782{ 783 int waited; 784 long us; 785 struct stat sb; 786 mode_t omask; 787 char *masterlock; 788 int status; 789#ifdef CVS_FUDGELOCKS 790 time_t now; 791#endif 792 793 masterlock = lock_name (lock->repository, lock->lockdirname); 794 795 /* 796 * Note that it is up to the callers of set_lock() to arrange for signal 797 * handlers that do the appropriate things, like remove the lock 798 * directory before they exit. 799 */ 800 waited = 0; 801 us = 1; 802 for (;;) 803 { 804 status = -1; 805 omask = umask (cvsumask); 806 SIG_beginCrSect (); 807 if (CVS_MKDIR (masterlock, 0777) == 0) 808 { 809 lock->lockdir = masterlock; 810 SIG_endCrSect (); 811 status = L_OK; 812 if (waited) 813 lock_obtained (lock->repository); 814 goto after_sig_unblock; 815 } 816 SIG_endCrSect (); 817 after_sig_unblock: 818 (void) umask (omask); 819 if (status != -1) 820 goto done; 821 822 if (errno != EEXIST) 823 { 824 error (0, errno, 825 "failed to create lock directory for `%s' (%s)", 826 lock->repository, masterlock); 827 status = L_ERROR; 828 goto done; 829 } 830 831 /* Find out who owns the lock. If the lock directory is 832 non-existent, re-try the loop since someone probably just 833 removed it (thus releasing the lock). */ 834 if (CVS_STAT (masterlock, &sb) < 0) 835 { 836 if (existence_error (errno)) 837 continue; 838 839 error (0, errno, "couldn't stat lock directory `%s'", masterlock); 840 status = L_ERROR; 841 goto done; 842 } 843 844#ifdef CVS_FUDGELOCKS 845 /* 846 * If the create time of the directory is more than CVSLCKAGE seconds 847 * ago, try to clean-up the lock directory, and if successful, just 848 * quietly retry to make it. 849 */ 850 (void) time (&now); 851 if (now >= (sb.st_ctime + CVSLCKAGE)) 852 { 853 if (CVS_RMDIR (masterlock) >= 0) 854 continue; 855 } 856#endif 857 858 /* set the lockers name */ 859 set_lockers_name (&sb); 860 861 /* if he wasn't willing to wait, return an error */ 862 if (!will_wait) 863 { 864 status = L_LOCKED; 865 goto done; 866 } 867 868 /* if possible, try a very short sleep without a message */ 869 if (!waited && us < 1000) 870 { 871 us += us; 872#if defined HAVE_NANOSLEEP 873 { 874 struct timespec ts; 875 ts.tv_sec = 0; 876 ts.tv_nsec = us * 1000; 877 (void)nanosleep (&ts, NULL); 878 continue; 879 } 880#elif defined HAVE_USLEEP 881 (void)usleep (us); 882 continue; 883#elif defined HAVE_SELECT 884 { 885 struct timeval tv; 886 tv.tv_sec = 0; 887 tv.tv_usec = us; 888 (void)select (0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, &tv); 889 continue; 890 } 891#endif 892 } 893 894 lock_wait (lock->repository); 895 waited = 1; 896 } 897done: 898 if (!lock->lockdir) free (masterlock); 899 return status; 900} 901 902 903 904/* 905 * Clear master lock. 906 * 907 * INPUTS 908 * lock The lock information. 909 * 910 * OUTPUTS 911 * Sets LOCK->lockdir to NULL after removing the directory it names and 912 * freeing the storage. 913 * 914 * ASSUMPTIONS 915 * If we own the master lock directory, its name is stored in LOCK->lockdir. 916 * We may free LOCK->lockdir. 917 * 918 */ 919static void 920clear_lock (lock) 921 struct lock *lock; 922{ 923 SIG_beginCrSect (); 924 if (lock->lockdir) 925 { 926 if (CVS_RMDIR (lock->lockdir) < 0) 927 error (0, errno, "failed to remove lock dir `%s'", lock->lockdir); 928 free (lock->lockdir); 929 lock->lockdir = NULL; 930 } 931 SIG_endCrSect (); 932} 933 934 935 936/* 937 * Print out a message that the lock is still held, then sleep a while. 938 */ 939static void 940lock_wait (repos) 941 char *repos; 942{ 943 time_t now; 944 char *msg; 945 struct tm *tm_p; 946 947 (void) time (&now); 948 tm_p = gmtime (&now); 949 msg = xmalloc (100 + strlen (lockers_name) + strlen (repos)); 950 sprintf (msg, "[%8.8s] waiting for %s's lock in %s", 951 (tm_p ? asctime (tm_p) : ctime (&now)) + 11, 952 lockers_name, repos); 953 error (0, 0, "%s", msg); 954 /* Call cvs_flusherr to ensure that the user sees this message as 955 soon as possible. */ 956 cvs_flusherr (); 957 free (msg); 958 (void) sleep (CVSLCKSLEEP); 959} 960 961/* 962 * Print out a message when we obtain a lock. 963 */ 964static void 965lock_obtained (repos) 966 char *repos; 967{ 968 time_t now; 969 char *msg; 970 struct tm *tm_p; 971 972 (void) time (&now); 973 tm_p = gmtime (&now); 974 msg = xmalloc (100 + strlen (repos)); 975 sprintf (msg, "[%8.8s] obtained lock in %s", 976 (tm_p ? asctime (tm_p) : ctime (&now)) + 11, repos); 977 error (0, 0, "%s", msg); 978 /* Call cvs_flusherr to ensure that the user sees this message as 979 soon as possible. */ 980 cvs_flusherr (); 981 free (msg); 982} 983 984 985 986static int lock_filesdoneproc PROTO ((void *callerdat, int err, 987 const char *repository, 988 const char *update_dir, 989 List *entries)); 990 991/* 992 * Create a list of repositories to lock 993 */ 994/* ARGSUSED */ 995static int 996lock_filesdoneproc (callerdat, err, repository, update_dir, entries) 997 void *callerdat; 998 int err; 999 const char *repository; 1000 const char *update_dir; 1001 List *entries; 1002{ 1003 Node *p; 1004 1005 p = getnode (); 1006 p->type = LOCK; 1007 p->key = xstrdup (repository); 1008 p->data = xmalloc (sizeof (struct lock)); 1009 ((struct lock *)p->data)->repository = p->key; 1010 ((struct lock *)p->data)->lockdirname = CVSLCK; 1011 ((struct lock *)p->data)->lockdir = NULL; 1012 1013 /* FIXME-KRP: this error condition should not simply be passed by. */ 1014 if (p->key == NULL || addnode (lock_tree_list, p) != 0) 1015 freenode (p); 1016 return (err); 1017} 1018 1019void 1020lock_tree_for_write (argc, argv, local, which, aflag) 1021 int argc; 1022 char **argv; 1023 int local; 1024 int which; 1025 int aflag; 1026{ 1027 /* 1028 * Run the recursion processor to find all the dirs to lock and lock all 1029 * the dirs 1030 */ 1031 lock_tree_list = getlist (); 1032 start_recursion ((FILEPROC) NULL, lock_filesdoneproc, 1033 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, 1034 argv, local, which, aflag, CVS_LOCK_NONE, 1035 (char *) NULL, 0, (char *) NULL); 1036 sortlist (lock_tree_list, fsortcmp); 1037 if (Writer_Lock (lock_tree_list) != 0) 1038 error (1, 0, "lock failed - giving up"); 1039} 1040 1041/* Lock a single directory in REPOSITORY. It is OK to call this if 1042 a lock has been set with lock_dir_for_write; the new lock will replace 1043 the old one. If REPOSITORY is NULL, don't do anything. */ 1044void 1045lock_dir_for_write (repository) 1046 char *repository; 1047{ 1048 if (repository != NULL 1049 && (locked_dir == NULL 1050 || strcmp (locked_dir, repository) != 0)) 1051 { 1052 Node *node; 1053 1054 if (locked_dir != NULL) 1055 Lock_Cleanup (); 1056 1057 locked_dir = xstrdup (repository); 1058 locked_list = getlist (); 1059 node = getnode (); 1060 node->type = LOCK; 1061 node->key = xstrdup (repository); 1062 node->data = xmalloc (sizeof (struct lock)); 1063 ((struct lock *)node->data)->repository = node->key; 1064 ((struct lock *)node->data)->lockdirname = CVSLCK; 1065 ((struct lock *)node->data)->lockdir = NULL; 1066 1067 (void) addnode (locked_list, node); 1068 Writer_Lock (locked_list); 1069 } 1070} 1071 1072 1073 1074/* This is the internal implementation behind history_lock & val_tags_lock. It 1075 * gets a write lock for the history or val-tags file. 1076 * 1077 * RETURNS 1078 * true, on success 1079 * false, on error 1080 */ 1081static int internal_lock PROTO ((struct lock *lock, const char *xrepository)); 1082static int 1083internal_lock (lock, xrepository) 1084 struct lock *lock; 1085 const char *xrepository; 1086{ 1087 /* remember what we're locking (for Lock_Cleanup) */ 1088 assert (!lock->repository); 1089 lock->repository = xmalloc (strlen (xrepository) + sizeof (CVSROOTADM) + 2); 1090 sprintf (lock->repository, "%s/%s", xrepository, CVSROOTADM); 1091 1092 /* get the lock dir for our own */ 1093 if (set_lock (lock, 1) != L_OK) 1094 { 1095 if (!really_quiet) 1096 error (0, 0, "failed to obtain history lock in repository `%s'", 1097 xrepository); 1098 1099 return 0; 1100 } 1101 1102 return 1; 1103} 1104 1105 1106 1107/* This is the internal implementation behind history_lock & val_tags_lock. It 1108 * removes the write lock for the history or val-tags file, when it exists. 1109 */ 1110static void internal_clear_lock PROTO((struct lock *lock)); 1111static void 1112internal_clear_lock (lock) 1113 struct lock *lock; 1114{ 1115 SIG_beginCrSect (); 1116 if (lock->repository) 1117 { 1118 free (lock->repository); 1119 lock->repository = NULL; 1120 } 1121 SIG_endCrSect (); 1122 1123 clear_lock (lock); 1124} 1125 1126 1127 1128/* Lock the CVSROOT/history file for write. 1129 */ 1130int 1131history_lock (xrepository) 1132 const char *xrepository; 1133{ 1134 return internal_lock (&global_history_lock, xrepository); 1135} 1136 1137 1138 1139/* Remove the CVSROOT/history lock, if it exists. 1140 */ 1141void 1142clear_history_lock () 1143{ 1144 internal_clear_lock (&global_history_lock); 1145} 1146 1147 1148 1149/* Lock the CVSROOT/val-tags file for write. 1150 */ 1151int 1152val_tags_lock (xrepository) 1153 const char *xrepository; 1154{ 1155 return internal_lock (&global_val_tags_lock, xrepository); 1156} 1157 1158 1159 1160/* Remove the CVSROOT/val-tags lock, if it exists. 1161 */ 1162void 1163clear_val_tags_lock () 1164{ 1165 internal_clear_lock (&global_val_tags_lock); 1166} 1167