Renamed the lock functions.
[gnupg.git] / common / dotlock.c
1 /* dotlock.c - dotfile locking
2  * Copyright (C) 1998, 2000, 2001, 2003, 2004,
3  *               2005, 2006, 2008, 2010, 2011 Free Software Foundation, Inc.
4  *
5  * This file is part of JNLIB, which is a subsystem of GnuPG.
6  *
7  * JNLIB is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 3 of
10  * the License, or (at your option) any later version.
11  *
12  * JNLIB is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 /* Some quick replacements for stuff we usually expect to be defined
26    in config.h.  Define HAVE_POSIX_SYSTEM for better readability. */
27 #if !defined (HAVE_DOSISH_SYSTEM) && defined(_WIN32)
28 # define HAVE_DOSISH_SYSTEM 1
29 #endif
30 #if !defined (HAVE_DOSISH_SYSTEM) && !defined (HAVE_POSIX_SYSTEM)
31 # define HAVE_POSIX_SYSTEM 1
32 #endif
33
34
35 /* Standard headers.  */
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #ifdef  HAVE_DOSISH_SYSTEM
43 # define WIN32_LEAN_AND_MEAN  /* We only need the OS core stuff.  */
44 # include <windows.h>
45 #else
46 # include <sys/utsname.h>
47 #endif
48 #include <sys/types.h>
49 #include <sys/time.h>
50 #include <sys/stat.h>
51 #include <fcntl.h>
52 #ifdef HAVE_SIGNAL_H
53 # include <signal.h>
54 #endif
55
56
57 #include "libjnlib-config.h"
58 #include "stringhelp.h"
59 #include "dotlock.h"
60 #ifdef HAVE_W32CE_SYSTEM
61 # include "utf8conv.h"  /* WindowsCE requires filename conversion.  */
62 #endif
63
64
65 /* Define constants for file name construction.  */
66 #if !defined(DIRSEP_C) && !defined(EXTSEP_S)
67 # ifdef HAVE_DOSISH_SYSTEM
68 #  define DIRSEP_C '\\'
69 #  define EXTSEP_S "."
70 #else
71 #  define DIRSEP_C '/'
72 #  define EXTSEP_S "."
73 # endif
74 #endif
75
76 /* In GnuPG we use wrappers around the malloc fucntions.  If they are
77    not defined we assume that this code is used outside of GnuPG and
78    fall back to the regular malloc functions.  */
79 #ifndef jnlib_malloc
80 # define jnlib_malloc(a)     malloc ((a))
81 # define jnlib_calloc(a,b)   calloc ((a), (b))
82 # define jnlib_free(a)       free ((a))
83 #endif
84
85 /* Wrapper to set ERRNO.  */
86 #ifndef jnlib_set_errno
87 # ifdef HAVE_W32CE_SYSTEM
88 #  define jnlib_set_errno(e)  gpg_err_set_errno ((e))
89 # else
90 #  define jnlib_set_errno(e)  do { errno = (e); } while (0)
91 # endif
92 #endif
93
94
95 \f
96 /* The object describing a lock.  */
97 struct dotlock_handle
98 {
99   struct dotlock_handle *next;
100   char *lockname;      /* Name of the actual lockfile.          */
101   int locked;          /* Lock status.                          */
102   int disable;         /* If true, locking is disabled.         */
103
104 #ifdef HAVE_DOSISH_SYSTEM
105   HANDLE lockhd;       /* The W32 handle of the lock file.      */
106 #else /*!HAVE_DOSISH_SYSTEM */
107   char *tname;         /* Name of the lockfile template.        */
108   size_t nodename_off; /* Offset in TNAME of the nodename part. */
109   size_t nodename_len; /* Length of the nodename part.          */
110 #endif /*!HAVE_DOSISH_SYSTEM */
111 };
112
113
114 /* A list of of all lock handles.  The volatile attribute might help
115    if used in an atexit handler.  */
116 static volatile dotlock_t all_lockfiles;
117
118 /* If this has the value true all locking is disabled.  */
119 static int never_lock;
120
121
122 \f
123 /* Entirely disable all locking.  This function should be called
124    before any locking is done.  It may be called right at startup of
125    the process as it only sets a global value.  */
126 void
127 dotlock_disable (void)
128 {
129   never_lock = 1;
130 }
131
132
133 #ifdef HAVE_POSIX_SYSTEM
134 static int
135 maybe_deadlock (dotlock_t h)
136 {
137   dotlock_t r;
138
139   for ( r=all_lockfiles; r; r = r->next )
140     {
141       if ( r != h && r->locked )
142         return 1;
143     }
144   return 0;
145 }
146 #endif /*HAVE_POSIX_SYSTEM*/
147
148
149 /* Read the lock file and return the pid, returns -1 on error.  True
150    will be stored in the integer at address SAME_NODE if the lock file
151    has been created on the same node. */
152 #ifdef HAVE_POSIX_SYSTEM
153 static int
154 read_lockfile (dotlock_t h, int *same_node )
155 {
156   char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node
157                                    names are usually shorter. */
158   int fd;
159   int pid = -1;
160   char *buffer, *p;
161   size_t expected_len;
162   int res, nread;
163
164   *same_node = 0;
165   expected_len = 10 + 1 + h->nodename_len + 1;
166   if ( expected_len >= sizeof buffer_space)
167     {
168       buffer = jnlib_malloc (expected_len);
169       if (!buffer)
170         return -1;
171     }
172   else
173     buffer = buffer_space;
174
175   if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
176     {
177       int e = errno;
178       log_info ("error opening lockfile `%s': %s\n",
179                 h->lockname, strerror(errno) );
180       if (buffer != buffer_space)
181         jnlib_free (buffer);
182       jnlib_set_errno (e); /* Need to return ERRNO here. */
183       return -1;
184     }
185
186   p = buffer;
187   nread = 0;
188   do
189     {
190       res = read (fd, p, expected_len - nread);
191       if (res == -1 && errno == EINTR)
192         continue;
193       if (res < 0)
194         {
195           log_info ("error reading lockfile `%s'", h->lockname );
196           close (fd);
197           if (buffer != buffer_space)
198             jnlib_free (buffer);
199           jnlib_set_errno (0); /* Do not return an inappropriate ERRNO. */
200           return -1;
201         }
202       p += res;
203       nread += res;
204     }
205   while (res && nread != expected_len);
206   close(fd);
207
208   if (nread < 11)
209     {
210       log_info ("invalid size of lockfile `%s'", h->lockname );
211       if (buffer != buffer_space)
212         jnlib_free (buffer);
213       jnlib_set_errno (0); /* Better don't return an inappropriate ERRNO. */
214       return -1;
215     }
216
217   if (buffer[10] != '\n'
218       || (buffer[10] = 0, pid = atoi (buffer)) == -1
219       || !pid )
220     {
221       log_error ("invalid pid %d in lockfile `%s'", pid, h->lockname );
222       if (buffer != buffer_space)
223         jnlib_free (buffer);
224       jnlib_set_errno (0);
225       return -1;
226     }
227
228   if (nread == expected_len
229       && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len)
230       && buffer[11+h->nodename_len] == '\n')
231     *same_node = 1;
232
233   if (buffer != buffer_space)
234     jnlib_free (buffer);
235   return pid;
236 }
237 #endif /*HAVE_POSIX_SYSTEM */
238
239
240 \f
241 #ifdef  HAVE_POSIX_SYSTEM
242 /* Locking core for Unix.  It used a temporary file and the link
243    system call to make locking an atomic operation. */
244 static dotlock_t
245 dotlock_create_unix (dotlock_t h, const char *file_to_lock)
246 {
247   int  fd = -1;
248   char pidstr[16];
249   const char *nodename;
250   const char *dirpart;
251   int dirpartlen;
252   struct utsname utsbuf;
253   size_t tnamelen;
254
255   snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() );
256
257   /* Create a temporary file. */
258   if ( uname ( &utsbuf ) )
259     nodename = "unknown";
260   else
261     nodename = utsbuf.nodename;
262
263   if ( !(dirpart = strrchr (file_to_lock, DIRSEP_C)) )
264     {
265       dirpart = EXTSEP_S;
266       dirpartlen = 1;
267     }
268   else
269     {
270       dirpartlen = dirpart - file_to_lock;
271       dirpart = file_to_lock;
272     }
273
274 #ifdef _REENTRANT
275     /* fixme: aquire mutex on all_lockfiles */
276 #endif
277   h->next = all_lockfiles;
278   all_lockfiles = h;
279
280   tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10;
281   h->tname = jnlib_malloc (tnamelen + 1);
282   if (!h->tname)
283     {
284       all_lockfiles = h->next;
285       jnlib_free (h);
286       return NULL;
287     }
288   h->nodename_len = strlen (nodename);
289
290   snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h );
291   h->nodename_off = strlen (h->tname);
292   snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off,
293            "%s.%d", nodename, (int)getpid ());
294
295   do
296     {
297       jnlib_set_errno (0);
298       fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL,
299                  S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
300     }
301   while (fd == -1 && errno == EINTR);
302
303   if ( fd == -1 )
304     {
305       all_lockfiles = h->next;
306       log_error (_("failed to create temporary file `%s': %s\n"),
307                   h->tname, strerror(errno));
308       jnlib_free (h->tname);
309       jnlib_free (h);
310       return NULL;
311     }
312   if ( write (fd, pidstr, 11 ) != 11 )
313     goto write_failed;
314   if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) )
315     goto write_failed;
316   if ( write (fd, "\n", 1 ) != 1 )
317     goto write_failed;
318   if ( close (fd) )
319     goto write_failed;
320
321 # ifdef _REENTRANT
322   /* release mutex */
323 # endif
324   h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 );
325   if (!h->lockname)
326     {
327       all_lockfiles = h->next;
328       unlink (h->tname);
329       jnlib_free (h->tname);
330       jnlib_free (h);
331       return NULL;
332     }
333   strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock");
334   return h;
335
336  write_failed:
337   all_lockfiles = h->next;
338 # ifdef _REENTRANT
339   /* fixme: release mutex */
340 # endif
341   log_error ( _("error writing to `%s': %s\n"), h->tname, strerror(errno) );
342   close (fd);
343   unlink (h->tname);
344   jnlib_free (h->tname);
345   jnlib_free (h);
346   return NULL;
347 }
348 #endif /*HAVE_POSIX_SYSTEM*/
349
350
351 #ifdef HAVE_DOSISH_SYSTEM
352 /* Locking core for Windows.  This version does not need a temporary
353    file but uses the plain lock file along with record locking.  We
354    create this file here so that we later only need to do the file
355    locking.  For error reporting it is useful to keep the name of the
356    file in the handle.  */
357 static dotlock_t
358 dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
359 {
360   h->next = all_lockfiles;
361   all_lockfiles = h;
362
363   h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 );
364   if (!h->lockname)
365     {
366       all_lockfiles = h->next;
367       jnlib_free (h);
368       return NULL;
369     }
370   strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
371
372   /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE
373      along with FILE_SHARE_DELETE but that does not work due to a race
374      condition: Despite the OPEN_ALWAYS flag CreateFile may return an
375      error and we can't reliable create/open the lock file unless we
376      would wait here until it works - however there are other valid
377      reasons why a lock file can't be created and thus the process
378      would not stop as expected but spin until Windows crashes.  Our
379      solution is to keep the lock file open; that does not harm. */
380   {
381 #ifdef HAVE_W32CE_SYSTEM
382     wchar_t *wname = utf8_to_wchar (h->lockname);
383
384     h->lockhd = INVALID_HANDLE_VALUE;
385     if (wname)
386       h->lockhd = CreateFile (wname,
387 #else
388     h->lockhd = CreateFile (h->lockname,
389 #endif
390                             GENERIC_READ|GENERIC_WRITE,
391                             FILE_SHARE_READ|FILE_SHARE_WRITE,
392                             NULL, OPEN_ALWAYS, 0, NULL);
393 #ifdef HAVE_W32CE_SYSTEM
394     jnlib_free (wname);
395 #endif
396   }
397   if (h->lockhd == INVALID_HANDLE_VALUE)
398     {
399       log_error (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
400       all_lockfiles = h->next;
401       jnlib_free (h->lockname);
402       jnlib_free (h);
403       return NULL;
404     }
405   return h;
406 }
407 #endif /*HAVE_DOSISH_SYSTEM*/
408
409
410 /* Create a lockfile for a file name FILE_TO_LOCK and returns an
411    object of type dotlock_t which may be used later to actually acquire
412    the lock.  A cleanup routine gets installed to cleanup left over
413    locks or other files used internally by the lock mechanism.
414
415    Calling this function with NULL does only install the atexit
416    handler and may thus be used to assure that the cleanup is called
417    after all other atexit handlers.
418
419    This function creates a lock file in the same directory as
420    FILE_TO_LOCK using that name and a suffix of ".lock".  Note that on
421    POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is
422    used.
423
424    The function returns an new handle which needs to be released using
425    destroy_dotlock but gets also released at the termination of the
426    process.  On error NULL is returned.
427  */
428 dotlock_t
429 dotlock_create (const char *file_to_lock)
430 {
431   static int initialized;
432   dotlock_t h;
433
434   if ( !initialized )
435     {
436       atexit (dotlock_remove_lockfiles);
437       initialized = 1;
438     }
439
440   if ( !file_to_lock )
441     return NULL;  /* Only initialization was requested.  */
442
443   h = jnlib_calloc (1, sizeof *h);
444   if (!h)
445     return NULL;
446
447   if (never_lock)
448     {
449       h->disable = 1;
450 #ifdef _REENTRANT
451       /* fixme: aquire mutex on all_lockfiles */
452 #endif
453       h->next = all_lockfiles;
454       all_lockfiles = h;
455       return h;
456     }
457
458 #ifdef HAVE_DOSISH_SYSTEM
459   return dotlock_create_w32 (h, file_to_lock);
460 #else /*!HAVE_DOSISH_SYSTEM */
461   return dotlock_create_unix (h, file_to_lock);
462 #endif /*!HAVE_DOSISH_SYSTEM*/
463 }
464
465
466 \f
467 #ifdef HAVE_POSIX_SYSTEM
468 /* Unix specific code of destroy_dotlock.  */
469 static void
470 dotlock_destroy_unix (dotlock_t h)
471 {
472   if (h->locked && h->lockname)
473     unlink (h->lockname);
474   if (h->tname)
475     unlink (h->tname);
476   jnlib_free (h->tname);
477 }
478 #endif /*HAVE_POSIX_SYSTEM*/
479
480
481 #ifdef HAVE_DOSISH_SYSTEM
482 /* Windows specific code of destroy_dotlock.  */
483 static void
484 dotlock_destroy_w32 (dotlock_t h)
485 {
486   if (h->locked)
487     {
488       OVERLAPPED ovl;
489
490       memset (&ovl, 0, sizeof ovl);
491       UnlockFileEx (h->lockhd, 0, 1, 0, &ovl);
492     }
493   CloseHandle (h->lockhd);
494 }
495 #endif /*HAVE_DOSISH_SYSTEM*/
496
497
498 /* Destroy the locck handle H and release the lock.  */
499 void
500 dotlock_destroy (dotlock_t h)
501 {
502   dotlock_t hprev, htmp;
503
504   if ( !h )
505     return;
506
507   /* First remove the handle from our global list of all locks. */
508   for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
509     if (htmp == h)
510       {
511         if (hprev)
512           hprev->next = htmp->next;
513         else
514           all_lockfiles = htmp->next;
515         h->next = NULL;
516         break;
517       }
518
519   /* Then destroy the lock. */
520   if (!h->disable)
521     {
522 #ifdef HAVE_DOSISH_SYSTEM
523       dotlock_destroy_w32 (h);
524 #else /* !HAVE_DOSISH_SYSTEM */
525       dotlock_destroy_unix (h);
526 #endif /* HAVE_DOSISH_SYSTEM */
527       jnlib_free (h->lockname);
528     }
529   jnlib_free(h);
530 }
531
532
533 \f
534 #ifdef HAVE_POSIX_SYSTEM
535 /* Unix specific code of make_dotlock.  Returns 0 on success, -1 on
536    error and 1 to try again.  */
537 static int
538 dotlock_take_unix (dotlock_t h, long timeout, int *backoff)
539 {
540   int  pid;
541   const char *maybe_dead="";
542   int same_node;
543
544   if ( !link(h->tname, h->lockname) )
545     {
546       /* fixme: better use stat to check the link count */
547       h->locked = 1;
548       return 0; /* okay */
549     }
550   if ( errno != EEXIST )
551     {
552       log_error ( "lock not made: link() failed: %s\n", strerror(errno) );
553       return -1;
554     }
555
556   if ( (pid = read_lockfile (h, &same_node)) == -1 )
557     {
558       if ( errno != ENOENT )
559         {
560           log_info ("cannot read lockfile\n");
561           return -1;
562         }
563       log_info( "lockfile disappeared\n");
564       return 1; /* Try again.  */
565     }
566   else if ( pid == getpid() && same_node )
567     {
568       log_info( "Oops: lock already held by us\n");
569       h->locked = 1;
570       return 0; /* okay */
571     }
572   else if ( same_node && kill (pid, 0) && errno == ESRCH )
573     {
574       log_info (_("removing stale lockfile (created by %d)\n"), pid );
575       unlink (h->lockname);
576       return 1; /* Try again.  */
577     }
578
579   if ( timeout == -1 )
580     {
581       /* Wait until lock has been released. */
582       struct timeval tv;
583
584       log_info (_("waiting for lock (held by %d%s) %s...\n"),
585                 pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):"");
586
587       /* We can't use sleep, cause signals may be blocked. */
588       tv.tv_sec = 1 + *backoff;
589       tv.tv_usec = 0;
590       select (0, NULL, NULL, NULL, &tv);
591       if ( *backoff < 10 )
592         ++*backoff;
593       return 1; /* Try again.  */
594     }
595
596   jnlib_set_errno (EACCES);
597   return -1;
598 }
599 #endif /*HAVE_POSIX_SYSTEM*/
600
601
602 #ifdef HAVE_DOSISH_SYSTEM
603 /* Windows specific code of make_dotlock.  Returns 0 on success, -1 on
604    error and 1 to try again.  */
605 static int
606 dotlock_take_w32 (dotlock_t h, long timeout, int *backoff)
607 {
608   int w32err;
609   OVERLAPPED ovl;
610
611   /* Lock one byte at offset 0.  The offset is given by OVL.  */
612   memset (&ovl, 0, sizeof ovl);
613   if (LockFileEx (h->lockhd, (LOCKFILE_EXCLUSIVE_LOCK
614                               | LOCKFILE_FAIL_IMMEDIATELY), 0, 1, 0, &ovl))
615     {
616       h->locked = 1;
617       return 0; /* okay */
618     }
619
620   w32err = GetLastError ();
621   if (w32err != ERROR_LOCK_VIOLATION)
622     {
623       log_error (_("lock `%s' not made: %s\n"),
624                  h->lockname, w32_strerror (w32err));
625       return -1;
626     }
627
628   if ( timeout == -1 )
629     {
630       /* Wait until lock has been released. */
631       log_info (_("waiting for lock %s...\n"), h->lockname);
632       Sleep ((1 + *backoff)*1000);
633       if ( *backoff < 10 )
634         ++*backoff;
635       return 1; /* Try again.  */
636     }
637
638   return -1;
639 }
640 #endif /*HAVE_DOSISH_SYSTEM*/
641
642
643 /* Take a lock on H.  A value of 0 for TIMEOUT returns immediately if
644    the lock can't be taked, -1 waits forever (hopefully not), other
645    values are reserved (planned to be timeouts in milliseconds).
646    Returns: 0 on success  */
647 int
648 dotlock_take (dotlock_t h, long timeout)
649 {
650   int backoff = 0;
651   int ret;
652
653   if ( h->disable )
654     return 0; /* Locks are completely disabled.  Return success. */
655
656   if ( h->locked )
657     {
658       log_debug ("Oops, `%s' is already locked\n", h->lockname);
659       return 0;
660     }
661
662   do
663     {
664 #ifdef HAVE_DOSISH_SYSTEM
665       ret = dotlock_take_w32 (h, timeout, &backoff);
666 #else /*!HAVE_DOSISH_SYSTEM*/
667       ret = dotlock_take_unix (h, timeout, &backoff);
668 #endif /*!HAVE_DOSISH_SYSTEM*/
669     }
670   while (ret == 1);
671
672   return ret;
673 }
674
675
676 \f
677 #ifdef HAVE_POSIX_SYSTEM
678 /* Unix specific code of release_dotlock.  */
679 static int
680 dotlock_release_unix (dotlock_t h)
681 {
682   int pid, same_node;
683
684   pid = read_lockfile (h, &same_node);
685   if ( pid == -1 )
686     {
687       log_error( "release_dotlock: lockfile error\n");
688       return -1;
689     }
690   if ( pid != getpid() || !same_node )
691     {
692       log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
693       return -1;
694     }
695
696   if ( unlink( h->lockname ) )
697     {
698       log_error ("release_dotlock: error removing lockfile `%s'\n",
699                  h->lockname);
700       return -1;
701     }
702   /* Fixme: As an extra check we could check whether the link count is
703      now really at 1. */
704   return 0;
705 }
706 #endif /*HAVE_POSIX_SYSTEM */
707
708
709 #ifdef HAVE_DOSISH_SYSTEM
710 /* Windows specific code of release_dotlock.  */
711 static int
712 dotlock_release_w32 (dotlock_t h)
713 {
714   OVERLAPPED ovl;
715
716   memset (&ovl, 0, sizeof ovl);
717   if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl))
718     {
719       log_error ("release_dotlock: error removing lockfile `%s': %s\n",
720                  h->lockname, w32_strerror (-1));
721       return -1;
722     }
723
724   return 0;
725 }
726 #endif /*HAVE_DOSISH_SYSTEM */
727
728
729 /* Release a lock.  Returns 0 on success.  */
730 int
731 dotlock_release (dotlock_t h)
732 {
733   int ret;
734
735   /* To avoid atexit race conditions we first check whether there are
736      any locks left.  It might happen that another atexit handler
737      tries to release the lock while the atexit handler of this module
738      already ran and thus H is undefined.  */
739   if (!all_lockfiles)
740     return 0;
741
742   if ( h->disable )
743     return 0;
744
745   if ( !h->locked )
746     {
747       log_debug("Oops, `%s' is not locked\n", h->lockname);
748       return 0;
749     }
750
751 #ifdef HAVE_DOSISH_SYSTEM
752   ret = dotlock_release_w32 (h);
753 #else
754   ret = dotlock_release_unix (h);
755 #endif
756
757   if (!ret)
758     h->locked = 0;
759   return ret;
760 }
761
762
763 \f
764 /* Remove all lockfiles.  This is usually called by the atexit handler
765    installed by this module but may also be called by other
766    termination handlers.  */
767 void
768 dotlock_remove_lockfiles (void)
769 {
770   dotlock_t h, h2;
771
772   h = all_lockfiles;
773   all_lockfiles = NULL;
774
775   while ( h )
776     {
777       h2 = h->next;
778       dotlock_destroy (h);
779       h = h2;
780     }
781 }