Updated FSF's address.
[gnupg.git] / jnlib / dotlock.c
index 29ab65d..89edb7b 100644 (file)
@@ -1,5 +1,6 @@
 /* dotlock.c - dotfile locking
- *     Copyright (C) 1998, 2000 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 2000, 2001, 2003, 2004, 
+ *               2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include "libjnlib-config.h"
 #include "dotlock.h"
 
+#if !defined(DIRSEP_C) && !defined(EXTSEP_C) \
+    && !defined(DIRSEP_S) && !defined(EXTSEP_S)
+#ifdef HAVE_DOSISH_SYSTEM
+#define DIRSEP_C '\\'
+#define EXTSEP_C '.'
+#define DIRSEP_S "\\"
+#define EXTSEP_S "."
+#else
+#define DIRSEP_C '/'
+#define EXTSEP_C '.'
+#define DIRSEP_S "/"
+#define EXTSEP_S "."
+#endif
+#endif
+
+
 struct dotlock_handle {
     struct dotlock_handle *next;
     char *tname;    /* name of lockfile template */
@@ -46,11 +64,10 @@ struct dotlock_handle {
 };
 
 
-static DOTLOCK all_lockfiles;
+static volatile DOTLOCK all_lockfiles;
 static int never_lock;
 
 static int read_lockfile( const char *name );
-static void remove_lockfiles(void);
 
 void
 disable_dotlock(void)
@@ -89,7 +106,7 @@ create_dotlock( const char *file_to_lock )
     int dirpartlen;
 
     if( !initialized ) {
-       atexit( remove_lockfiles );
+       atexit( dotlock_remove_lockfiles );
        initialized = 1;
     }
     if( !file_to_lock )
