PageRenderTime 73ms CodeModel.GetById 21ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/cvs/src/lock.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1166 lines | 721 code | 155 blank | 290 comment | 152 complexity | 4e7f7379d3ba7b898cd1a98e5093dcba MD5 | raw file
   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}