aboutsummaryrefslogtreecommitdiffstats
path: root/jnlib/dotlock.c
diff options
context:
space:
mode:
Diffstat (limited to 'jnlib/dotlock.c')
-rw-r--r--jnlib/dotlock.c713
1 files changed, 0 insertions, 713 deletions
diff --git a/jnlib/dotlock.c b/jnlib/dotlock.c
deleted file mode 100644
index 0d5a7bce9..000000000
--- a/jnlib/dotlock.c
+++ /dev/null
@@ -1,713 +0,0 @@
-/* dotlock.c - dotfile locking
- * Copyright (C) 1998, 2000, 2001, 2003, 2004,
- * 2005, 2006, 2008 Free Software Foundation, Inc.
- *
- * This file is part of JNLIB.
- *
- * 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.
- *
- * 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.
- *
- * 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/>.
- */
-
-#include <config.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <ctype.h>
-#include <errno.h>
-#include <unistd.h>
-#ifdef HAVE_DOSISH_SYSTEM
-# define WIN32_LEAN_AND_MEAN
-# include <windows.h>
-#else
-# include <sys/utsname.h>
-#endif
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifdef HAVE_SIGNAL_H
-# include <signal.h>
-#endif
-
-#include "libjnlib-config.h"
-#include "stringhelp.h"
-#include "dotlock.h"
-#include "utf8conv.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
-
-
-/* The object describing a lock. */
-struct dotlock_handle
-{
- struct dotlock_handle *next;
- char *lockname; /* Name of the actual lockfile. */
- int locked; /* Lock status. */
- int disable; /* If true, locking is disabled. */
-
-#ifdef HAVE_DOSISH_SYSTEM
- HANDLE lockhd; /* The W32 handle of the lock file. */
-#else
- char *tname; /* Name of the lockfile template. */
- size_t nodename_off; /* Offset in TNAME of the nodename part. */
- size_t nodename_len; /* Length of the nodename part. */
-#endif /* HAVE_DOSISH_SYSTEM */
-};
-
-
-/* A list of of all lock handles. */
-static volatile dotlock_t all_lockfiles;
-
-/* If this has the value true all locking is disabled. */
-static int never_lock;
-
-
-/* Local protototypes. */
-#ifndef HAVE_DOSISH_SYSTEM
-static int read_lockfile (dotlock_t h, int *same_node);
-#endif /*!HAVE_DOSISH_SYSTEM*/
-
-
-
-
-/* Entirely disable all locking. This function should be called
- before any locking is done. It may be called right at startup of
- the process as it only sets a global value. */
-void
-disable_dotlock(void)
-{
- never_lock = 1;
-}
-
-
-
-/* Create a lockfile for a file name FILE_TO_LOCK and returns an
- object of type dotlock_t which may be used later to actually acquire
- the lock. A cleanup routine gets installed to cleanup left over
- locks or other files used internally by the lock mechanism.
-
- Calling this function with NULL does only install the atexit
- handler and may thus be used to assure that the cleanup is called
- after all other atexit handlers.
-
- This function creates a lock file in the same directory as
- FILE_TO_LOCK using that name and a suffix of ".lock". Note that on
- POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is
- used.
-
- 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
-create_dotlock (const char *file_to_lock)
-{
- static int initialized;
- dotlock_t h;
-#ifndef HAVE_DOSISH_SYSTEM
- int fd = -1;
- char pidstr[16];
- const char *nodename;
- const char *dirpart;
- int dirpartlen;
- struct utsname utsbuf;
- size_t tnamelen;
-#endif
-
- if ( !initialized )
- {
- atexit (dotlock_remove_lockfiles);
- initialized = 1;
- }
-
- if ( !file_to_lock )
- return NULL; /* Only initialization was requested. */
-
- h = jnlib_calloc (1, sizeof *h);
- if (!h)
- return NULL;
-
- if (never_lock)
- {
- h->disable = 1;
-#ifdef _REENTRANT
- /* fixme: aquire mutex on all_lockfiles */
-#endif
- h->next = all_lockfiles;
- all_lockfiles = h;
- return h;
- }
-
-#ifndef HAVE_DOSISH_SYSTEM
- /*
- This is the POSIX version which uses a temporary file and the
- link system call to make locking an atomic operation.
- */
-
- snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() );
-
- /* Create a temporary file. */
- if ( uname ( &utsbuf ) )
- nodename = "unknown";
- else
- nodename = utsbuf.nodename;
-
-#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
- {
- dirpartlen = dirpart - file_to_lock;
- dirpart = file_to_lock;
- }
-
-#ifdef _REENTRANT
- /* fixme: aquire mutex on all_lockfiles */
-#endif
- h->next = all_lockfiles;
- all_lockfiles = h;
-
- tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10;
- h->tname = jnlib_malloc (tnamelen + 1);
- if (!h->tname)
- {
- all_lockfiles = h->next;
- jnlib_free (h);
- return NULL;
- }
- h->nodename_len = strlen (nodename);
-
-#ifndef __riscos__
- snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h );
- h->nodename_off = strlen (h->tname);
- snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off,
- "%s.%d", nodename, (int)getpid ());
-#else /* __riscos__ */
- snprintf (h->tname, tnamelen, "%.*s.lk%p/", dirpartlen, dirpart, h );
- h->nodename_off = strlen (h->tname);
- snprintf (h->tname+h->nodename_off, tnamelen - h->modename_off,
- "%s/%d", nodename, (int)getpid () );
-#endif /* __riscos__ */
-
- do
- {
- jnlib_set_errno (0);
- fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL,
- S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
- }
- while (fd == -1 && errno == EINTR);
-
- if ( fd == -1 )
- {
- all_lockfiles = h->next;
- log_error (_("failed to create temporary file `%s': %s\n"),
- h->tname, strerror(errno));
- jnlib_free (h->tname);
- jnlib_free (h);
- return NULL;
- }
- if ( write (fd, pidstr, 11 ) != 11 )
- goto write_failed;
- if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) )
- goto write_failed;
- if ( write (fd, "\n", 1 ) != 1 )
- goto write_failed;
- if ( close (fd) )
- goto write_failed;
-
-# ifdef _REENTRANT
- /* release mutex */
-# endif
- h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 );
- if (!h->lockname)
- {
- all_lockfiles = h->next;
- unlink (h->tname);
- jnlib_free (h->tname);
- jnlib_free (h);
- return NULL;
- }
- strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock");
- return h;
-
- write_failed:
- all_lockfiles = h->next;
-# ifdef _REENTRANT
- /* fixme: release mutex */
-# endif
- log_error ( _("error writing to `%s': %s\n"), h->tname, strerror(errno) );
- close (fd);
- unlink (h->tname);
- jnlib_free (h->tname);
- jnlib_free (h);
- return NULL;
-
-#else /* HAVE_DOSISH_SYSTEM */
-
- /* The Windows version does not need a temporary file but uses the
- plain lock file along with record locking. We create this file
- here so that we later do only need to do the file locking. For
- error reporting it is useful to keep the name of the file in the
- handle. */
- h->next = all_lockfiles;
- all_lockfiles = h;
-
- h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 );
- if (!h->lockname)
- {
- all_lockfiles = h->next;
- jnlib_free (h);
- return NULL;
- }
- strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
-
- /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE
- along with FILE_SHARE_DELETE but that does not work due to a race
- condition: Despite the OPEN_ALWAYS flag CreateFile may return an
- error and we can't reliable create/open the lock file unless we
- would wait here until it works - however there are other valid
- reasons why a lock file can't be created and thus the process
- would not stop as expected but spin til until Windows crashes.
- Our solution is to keep the lock file open; that does not
- harm. */
- {
-#ifdef HAVE_W32CE_SYSTEM
- wchar_t *wname = utf8_to_wchar (h->lockname);
-
- h->lockhd = INVALID_HANDLE_VALUE;
- if (wname)
- h->lockhd = CreateFile (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)
- {
- log_error (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
- all_lockfiles = h->next;
- jnlib_free (h->lockname);
- jnlib_free (h);
- return NULL;
- }
- return h;
-
-#endif /* HAVE_DOSISH_SYSTEM */
-}
-
-
-/* Destroy the local handle H and release the lock. */
-void
-destroy_dotlock (dotlock_t h)
-{
- dotlock_t hprev, htmp;
-
- if ( !h )
- return;
-
- /* 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;
- }
-
- /* Then destroy the lock. */
- if (!h->disable)
- {
-#ifdef HAVE_DOSISH_SYSTEM
- if (h->locked)
- {
- UnlockFile (h->lockhd, 0, 0, 1, 0);
- }
- CloseHandle (h->lockhd);
-#else /* !HAVE_DOSISH_SYSTEM */
- if (h->locked && h->lockname)
- unlink (h->lockname);
- if (h->tname)
- unlink (h->tname);
- jnlib_free (h->tname);
-#endif /* HAVE_DOSISH_SYSTEM */
- jnlib_free (h->lockname);
- }
- jnlib_free(h);
-}
-
-
-#ifndef HAVE_DOSISH_SYSTEM
-static int
-maybe_deadlock (dotlock_t h)
-{
- dotlock_t r;
-
- for ( r=all_lockfiles; r; r = r->next )
- {
- if ( r != h && r->locked )
- return 1;
- }
- return 0;
-}
-#endif /*!HAVE_DOSISH_SYSTEM*/
-
-
-
-/* Do a lock on H. A TIMEOUT of 0 returns immediately, -1 waits
- forever (hopefully not), other values are reserved (should then be
- timeouts in milliseconds). Returns: 0 on success */
-int
-make_dotlock (dotlock_t h, long timeout)
-{
- int backoff = 0;
-#ifndef HAVE_DOSISH_SYSTEM
- int pid;
- const char *maybe_dead="";
- int same_node;
-#endif /*!HAVE_DOSISH_SYSTEM*/
-
- if ( h->disable )
- return 0; /* Locks are completely disabled. Return success. */
-
- if ( h->locked )
- {
-#ifndef __riscos__
- log_debug ("Oops, `%s' is already locked\n", h->lockname);
-#endif /* !__riscos__ */
- return 0;
- }
-
- for (;;)
- {
-#ifndef HAVE_DOSISH_SYSTEM
-# ifndef __riscos__
- if ( !link(h->tname, h->lockname) )
- {
- /* fixme: better use stat to check the link count */
- h->locked = 1;
- return 0; /* okay */
- }
- if ( errno != EEXIST )
- {
- 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, &same_node)) == -1 )
- {
- if ( errno != ENOENT )
- {
- log_info ("cannot read lockfile\n");
- return -1;
- }
- log_info( "lockfile disappeared\n");
- continue;
- }
- else if ( pid == getpid() && same_node )
- {
- log_info( "Oops: lock already held by us\n");
- h->locked = 1;
- return 0; /* okay */
- }
- else if ( same_node && kill (pid, 0) && errno == ESRCH )
- {
-# ifndef __riscos__
- log_info (_("removing stale lockfile (created by %d)\n"), pid );
- unlink (h->lockname);
- continue;
-# else /* __riscos__ */
- /* Under RISCOS we are *pretty* sure that the other task
- is dead and therefore we remove the stale lock file. */
- maybe_dead = _(" - probably dead - removing lock");
- unlink(h->lockname);
-# endif /* __riscos__ */
- }
-
- if ( timeout == -1 )
- {
- /* Wait until lock has been released. */
- struct timeval tv;
-
- log_info (_("waiting for lock (held by %d%s) %s...\n"),
- pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):"");
-
-
- /* We can't use sleep, cause signals may be blocked. */
- tv.tv_sec = 1 + backoff;
- tv.tv_usec = 0;
- select(0, NULL, NULL, NULL, &tv);
- if ( backoff < 10 )
- backoff++ ;
- }
- else
- return -1;
-#else /*HAVE_DOSISH_SYSTEM*/
- int w32err;
-
- if (LockFile (h->lockhd, 0, 0, 1, 0))
- {
- h->locked = 1;
- return 0; /* okay */
- }
- w32err = GetLastError ();
- if (w32err != ERROR_LOCK_VIOLATION)
- {
- log_error (_("lock `%s' not made: %s\n"),
- h->lockname, w32_strerror (w32err));
- return -1;
- }
-
- if ( timeout == -1 )
- {
- /* Wait until lock has been released. */
- log_info (_("waiting for lock %s...\n"), h->lockname);
- Sleep ((1 + backoff)*1000);
- if ( backoff < 10 )
- backoff++ ;
- }
- else
- return -1;
-#endif /*HAVE_DOSISH_SYSTEM*/
- }
- /*NOTREACHED*/
-}
-
-
-/* Release a lock. Returns 0 on success. */
-int
-release_dotlock (dotlock_t h)
-{
-#ifndef HAVE_DOSISH_SYSTEM
- int pid, same_node;
-#endif
-
- /* 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;
-
- if ( !h->locked )
- {
- log_debug("Oops, `%s' is not locked\n", h->lockname);
- return 0;
- }
-
-#ifdef HAVE_DOSISH_SYSTEM
- if (!UnlockFile (h->lockhd, 0, 0, 1, 0))
- {
- log_error ("release_dotlock: error removing lockfile `%s': %s\n",
- h->lockname, w32_strerror (-1));
- return -1;
- }
-#else
-
- pid = read_lockfile (h, &same_node);
- if ( pid == -1 )
- {
- log_error( "release_dotlock: lockfile error\n");
- return -1;
- }
- if ( pid != getpid() || !same_node )
- {
- 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'\n",
- h->lockname);
- return -1;
- }
- /* Fixme: As an extra check we could check whether the link count is
- now really at 1. */
-#else /* __riscos__ */
- if ( renamefile (h->lockname, h->tname) )
- {
- log_error ("release_dotlock: error renaming lockfile `%s' to `%s'\n",
- h->lockname, h->tname);
- return -1;
- }
-#endif /* __riscos__ */
-
-#endif /* !HAVE_DOSISH_SYSTEM */
- h->locked = 0;
- return 0;
-}
-
-
-/* Read the lock file and return the pid, returns -1 on error. True
- will be stored in the integer at address SAME_NODE if the lock file
- has been created on the same node. */
-#ifndef HAVE_DOSISH_SYSTEM
-static int
-read_lockfile (dotlock_t h, int *same_node )
-{
- char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node
- name are usually shorter. */
- int fd;
- int pid = -1;
- char *buffer, *p;
- size_t expected_len;
- int res, nread;
-
- *same_node = 0;
- expected_len = 10 + 1 + h->nodename_len + 1;
- if ( expected_len >= sizeof buffer_space)
- {
- buffer = jnlib_malloc (expected_len);
- if (!buffer)
- return -1;
- }
- else
- buffer = buffer_space;
-
- if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
- {
- int e = errno;
- log_info ("error opening lockfile `%s': %s\n",
- h->lockname, strerror(errno) );
- if (buffer != buffer_space)
- jnlib_free (buffer);
- jnlib_set_errno (e); /* Need to return ERRNO here. */
- return -1;
- }
-
- p = buffer;
- nread = 0;
- do
- {
- res = read (fd, p, expected_len - nread);
- if (res == -1 && errno == EINTR)
- continue;
- if (res < 0)
- {
- log_info ("error reading lockfile `%s'", h->lockname );
- close (fd);
- if (buffer != buffer_space)
- jnlib_free (buffer);
- jnlib_set_errno (0); /* Do not return an inappropriate ERRNO. */
- return -1;
- }
- p += res;
- nread += res;
- }
- while (res && nread != expected_len);
- close(fd);
-
- if (nread < 11)
- {
- log_info ("invalid size of lockfile `%s'", h->lockname );
- if (buffer != buffer_space)
- jnlib_free (buffer);
- jnlib_set_errno (0); /* Better don't return an inappropriate ERRNO. */
- return -1;
- }
-
- if (buffer[10] != '\n'
- || (buffer[10] = 0, pid = atoi (buffer)) == -1
-#ifndef __riscos__
- || !pid
-#else /* __riscos__ */
- || (!pid && riscos_getpid())
-#endif /* __riscos__ */
- )
- {
- log_error ("invalid pid %d in lockfile `%s'", pid, h->lockname );
- if (buffer != buffer_space)
- jnlib_free (buffer);
- jnlib_set_errno (0);
- return -1;
- }
-
- if (nread == expected_len
- && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len)
- && buffer[11+h->nodename_len] == '\n')
- *same_node = 1;
-
- if (buffer != buffer_space)
- jnlib_free (buffer);
- return pid;
-}
-#endif /* !HAVE_DOSISH_SYSTEM */
-
-
-/* Remove all lockfiles. This is usually called by the atexit handler
- installed by this module but may also be called by other
- termination handlers. */
-void
-dotlock_remove_lockfiles (void)
-{
- dotlock_t h, h2;
-
- h = all_lockfiles;
- all_lockfiles = NULL;
-
- while ( h )
- {
- h2 = h->next;
- destroy_dotlock (h);
- h = h2;
- }
-}
-