Merge branch 'master' into key-storage-work
[gnupg.git] / common / dotlock.c
index e3e9fa3..c111159 100644 (file)
@@ -5,17 +5,64 @@
  * This file is part of JNLIB, which is a subsystem of GnuPG.
  *
  * JNLIB is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 3 of
- * the License, or (at your option) any later version.
+ * under the terms of either
+ *
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
  *
  * JNLIB is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
+ * General Public License for more details.
+ *
+ * You should have received a copies of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * ALTERNATIVELY, this file may be distributed under the terms of the
+ * following license, in which case the provisions of this license are
+ * required INSTEAD OF the GNU Lesser General License or the GNU
+ * General Public License. If you wish to allow use of your version of
+ * this file only under the terms of the GNU Lesser General License or
+ * the GNU General Public License, and not to allow others to use your
+ * version of this file under the terms of the following license,
+ * indicate your decision by deleting this paragraph and the license
+ * below.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 /*
    At program initialization time, the module should be explicitly
    initialized:
 
-      dotlock_create (NULL);
+      dotlock_create (NULL, 0);
 
    This installs an atexit handler and may also initialize mutex etc.
    It is optional for non-threaded applications.  Only the first call
-   has an effect.
+   has an effect.  This needs to be done before any extra threads are
+   started.
 
    To create a lock file (which  prepares it but does not take the
    lock) you do:
 
      dotlock_t h
 
-     h = dotlock_create (fname);
+     h = dotlock_create (fname, 0);
      if (!h)
        error ("error creating lock file: %s\n", strerror (errno));
 
    It is important to handle the error.  For example on a read-only
    file system a lock can't be created (but is usually not needed).
    FNAME is the file you want to lock; the actual lockfile is that
-   name with the suffix ".lock" appended.  This call creates a unique
-   file temporary file (".#lk*") in the same directory as FNAME and
-   returns a handle for further operations.  The module keeps track of
-   theses unique files so that they will be unlinked using the atexit
-   handler.  If you don't need the lock file anymore, you may also
-   explicitly remove it with a call to:
+   name with the suffix ".lock" appended.  On success a handle to be
+   used with the other functions is returned or NULL on error.  Note
+   that the handle shall only be used by one thread at a time.  This
+   function creates a unique file temporary file (".#lk*") in the same
+   directory as FNAME and returns a handle for further operations.
+   The module keeps track of theses unique files so that they will be
+   unlinked using the atexit handler.  If you don't need the lock file
+   anymore, you may also explicitly remove it with a call to:
 
      dotlock_destroy (h);
 
      if (dotlock_release (h))
        error ("error releasing lock: %s\n", strerror (errno));
 
-   or, if the lock file is not anymore needed, you may call
-   dotlock_destroy.
+   or, if the lock file is not anymore needed, you may just call
+   dotlock_destroy.  However dotlock_release does some extra checks
+   before releasing the lock and prints diagnostics to help detecting
+   bugs.
 
    If you want to explicitly destroy all lock files you may call
 
 
    before any locks are created.
 
+   There are two convenience functions to store an integer (e.g. a
+   file descriptor) value with the handle:
+
+     void dotlock_set_fd (dotlock_t h, int fd);
+     int  dotlock_get_fd (dotlock_t h);
+
+   If nothing has been stored dotlock_get_fd returns -1.
+
+
 
    How to build:
    =============
    to pass -DHAVE_CONFIG_H to the compiler.  Macros used by this
    module are:
 
+     DOTLOCK_USE_PTHREAD  - Define if POSIX threads are in use.
+
      DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions.
 
+     DOTLOCK_EXT_SYM_PREFIX - Prefix all external symbols with the
+                              string to which this macro evaluates.
+
      GNUPG_MAJOR_VERSION - Defined when used by GnuPG.
 
-     HAVE_DOSISH_SYSTEM   - Defined for Windows etc.  Will be
-                            automatically defined if a the target is
-                            Windows.
-     HAVE_POSIX_SYSTEM    - Internally defined to !HAVE_DOSISH_SYSTEM.
+     HAVE_DOSISH_SYSTEM  - Defined for Windows etc.  Will be
+                           automatically defined if a the target is
+                           Windows.
+
+     HAVE_POSIX_SYSTEM   - Internally defined to !HAVE_DOSISH_SYSTEM.
 
-     HAVE_SIGNAL_H        - Should be defined on Posix systems.  If config.h
-                            is not used defaults to defined.
+     HAVE_SIGNAL_H       - Should be defined on Posix systems.  If config.h
+                           is not used defaults to defined.
 
      DIRSEP_C            - Separation character for file name parts.
                            Usually not redefined.
-     EXTSEP_S "."        - Separation string for file name suffixes.
+
+     EXTSEP_S            - Separation string for file name suffixes.
                            Usually not redefined.
 
      HAVE_W32CE_SYSTEM   - Currently only used by GnuPG.
    it on the command line, remember to pass -D_FILE_OFFSET_BITS=64
 
 
+   Bugs:
+   =====
+
+   On Windows this module is not yet thread-safe.
+
+
    Miscellaneous notes:
    ====================
 
 #ifdef HAVE_SIGNAL_H
 # include <signal.h>
 #endif
+#ifdef DOTLOCK_USE_PTHREAD
+# include <pthread.h>
+#endif
 
 #ifdef DOTLOCK_GLIB_LOGGING
 # include <glib.h>
 # define my_error_1(a,b)    log_error ((a), (b))
 # define my_error_2(a,b,c)  log_error ((a), (b), (c))
 # define my_debug_1(a,b)    log_debug ((a), (b))
+# define my_fatal_0(a)      log_fatal ((a))
 #elif defined (DOTLOCK_GLIB_LOGGING)
 # define my_info_0(a)       g_message ((a))
 # define my_info_1(a,b)     g_message ((a), (b))
 # define my_info_3(a,b,c,d) g_message ((a), (b), (c), (d))
 # define my_error_0(a)      g_warning ((a))
 # define my_error_1(a,b)    g_warning ((a), (b))
-# define my_error_2(a,b,c   g_warning ((a), (b), (c))
+# define my_error_2(a,b,c)  g_warning ((a), (b), (c))
 # define my_debug_1(a,b)    g_debug ((a), (b))
+# define my_fatal_0(a)      g_error ((a))
 #else
 # define my_info_0(a)       fprintf (stderr, (a))
 # define my_info_1(a,b)     fprintf (stderr, (a), (b))
 # define my_error_1(a,b)    fprintf (stderr, (a), (b))
 # define my_error_2(a,b,c)  fprintf (stderr, (a), (b), (c))
 # define my_debug_1(a,b)    fprintf (stderr, (a), (b))
+# define my_fatal_0(a)      do { fprintf (stderr,(a)); fflush (stderr); \
+                                 abort (); } while (0)
 #endif
 
 
@@ -318,6 +399,8 @@ struct dotlock_handle
   unsigned int disable:1;    /* If true, locking is disabled.         */
   unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking.        */
 