@@ -98,9 +115,9 @@ create_dotlock( const char *file_to_lock )
     h = jnlib_xcalloc( 1, sizeof *h );
     if( never_lock ) {
        h->disable = 1;
-      #ifdef _REENTRANT
+#ifdef _REENTRANT
        /* fixme: aquire mutex on all_lockfiles */
-      #endif
+#endif
        h->next = all_lockfiles;
        all_lockfiles = h;
        return h;
@@ -116,8 +133,17 @@ create_dotlock( const char *file_to_lock )
     else
        nodename = utsbuf.nodename;
 
-    if( !(dirpart = strrchr( file_to_lock, '/' )) ) {
-       dirpart = ".";
+#ifdef __riscos__
+    {
+        char *iter = (char *) nodename;
+        for (; iter[0]; iter++)
+            if (iter[0] == '.')
+                iter[0] = '/';
+    }
+#endif /* __riscos__ */
+
+    if( !(dirpart = strrchr( file_to_lock, DIRSEP_C )) ) {
+       dirpart = EXTSEP_S;
        dirpartlen = 1;
     }
     else {
@@ -125,15 +151,20 @@ create_dotlock( const char *file_to_lock )
        dirpart = file_to_lock;
     }
 
-  #ifdef _REENTRANT
+#ifdef _REENTRANT
     /* fixme: aquire mutex on all_lockfiles */
-  #endif
+#endif
     h->next = all_lockfiles;
     all_lockfiles = h;
 
     h->tname = jnlib_xmalloc( dirpartlen + 6+30+ strlen(nodename) + 11 );
-    sprintf( h->tname, "%.*s/.#lk%p.%s.%d",
+#ifndef __riscos__
+     sprintf( h->tname, "%.*s/.#lk%p.%s.%d",
+            dirpartlen, dirpart, h, nodename, (int)getpid() );
+#else /* __riscos__ */
+    sprintf( h->tname, "%.*s.lk%p/%s/%d",
             dirpartlen, dirpart, h, nodename, (int)getpid() );
+#endif /* __riscos__ */
 
     do {
        errno = 0;
@@ -165,22 +196,62 @@ create_dotlock( const char *file_to_lock )
       #ifdef _REENTRANT
        /* release mutex */
       #endif
-       log_error( "error closing `%s': %s\n", h->tname, strerror(errno));
+       log_fatal( "error writing to `%s': %s\n", h->tname, strerror(errno) );
+       close(fd);
        unlink(h->tname);
        jnlib_free(h->tname);
        jnlib_free(h);
        return NULL;
     }
 
-  #ifdef _REENTRANT
+ifdef _REENTRANT
     /* release mutex */
-  #endif
+endif
 #endif /* !HAVE_DOSISH_SYSTEM */
     h->lockname = jnlib_xmalloc( strlen(file_to_lock) + 6 );
-    strcpy(stpcpy(h->lockname, file_to_lock), ".lock");
+    strcpy(stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
     return h;
 }
 
+
+void
+destroy_dotlock ( DOTLOCK h )
+{
+#if !defined (HAVE_DOSISH_SYSTEM)
+    if ( h )
+      {
+        DOTLOCK hprev, htmp;
+
+        /* First remove the handle from our global list of all locks. */
+        for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
+          if (htmp == h)
+            {
+              if (hprev)
+                hprev->next = htmp->next;
+              else
+                all_lockfiles = htmp->next;
+              h->next = NULL;
+              break;
+            }
+
+        /* Second destroy the lock. */
+       if (!h->disable)
+          {
+           if (h->locked && h->lockname)
+              unlink (h->lockname);
+            if (h->tname)
+              unlink (h->tname);
+           jnlib_free (h->tname);
+           jnlib_free (h->lockname);
+          }
+       jnlib_free(h);
+
+      }
+#endif
+}
+
+
+
 static int
 maybe_deadlock( DOTLOCK h )
 {
@@ -214,11 +285,14 @@ make_dotlock( DOTLOCK h, long timeout )
     }
 
     if( h->locked ) {
+#ifndef __riscos__
        log_debug("oops, `%s' is already locked\n", h->lockname );
+#endif /* !__riscos__ */
        return 0;
     }
 
     for(;;) {
+#ifndef __riscos__
        if( !link(h->tname, h->lockname) ) {
            /* fixme: better use stat to check the link count */
            h->locked = 1;
@@ -228,6 +302,16 @@ make_dotlock( DOTLOCK h, long timeout )
            log_error( "lock not made: link() failed: %s\n", strerror(errno) );
            return -1;
        }
+#else /* __riscos__ */
+        if( !renamefile(h->tname, h->lockname) ) {
+            h->locked = 1;
+            return 0; /* okay */
+        }
+        if( errno != EEXIST ) {
+           log_error( "lock not made: rename() failed: %s\n", strerror(errno) );
+           return -1;
+        }
+#endif /* __riscos__ */
        if( (pid = read_lockfile(h->lockname)) == -1 ) {
            if( errno != ENOENT ) {
                log_info("cannot read lockfile\n");
@@ -237,20 +321,27 @@ make_dotlock( DOTLOCK h, long timeout )
            continue;
        }
        else if( pid == getpid() ) {
-           log_info( "Oops: lock already hold by us\n");
+           log_info( "Oops: lock already held by us\n");
            h->locked = 1;
            return 0; /* okay */
        }
        else if( kill(pid, 0) && errno == ESRCH ) {
+#ifndef __riscos__
            maybe_dead = " - probably dead";
-        #if 0 /* we should not do this without checking the permissions */
+#if 0 /* we should not do this without checking the permissions */
               /* and the hostname */
            log_info( "removing stale lockfile (created by %d)", pid );
-        #endif
+#endif
+#else /* __riscos__ */
+            /* we are *pretty* sure that the other task is dead and therefore
+               we remove the other lock file */
+            maybe_dead = " - probably dead - removing lock";
+            unlink(h->lockname);
+#endif /* __riscos__ */
        }
        if( timeout == -1 ) {
            struct timeval tv;
-           log_info( "waiting for lock (hold by %d%s) %s...\n",
+           log_info( "waiting for lock (held by %d%s) %s...\n",
                      pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
 
 
@@ -281,6 +372,13 @@ release_dotlock( DOTLOCK h )
 #else
     int pid;
 
+    /* To avoid atexit race conditions we first check whether there
+       are 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)
+        return 0;
+
     if( h->disable ) {
        return 0;
     }
@@ -299,11 +397,19 @@ release_dotlock( DOTLOCK h )
        log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
        return -1;
     }
+#ifndef __riscos__
     if( unlink( h->lockname ) ) {
        log_error( "release_dotlock: error removing lockfile `%s'",
                                                        h->lockname);
        return -1;
     }
+#else /* __riscos__ */
+    if( renamefile(h->lockname, h->tname) ) {
+       log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
+                                                       h->lockname, h->tname);
+       return -1;
+    }
+#endif /* __riscos__ */
     /* fixme: check that the link count is now 1 */
     h->locked = 0;
     return 0;
@@ -317,9 +423,9 @@ release_dotlock( DOTLOCK h )
 static int
 read_lockfile( const char *name )
 {
-  #ifdef HAVE_DOSISH_SYSTEM
+#ifdef HAVE_DOSISH_SYSTEM
     return 0;
-  #else
+#else
     int fd, pid;
     char pidstr[16];
 
@@ -338,37 +444,35 @@ read_lockfile( const char *name )
     pidstr[10] = 0;  /* terminate pid string */
     close(fd);
     pid = atoi(pidstr);
+#ifndef __riscos__
     if( !pid || pid == -1 ) {
+#else /* __riscos__ */
+    if( (!pid && riscos_getpid()) || pid == -1 ) {
+#endif /* __riscos__ */
        log_error("invalid pid %d in lockfile `%s'", pid, name );
        errno = 0;
        return -1;
     }
     return pid;
-  #endif
+#endif
 }
 
 
-static void
-remove_lockfiles()
+void
+dotlock_remove_lockfiles()
 {
-  #ifndef HAVE_DOSISH_SYSTEM
-    DOTLOCK h, h2;
-
-    h = all_lockfiles;
-    all_lockfiles = NULL;
-
-    while( h ) {
-       h2 = h->next;
-       if( !h->disable ) {
-           if( h->locked )
-               unlink( h->lockname );
-           unlink(h->tname);
-           jnlib_free(h->tname);
-           jnlib_free(h->lockname);
-       }
-       jnlib_free(h);
-       h = h2;
+#ifndef HAVE_DOSISH_SYSTEM
+  DOTLOCK h, h2;
+  
+  h = all_lockfiles;
+  all_lockfiles = NULL;
+    
+  while ( h )
+    {
+      h2 = h->next;
+      destroy_dotlock (h);
+      h = h2;
     }
-  #endif
+#endif
 }