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