+  int extra_fd;              /* A place for the caller to store an FD.  */
+
 #ifdef HAVE_DOSISH_SYSTEM
   HANDLE lockhd;       /* The W32 handle of the lock file.      */
 #else /*!HAVE_DOSISH_SYSTEM */
@@ -331,11 +414,27 @@ struct dotlock_handle
 /* A list of of all lock handles.  The volatile attribute might help
    if used in an atexit handler.  */
 static volatile dotlock_t all_lockfiles;
+#ifdef DOTLOCK_USE_PTHREAD
+static pthread_mutex_t all_lockfiles_mutex = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK_all_lockfiles() do {                               \
+        if (pthread_mutex_lock (&all_lockfiles_mutex))           \
+          my_fatal_0 ("locking all_lockfiles_mutex failed\n");   \
+      } while (0)
+# define UNLOCK_all_lockfiles() do {                             \
+        if (pthread_mutex_unlock (&all_lockfiles_mutex))         \
+          my_fatal_0 ("unlocking all_lockfiles_mutex failed\n"); \
+      } while (0)
+#else  /*!DOTLOCK_USE_PTHREAD*/
+# define LOCK_all_lockfiles()   do { } while (0)
+# define UNLOCK_all_lockfiles() do { } while (0)
+#endif /*!DOTLOCK_USE_PTHREAD*/
 
 /* If this has the value true all locking is disabled.  */
 static int never_lock;
 
 
