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