mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-31 11:41:32 +01:00
Improved the dotlock module.
- It is now more portable and may be used outside of GnuPG - vfat file systems are now supported. - The use of link(2) is more robust. - Wrote extensive documentation.
This commit is contained in:
parent
537be4ca47
commit
8a033fecfb
@ -1,3 +1,11 @@
|
||||
2011-09-27 Werner Koch <wk@g10code.com>
|
||||
|
||||
* dotlock.c (dotlock_take_unix): Check only the link count and not
|
||||
the error return from link.
|
||||
(use_hardlinks_p): New.
|
||||
(dotlock_create_unix): Test for hardlinks.
|
||||
(dotlock_take_unix): Implement O_EXCL locking.
|
||||
|
||||
2011-09-23 Werner Koch <wk@g10code.com>
|
||||
|
||||
* dotlock.c: Factor Unix and W32 specific code out into specific
|
||||
|
413
common/dotlock.c
413
common/dotlock.c
@ -18,6 +18,172 @@
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
Overview:
|
||||
=========
|
||||
|
||||
This module implements advisory file locking in a portable way.
|
||||
Due to the problems with POSIX fcntl locking a separate lock file
|
||||
is used. It would be possible to use fcntl locking on this lock
|
||||
file and thus avoid the weird auto unlock bug of POSIX while still
|
||||
having an unproved better performance of fcntl locking. However
|
||||
there are still problems left, thus we resort to use a hardlink
|
||||
which has the well defined property that a link call will fail if
|
||||
the target file already exists.
|
||||
|
||||
Given that hardlinks are also available on NTFS file systems since
|
||||
Windows XP; it will be possible to enhance this module to use
|
||||
hardlinks even on Windows and thus allow Windows and Posix clients
|
||||
to use locking on the same directory. This is not yet implemented;
|
||||
instead we use a lockfile on Windows along with W32 style file
|
||||
locking.
|
||||
|
||||
On FAT file systems hardlinks are not supported. Thus this method
|
||||
does not work. Our solution is to use a O_EXCL locking instead.
|
||||
Querying the type of the file system is not easy to do in a
|
||||
portable way (e.g. Linux has a statfs, BSDs have a the same call
|
||||
but using different structures and constants). What we do instead
|
||||
is to check at runtime whether link(2) works for a specific lock
|
||||
file.
|
||||
|
||||
|
||||
How to use:
|
||||
===========
|
||||
|
||||
At program initialization time, the module should be explicitly
|
||||
initialized:
|
||||
|
||||
dotlock_create (NULL);
|
||||
|
||||
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.
|
||||
|
||||
To create a lock file (which prepares it but does not take the
|
||||
lock) you do:
|
||||
|
||||
dotlock_t h
|
||||
|
||||
h = dotlock_create (fname);
|
||||
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:
|
||||
|
||||
dotlock_destroy (h);
|
||||
|
||||
To actually lock the file, you use:
|
||||
|
||||
if (dotlock_take (h, -1))
|
||||
error ("error taking lock: %s\n", strerror (errno));
|
||||
|
||||
This function will wait until the lock is acquired. If an
|
||||
unexpected error occurs if will return non-zero and set ERRNO. If
|
||||
you pass (0) instead of (-1) the function does not wait if the file
|
||||
is already locked but returns -1 and sets ERRNO to EACCES.
|
||||
|
||||
To release the lock you call:
|
||||
|
||||
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.
|
||||
|
||||
If you want to explicitly destroy all lock files you may call
|
||||
|
||||
dotlock_remove_lockfiles ();
|
||||
|
||||
which is the core of the installed atexit handler. In case your
|
||||
application wants to disable locking completely it may call
|
||||
|
||||
disable_locking ()
|
||||
|
||||
before any locks are created.
|
||||
|
||||
|
||||
How to build:
|
||||
=============
|
||||
|
||||
This module was originally developed for GnuPG but later changed to
|
||||
allow its use without any GnuPG dependency. If you want to use it
|
||||
with you application you may simply use it and it should figure out
|
||||
most things automagically.
|
||||
|
||||
You may use the common config.h file to pass macros, but take care
|
||||
to pass -DHAVE_CONFIG_H to the compiler. Macros used by this
|
||||
module are:
|
||||
|
||||
DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions.
|
||||
|
||||
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_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.
|
||||
Usually not redefined.
|
||||
|
||||
HAVE_W32CE_SYSTEM - Currently only used by GnuPG.
|
||||
|
||||
Note that there is a test program t-dotlock which has compile
|
||||
instructions at its end. At least for SMBFS and CIFS it is
|
||||
important that 64 bit versions of stat are used; most programming
|
||||
environments do this these days, just in case you want to compile
|
||||
it on the command line, remember to pass -D_FILE_OFFSET_BITS=64
|
||||
|
||||
|
||||
Miscellaneous notes:
|
||||
====================
|
||||
|
||||
On hardlinks:
|
||||
- Hardlinks are supported under Windows with NTFS since XP/Server2003.
|
||||
- In Linux 2.6.33 both SMBFS and CIFS seem to support hardlinks.
|
||||
- NFS supports hard links. But there are solvable problems.
|
||||
- FAT does not support links
|
||||
|
||||
On the file locking API:
|
||||
- CIFS on Linux 2.6.33 supports several locking methods.
|
||||
SMBFS seems not to support locking. No closer checks done.
|
||||
- NFS supports Posix locks. flock is emulated in the server.
|
||||
However there are a couple of problems; see below.
|
||||
- FAT does not support locks.
|
||||
- An advantage of fcntl locking is that R/W locks can be
|
||||
implemented which is not easy with a straight lock file.
|
||||
|
||||
On O_EXCL:
|
||||
- Does not work reliable on NFS
|
||||
- Should work on CIFS and SMBFS but how can we delete lockfiles?
|
||||
|
||||
On NFS problems:
|
||||
- Locks vanish if the server crashes and reboots.
|
||||
- Client crashes keep the lock in the server until the client
|
||||
re-connects.
|
||||
- Communication problems may return unreliable error codes. The
|
||||
MUA Postfix's workaround is to compare the link count after
|
||||
seeing an error for link. However that gives a race. If using a
|
||||
unique file to link to a lockfile and using stat to check the
|
||||
link count instead of looking at the error return of link(2) is
|
||||
the best solution.
|
||||
- O_EXCL seems to have a race and may re-create a file anyway.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
@ -31,9 +197,14 @@
|
||||
# define HAVE_POSIX_SYSTEM 1
|
||||
#endif
|
||||
|
||||
/* With no config.h assume that we have sitgnal.h. */
|
||||
#if !defined (HAVE_CONFIG_H) && defined (HAVE_POSIX_SYSTEM)
|
||||
# define HAVE_SIGNAL_H 1
|
||||
#endif
|
||||
|
||||
/* Standard headers. */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
@ -43,6 +214,8 @@
|
||||
# define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <sys/utsname.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
@ -53,14 +226,19 @@
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
#ifdef DOTLOCK_GLIB_LOGGING
|
||||
# include <glib.h>
|
||||
#endif
|
||||
|
||||
#include "libjnlib-config.h"
|
||||
#include "stringhelp.h"
|
||||
#include "dotlock.h"
|
||||
#ifdef GNUPG_MAJOR_VERSION
|
||||
# include "libjnlib-config.h"
|
||||
#endif
|
||||
#ifdef HAVE_W32CE_SYSTEM
|
||||
# include "utf8conv.h" /* WindowsCE requires filename conversion. */
|
||||
#endif
|
||||
|
||||
#include "dotlock.h"
|
||||
|
||||
|
||||
/* Define constants for file name construction. */
|
||||
#if !defined(DIRSEP_C) && !defined(EXTSEP_S)
|
||||
@ -91,15 +269,52 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Gettext macro replacement. */
|
||||
#ifndef _
|
||||
# define _(a) (a)
|
||||
#endif
|
||||
|
||||
#ifdef GNUPG_MAJOR_VERSION
|
||||
# define my_info_0(a) log_info ((a))
|
||||
# define my_info_1(a,b) log_info ((a), (b))
|
||||
# define my_info_2(a,b,c) log_info ((a), (b), (c))
|
||||
# define my_info_3(a,b,c,d) log_info ((a), (b), (c), (d))
|
||||
# define my_error_0(a) log_error ((a))
|
||||
# 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))
|
||||
#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_2(a,b,c) g_message ((a), (b), (c))
|
||||
# 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_debug_1(a,b) g_debug ((a), (b))
|
||||
#else
|
||||
# define my_info_0(a) fprintf (stderr, (a))
|
||||
# define my_info_1(a,b) fprintf (stderr, (a), (b))
|
||||
# define my_info_2(a,b,c) fprintf (stderr, (a), (b), (c))
|
||||
# define my_info_3(a,b,c,d) fprintf (stderr, (a), (b), (c), (d))
|
||||
# define my_error_0(a) fprintf (stderr, (a))
|
||||
# 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))
|
||||
#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. */
|
||||
char *lockname; /* Name of the actual lockfile. */
|
||||
unsigned int locked:1; /* Lock status. */
|
||||
unsigned int disable:1; /* If true, locking is disabled. */
|
||||
unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking. */
|
||||
|
||||
#ifdef HAVE_DOSISH_SYSTEM
|
||||
HANDLE lockhd; /* The W32 handle of the lock file. */
|
||||
@ -175,8 +390,8 @@ read_lockfile (dotlock_t h, int *same_node )
|
||||
if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
|
||||
{
|
||||
int e = errno;
|
||||
log_info ("error opening lockfile `%s': %s\n",
|
||||
h->lockname, strerror(errno) );
|
||||
my_info_2 ("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. */
|
||||
@ -192,7 +407,7 @@ read_lockfile (dotlock_t h, int *same_node )
|
||||
continue;
|
||||
if (res < 0)
|
||||
{
|
||||
log_info ("error reading lockfile `%s'", h->lockname );
|
||||
my_info_1 ("error reading lockfile `%s'\n", h->lockname );
|
||||
close (fd);
|
||||
if (buffer != buffer_space)
|
||||
jnlib_free (buffer);
|
||||
@ -207,7 +422,7 @@ read_lockfile (dotlock_t h, int *same_node )
|
||||
|
||||
if (nread < 11)
|
||||
{
|
||||
log_info ("invalid size of lockfile `%s'", 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. */
|
||||
@ -218,7 +433,7 @@ read_lockfile (dotlock_t h, int *same_node )
|
||||
|| (buffer[10] = 0, pid = atoi (buffer)) == -1
|
||||
|| !pid )
|
||||
{
|
||||
log_error ("invalid pid %d in lockfile `%s'", 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);
|
||||
@ -237,6 +452,46 @@ read_lockfile (dotlock_t h, int *same_node )
|
||||
#endif /*HAVE_POSIX_SYSTEM */
|
||||
|
||||
|
||||
/* Check whether the file system which stores TNAME supports
|
||||
hardlinks. Instead of using the non-portable statsfs call which
|
||||
differs between various Unix versions, we do a runtime test.
|
||||
Returns: 0 supports hardlinks; 1 no hardlink support, -1 unknown
|
||||
(test error). */
|
||||
#ifdef HAVE_POSIX_SYSTEM
|
||||
static int
|
||||
use_hardlinks_p (const char *tname)
|
||||
{
|
||||
char *lname;
|
||||
struct stat sb;
|
||||
unsigned int nlink;
|
||||
int res;
|
||||
|
||||
if (stat (tname, &sb))
|
||||
return -1;
|
||||
nlink = (unsigned int)sb.st_nlink;
|
||||
|
||||
lname = jnlib_malloc (strlen (tname) + 1 + 1);
|
||||
if (!lname)
|
||||
return -1;
|
||||
strcpy (lname, tname);
|
||||
strcat (lname, "x");
|
||||
|
||||
link (tname, lname);
|
||||
|
||||
if (stat (tname, &sb))
|
||||
res = -1; /* Ooops. */
|
||||
else if (sb.st_nlink == nlink + 1)
|
||||
res = 0; /* Yeah, hardlinks are supported. */
|
||||
else
|
||||
res = 1; /* No hardlink support. */
|
||||
|
||||
unlink (lname);
|
||||
jnlib_free (lname);
|
||||
return res;
|
||||
}
|
||||
#endif /*HAVE_POSIX_SYSTEM */
|
||||
|
||||
|
||||
|
||||
#ifdef HAVE_POSIX_SYSTEM
|
||||
/* Locking core for Unix. It used a temporary file and the link
|
||||
@ -277,7 +532,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
||||
h->next = all_lockfiles;
|
||||
all_lockfiles = h;
|
||||
|
||||
tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10;
|
||||
tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1;
|
||||
h->tname = jnlib_malloc (tnamelen + 1);
|
||||
if (!h->tname)
|
||||
{
|
||||
@ -303,7 +558,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
||||
if ( fd == -1 )
|
||||
{
|
||||
all_lockfiles = h->next;
|
||||
log_error (_("failed to create temporary file `%s': %s\n"),
|
||||
my_error_2 (_("failed to create temporary file `%s': %s\n"),
|
||||
h->tname, strerror(errno));
|
||||
jnlib_free (h->tname);
|
||||
jnlib_free (h);
|
||||
@ -318,10 +573,25 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
||||
if ( close (fd) )
|
||||
goto write_failed;
|
||||
|
||||
/* Check whether we support hard links. */
|
||||
switch (use_hardlinks_p (h->tname))
|
||||
{
|
||||
case 0: /* Yes. */
|
||||
break;
|
||||
case 1: /* No. */
|
||||
unlink (h->tname);
|
||||
h->use_o_excl = 1;
|
||||
break;
|
||||
default:
|
||||
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 );
|
||||
h->lockname = jnlib_malloc (strlen (file_to_lock) + 6 );
|
||||
if (!h->lockname)
|
||||
{
|
||||
all_lockfiles = h->next;
|
||||
@ -331,6 +601,9 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
||||
return NULL;
|
||||
}
|
||||
strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock");
|
||||
if (h->use_o_excl)
|
||||
my_debug_1 ("locking for `%s' done via O_EXCL\n", h->lockname);
|
||||
|
||||
return h;
|
||||
|
||||
write_failed:
|
||||
@ -338,7 +611,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
||||
# ifdef _REENTRANT
|
||||
/* fixme: release mutex */
|
||||
# endif
|
||||
log_error ( _("error writing to `%s': %s\n"), h->tname, strerror(errno) );
|
||||
my_error_2 (_("error writing to `%s': %s\n"), h->tname, strerror (errno));
|
||||
close (fd);
|
||||
unlink (h->tname);
|
||||
jnlib_free (h->tname);
|
||||
@ -396,7 +669,7 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
|
||||
}
|
||||
if (h->lockhd == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
log_error (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
|
||||
my_error_2 (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
|
||||
all_lockfiles = h->next;
|
||||
jnlib_free (h->lockname);
|
||||
jnlib_free (h);
|
||||
@ -471,7 +744,7 @@ dotlock_destroy_unix (dotlock_t h)
|
||||
{
|
||||
if (h->locked && h->lockname)
|
||||
unlink (h->lockname);
|
||||
if (h->tname)
|
||||
if (h->tname && !h->use_o_excl)
|
||||
unlink (h->tname);
|
||||
jnlib_free (h->tname);
|
||||
}
|
||||
@ -541,37 +814,95 @@ dotlock_take_unix (dotlock_t h, long timeout, int *backoff)
|
||||
const char *maybe_dead="";
|
||||
int same_node;
|
||||
|
||||
if ( !link(h->tname, h->lockname) )
|
||||
if (h->use_o_excl)
|
||||
{
|
||||
/* fixme: better use stat to check the link count */
|
||||
h->locked = 1;
|
||||
return 0; /* okay */
|
||||
/* No hardlink support - use open(O_EXCL). */
|
||||
int fd;
|
||||
|
||||
do
|
||||
{
|
||||
jnlib_set_errno (0);
|
||||
fd = open (h->lockname, O_WRONLY|O_CREAT|O_EXCL,
|
||||
S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
|
||||
}
|
||||
while (fd == -1 && errno == EINTR);
|
||||
|
||||
if (fd == -1 && errno == EEXIST)
|
||||
; /* Lock held by another process. */
|
||||
else if (fd == -1)
|
||||
{
|
||||
my_error_2 ("lock not made: open(O_EXCL) of `%s' failed: %s\n",
|
||||
h->lockname, strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
char pidstr[16];
|
||||
|
||||
snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid());
|
||||
if (write (fd, pidstr, 11 ) == 11
|
||||
&& write (fd, h->tname + h->nodename_off,h->nodename_len)
|
||||
== h->nodename_len
|
||||
&& write (fd, "\n", 1) == 1
|
||||
&& !close (fd))
|
||||
{
|
||||
h->locked = 1;
|
||||
return 0;
|
||||
}
|
||||
/* Write error. */
|
||||
my_error_2 ("lock not made: writing to `%s' failed: %s\n",
|
||||
h->lockname, strerror (errno));
|
||||
close (fd);
|
||||
unlink (h->lockname);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if ( errno != EEXIST )
|
||||
else /* Standard method: Use hardlinks. */
|
||||
{
|
||||
log_error ( "lock not made: link() failed: %s\n", strerror(errno) );
|
||||
return -1;
|
||||
struct stat sb;
|
||||
|
||||
link (h->tname, h->lockname);
|
||||
|
||||
if (stat (h->tname, &sb))
|
||||
{
|
||||
my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n",
|
||||
strerror (errno));
|
||||
/* In theory this might be a severe error: It is possible
|
||||
that link succeeded but stat failed due to changed
|
||||
permissions. We can't do anything about it, though. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sb.st_nlink == 2)
|
||||
{
|
||||
h->locked = 1;
|
||||
return 0; /* Okay. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for stale lock files. */
|
||||
if ( (pid = read_lockfile (h, &same_node)) == -1 )
|
||||
{
|
||||
if ( errno != ENOENT )
|
||||
{
|
||||
log_info ("cannot read lockfile\n");
|
||||
my_info_0 ("cannot read lockfile\n");
|
||||
return -1;
|
||||
}
|
||||
log_info( "lockfile disappeared\n");
|
||||
my_info_0 ("lockfile disappeared\n");
|
||||
return 1; /* Try again. */
|
||||
}
|
||||
else if ( pid == getpid() && same_node )
|
||||
{
|
||||
log_info( "Oops: lock already held by us\n");
|
||||
my_info_0 ("Oops: lock already held by us\n");
|
||||
h->locked = 1;
|
||||
return 0; /* okay */
|
||||
}
|
||||
else if ( same_node && kill (pid, 0) && errno == ESRCH )
|
||||
{
|
||||
log_info (_("removing stale lockfile (created by %d)\n"), pid );
|
||||
/* Note: It is unlikley that we get a race here unless a pid is
|
||||
reused too fast or a new process with the same pid as the one
|
||||
of the stale file tries to lock right at the same time as we. */
|
||||
my_info_1 (_("removing stale lockfile (created by %d)\n"), pid);
|
||||
unlink (h->lockname);
|
||||
return 1; /* Try again. */
|
||||
}
|
||||
@ -581,8 +912,8 @@ dotlock_take_unix (dotlock_t h, long timeout, int *backoff)
|
||||
/* 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?) "):"");
|
||||
my_info_3 (_("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;
|
||||
@ -620,15 +951,15 @@ dotlock_take_w32 (dotlock_t h, long timeout, int *backoff)
|
||||
w32err = GetLastError ();
|
||||
if (w32err != ERROR_LOCK_VIOLATION)
|
||||
{
|
||||
log_error (_("lock `%s' not made: %s\n"),
|
||||
h->lockname, w32_strerror (w32err));
|
||||
my_error_2 (_("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);
|
||||
my_info_1 (_("waiting for lock %s...\n"), h->lockname);
|
||||
Sleep ((1 + *backoff)*1000);
|
||||
if ( *backoff < 10 )
|
||||
++*backoff;
|
||||
@ -655,7 +986,7 @@ dotlock_take (dotlock_t h, long timeout)
|
||||
|
||||
if ( h->locked )
|
||||
{
|
||||
log_debug ("Oops, `%s' is already locked\n", h->lockname);
|
||||
my_debug_1 ("Oops, `%s' is already locked\n", h->lockname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -684,19 +1015,19 @@ dotlock_release_unix (dotlock_t h)
|
||||
pid = read_lockfile (h, &same_node);
|
||||
if ( pid == -1 )
|
||||
{
|
||||
log_error( "release_dotlock: lockfile error\n");
|
||||
my_error_0 ("release_dotlock: lockfile error\n");
|
||||
return -1;
|
||||
}
|
||||
if ( pid != getpid() || !same_node )
|
||||
{
|
||||
log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
|
||||
my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( unlink( h->lockname ) )
|
||||
{
|
||||
log_error ("release_dotlock: error removing lockfile `%s'\n",
|
||||
h->lockname);
|
||||
my_error_1 ("release_dotlock: error removing lockfile `%s'\n",
|
||||
h->lockname);
|
||||
return -1;
|
||||
}
|
||||
/* Fixme: As an extra check we could check whether the link count is
|
||||
@ -716,8 +1047,8 @@ dotlock_release_w32 (dotlock_t h)
|
||||
memset (&ovl, 0, sizeof ovl);
|
||||
if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl))
|
||||
{
|
||||
log_error ("release_dotlock: error removing lockfile `%s': %s\n",
|
||||
h->lockname, w32_strerror (-1));
|
||||
my_error_2 ("release_dotlock: error removing lockfile `%s': %s\n",
|
||||
h->lockname, w32_strerror (-1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -744,7 +1075,7 @@ dotlock_release (dotlock_t h)
|
||||
|
||||
if ( !h->locked )
|
||||
{
|
||||
log_debug("Oops, `%s' is not locked\n", h->lockname);
|
||||
my_debug_1 ("Oops, `%s' is not locked\n", h->lockname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* dotlock.h - dotfile locking
|
||||
* Copyright (C) 2000, 2001, 2006, 2011 Free Software Foundation, Inc.
|
||||
/* dotlock.h - dotfile locking declarations
|
||||
* Copyright (C) 2000, 2001, 2006, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of JNLIB, which is a subsystem of GnuPG.
|
||||
*
|
||||
@ -20,6 +20,8 @@
|
||||
#ifndef LIBJNLIB_DOTLOCK_H
|
||||
#define LIBJNLIB_DOTLOCK_H
|
||||
|
||||
/* See dotlock.c for a description. */
|
||||
|
||||
struct dotlock_handle;
|
||||
typedef struct dotlock_handle *dotlock_t;
|
||||
|
||||
|
145
common/t-dotlock.c
Normal file
145
common/t-dotlock.c
Normal file
@ -0,0 +1,145 @@
|
||||
/* t-dotlock.c - Module test for dotlock.c
|
||||
* Copyright (C) 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Note: This is a standalone test program which does not rely on any
|
||||
GnuPG helper files. However, it may also be build as part of the
|
||||
GnuPG build system. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
/* Some quick replacements for stuff we usually expect to be defined
|
||||
in config.h. Define HAVE_POSIX_SYSTEM for better readability. */
|
||||
#if !defined (HAVE_DOSISH_SYSTEM) && defined(_WIN32)
|
||||
# define HAVE_DOSISH_SYSTEM 1
|
||||
#endif
|
||||
#if !defined (HAVE_DOSISH_SYSTEM) && !defined (HAVE_POSIX_SYSTEM)
|
||||
# define HAVE_POSIX_SYSTEM 1
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "dotlock.h"
|
||||
|
||||
#define PGM "t-dotlock"
|
||||
|
||||
|
||||
static volatile int ctrl_c_pending;
|
||||
|
||||
static void
|
||||
control_c_handler (int signo)
|
||||
{
|
||||
(void)signo;
|
||||
ctrl_c_pending = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
die (const char *format, ...)
|
||||
{
|
||||
va_list arg_ptr;
|
||||
|
||||
va_start (arg_ptr, format);
|
||||
fprintf (stderr, PGM "[%lu]: ", (unsigned long)getpid ());
|
||||
vfprintf (stderr, format, arg_ptr);
|
||||
putc ('\n', stderr);
|
||||
va_end (arg_ptr);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
inf (const char *format, ...)
|
||||
{
|
||||
va_list arg_ptr;
|
||||
|
||||
va_start (arg_ptr, format);
|
||||
fprintf (stderr, PGM "[%lu]: ", (unsigned long)getpid ());
|
||||
vfprintf (stderr, format, arg_ptr);
|
||||
putc ('\n', stderr);
|
||||
va_end (arg_ptr);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lock_and_unlock (const char *fname)
|
||||
{
|
||||
dotlock_t h;
|
||||
|
||||
h = dotlock_create (fname);
|
||||
if (!h)
|
||||
die ("error creating lock file for `%s': %s", fname, strerror (errno));
|
||||
inf ("lock created");
|
||||
|
||||
while (!ctrl_c_pending)
|
||||
{
|
||||
if (dotlock_take (h, -1))
|
||||
die ("error taking lock");
|
||||
inf ("lock taken");
|
||||
sleep (1);
|
||||
if (dotlock_release (h))
|
||||
die ("error releasing lock");
|
||||
inf ("lock released");
|
||||
sleep (1);
|
||||
}
|
||||
dotlock_destroy (h);
|
||||
inf ("lock destroyed");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
const char *fname;
|
||||
|
||||
if (argc > 1)
|
||||
fname = argv[1];
|
||||
else
|
||||
fname = "t-dotlock.tmp";
|
||||
|
||||
{
|
||||
struct sigaction nact;
|
||||
|
||||
nact.sa_handler = control_c_handler;
|
||||
nact.sa_flags = 0;
|
||||
sigaction (SIGINT, &nact, NULL);
|
||||
}
|
||||
|
||||
dotlock_create (NULL); /* Initialize (optional). */
|
||||
|
||||
lock_and_unlock (fname);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
compile-command: "cc -Wall -O2 -D_FILE_OFFSET_BITS=64 -o t-dotlock t-dotlock.c dotlock.c"
|
||||
End:
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user