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
78 struct 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
93 static void remove_locks PROTO((void));
94 static int readers_exist PROTO((char *repository));
95 static int set_lock PROTO ((struct lock *lock, int will_wait));
96 static void clear_lock PROTO ((struct lock *lock));
97 static void set_lockers_name PROTO((struct stat *statp));
98 static int set_writelock_proc PROTO((Node * p, void *closure));
99 static int unlock_proc PROTO((Node * p, void *closure));
100 static int write_lock PROTO ((struct lock *lock));
101 static void lock_simple_remove PROTO ((struct lock *lock));
102 static void lock_wait PROTO((char *repository));
103 static 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. */
107 static char *lockers_name;
108 /* Malloc'd array specifying name of a readlock within a directory.
109 Or NULL if none. */
110 static char *readlock;
111 /* Malloc'd array specifying name of a writelock within a directory.
112 Or NULL if none. */
113 static 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. */
116 static char *masterlock;
117 static 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. */
125 static struct lock global_readlock;
126
127 /* List of locks set by lock_tree_for_write. This is redundant
128 with locklist, sort of. */
129 static 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. */
135 static char *locked_dir;
136 static List *locked_list;
137
138 /* LockDir from CVSROOT/config. */
139 char *lock_dir;
140
141 static 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). */
149 static char *
lock_name(repository,name)150 lock_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 */
285 void
Lock_Cleanup()286 Lock_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 */
315 static void
remove_locks()316 remove_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 */
336 static int
unlock_proc(p,closure)337 unlock_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. */
346 static void
lock_simple_remove(lock)347 lock_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 */
391 int
Reader_Lock(xrepository)392 Reader_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 */
460 static char *lock_error_repos;
461 static int lock_error;
462
463 static int Writer_Lock PROTO ((List * list));
464
465 static int
Writer_Lock(list)466 Writer_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 */
530 static int
set_writelock_proc(p,closure)531 set_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 */
549 static int
write_lock(lock)550 write_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 */
619 static int
readers_exist(repository)620 readers_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
630 again:
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 */
692 static void
set_lockers_name(statp)693 set_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 */
717 static int
set_lock(lock,will_wait)718 set_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 */
809 static void
clear_lock(lock)810 clear_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 */
823 static void
lock_wait(repos)824 lock_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 */
845 static void
lock_obtained(repos)846 lock_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
862 static 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 */
870 static int
lock_filesdoneproc(callerdat,err,repository,update_dir,entries)871 lock_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
893 void
lock_tree_for_write(argc,argv,local,which,aflag)894 lock_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. */
918 void
lock_dir_for_write(repository)919 lock_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