summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2023-12-13 10:08:12 +0100
committerWerner Koch <wk@gnupg.org>2023-12-18 15:21:26 +0100
commit937aeb1904eb5cbe7a8c1c686877c7a9e1196ca6 (patch)
treedc8e4dbd870fff29ceb2248d61d19769a81338af
parentcommon: Improve error return for dotlock. (diff)
downloadgnupg2-937aeb1904eb5cbe7a8c1c686877c7a9e1196ca6.tar.xz
gnupg2-937aeb1904eb5cbe7a8c1c686877c7a9e1196ca6.zip
common: Add an info callback to dotlock.
* common/dotlock.h (enum dotlock_reasons): New. (DOTLOCK_PREPARE_CREATE): New flag. * common/dotlock.c (struct dotlock_handle): Add info_cb and info_cb_value. (dotlock_create): Support the new flag. (dotlock_finish_create): New. (read_lockfile): Silence in case of ENOENT. (dotlock_set_info_cb): New. Use callback after all error and info messages. (dotlock_take_unix, dotlock_take_w32): Allow termination by callback.
-rw-r--r--common/dotlock.c158
-rw-r--r--common/dotlock.h20
-rw-r--r--common/t-dotlock.c36
3 files changed, 203 insertions, 11 deletions
diff --git a/common/dotlock.c b/common/dotlock.c
index d1058845e..261e2cce7 100644
--- a/common/dotlock.c
+++ b/common/dotlock.c
@@ -396,6 +396,13 @@ struct dotlock_handle
int extra_fd; /* A place for the caller to store an FD. */
+ /* An optional info callback - see dotlock_set_info_cb. */
+ int (*info_cb)(dotlock_t, void *,
+ enum dotlock_reasons reason,
+ const char *,...);
+ void *info_cb_value;
+
+
#ifdef HAVE_DOSISH_SYSTEM
HANDLE lockhd; /* The W32 handle of the lock file. */
#else /*!HAVE_DOSISH_SYSTEM */
@@ -545,8 +552,15 @@ read_lockfile (dotlock_t h, int *same_node, int *r_fd)
if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
{
int e = errno;
- my_info_2 ("error opening lockfile '%s': %s\n",
- h->lockname, strerror(errno) );
+ if (errno != ENOENT)
+ {
+ my_info_2 ("error opening lockfile '%s': %s\n",
+ h->lockname, strerror(errno) );
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ "error opening lockfile '%s': %s\n",
+ h->lockname, strerror (errno) );
+ }
if (buffer != buffer_space)
xfree (buffer);
my_set_errno (e); /* Need to return ERRNO here. */
@@ -564,6 +578,10 @@ read_lockfile (dotlock_t h, int *same_node, int *r_fd)
{
int e = errno;
my_info_1 ("error reading lockfile '%s'\n", h->lockname );
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ "error reading lockfile '%s': %s\n",
+ h->lockname, strerror (errno) );
close (fd);
if (buffer != buffer_space)
xfree (buffer);
@@ -583,6 +601,9 @@ read_lockfile (dotlock_t h, int *same_node, int *r_fd)
if (nread < 11)
{
my_info_1 ("invalid size of lockfile '%s'\n", h->lockname);
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_INV_FILE,
+ "invalid size of lockfile '%s'\n", h->lockname);
if (buffer != buffer_space)
xfree (buffer);
my_set_errno (EINVAL);
@@ -594,6 +615,9 @@ read_lockfile (dotlock_t h, int *same_node, int *r_fd)
|| !pid )
{
my_error_2 ("invalid pid %d in lockfile '%s'\n", pid, h->lockname);
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_INV_FILE,
+ "invalid pid %d in lockfile '%s'\n", pid, h->lockname);
if (buffer != buffer_space)
xfree (buffer);
my_set_errno (EINVAL);
@@ -722,6 +746,10 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
UNLOCK_all_lockfiles ();
my_error_2 (_("failed to create temporary file '%s': %s\n"),
h->tname, strerror (errno));
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_WAITING,
+ _("failed to create temporary file '%s': %s\n"),
+ h->tname, strerror (errno));
xfree (h->tname);
xfree (h);
my_set_errno (saveerrno);
@@ -755,6 +783,10 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
int saveerrno = errno;
my_error_2 ("can't check whether hardlinks are supported for '%s': %s\n"
, h->tname, strerror (saveerrno));
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_CONFIG_TEST,
+ "can't check whether hardlinks are supported for '%s': %s\n"
+ , h->tname, strerror (saveerrno));
my_set_errno (saveerrno);
}
goto write_failed;
@@ -783,6 +815,11 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
all_lockfiles = h->next;
UNLOCK_all_lockfiles ();
my_error_2 (_("error writing to '%s': %s\n"), h->tname, strerror (errno));
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ _("error writing to '%s': %s\n"),
+ h->tname, strerror (errno));
+
if ( fd != -1 )
close (fd);
unlink (h->tname);
@@ -849,6 +886,10 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
all_lockfiles = h->next;
UNLOCK_all_lockfiles ();
my_error_2 (_("can't create '%s': %s\n"), h->lockname, w32_strerror (-1));
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ _("can't create '%s': %s\n"),
+ h->lockname, w32_strerror (-1));
xfree (h->lockname);
xfree (h);
my_set_errno (saveerrno);
@@ -873,7 +914,10 @@ 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 only defined FLAG bit is DOTLOCK_PREPARE_CREATE, which only
+ allocates the handle and requires a further call to
+ dotlock_finish_create. This can be used to set a callback between
+ these calls.
The function returns an new handle which needs to be released using
destroy_dotlock but gets also released at the termination of the
@@ -895,7 +939,7 @@ dotlock_create (const char *file_to_lock, unsigned int flags)
if ( !file_to_lock )
return NULL; /* Only initialization was requested. */
- if (flags)
+ if ((flags & ~DOTLOCK_PREPARE_CREATE))
{
my_set_errno (EINVAL);
return NULL;
@@ -916,6 +960,24 @@ dotlock_create (const char *file_to_lock, unsigned int flags)
return h;
}
+ if ((flags & DOTLOCK_PREPARE_CREATE))
+ return h;
+ else
+ return dotlock_finish_create (h, file_to_lock);
+}
+
+
+/* This function may be used along with dotlock_create (file_name,
+ * DOTLOCK_PREPARE_CREATE) to finish the creation call. The given
+ * filename shall be the same as passed to dotlock_create. On success
+ * the same handle H is returned, on error NULL is returned and H is
+ * released. */
+dotlock_t
+dotlock_finish_create (dotlock_t h, const char *file_to_lock)
+{
+ if (!h || !file_to_lock)
+ return NULL;
+
#ifdef HAVE_DOSISH_SYSTEM
return dotlock_create_w32 (h, file_to_lock);
#else /*!HAVE_DOSISH_SYSTEM */
@@ -942,6 +1004,24 @@ dotlock_get_fd (dotlock_t h)
}
+/* Set a callback function for info diagnostics. The callback
+ * function CB is called with the handle, the opaque value OPAQUE, a
+ * reason code, and a format string with its arguments. The callback
+ * shall return 0 to continue operation or true in which case the
+ * current function will be terminated with an error. */
+void
+dotlock_set_info_cb (dotlock_t h,
+ int (*cb)(dotlock_t, void *,
+ enum dotlock_reasons reason,
+ const char *,...),
+ void *opaque)
+{
+ h->info_cb = cb;
+ h->info_cb_value = opaque;
+}
+
+
+
#ifdef HAVE_POSIX_SYSTEM
/* Unix specific code of destroy_dotlock. */
@@ -1090,6 +1170,10 @@ dotlock_take_unix (dotlock_t h, long timeout)
saveerrno = errno;
my_error_2 ("lock not made: open(O_EXCL) of '%s' failed: %s\n",
h->lockname, strerror (saveerrno));
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ "lock not made: open(O_EXCL) of '%s' failed: %s\n",
+ h->lockname, strerror (saveerrno));
my_set_errno (saveerrno);
return -1;
}
@@ -1111,6 +1195,10 @@ dotlock_take_unix (dotlock_t h, long timeout)
saveerrno = errno;
my_error_2 ("lock not made: writing to '%s' failed: %s\n",
h->lockname, strerror (errno));
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ "lock not made: writing to '%s' failed: %s\n",
+ h->lockname, strerror (errno));
close (fd);
unlink (h->lockname);
my_set_errno (saveerrno);
@@ -1129,6 +1217,10 @@ dotlock_take_unix (dotlock_t h, long timeout)
saveerrno = errno;
my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n",
strerror (errno));
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ "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. */
@@ -1150,6 +1242,9 @@ dotlock_take_unix (dotlock_t h, long timeout)
{
saveerrno = errno;
my_info_0 ("cannot read lockfile\n");
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ "cannot read lockfile\n");
my_set_errno (saveerrno);
return -1;
}
@@ -1158,8 +1253,8 @@ dotlock_take_unix (dotlock_t h, long timeout)
}
else if ( (pid == getpid() && same_node)
|| (same_node && kill (pid, 0) && errno == ESRCH) )
- /* Stale lockfile is detected. */
{
+ /* Stale lockfile is detected. */
struct stat sb;
/* Check if it's unlocked during examining the lockfile. */
@@ -1202,6 +1297,9 @@ dotlock_take_unix (dotlock_t h, long timeout)
unlink (h->lockname);
my_info_1 (_("removing stale lockfile (created by %d)\n"), pid);
close (fd);
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_STALE_REMOVED,
+ _("removing stale lockfile (created by %d)\n"), pid);
goto again;
}
@@ -1228,6 +1326,15 @@ dotlock_take_unix (dotlock_t h, long timeout)
sumtime = 0;
my_info_3 (_("waiting for lock (held by %d%s) %s...\n"),
pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):"");
+ if (h->info_cb
+ && h->info_cb (h, h->info_cb_value, DOTLOCK_WAITING,
+ _("waiting for lock (held by %d%s) %s...\n"),
+ pid, maybe_dead,
+ maybe_deadlock(h)? _("(deadlock?) "):""))
+ {
+ my_set_errno (ECANCELED);
+ return -1;
+ }
}
tv.tv_sec = wtimereal / 1000;
@@ -1268,7 +1375,11 @@ dotlock_take_w32 (dotlock_t h, long timeout)
{
my_error_2 (_("lock '%s' not made: %s\n"),
h->lockname, w32_strerror (w32err));
- my_set_errno (map_w32_to_errno (w32err));
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ _("lock '%s' not made: %s\n"),
+ h->lockname, w32_strerror (w32err));
+ _set_errno (map_w32_to_errno (w32err));
return -1;
}
@@ -1281,7 +1392,16 @@ dotlock_take_w32 (dotlock_t h, long timeout)
timedout = 1; /* remember. */
if (wtime >= 800)
- my_info_1 (_("waiting for lock %s...\n"), h->lockname);
+ {
+ my_info_1 (_("waiting for lock %s...\n"), h->lockname);
+ if (h->info_cb
+ && h->info_cb (h, h->info_cb_value, DOTLOCK_WAITING,
+ _("waiting for lock %s...\n"), h->lockname))
+ {
+ my_set_errno (ECANCELED);
+ return -1;
+ }
+ }
Sleep (wtimereal);
goto again;
@@ -1334,12 +1454,18 @@ dotlock_release_unix (dotlock_t h)
{
saveerrno = errno;
my_error_0 ("release_dotlock: lockfile error\n");
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ "release_dotlock: lockfile error\n");
my_set_errno (saveerrno);
return -1;
}
if ( pid != getpid() || !same_node )
{
my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid);
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_CONFLICT,
+ "release_dotlock: not our lock (pid=%d)\n", pid);
my_set_errno (EACCES);
return -1;
}
@@ -1349,6 +1475,10 @@ dotlock_release_unix (dotlock_t h)
saveerrno = errno;
my_error_1 ("release_dotlock: error removing lockfile '%s'\n",
h->lockname);
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ "release_dotlock: error removing lockfile '%s'\n",
+ h->lockname);
my_set_errno (saveerrno);
return -1;
}
@@ -1369,10 +1499,15 @@ dotlock_release_w32 (dotlock_t h)
memset (&ovl, 0, sizeof ovl);
if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl))
{
- int saveerrno = map_w32_to_errno (GetLastError ());
+ int ec = (int)GetLastError ();
+
my_error_2 ("release_dotlock: error removing lockfile '%s': %s\n",
- h->lockname, w32_strerror (-1));
- my_set_errno (saveerrno);
+ h->lockname, w32_strerror (ec));
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR,
+ "release_dotlock: error removing lockfile '%s': %s\n",
+ h->lockname, w32_strerror (ec));
+ my_set_errno (map_w32_to_errno (ec));
return -1;
}
@@ -1403,6 +1538,9 @@ dotlock_release (dotlock_t h)
if ( !h->locked )
{
my_debug_1 ("Oops, '%s' is not locked\n", h->lockname);
+ if (h->info_cb)
+ h->info_cb (h, h->info_cb_value, DOTLOCK_NOT_LOCKED,
+ "Oops, '%s' is not locked\n", h->lockname);
return 0;
}
diff --git a/common/dotlock.h b/common/dotlock.h
index 03131bb0c..915279176 100644
--- a/common/dotlock.h
+++ b/common/dotlock.h
@@ -97,12 +97,32 @@ extern "C"
struct dotlock_handle;
typedef struct dotlock_handle *dotlock_t;
+enum dotlock_reasons
+ {
+ DOTLOCK_CONFIG_TEST, /* Can't check system - function terminates. */
+ DOTLOCK_FILE_ERROR, /* General file error - function terminates. */
+ DOTLOCK_INV_FILE, /* Invalid file - function terminates. */
+ DOTLOCK_CONFLICT, /* Something is wrong - function terminates. */
+ DOTLOCK_NOT_LOCKED, /* Not locked - No action required. */
+ DOTLOCK_STALE_REMOVED, /* Stale lock file was removed - retrying. */
+ DOTLOCK_WAITING /* Waiting for the lock - may be terminated. */
+ };
+
+#define DOTLOCK_PREPARE_CREATE (1<<5) /* Require dotlock_finish_create. */
+
void dotlock_disable (void);
dotlock_t dotlock_create (const char *file_to_lock, unsigned int flags);
+dotlock_t dotlock_finish_create (dotlock_t h, const char *file_to_lock);
void dotlock_set_fd (dotlock_t h, int fd);
int dotlock_get_fd (dotlock_t h);
+void dotlock_set_info_cb (dotlock_t h,
+ int (*cb)(dotlock_t, void *,
+ enum dotlock_reasons reason,
+ const char *,...),
+ void *opaque);
void dotlock_destroy (dotlock_t h);
int dotlock_take (dotlock_t h, long timeout);
+int dotlock_is_locked (dotlock_t h);
int dotlock_release (dotlock_t h);
void dotlock_remove_lockfiles (void);
diff --git a/common/t-dotlock.c b/common/t-dotlock.c
index 994ef1be3..87e62bb33 100644
--- a/common/t-dotlock.c
+++ b/common/t-dotlock.c
@@ -52,6 +52,7 @@
#ifdef HAVE_W32_SYSTEM
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
+
const char *
w32_strerror (int ec)
{
@@ -174,6 +175,11 @@ strconcat (const char *s1, ...)
#define PGM "t-dotlock"
+static int opt_silent;
+
+
+
+
#ifndef HAVE_W32_SYSTEM
static volatile int ctrl_c_pending_flag;
static void
@@ -217,6 +223,9 @@ inf (const char *format, ...)
{
va_list arg_ptr;
+ if (opt_silent)
+ return;
+
va_start (arg_ptr, format);
fprintf (stderr, PGM "[%lu]: ", (unsigned long)getpid ());
vfprintf (stderr, format, arg_ptr);
@@ -225,15 +234,35 @@ inf (const char *format, ...)
}
+static int
+lock_info_cb (dotlock_t h, void *opaque, enum dotlock_reasons reason,
+ const char *format, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, format);
+ fprintf (stderr, PGM "[%lu]: info_cb: reason %d, ",
+ (unsigned long)getpid (), (int)reason);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ return 0;
+}
+
+
static void
lock_and_unlock (const char *fname)
{
dotlock_t h;
unsigned long usec;
- h = dotlock_create (fname, 0);
+ h = dotlock_create (fname, DOTLOCK_PREPARE_CREATE);
if (!h)
die ("error creating lock file for '%s': %s", fname, strerror (errno));
+ dotlock_set_info_cb (h, lock_info_cb, NULL);
+ h = dotlock_finish_create (h, fname);
+ if (!h)
+ die ("error finishing lock file creation for '%s': %s",
+ fname, strerror (errno));
inf ("lock created");
do
@@ -270,6 +299,11 @@ main (int argc, char **argv)
ctrl_c_pending_flag = 1;
argc--;
}
+ if (argc > 1 && !strcmp (argv[1], "--silent"))
+ {
+ opt_silent = 1;
+ argc--;
+ }
if (argc > 1)
fname = argv[argc-1];