+
+
 \f
 /* Entirely disable all locking.  This function should be called
    before any locking is done.  It may be called right at startup of
@@ -352,13 +451,19 @@ static int
 maybe_deadlock (dotlock_t h)
 {
   dotlock_t r;
+  int res = 0;
 
-  for ( r=all_lockfiles; r; r = r->next )
+  LOCK_all_lockfiles ();
+  for (r=all_lockfiles; r; r = r->next)
     {
       if ( r != h && r->locked )
-        return 1;
+        {
+          res = 1;
+          break;
+        }
     }
-  return 0;
+  UNLOCK_all_lockfiles ();
+  return res;
 }
 #endif /*HAVE_POSIX_SYSTEM*/
 
@@ -392,7 +497,7 @@ read_lockfile (dotlock_t h, int *same_node )
   if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
     {
       int e = errno;
-      my_info_2 ("error opening lockfile `%s': %s\n",
+      my_info_2 ("error opening lockfile '%s': %s\n",
                  h->lockname, strerror(errno) );
       if (buffer != buffer_space)
         jnlib_free (buffer);
@@ -409,7 +514,7 @@ read_lockfile (dotlock_t h, int *same_node )
         continue;
       if (res < 0)
         {
-          my_info_1 ("error reading lockfile `%s'\n", h->lockname );
+          my_info_1 ("error reading lockfile '%s'\n", h->lockname );
           close (fd);
           if (buffer != buffer_space)
             jnlib_free (buffer);
@@ -424,7 +529,7 @@ read_lockfile (dotlock_t h, int *same_node )
 
   if (nread < 11)
     {
-      my_info_1 ("invalid size of lockfile `%s'\n", h->lockname);
+      my_info_1 ("invalid size of lockfile '%s'\n", h->lockname);
       if (buffer != buffer_space)
         jnlib_free (buffer);
       jnlib_set_errno (0); /* Better don't return an inappropriate ERRNO. */
@@ -435,7 +540,7 @@ read_lockfile (dotlock_t h, int *same_node )
       || (buffer[10] = 0, pid = atoi (buffer)) == -1
       || !pid )
     {
-      my_error_2 ("invalid pid %d in lockfile `%s'\n", pid, h->lockname);
+      my_error_2 ("invalid pid %d in lockfile '%s'\n", pid, h->lockname);
       if (buffer != buffer_space)
         jnlib_free (buffer);
       jnlib_set_errno (0);
@@ -478,7 +583,8 @@ use_hardlinks_p (const char *tname)
   strcpy (lname, tname);
   strcat (lname, "x");
 
-  link (tname, lname);
+  /* We ignore the return value of link() because it is unreliable.  */
+  (void) link (tname, lname);
 
   if (stat (tname, &sb))
     res = -1;  /* Ooops.  */
@@ -528,9 +634,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
       dirpart = file_to_lock;
     }
 
-#ifdef _REENTRANT
-    /* fixme: aquire mutex on all_lockfiles */
-#endif
+  LOCK_all_lockfiles ();
   h->next = all_lockfiles;
   all_lockfiles = h;
 
@@ -539,6 +643,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
   if (!h->tname)
     {
       all_lockfiles = h->next;
+      UNLOCK_all_lockfiles ();
       jnlib_free (h);
       return NULL;
     }
@@ -560,7 +665,8 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
   if ( fd == -1 )
     {
       all_lockfiles = h->next;
-      my_error_2 (_("failed to create temporary file `%s': %s\n"),
+      UNLOCK_all_lockfiles ();
+      my_error_2 (_("failed to create temporary file '%s': %s\n"),
                   h->tname, strerror(errno));
       jnlib_free (h->tname);
       jnlib_free (h);
@@ -585,35 +691,32 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
       h->use_o_excl = 1;
       break;
     default:
-      my_error_2 ("can't check whether hardlinks are supported for `%s': %s\n",
+      my_error_2 ("can't check whether hardlinks are supported for '%s': %s\n",
                   h->tname, strerror(errno));
       goto write_failed;
     }
 
-# ifdef _REENTRANT
-  /* release mutex */
-# endif
   h->lockname = jnlib_malloc (strlen (file_to_lock) + 6 );
   if (!h->lockname)
     {
       all_lockfiles = h->next;
+      UNLOCK_all_lockfiles ();
       unlink (h->tname);
       jnlib_free (h->tname);
       jnlib_free (h);
       return NULL;
     }
   strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock");
+  UNLOCK_all_lockfiles ();
   if (h->use_o_excl)
-    my_debug_1 ("locking for `%s' done via O_EXCL\n", h->lockname);
+    my_debug_1 ("locking for '%s' done via O_EXCL\n", h->lockname);
 
   return h;
 
  write_failed:
   all_lockfiles = h->next;
-# ifdef _REENTRANT
-  /* fixme: release mutex */
-# endif
-  my_error_2 (_("error writing to `%s': %s\n"), h->tname, strerror (errno));
+  UNLOCK_all_lockfiles ();
+  my_error_2 (_("error writing to '%s': %s\n"), h->tname, strerror (errno));
   close (fd);
   unlink (h->tname);
   jnlib_free (h->tname);
@@ -632,6 +735,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
 static dotlock_t
 dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
 {
+  LOCK_all_lockfiles ();
   h->next = all_lockfiles;
   all_lockfiles = h;
 
@@ -639,6 +743,7 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
   if (!h->lockname)
     {
       all_lockfiles = h->next;
+      UNLOCK_all_lockfiles ();
       jnlib_free (h);
       return NULL;
     }
@@ -656,23 +761,26 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
 #ifdef HAVE_W32CE_SYSTEM
     wchar_t *wname = utf8_to_wchar (h->lockname);
 
-    h->lockhd = INVALID_HANDLE_VALUE;
     if (wname)
       h->lockhd = CreateFile (wname,
+                              GENERIC_READ|GENERIC_WRITE,
+                              FILE_SHARE_READ|FILE_SHARE_WRITE,
+                              NULL, OPEN_ALWAYS, 0, NULL);
+    else
+      h->lockhd = INVALID_HANDLE_VALUE;
+    jnlib_free (wname);
 #else
     h->lockhd = CreateFile (h->lockname,
-#endif
                             GENERIC_READ|GENERIC_WRITE,
                             FILE_SHARE_READ|FILE_SHARE_WRITE,
                             NULL, OPEN_ALWAYS, 0, NULL);
-#ifdef HAVE_W32CE_SYSTEM
-    jnlib_free (wname);
 #endif
   }
   if (h->lockhd == INVALID_HANDLE_VALUE)
     {
-      my_error_2 (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
       all_lockfiles = h->next;
+      UNLOCK_all_lockfiles ();
+      my_error_2 (_("can't create '%s': %s\n"), h->lockname, w32_strerror (-1));
       jnlib_free (h->lockname);
       jnlib_free (h);
       return NULL;
@@ -696,12 +804,15 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
    POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is
    used.
 
+   FLAGS must be 0.
+
    The function returns an new handle which needs to be released using
    destroy_dotlock but gets also released at the termination of the
    process.  On error NULL is returned.
  */
+
 dotlock_t
-dotlock_create (const char *file_to_lock)
+dotlock_create (const char *file_to_lock, unsigned int flags)
 {
   static int initialized;
   dotlock_t h;
@@ -715,18 +826,24 @@ dotlock_create (const char *file_to_lock)
   if ( !file_to_lock )
     return NULL;  /* Only initialization was requested.  */
 
+  if (flags)
+    {
+      jnlib_set_errno (EINVAL);
+      return NULL;
+    }
+
   h = jnlib_calloc (1, sizeof *h);
   if (!h)
     return NULL;
+  h->extra_fd = -1;
 
   if (never_lock)
     {
       h->disable = 1;
-#ifdef _REENTRANT
-      /* fixme: aquire mutex on all_lockfiles */
-#endif
+      LOCK_all_lockfiles ();
       h->next = all_lockfiles;
       all_lockfiles = h;
+      UNLOCK_all_lockfiles ();
       return h;
     }
 
@@ -739,6 +856,24 @@ dotlock_create (const char *file_to_lock)
 
 
 \f
+/* Convenience function to store a file descriptor (or any any other
+   integer value) in the context of handle H.  */
+void
+dotlock_set_fd (dotlock_t h, int fd)
+{
+  h->extra_fd = fd;
+}
+
+/* Convenience function to retrieve a file descriptor (or any any other
+   integer value) stored in the context of handle H.  */
+int
+dotlock_get_fd (dotlock_t h)
+{
+  return h->extra_fd;
+}
+
+
+\f
 #ifdef HAVE_POSIX_SYSTEM
 /* Unix specific code of destroy_dotlock.  */
 static void
@@ -780,6 +915,7 @@ dotlock_destroy (dotlock_t h)
     return;
 
   /* First remove the handle from our global list of all locks. */
+  LOCK_all_lockfiles ();
   for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
     if (htmp == h)
       {
@@ -790,6 +926,7 @@ dotlock_destroy (dotlock_t h)
         h->next = NULL;
         break;
       }
+  UNLOCK_all_lockfiles ();
 
   /* Then destroy the lock. */
   if (!h->disable)
@@ -838,7 +975,7 @@ dotlock_take_unix (dotlock_t h, long timeout)
         ; /* Lock held by another process.  */
       else if (fd == -1)
         {
-          my_error_2 ("lock not made: open(O_EXCL) of `%s' failed: %s\n",
+          my_error_2 ("lock not made: open(O_EXCL) of '%s' failed: %s\n",
                       h->lockname, strerror (errno));
           return -1;
         }
@@ -857,7 +994,7 @@ dotlock_take_unix (dotlock_t h, long timeout)
               return 0;
             }
           /* Write error.  */
-          my_error_2 ("lock not made: writing to `%s' failed: %s\n",
+          my_error_2 ("lock not made: writing to '%s' failed: %s\n",
                       h->lockname, strerror (errno));
           close (fd);
           unlink (h->lockname);
@@ -868,7 +1005,8 @@ dotlock_take_unix (dotlock_t h, long timeout)
     {
       struct stat sb;
 
-      link (h->tname, h->lockname);
+      /* We ignore the return value of link() because it is unreliable.  */
+      (void) link (h->tname, h->lockname);
 
       if (stat (h->tname, &sb))
         {
@@ -985,7 +1123,7 @@ dotlock_take_w32 (dotlock_t h, long timeout)
   w32err = GetLastError ();
   if (w32err != ERROR_LOCK_VIOLATION)
     {
-      my_error_2 (_("lock `%s' not made: %s\n"),
+      my_error_2 (_("lock '%s' not made: %s\n"),
                   h->lockname, w32_strerror (w32err));
       return -1;
     }
@@ -1035,7 +1173,7 @@ dotlock_take (dotlock_t h, long timeout)
 
   if ( h->locked )
     {
-      my_debug_1 ("Oops, `%s' is already locked\n", h->lockname);
+      my_debug_1 ("Oops, '%s' is already locked\n", h->lockname);
       return 0;
     }
 
@@ -1071,7 +1209,7 @@ dotlock_release_unix (dotlock_t h)
 
   if ( unlink( h->lockname ) )
     {
-      my_error_1 ("release_dotlock: error removing lockfile `%s'\n",
+      my_error_1 ("release_dotlock: error removing lockfile '%s'\n",
                   h->lockname);
       return -1;
     }
@@ -1092,7 +1230,7 @@ dotlock_release_w32 (dotlock_t h)
   memset (&ovl, 0, sizeof ovl);
   if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl))
     {
-      my_error_2 ("release_dotlock: error removing lockfile `%s': %s\n",
+      my_error_2 ("release_dotlock: error removing lockfile '%s': %s\n",
                   h->lockname, w32_strerror (-1));
       return -1;
     }
@@ -1112,7 +1250,10 @@ dotlock_release (dotlock_t h)
      any locks left.  It might happen that another atexit handler
      tries to release the lock while the atexit handler of this module
      already ran and thus H is undefined.  */
-  if (!all_lockfiles)
+  LOCK_all_lockfiles ();
+  ret = !all_lockfiles;
+  UNLOCK_all_lockfiles ();
+  if (ret)
     return 0;
 
   if ( h->disable )
@@ -1120,7 +1261,7 @@ dotlock_release (dotlock_t h)
 
   if ( !h->locked )
     {
-      my_debug_1 ("Oops, `%s' is not locked\n", h->lockname);
+      my_debug_1 ("Oops, '%s' is not locked\n", h->lockname);
       return 0;
     }
 
@@ -1137,7 +1278,7 @@ dotlock_release (dotlock_t h)
 
 
 \f
-/* Remove all lockfiles.  This is usually called by the atexit handler
+/* Remove all lockfiles.  This is called by the atexit handler
    installed by this module but may also be called by other
    termination handlers.  */
 void
@@ -1145,8 +1286,13 @@ dotlock_remove_lockfiles (void)
 {
   dotlock_t h, h2;
 
+  /* First set the lockfiles list to NULL so that for example
+     dotlock_release is ware that this fucntion is currently
+     running.  */
+  LOCK_all_lockfiles ();
   h = all_lockfiles;
   all_lockfiles = NULL;
+  UNLOCK_all_lockfiles ();
 
   while ( h )
     {