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.
This commit is contained in:
Werner Koch 2023-12-13 10:08:12 +01:00
parent f57717bf23
commit 937aeb1904
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 203 additions and 11 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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];