diff options
Diffstat (limited to 'contrib/cvs/src/lock.c')
-rw-r--r-- | contrib/cvs/src/lock.c | 1166 |
1 files changed, 0 insertions, 1166 deletions
diff --git a/contrib/cvs/src/lock.c b/contrib/cvs/src/lock.c deleted file mode 100644 index d8e78fa..0000000 --- a/contrib/cvs/src/lock.c +++ /dev/null @@ -1,1166 +0,0 @@ -/* - * Copyright (C) 1986-2005 The Free Software Foundation, Inc. - * - * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, - * and others. - * - * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk - * Portions Copyright (C) 1989-1992, Brian Berliner - * - * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS source distribution. - * - * Set Lock - * - * Lock file support for CVS. - * - * $FreeBSD$ - */ - -/* The node Concurrency in doc/cvs.texinfo has a brief introduction to - how CVS locks function, and some of the user-visible consequences of - their existence. Here is a summary of why they exist (and therefore, - the consequences of hacking CVS to read a repository without creating - locks): - - There are two uses. One is the ability to prevent there from being - two writers at the same time. This is necessary for any number of - reasons (fileattr code, probably others). Commit needs to lock the - whole tree so that nothing happens between the up-to-date check and - the actual checkin. - - The second use is the ability to ensure that there is not a writer - and a reader at the same time (several readers are allowed). Reasons - for this are: - - * Readlocks ensure that once CVS has found a collection of rcs - files using Find_Names, the files will still exist when it reads - them (they may have moved in or out of the attic). - - * Readlocks provide some modicum of consistency, although this is - kind of limited--see the node Concurrency in cvs.texinfo. - - * Readlocks ensure that the RCS file does not change between - RCS_parse and RCS_reparsercsfile time. This one strikes me as - important, although I haven't thought up what bad scenarios might - be. - - * Readlocks ensure that we won't find the file in the state in - which it is in between the calls to add_rcs_file and RCS_checkin in - commit.c (when a file is being added). This state is a state in - which the RCS file parsing routines in rcs.c cannot parse the file. - - * Readlocks ensure that a reader won't try to look at a - half-written fileattr file (fileattr is not updated atomically). - - (see also the description of anonymous read-only access in - "Password authentication security" node in doc/cvs.texinfo). - - While I'm here, I'll try to summarize a few random suggestions - which periodically get made about how locks might be different: - - 1. Check for EROFS. Maybe useful, although in the presence of NFS - EROFS does *not* mean that the file system is unchanging. - - 2. Provide an option to disable locks for operations which only - read (see above for some of the consequences). - - 3. Have a server internally do the locking. Probably a good - long-term solution, and many people have been working hard on code - changes which would eventually make it possible to have a server - which can handle various connections in one process, but there is - much, much work still to be done before this is feasible. */ - -#include "cvs.h" -#include <assert.h> - -#ifdef HAVE_NANOSLEEP -# include "xtime.h" -#else /* HAVE_NANOSLEEP */ -# if !defined HAVE_USLEEP && defined HAVE_SELECT - /* use select as a workaround */ -# include "xselect.h" -# endif /* !defined HAVE_USLEEP && defined HAVE_SELECT */ -#endif /* !HAVE_NANOSLEEP */ - - -struct lock { - /* This is the directory in which we may have a lock named by the - readlock variable, a lock named by the writelock variable, and/or - a lock named CVSLCK. The storage is not allocated along with the - struct lock; it is allocated by the Reader_Lock caller or in the - case of writelocks, it is just a pointer to the storage allocated - for the ->key field. */ - char *repository; - - /* The name of the master lock dir. Usually CVSLCK. */ - const char *lockdirname; - - /* The full path to the lock dir, if we are currently holding it. - * - * This will be LOCKDIRNAME catted onto REPOSITORY. We waste a little - * space by storing it, but save a later malloc/free. - */ - char *lockdir; - - /* Note there is no way of knowing whether the readlock and writelock - exist. The code which sets the locks doesn't use SIG_beginCrSect - to set a flag like we do for CVSLCK. */ -}; - -static void remove_locks PROTO((void)); -static int readers_exist PROTO((char *repository)); -static int set_lock PROTO ((struct lock *lock, int will_wait)); -static void clear_lock PROTO ((struct lock *lock)); -static void set_lockers_name PROTO((struct stat *statp)); -static int set_writelock_proc PROTO((Node * p, void *closure)); -static int unlock_proc PROTO((Node * p, void *closure)); -static int write_lock PROTO ((struct lock *lock)); -static void lock_simple_remove PROTO ((struct lock *lock)); -static void lock_wait PROTO((char *repository)); -static void lock_obtained PROTO((char *repository)); - -/* Malloc'd array containing the username of the whoever has the lock. - Will always be non-NULL in the cases where it is needed. */ -static char *lockers_name; -/* Malloc'd array specifying name of a readlock within a directory. - Or NULL if none. */ -static char *readlock; -/* Malloc'd array specifying name of a writelock within a directory. - Or NULL if none. */ -static char *writelock; -/* Malloc'd array specifying the name of a CVSLCK file (absolute pathname). - Will always be non-NULL in the cases where it is used. */ -static List *locklist; - -#define L_OK 0 /* success */ -#define L_ERROR 1 /* error condition */ -#define L_LOCKED 2 /* lock owned by someone else */ - -/* This is the (single) readlock which is set by Reader_Lock. The - repository field is NULL if there is no such lock. */ -static struct lock global_readlock = {NULL, CVSLCK, NULL}; - -static struct lock global_history_lock = {NULL, CVSHISTORYLCK, NULL}; -static struct lock global_val_tags_lock = {NULL, CVSVALTAGSLCK, NULL}; - -/* List of locks set by lock_tree_for_write. This is redundant - with locklist, sort of. */ -static List *lock_tree_list; - -/* If we set locks with lock_dir_for_write, then locked_dir contains - the malloc'd name of the repository directory which we have locked. - locked_list is the same thing packaged into a list and is redundant - with locklist the same way that lock_tree_list is. */ -static char *locked_dir; -static List *locked_list; - -/* LockDir from CVSROOT/config. */ -char *lock_dir; - -static char *lock_name PROTO ((const char *repository, const char *name)); - -/* Return a newly malloc'd string containing the name of the lock for the - repository REPOSITORY and the lock file name within that directory - NAME. Also create the directories in which to put the lock file - if needed (if we need to, could save system call(s) by doing - that only if the actual operation fails. But for now we'll keep - things simple). */ -static char * -lock_name (repository, name) - const char *repository; - const char *name; -{ - char *retval; - const char *p; - char *q; - const char *short_repos; - mode_t save_umask = 0; - int saved_umask = 0; - - if (lock_dir == NULL) - { - /* This is the easy case. Because the lock files go directly - in the repository, no need to create directories or anything. */ - retval = xmalloc (strlen (repository) + strlen (name) + 10); - (void) sprintf (retval, "%s/%s", repository, name); - } - else - { - struct stat sb; - mode_t new_mode = 0; - - /* The interesting part of the repository is the part relative - to CVSROOT. */ - assert (current_parsed_root != NULL); - assert (current_parsed_root->directory != NULL); - assert (strncmp (repository, current_parsed_root->directory, - strlen (current_parsed_root->directory)) == 0); - short_repos = repository + strlen (current_parsed_root->directory) + 1; - - if (strcmp (repository, current_parsed_root->directory) == 0) - short_repos = "."; - else - assert (short_repos[-1] == '/'); - - retval = xmalloc (strlen (lock_dir) - + strlen (short_repos) - + strlen (name) - + 10); - strcpy (retval, lock_dir); - q = retval + strlen (retval); - *q++ = '/'; - - strcpy (q, short_repos); - - /* In the common case, where the directory already exists, let's - keep it to one system call. */ - if (CVS_STAT (retval, &sb) < 0) - { - /* If we need to be creating more than one directory, we'll - get the existence_error here. */ - if (!existence_error (errno)) - error (1, errno, "cannot stat directory %s", retval); - } - else - { - if (S_ISDIR (sb.st_mode)) - goto created; - else - error (1, 0, "%s is not a directory", retval); - } - - /* Now add the directories one at a time, so we can create - them if needed. - - The idea behind the new_mode stuff is that the directory we - end up creating will inherit permissions from its parent - directory (we re-set new_mode with each EEXIST). CVSUMASK - isn't right, because typically the reason for LockDir is to - use a different set of permissions. We probably want to - inherit group ownership also (but we don't try to deal with - that, some systems do it for us either always or when g+s is on). - - We don't try to do anything about the permissions on the lock - files themselves. The permissions don't really matter so much - because the locks will generally be removed by the process - which created them. */ - - if (CVS_STAT (lock_dir, &sb) < 0) - error (1, errno, "cannot stat %s", lock_dir); - new_mode = sb.st_mode; - save_umask = umask (0000); - saved_umask = 1; - - p = short_repos; - while (1) - { - while (!ISDIRSEP (*p) && *p != '\0') - ++p; - if (ISDIRSEP (*p)) - { - strncpy (q, short_repos, p - short_repos); - q[p - short_repos] = '\0'; - if (!ISDIRSEP (q[p - short_repos - 1]) - && CVS_MKDIR (retval, new_mode) < 0) - { - int saved_errno = errno; - if (saved_errno != EEXIST) - error (1, errno, "cannot make directory %s", retval); - else - { - if (CVS_STAT (retval, &sb) < 0) - error (1, errno, "cannot stat %s", retval); - new_mode = sb.st_mode; - } - } - ++p; - } - else - { - strcpy (q, short_repos); - if (CVS_MKDIR (retval, new_mode) < 0 - && errno != EEXIST) - error (1, errno, "cannot make directory %s", retval); - goto created; - } - } - created:; - - strcat (retval, "/"); - strcat (retval, name); - - if (saved_umask) - { - assert (umask (save_umask) == 0000); - saved_umask = 0; - } - } - return retval; -} - -/* - * Clean up all outstanding locks - */ -void -Lock_Cleanup () -{ - /* FIXME: error handling here is kind of bogus; we sometimes will call - error, which in turn can call us again. For the moment work around - this by refusing to reenter this function (this is a kludge). */ - /* FIXME-reentrancy: the workaround isn't reentrant. */ - static int in_lock_cleanup = 0; - - if (trace) - (void) fprintf (stderr, "%s-> Lock_Cleanup()\n", CLIENT_SERVER_STR); - - if (in_lock_cleanup) - return; - in_lock_cleanup = 1; - - remove_locks (); - - dellist (&lock_tree_list); - - if (locked_dir != NULL) - { - dellist (&locked_list); - free (locked_dir); - locked_dir = NULL; - locked_list = NULL; - } - - if (global_history_lock.repository) clear_history_lock (); - if (global_val_tags_lock.repository) clear_val_tags_lock (); - - in_lock_cleanup = 0; -} - -/* - * Remove locks without discarding the lock information - */ -static void -remove_locks () -{ - /* clean up simple locks (if any) */ - if (global_readlock.repository != NULL) - { - lock_simple_remove (&global_readlock); - global_readlock.repository = NULL; - } - - /* clean up multiple locks (if any) */ - if (locklist != (List *) NULL) - { - (void) walklist (locklist, unlock_proc, NULL); - locklist = (List *) NULL; - } -} - -/* - * walklist proc for removing a list of locks - */ -static int -unlock_proc (p, closure) - Node *p; - void *closure; -{ - lock_simple_remove (p->data); - return (0); -} - - - -/* Remove the lock files. */ -static void -lock_simple_remove (lock) - struct lock *lock; -{ - char *tmp; - - /* If readlock is set, the lock directory *might* have been created, but - since Reader_Lock doesn't use SIG_beginCrSect the way that set_lock - does, we don't know that. That is why we need to check for - existence_error here. */ - if (readlock != NULL) - { - tmp = lock_name (lock->repository, readlock); - if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) - error (0, errno, "failed to remove lock %s", tmp); - free (tmp); - } - - /* If writelock is set, the lock directory *might* have been created, but - since write_lock doesn't use SIG_beginCrSect the way that set_lock - does, we don't know that. That is why we need to check for - existence_error here. */ - if (writelock != NULL) - { - tmp = lock_name (lock->repository, writelock); - if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) - error (0, errno, "failed to remove lock %s", tmp); - free (tmp); - } - - clear_lock (lock); -} - - - -/* - * Create a lock file for readers - */ -int -Reader_Lock (xrepository) - char *xrepository; -{ - int err = 0; - FILE *fp; - char *tmp; - - if (trace) - (void) fprintf (stderr, "%s-> Reader_Lock(%s)\n", CLIENT_SERVER_STR, - xrepository); - - if (noexec || readonlyfs) - return 0; - - /* we only do one directory at a time for read locks! */ - if (global_readlock.repository != NULL) - { - error (0, 0, "Reader_Lock called while read locks set - Help!"); - return 1; - } - - if (readlock == NULL) - { - readlock = xmalloc (strlen (hostname) + sizeof (CVSRFL) + 40); - (void) sprintf (readlock, -#ifdef HAVE_LONG_FILE_NAMES - "%s.%s.%ld", CVSRFL, hostname, -#else - "%s.%ld", CVSRFL, -#endif - (long) getpid ()); - } - - /* remember what we're locking (for Lock_Cleanup) */ - global_readlock.repository = xrepository; - - /* get the lock dir for our own */ - if (set_lock (&global_readlock, 1) != L_OK) - { - error (0, 0, "failed to obtain dir lock in repository `%s'", - xrepository); - if (readlock != NULL) - free (readlock); - readlock = NULL; - /* We don't set global_readlock.repository to NULL. I think this - only works because recurse.c will give a fatal error if we return - a nonzero value. */ - return 1; - } - - /* write a read-lock */ - tmp = lock_name (xrepository, readlock); - if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF) - { - error (0, errno, "cannot create read lock in repository `%s'", - xrepository); - if (readlock != NULL) - free (readlock); - readlock = NULL; - err = 1; - } - free (tmp); - - /* free the lock dir */ - clear_lock (&global_readlock); - - return err; -} - - - -/* - * Lock a list of directories for writing - */ -static char *lock_error_repos; -static int lock_error; - -static int Writer_Lock PROTO ((List * list)); - -static int -Writer_Lock (list) - List *list; -{ - char *wait_repos; - - if (noexec) - return 0; - - if (readonlyfs) { - error (0, 0, "write lock failed - read-only repository"); - return (1); - } - - /* We only know how to do one list at a time */ - if (locklist != (List *) NULL) - { - error (0, 0, "Writer_Lock called while write locks set - Help!"); - return 1; - } - - wait_repos = NULL; - for (;;) - { - /* try to lock everything on the list */ - lock_error = L_OK; /* init for set_writelock_proc */ - lock_error_repos = (char *) NULL; /* init for set_writelock_proc */ - locklist = list; /* init for Lock_Cleanup */ - if (lockers_name != NULL) - free (lockers_name); - lockers_name = xstrdup ("unknown"); - - (void) walklist (list, set_writelock_proc, NULL); - - switch (lock_error) - { - case L_ERROR: /* Real Error */ - if (wait_repos != NULL) - free (wait_repos); - Lock_Cleanup (); /* clean up any locks we set */ - error (0, 0, "lock failed - giving up"); - return 1; - - case L_LOCKED: /* Someone already had a lock */ - remove_locks (); /* clean up any locks we set */ - lock_wait (lock_error_repos); /* sleep a while and try again */ - wait_repos = xstrdup (lock_error_repos); - continue; - - case L_OK: /* we got the locks set */ - if (wait_repos != NULL) - { - lock_obtained (wait_repos); - free (wait_repos); - } - return 0; - - default: - if (wait_repos != NULL) - free (wait_repos); - error (0, 0, "unknown lock status %d in Writer_Lock", - lock_error); - return 1; - } - } -} - - - -/* - * walklist proc for setting write locks - */ -static int -set_writelock_proc (p, closure) - Node *p; - void *closure; -{ - /* if some lock was not OK, just skip this one */ - if (lock_error != L_OK) - return 0; - - /* apply the write lock */ - lock_error_repos = p->key; - lock_error = write_lock (p->data); - return 0; -} - - - -/* - * Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if - * lock held by someone else or L_ERROR if an error occurred - */ -static int -write_lock (lock) - struct lock *lock; -{ - int status; - FILE *fp; - char *tmp; - - if (trace) - (void) fprintf (stderr, "%s-> write_lock(%s)\n", - CLIENT_SERVER_STR, lock->repository); - - if (writelock == NULL) - { - writelock = xmalloc (strlen (hostname) + sizeof (CVSWFL) + 40); - (void) sprintf (writelock, -#ifdef HAVE_LONG_FILE_NAMES - "%s.%s.%ld", CVSWFL, hostname, -#else - "%s.%ld", CVSWFL, -#endif - (long) getpid()); - } - - /* make sure the lock dir is ours (not necessarily unique to us!) */ - status = set_lock (lock, 0); - if (status == L_OK) - { - /* we now own a writer - make sure there are no readers */ - if (readers_exist (lock->repository)) - { - /* clean up the lock dir if we created it */ - if (status == L_OK) - { - clear_lock (lock); - } - - /* indicate we failed due to read locks instead of error */ - return L_LOCKED; - } - - /* write the write-lock file */ - tmp = lock_name (lock->repository, writelock); - if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF) - { - int xerrno = errno; - - if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) - error (0, errno, "failed to remove lock %s", tmp); - - /* free the lock dir if we created it */ - if (status == L_OK) - { - clear_lock (lock); - } - - /* return the error */ - error (0, xerrno, "cannot create write lock in repository `%s'", - lock->repository); - free (tmp); - return L_ERROR; - } - free (tmp); - return L_OK; - } - else - return status; -} - - - -/* - * readers_exist() returns 0 if there are no reader lock files remaining in - * the repository; else 1 is returned, to indicate that the caller should - * sleep a while and try again. - */ -static int -readers_exist (repository) - char *repository; -{ - char *lockdir; - char *line; - DIR *dirp; - struct dirent *dp; - struct stat sb; - int ret; -#ifdef CVS_FUDGELOCKS - time_t now; - (void)time (&now); -#endif - - lockdir = lock_name (repository, ""); - - assert (lockdir != NULL); - - lockdir[strlen (lockdir) - 1] = '\0'; /* remove trailing slash */ - - do { - if ((dirp = CVS_OPENDIR (lockdir)) == NULL) - error (1, 0, "cannot open directory %s", lockdir); - - ret = 0; - errno = 0; - while ((dp = CVS_READDIR (dirp)) != NULL) - { - if (CVS_FNMATCH (CVSRFLPAT, dp->d_name, 0) == 0) - { - line = xmalloc (strlen (lockdir) + 1 + strlen (dp->d_name) + 1); - (void)sprintf (line, "%s/%s", lockdir, dp->d_name); - if (CVS_STAT (line, &sb) != -1) - { -#ifdef CVS_FUDGELOCKS - /* - * If the create time of the file is more than CVSLCKAGE - * seconds ago, try to clean-up the lock file, and if - * successful, re-open the directory and try again. - */ - if (now >= (sb.st_ctime + CVSLCKAGE) && - CVS_UNLINK (line) != -1) - { - free (line); - ret = -1; - break; - } -#endif - set_lockers_name (&sb); - } - else - { - /* If the file doesn't exist, it just means that it - * disappeared between the time we did the readdir and the - * time we did the stat. - */ - if (!existence_error (errno)) - error (0, errno, "cannot stat %s", line); - } - errno = 0; - free (line); - ret = 1; - break; - } - errno = 0; - } - if (errno != 0) - error (0, errno, "error reading directory %s", repository); - - CVS_CLOSEDIR (dirp); - } while (ret < 0); - - if (lockdir != NULL) - free (lockdir); - return ret; -} - - - -/* - * Set the static variable lockers_name appropriately, based on the stat - * structure passed in. - */ -static void -set_lockers_name (statp) - struct stat *statp; -{ - struct passwd *pw; - - if (lockers_name != NULL) - free (lockers_name); - if ((pw = (struct passwd *)getpwuid (statp->st_uid)) != - (struct passwd *)NULL) - { - lockers_name = xstrdup (pw->pw_name); - } - else - { - lockers_name = xmalloc (20); - (void)sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid); - } -} - - - -/* - * Persistently tries to make the directory "lckdir", which serves as a - * lock. - * - * #ifdef CVS_FUDGELOCKS - * If the create time on the directory is greater than CVSLCKAGE - * seconds old, just try to remove the directory. - * #endif - * - */ -static int -set_lock (lock, will_wait) - struct lock *lock; - int will_wait; -{ - int waited; - long us; - struct stat sb; - mode_t omask; - char *masterlock; - int status; -#ifdef CVS_FUDGELOCKS - time_t now; -#endif - - masterlock = lock_name (lock->repository, lock->lockdirname); - - /* - * Note that it is up to the callers of set_lock() to arrange for signal - * handlers that do the appropriate things, like remove the lock - * directory before they exit. - */ - waited = 0; - us = 1; - for (;;) - { - status = -1; - omask = umask (cvsumask); - SIG_beginCrSect (); - if (CVS_MKDIR (masterlock, 0777) == 0) - { - lock->lockdir = masterlock; - SIG_endCrSect (); - status = L_OK; - if (waited) - lock_obtained (lock->repository); - goto after_sig_unblock; - } - SIG_endCrSect (); - after_sig_unblock: - (void) umask (omask); - if (status != -1) - goto done; - - if (errno != EEXIST) - { - error (0, errno, - "failed to create lock directory for `%s' (%s)", - lock->repository, masterlock); - status = L_ERROR; - goto done; - } - - /* Find out who owns the lock. If the lock directory is - non-existent, re-try the loop since someone probably just - removed it (thus releasing the lock). */ - if (CVS_STAT (masterlock, &sb) < 0) - { - if (existence_error (errno)) - continue; - - error (0, errno, "couldn't stat lock directory `%s'", masterlock); - status = L_ERROR; - goto done; - } - -#ifdef CVS_FUDGELOCKS - /* - * If the create time of the directory is more than CVSLCKAGE seconds - * ago, try to clean-up the lock directory, and if successful, just - * quietly retry to make it. - */ - (void) time (&now); - if (now >= (sb.st_ctime + CVSLCKAGE)) - { - if (CVS_RMDIR (masterlock) >= 0) - continue; - } -#endif - - /* set the lockers name */ - set_lockers_name (&sb); - - /* if he wasn't willing to wait, return an error */ - if (!will_wait) - { - status = L_LOCKED; - goto done; - } - - /* if possible, try a very short sleep without a message */ - if (!waited && us < 1000) - { - us += us; -#if defined HAVE_NANOSLEEP - { - struct timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = us * 1000; - (void)nanosleep (&ts, NULL); - continue; - } -#elif defined HAVE_USLEEP - (void)usleep (us); - continue; -#elif defined HAVE_SELECT - { - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = us; - (void)select (0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, &tv); - continue; - } -#endif - } - - lock_wait (lock->repository); - waited = 1; - } -done: - if (!lock->lockdir) free (masterlock); - return status; -} - - - -/* - * Clear master lock. - * - * INPUTS - * lock The lock information. - * - * OUTPUTS - * Sets LOCK->lockdir to NULL after removing the directory it names and - * freeing the storage. - * - * ASSUMPTIONS - * If we own the master lock directory, its name is stored in LOCK->lockdir. - * We may free LOCK->lockdir. - * - */ -static void -clear_lock (lock) - struct lock *lock; -{ - SIG_beginCrSect (); - if (lock->lockdir) - { - if (CVS_RMDIR (lock->lockdir) < 0) - error (0, errno, "failed to remove lock dir `%s'", lock->lockdir); - free (lock->lockdir); - lock->lockdir = NULL; - } - SIG_endCrSect (); -} - - - -/* - * Print out a message that the lock is still held, then sleep a while. - */ -static void -lock_wait (repos) - char *repos; -{ - time_t now; - char *msg; - struct tm *tm_p; - - (void) time (&now); - tm_p = gmtime (&now); - msg = xmalloc (100 + strlen (lockers_name) + strlen (repos)); - sprintf (msg, "[%8.8s] waiting for %s's lock in %s", - (tm_p ? asctime (tm_p) : ctime (&now)) + 11, - lockers_name, repos); - error (0, 0, "%s", msg); - /* Call cvs_flusherr to ensure that the user sees this message as - soon as possible. */ - cvs_flusherr (); - free (msg); - (void) sleep (CVSLCKSLEEP); -} - -/* - * Print out a message when we obtain a lock. - */ -static void -lock_obtained (repos) - char *repos; -{ - time_t now; - char *msg; - struct tm *tm_p; - - (void) time (&now); - tm_p = gmtime (&now); - msg = xmalloc (100 + strlen (repos)); - sprintf (msg, "[%8.8s] obtained lock in %s", - (tm_p ? asctime (tm_p) : ctime (&now)) + 11, repos); - error (0, 0, "%s", msg); - /* Call cvs_flusherr to ensure that the user sees this message as - soon as possible. */ - cvs_flusherr (); - free (msg); -} - - - -static int lock_filesdoneproc PROTO ((void *callerdat, int err, - const char *repository, - const char *update_dir, - List *entries)); - -/* - * Create a list of repositories to lock - */ -/* ARGSUSED */ -static int -lock_filesdoneproc (callerdat, err, repository, update_dir, entries) - void *callerdat; - int err; - const char *repository; - const char *update_dir; - List *entries; -{ - Node *p; - - p = getnode (); - p->type = LOCK; - p->key = xstrdup (repository); - p->data = xmalloc (sizeof (struct lock)); - ((struct lock *)p->data)->repository = p->key; - ((struct lock *)p->data)->lockdirname = CVSLCK; - ((struct lock *)p->data)->lockdir = NULL; - - /* FIXME-KRP: this error condition should not simply be passed by. */ - if (p->key == NULL || addnode (lock_tree_list, p) != 0) - freenode (p); - return (err); -} - -void -lock_tree_for_write (argc, argv, local, which, aflag) - int argc; - char **argv; - int local; - int which; - int aflag; -{ - /* - * Run the recursion processor to find all the dirs to lock and lock all - * the dirs - */ - lock_tree_list = getlist (); - start_recursion ((FILEPROC) NULL, lock_filesdoneproc, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, - argv, local, which, aflag, CVS_LOCK_NONE, - (char *) NULL, 0, (char *) NULL); - sortlist (lock_tree_list, fsortcmp); - if (Writer_Lock (lock_tree_list) != 0) - error (1, 0, "lock failed - giving up"); -} - -/* Lock a single directory in REPOSITORY. It is OK to call this if - a lock has been set with lock_dir_for_write; the new lock will replace - the old one. If REPOSITORY is NULL, don't do anything. */ -void -lock_dir_for_write (repository) - char *repository; -{ - if (repository != NULL - && (locked_dir == NULL - || strcmp (locked_dir, repository) != 0)) - { - Node *node; - - if (locked_dir != NULL) - Lock_Cleanup (); - - locked_dir = xstrdup (repository); - locked_list = getlist (); - node = getnode (); - node->type = LOCK; - node->key = xstrdup (repository); - node->data = xmalloc (sizeof (struct lock)); - ((struct lock *)node->data)->repository = node->key; - ((struct lock *)node->data)->lockdirname = CVSLCK; - ((struct lock *)node->data)->lockdir = NULL; - - (void) addnode (locked_list, node); - Writer_Lock (locked_list); - } -} - - - -/* This is the internal implementation behind history_lock & val_tags_lock. It - * gets a write lock for the history or val-tags file. - * - * RETURNS - * true, on success - * false, on error - */ -static int internal_lock PROTO ((struct lock *lock, const char *xrepository)); -static int -internal_lock (lock, xrepository) - struct lock *lock; - const char *xrepository; -{ - /* remember what we're locking (for Lock_Cleanup) */ - assert (!lock->repository); - lock->repository = xmalloc (strlen (xrepository) + sizeof (CVSROOTADM) + 2); - sprintf (lock->repository, "%s/%s", xrepository, CVSROOTADM); - - /* get the lock dir for our own */ - if (set_lock (lock, 1) != L_OK) - { - if (!really_quiet) - error (0, 0, "failed to obtain history lock in repository `%s'", - xrepository); - - return 0; - } - - return 1; -} - - - -/* This is the internal implementation behind history_lock & val_tags_lock. It - * removes the write lock for the history or val-tags file, when it exists. - */ -static void internal_clear_lock PROTO((struct lock *lock)); -static void -internal_clear_lock (lock) - struct lock *lock; -{ - SIG_beginCrSect (); - if (lock->repository) - { - free (lock->repository); - lock->repository = NULL; - } - SIG_endCrSect (); - - clear_lock (lock); -} - - - -/* Lock the CVSROOT/history file for write. - */ -int -history_lock (xrepository) - const char *xrepository; -{ - return internal_lock (&global_history_lock, xrepository); -} - - - -/* Remove the CVSROOT/history lock, if it exists. - */ -void -clear_history_lock () -{ - internal_clear_lock (&global_history_lock); -} - - - -/* Lock the CVSROOT/val-tags file for write. - */ -int -val_tags_lock (xrepository) - const char *xrepository; -{ - return internal_lock (&global_val_tags_lock, xrepository); -} - - - -/* Remove the CVSROOT/val-tags lock, if it exists. - */ -void -clear_val_tags_lock () -{ - internal_clear_lock (&global_val_tags_lock); -} |