summaryrefslogtreecommitdiff
path: root/libmailutils
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2021-05-01 11:55:48 +0300
committerSergey Poznyakoff <gray@gnu.org>2021-05-01 12:22:02 +0300
commit5a5dddb6e1fc39efaeaa029d0b5fb7a29306fba4 (patch)
treed5c1a656797d2e694f6164480bbe45901ad11f39 /libmailutils
parent5daca02feaa90dcac68c03e479a0a61e51d2c7ca (diff)
downloadmailutils-5a5dddb6e1fc39efaeaa029d0b5fb7a29306fba4.tar.gz
mailutils-5a5dddb6e1fc39efaeaa029d0b5fb7a29306fba4.tar.bz2
Revise the mailbox locking functions.
The locker is created by the following call: mu_locker_create_ext(&locker, filename, &hints); The last arguments points to a structure mu_locker_hints_t, which defines the settings for the locker. NULL value implies default. Settings of an existing locker can be modified using the following call: mu_locker_modify(locker, &hints); Current settings can be examined using mu_locker_get_hints. The functions mu_locker_destroy, mu_locker_lock_mode, mu_locker_lock, mu_locker_touchlock, mu_locker_unlock, and mu_locker_remove_lock remained unchanged. Rest of functions is deprecated, but retained for a couple of releases for the sake of backward compatibility. Constants (locker types, flags, etc) are also revisited. This commit also fixes a bug in mu_spawnvp function, which was discovered when testing external lockers. * include/mailutils/locker.h: Major rewrite. (MU_LOCKER_DEFAULT_EXPIRE_TIME) (MU_LOCKER_DEFAULT_RETRY_COUNT) (MU_LOCKER_DEFAULT_RETRY_SLEEP) (MU_LOCKER_DEFAULT_EXT_LOCKER): New defines. (mu_locker_hints_t): New data type. (MU_LOCKER_FLAG_RETRY) (MU_LOCKER_FLAG_EXPIRE_TIME) (MU_LOCKER_FLAG_CHECK_PID) (MU_LOCKER_FLAG_TYPE,MU_LOCKER_FLAGS_ALL): New defines. (mu_locker_create_ext, mu_locker_modify) (mu_locker_get_hints): New functions. (mu_locker_create,mu_locker_set_default_flags) (mu_locker_set_default_retry_timeout) (mu_locker_set_default_retry_count) (mu_locker_set_default_expire_timeout) (mu_locker_set_default_external_program) (mu_locker_set_flags,mu_locker_mod_flags) (mu_locker_set_expire_time,mu_locker_set_retries) (mu_locker_set_retry_sleep,mu_locker_set_external) (mu_locker_get_flags,mu_locker_get_expire_time) (mu_locker_get_retries,mu_locker_get_retry_sleep): Mark as deprecated. * libmailutils/base/locker.c: Rewrite. * libmailutils/cli/stdcapa.c: New locking statements: type and retry-sleep. retry-timeout is deprecated. * libmailutils/tests/lck.c: Use new locker functions. * libmailutils/tests/lock.at: Test the external locker. * libmailutils/base/spawnvp.c (mu_spawnvp): Restore the default SIGCHLD handler while waiting for the process to terminate. * dotlock/dotlock.c: Use new locker functions. * lib/manlock.c: Likewise. * libmailutils/base/amd.c: Likewise. * libmu_dbm/berkeley.c: Likewise. * libmu_sieve/extensions/vacation.c: Likewise. * libproto/dotmail/dotmail.c: Likewise. * libproto/mbox/mboxrd.c: Likewise. * mail/copy.c: Likewise. * mda/lmtpd/lmtpd.c: Modify mu_locker_defaults instead of using deprecated functions. * mda/mda/mda.c: Likewise. * mda/putmail/putmail.c: Likewise. * NEWS: Document changes. * configure.ac: Version 3.12.90. * doc/texinfo/Makefile.am: Add programs/dotlock.texi * doc/texinfo/programs.texi: Rewrite the locking section. New section: dotlock. * doc/texinfo/programs/dotlock.texi: New file.
Diffstat (limited to 'libmailutils')
-rw-r--r--libmailutils/base/amd.c2
-rw-r--r--libmailutils/base/locker.c1276
-rw-r--r--libmailutils/base/spawnvp.c117
-rw-r--r--libmailutils/cli/stdcapa.c106
-rw-r--r--libmailutils/tests/lck.c126
-rw-r--r--libmailutils/tests/lock.at92
6 files changed, 979 insertions, 740 deletions
diff --git a/libmailutils/base/amd.c b/libmailutils/base/amd.c
index 349a8060a..e488da153 100644
--- a/libmailutils/base/amd.c
+++ b/libmailutils/base/amd.c
@@ -500,7 +500,7 @@ amd_open (mu_mailbox_t mailbox, int flags)
_amd_prop_create (amd);
if (mailbox->locker == NULL)
- mu_locker_create (&mailbox->locker, "/dev/null", 0);
+ mu_locker_create_ext (&mailbox->locker, "/dev/null", NULL);
return 0;
}
diff --git a/libmailutils/base/locker.c b/libmailutils/base/locker.c
index ef26fc52b..520bdca11 100644
--- a/libmailutils/base/locker.c
+++ b/libmailutils/base/locker.c
@@ -41,8 +41,6 @@
#include <mailutils/util.h>
#include <mailutils/io.h>
-#define LOCKFILE_ATTR 0644
-
/* First draft by Brian Edmond. */
/* For subsequent modifications, see the GNU mailutils ChangeLog. */
@@ -51,10 +49,11 @@ struct _mu_locker
unsigned refcnt; /* Number of times mu_locker_lock was called */
enum mu_locker_mode mode; /* Current locking mode (if refcnt > 0) */
+ int type;
char *file;
int flags;
int expire_time;
- int retries;
+ int retry_count;
int retry_sleep;
union lock_data
@@ -63,62 +62,16 @@ struct _mu_locker
{
char *dotlock;
char *nfslock;
- } dot;
+ } dot; /* MU_LOCKER_TYPE_DOTLOCK */
struct
{
char *name;
- } external;
+ } external; /* MU_LOCKER_TYPE_EXTERNAL */
- struct
- {
- int fd;
- } kernel;
+ int fd; /* MU_LOCKER_TYPE_KERNEL */
} data;
};
-
-#define MU_LOCKER_TYPE(l) MU_LOCKER_FLAG_TO_TYPE((l)->flags)
-
-struct locker_tab
-{
- int (*init) (mu_locker_t);
- void (*destroy) (mu_locker_t);
- int (*prelock) (mu_locker_t);
- int (*lock) (mu_locker_t, enum mu_locker_mode);
- int (*unlock) (mu_locker_t);
-};
-
-static int init_dotlock (mu_locker_t);
-static void destroy_dotlock (mu_locker_t);
-static int lock_dotlock (mu_locker_t, enum mu_locker_mode);
-static int unlock_dotlock (mu_locker_t);
-
-static int init_external (mu_locker_t);
-static void destroy_external (mu_locker_t);
-static int lock_external (mu_locker_t, enum mu_locker_mode);
-static int unlock_external (mu_locker_t);
-
-static int init_kernel (mu_locker_t);
-static int lock_kernel (mu_locker_t, enum mu_locker_mode);
-static int unlock_kernel (mu_locker_t);
-
-static int prelock_common (mu_locker_t);
-
-static struct locker_tab locker_tab[] = {
- /* MU_LOCKER_TYPE_DOTLOCK */
- { init_dotlock, destroy_dotlock, prelock_common,
- lock_dotlock, unlock_dotlock },
- /* MU_LOCKER_TYPE_EXTERNAL */
- { init_external, destroy_external, prelock_common,
- lock_external, unlock_external },
- /* MU_LOCKER_TYPE_KERNEL */
- { init_kernel, NULL, NULL, lock_kernel, unlock_kernel },
- /* MU_LOCKER_TYPE_NULL */
- { NULL, NULL, NULL, NULL, NULL }
-};
-
-#define MU_LOCKER_NTYPES (sizeof (locker_tab) / sizeof (locker_tab[0]))
-
static int
stat_check (const char *file, int fd, int links)
@@ -145,20 +98,17 @@ stat_check (const char *file, int fd, int links)
{
/* If the link and stat don't report the same info, or the
file is a symlink, fail the locking. */
-#define CHK(X) if(X) err = EINVAL
-
- CHK (!S_ISREG (fn_stat.st_mode));
- CHK (!S_ISREG (fd_stat.st_mode));
- CHK (fn_stat.st_nlink != links);
- CHK (fn_stat.st_dev != fd_stat.st_dev);
- CHK (fn_stat.st_ino != fd_stat.st_ino);
- CHK (fn_stat.st_mode != fd_stat.st_mode);
- CHK (fn_stat.st_nlink != fd_stat.st_nlink);
- CHK (fn_stat.st_uid != fd_stat.st_uid);
- CHK (fn_stat.st_gid != fd_stat.st_gid);
- CHK (fn_stat.st_rdev != fd_stat.st_rdev);
-
-#undef CHK
+ if (!S_ISREG (fn_stat.st_mode)
+ || !S_ISREG (fd_stat.st_mode)
+ || fn_stat.st_nlink != links
+ || fn_stat.st_dev != fd_stat.st_dev
+ || fn_stat.st_ino != fd_stat.st_ino
+ || fn_stat.st_mode != fd_stat.st_mode
+ || fn_stat.st_nlink != fd_stat.st_nlink
+ || fn_stat.st_uid != fd_stat.st_uid
+ || fn_stat.st_gid != fd_stat.st_gid
+ || fn_stat.st_rdev != fd_stat.st_rdev)
+ err = EINVAL;
}
if (localfd != -1)
close (localfd);
@@ -195,471 +145,23 @@ prelock_common (mu_locker_t locker)
of 1, that we have permission to read, etc., or don't lock it. */
return check_file_permissions (locker->file);
}
-
-
-static int mu_locker_default_flags = MU_LOCKER_DEFAULT;
-static time_t mu_locker_retry_timeout = MU_LOCKER_RETRY_SLEEP;
-static size_t mu_locker_retry_count = MU_LOCKER_RETRIES;
-static time_t mu_locker_expire_timeout = MU_LOCKER_EXPIRE_TIME;
-static char *mu_locker_external_program = NULL;
-
-int
-mu_locker_set_default_flags (int flags, enum mu_locker_set_mode mode)
-{
- switch (mode)
- {
- case mu_locker_assign:
- mu_locker_default_flags = flags;
- break;
-
- case mu_locker_set_bit:
- mu_locker_default_flags |= flags;
- break;
-
- case mu_locker_clear_bit:
- mu_locker_default_flags &= ~flags;
- break;
-
- default:
- return EINVAL;
- }
- return 0;
-}
-
-void
-mu_locker_set_default_retry_timeout (time_t to)
-{
- mu_locker_retry_timeout = to;
-}
-
-void
-mu_locker_set_default_retry_count (size_t n)
-{
- mu_locker_retry_count = n;
-}
-
-void
-mu_locker_set_default_expire_timeout (time_t t)
-{
- mu_locker_expire_timeout = t;
-}
-
-int
-mu_locker_set_default_external_program (char const *path)
-{
- char *p = strdup (path);
- if (!p)
- return ENOMEM;
- free (mu_locker_external_program);
- mu_locker_external_program = p;
- return 0;
-}
-
-int
-mu_locker_mod_flags (mu_locker_t locker, int flags,
- enum mu_locker_set_mode mode)
-{
- unsigned otype, ntype;
- int new_flags;
-
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- switch (mode)
- {
- case mu_locker_assign:
- new_flags = flags;
- break;
-
- case mu_locker_set_bit:
- new_flags = locker->flags | flags;
- break;
-
- case mu_locker_clear_bit:
- new_flags = locker->flags & ~flags;
- break;
-
- default:
- return EINVAL;
- }
-
- otype = MU_LOCKER_TYPE (locker);
- if (otype >= MU_LOCKER_NTYPES)
- return EINVAL;
- ntype = MU_LOCKER_FLAG_TO_TYPE (new_flags);
- if (ntype >= MU_LOCKER_NTYPES)
- return EINVAL;
-
- if (ntype != otype)
- {
- int rc;
-
- if (locker_tab[otype].destroy)
- locker_tab[otype].destroy (locker);
- locker->flags = new_flags;
- if (locker_tab[ntype].init)
- {
- rc = locker_tab[ntype].init (locker);
- if (rc)
- locker->flags = MU_LOCKER_NULL;
- return rc;
- }
- }
- else
- locker->flags = new_flags;
-
- return 0;
-}
-
-int
-mu_locker_set_flags (mu_locker_t locker, int flags)
-{
- return mu_locker_mod_flags (locker, flags, mu_locker_assign);
-}
-
-int
-mu_locker_set_expire_time (mu_locker_t locker, int etime)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (etime <= 0)
- return EINVAL;
-
- locker->expire_time = etime;
-
- return 0;
-}
-
-int
-mu_locker_set_retries (mu_locker_t locker, int retries)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (retries <= 0)
- return EINVAL;
-
- locker->retries = retries;
-
- return 0;
-}
-
-int
-mu_locker_set_retry_sleep (mu_locker_t locker, int retry_sleep)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (retry_sleep <= 0)
- return EINVAL;
-
- locker->retry_sleep = retry_sleep;
-
- return 0;
-}
-
-int
-mu_locker_set_external (mu_locker_t locker, const char* program)
-{
- char* p = NULL;
-
- if (!locker)
- return MU_ERR_LOCKER_NULL;
- if (MU_LOCKER_TYPE (locker) != MU_LOCKER_TYPE_EXTERNAL)
- return EINVAL;
-
- /* program can be NULL */
- if (program != 0)
- {
- p = strdup (program);
- if (!p)
- return ENOMEM;
- }
-
- free (locker->data.external.name);
- locker->data.external.name = p;
-
- return 0;
-}
-
-int
-mu_locker_get_flags (mu_locker_t locker, int *flags)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (!flags)
- return EINVAL;
-
- *flags = locker->flags;
-
- return 0;
-}
-
-int
-mu_locker_get_expire_time (mu_locker_t locker, int *ptime)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (!ptime)
- return EINVAL;
-
- *ptime = locker->expire_time;
-
- return 0;
-}
-
-int
-mu_locker_get_retries (mu_locker_t locker, int *retries)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (!retries)
- return EINVAL;
-
- *retries = locker->retries;
-
- return 0;
-}
-
-int
-mu_locker_get_retry_sleep (mu_locker_t locker, int *retry_sleep)
-{
- if (!locker)
- return MU_ERR_LOCKER_NULL;
-
- if (!retry_sleep)
- return EINVAL;
-
- *retry_sleep = locker->retry_sleep;
-
- return 0;
-}
-
-int
-mu_locker_create (mu_locker_t *plocker, const char *fname, int flags)
-{
- unsigned type;
- mu_locker_t l;
- char *filename;
- int err = 0;
-
- if (plocker == NULL)
- return MU_ERR_OUT_PTR_NULL;
-
- if (fname == NULL)
- return EINVAL;
-
- if ((err = mu_unroll_symlink (fname, &filename)))
- {
- if (err == ENOENT)
- {
- /* Try the directory part. If it unrolls successfully (i.e.
- all its components exist), tuck the filename part back in
- the resulting path and use it as the lock filename. */
- char *p, *new_name, *tmp = strdup (fname);
- if (!tmp)
- return ENOMEM;
- p = strrchr (tmp, '/');
- if (!p)
- filename = tmp;
- else
- {
- *p = 0;
- err = mu_unroll_symlink (tmp, &filename);
- if (err)
- {
- free (tmp);
- return err;
- }
-
- new_name = mu_make_file_name_suf (filename, p + 1, NULL);
- free (tmp);
- free (filename);
- if (!new_name)
- return ENOMEM;
- filename = new_name;
- }
- }
- else
- return err;
- }
-
- l = calloc (1, sizeof (*l));
-
- if (l == NULL)
- {
- free (filename);
- return ENOMEM;
- }
-
- l->file = filename;
-
- if (l->file == NULL)
- {
- free (l);
- return ENOMEM;
- }
-
- if (strcmp (filename, "/dev/null") == 0)
- l->flags = MU_LOCKER_NULL;
- else if (flags)
- l->flags = flags;
- else
- l->flags = mu_locker_default_flags;
-
- l->expire_time = mu_locker_expire_timeout;
- l->retries = mu_locker_retry_count;
- l->retry_sleep = mu_locker_retry_timeout;
-
- type = MU_LOCKER_TYPE (l);
-
- if (type >= MU_LOCKER_NTYPES)
- {
- free (l->file);
- return EINVAL;
- }
-
- /* Initialize locker-type-specific data */
- err = locker_tab[type].init ? locker_tab[type].init (l) : 0;
- if (err)
- {
- mu_locker_destroy (&l);
- return err;
- }
-
- *plocker = l;
-
- return 0;
-}
-
-void
-mu_locker_destroy (mu_locker_t *plocker)
-{
- if (plocker && *plocker)
- {
- unsigned type = MU_LOCKER_TYPE (*plocker);
- if (type < MU_LOCKER_NTYPES)
- {
- if (locker_tab[type].destroy)
- locker_tab[type].destroy (*plocker);
- free ((*plocker)->file);
- free (*plocker);
- *plocker = NULL;
- }
- }
-}
-
-int
-mu_locker_lock_mode (mu_locker_t lock, enum mu_locker_mode mode)
-{
- int rc;
- unsigned type;
- unsigned retries = 1;
-
- if (lock == NULL || (type = MU_LOCKER_TYPE (lock)) >= MU_LOCKER_NTYPES)
- return EINVAL;
-
- if (locker_tab[type].prelock && (rc = locker_tab[type].prelock (lock)))
- return rc;
-
- /* Is the lock already applied? */
- if (lock->refcnt > 0)
- {
- lock->refcnt++;
- if (mode == lock->mode)
- return 0;
- }
-
- lock->mode = mode;
-
- if (lock->flags & MU_LOCKER_RETRY)
- retries = lock->retries;
-
- if (locker_tab[type].lock)
- {
- while (retries--)
- {
- rc = locker_tab[type].lock (lock, mode);
- if (rc == EAGAIN && retries)
- sleep (lock->retry_sleep);
- else
- break;
- }
-
- if (rc == EAGAIN)
- rc = MU_ERR_LOCK_CONFLICT;
- }
- else
- rc = 0;
-
- if (rc == 0)
- lock->refcnt++;
-
- return rc;
-}
-
-int
-mu_locker_lock (mu_locker_t lock)
-{
- return mu_locker_lock_mode (lock, mu_lck_exc);
-}
-
-int
-mu_locker_unlock (mu_locker_t lock)
-{
- int rc = 0;
- unsigned type;
-
- if (!lock)
- return MU_ERR_LOCKER_NULL;
-
- if (lock->refcnt == 0)
- return MU_ERR_LOCK_NOT_HELD;
-
- if ((rc = check_file_permissions (lock->file)))
- return rc;
-
- if (--lock->refcnt > 0)
- return 0;
-
- type = MU_LOCKER_TYPE (lock);
- if (locker_tab[type].unlock)
- rc = locker_tab[type].unlock (lock);
- else
- rc = 0;
-
- return rc;
-}
-
-int
-mu_locker_remove_lock (mu_locker_t lock)
-{
- if (!lock)
- return MU_ERR_LOCKER_NULL;
-
- /* Force the reference count to 1 to unlock the file. */
- lock->refcnt = 1;
- return mu_locker_unlock (lock);
-}
-
+/* Dotlock type */
#define DOTLOCK_SUFFIX ".lock"
-/* expire a stale lock (if MU_LOCKER_PID or MU_LOCKER_TIME) */
+/* expire a stale lock (if MU_LOCKER_FLAG_CHECK_PID or
+ MU_LOCKER_FLAG_EXPIRE_TIME) */
static void
expire_stale_lock (mu_locker_t lock)
{
int stale = 0;
int fd = open (lock->data.dot.dotlock, O_RDONLY);
+
if (fd == -1)
return;
/* Check to see if this process is still running. */
- if (lock->flags & MU_LOCKER_PID)
+ if (lock->flags & MU_LOCKER_FLAG_CHECK_PID)
{
char buf[16];
pid_t pid;
@@ -680,7 +182,7 @@ expire_stale_lock (mu_locker_t lock)
}
/* Check to see if the lock expired. */
- if (lock->flags & MU_LOCKER_TIME)
+ if (lock->flags & MU_LOCKER_FLAG_EXPIRE_TIME)
{
struct stat stbuf;
@@ -696,16 +198,16 @@ expire_stale_lock (mu_locker_t lock)
}
static int
-init_dotlock (mu_locker_t locker)
+init_dotlock (mu_locker_t lck, mu_locker_hints_t *hints)
{
char *tmp, *p;
/* Make sure the spool directory is writable */
- tmp = strdup (locker->file);
+ tmp = strdup (lck->file);
if (!tmp)
return ENOMEM;
- strcpy (tmp, locker->file);
+ strcpy (tmp, lck->file);
p = strrchr (tmp, '/');
if (!p)
{
@@ -720,33 +222,36 @@ init_dotlock (mu_locker_t locker)
if (access (tmp, W_OK))
{
/* Fallback to kernel locking */
+ mu_locker_hints_t hints = {
+ .flags = MU_LOCKER_FLAG_TYPE,
+ .type = MU_LOCKER_TYPE_KERNEL
+ };
free (tmp);
- return mu_locker_set_flags (locker,
- MU_LOCKER_KERNEL|MU_LOCKER_OPTIONS(locker->flags));
+ return mu_locker_modify (lck, &hints);
}
free (tmp);
+
+ lck->data.dot.dotlock = malloc (strlen (lck->file)
+ + sizeof (DOTLOCK_SUFFIX));
- locker->data.dot.dotlock = malloc (strlen (locker->file)
- + sizeof (DOTLOCK_SUFFIX));
-
- if (!locker->data.dot.dotlock)
+ if (!lck->data.dot.dotlock)
return ENOMEM;
- strcpy (locker->data.dot.dotlock, locker->file);
- strcat (locker->data.dot.dotlock, DOTLOCK_SUFFIX);
+ strcpy (lck->data.dot.dotlock, lck->file);
+ strcat (lck->data.dot.dotlock, DOTLOCK_SUFFIX);
return 0;
}
static void
-destroy_dotlock (mu_locker_t locker)
+destroy_dotlock (mu_locker_t lck)
{
- free (locker->data.dot.dotlock);
- free (locker->data.dot.nfslock);
+ free (lck->data.dot.dotlock);
+ free (lck->data.dot.nfslock);
}
static int
-lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
+lock_dotlock (mu_locker_t lck, enum mu_locker_mode mode)
{
int rc;
char *host = NULL;
@@ -754,14 +259,14 @@ lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
int err = 0;
int fd;
- if (locker->data.dot.nfslock)
+ if (lck->data.dot.nfslock)
{
- unlink (locker->data.dot.nfslock);
- free (locker->data.dot.nfslock);
- locker->data.dot.nfslock = NULL;
+ unlink (lck->data.dot.nfslock);
+ free (lck->data.dot.nfslock);
+ lck->data.dot.nfslock = NULL;
}
- expire_stale_lock (locker);
+ expire_stale_lock (lck);
/* build the NFS hitching-post to the lock file */
@@ -769,17 +274,17 @@ lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
if (rc)
return rc;
time (&now);
- rc = mu_asprintf (&locker->data.dot.nfslock,
+ rc = mu_asprintf (&lck->data.dot.nfslock,
"%s.%lu.%lu.%s",
- locker->file,
+ lck->file,
(unsigned long) getpid (),
(unsigned long) now, host);
free (host);
if (rc)
return rc;
- fd = open (locker->data.dot.nfslock,
- O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR);
+ fd = open (lck->data.dot.nfslock,
+ O_WRONLY | O_CREAT | O_EXCL, MU_LOCKFILE_MODE);
if (fd == -1)
{
if (errno == EEXIST)
@@ -790,32 +295,32 @@ lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
close (fd);
/* Try to link to the lockfile. */
- if (link (locker->data.dot.nfslock, locker->data.dot.dotlock) == -1)
+ if (link (lck->data.dot.nfslock, lck->data.dot.dotlock) == -1)
{
- unlink (locker->data.dot.nfslock);
+ unlink (lck->data.dot.nfslock);
if (errno == EEXIST)
return EAGAIN;
return errno;
}
- if ((fd = open (locker->data.dot.dotlock, O_RDWR)) == -1)
+ if ((fd = open (lck->data.dot.dotlock, O_RDWR)) == -1)
{
- unlink (locker->data.dot.nfslock);
+ unlink (lck->data.dot.nfslock);
return errno;
}
- err = stat_check (locker->data.dot.nfslock, fd, 2);
+ err = stat_check (lck->data.dot.nfslock, fd, 2);
if (err)
{
- unlink (locker->data.dot.nfslock);
+ unlink (lck->data.dot.nfslock);
if (err == EINVAL)
return MU_ERR_LOCK_BAD_LOCK;
return errno;
}
- unlink (locker->data.dot.nfslock);
+ unlink (lck->data.dot.nfslock);
- if (locker->flags & MU_LOCKER_PID)
+ if (lck->flags & MU_LOCKER_FLAG_CHECK_PID)
{
char buf[16];
sprintf (buf, "%ld", (long) getpid ());
@@ -826,14 +331,14 @@ lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
}
static int
-unlock_dotlock (mu_locker_t locker)
+unlock_dotlock (mu_locker_t lck)
{
- if (unlink (locker->data.dot.dotlock) == -1)
+ if (unlink (lck->data.dot.dotlock) == -1)
{
int err = errno;
if (err == ENOENT)
{
- locker->refcnt = 0; /*FIXME?*/
+ lck->refcnt = 0; /*FIXME?*/
err = MU_ERR_LOCK_NOT_HELD;
return err;
}
@@ -841,32 +346,10 @@ unlock_dotlock (mu_locker_t locker)
}
return 0;
}
-
-int
-mu_locker_touchlock (mu_locker_t lock)
-{
- if (!lock)
- return MU_ERR_LOCKER_NULL;
-
- if (MU_LOCKER_TYPE (lock) != MU_LOCKER_TYPE_DOTLOCK)
- return 0;
-
- if (lock->refcnt > 0)
- return utime (lock->data.dot.dotlock, NULL);
-
- return MU_ERR_LOCK_NOT_HELD;
-}
-
/* Kernel locking */
static int
-init_kernel (mu_locker_t locker)
-{
- return 0;
-}
-
-static int
-lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
+lock_kernel (mu_locker_t lck, enum mu_locker_mode mode)
{
int fd;
struct flock fl;
@@ -888,10 +371,10 @@ lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
return EINVAL;
}
- fd = open (locker->file, O_RDWR);
+ fd = open (lck->file, O_RDWR);
if (fd == -1)
return errno;
- locker->data.kernel.fd = fd;
+ lck->data.fd = fd;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
@@ -910,7 +393,7 @@ lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
}
static int
-unlock_kernel (mu_locker_t locker)
+unlock_kernel (mu_locker_t lck)
{
struct flock fl;
@@ -918,7 +401,7 @@ unlock_kernel (mu_locker_t locker)
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0; /* Unlock entire file */
- if (fcntl (locker->data.kernel.fd, F_SETLK, &fl))
+ if (fcntl (lck->data.fd, F_SETLK, &fl))
{
#ifdef EACCESS
if (errno == EACCESS)
@@ -928,24 +411,27 @@ unlock_kernel (mu_locker_t locker)
return EAGAIN;
return errno;
}
- close (locker->data.kernel.fd);
+ close (lck->data.fd);
+ lck->data.fd = -1;
return 0;
}
+/* External locking */
static int
-init_external (mu_locker_t locker)
+init_external (mu_locker_t lck, mu_locker_hints_t *hints)
{
- if (!(locker->data.external.name = strdup (mu_locker_external_program ?
- mu_locker_external_program :
- MU_LOCKER_EXTERNAL_PROGRAM)))
+ char const *ext_locker = hints->flags & MU_LOCKER_FLAG_EXT_LOCKER
+ ? hints->ext_locker
+ : MU_LOCKER_DEFAULT_EXT_LOCKER;
+ if (!(lck->data.external.name = strdup (ext_locker)))
return ENOMEM;
return 0;
}
static void
-destroy_external (mu_locker_t locker)
+destroy_external (mu_locker_t lck)
{
- free (locker->data.external.name);
+ free (lck->data.external.name);
}
/*
@@ -954,28 +440,27 @@ destroy_external (mu_locker_t locker)
#define DEC_DIGS_PER_INT (sizeof(int) * 8 / 3 + 1)
static int
-external_locker (mu_locker_t l, int lock)
+external_locker (mu_locker_t lck, int lock)
{
int err = 0;
char *av[6];
int ac = 0;
char aforce[3 + DEC_DIGS_PER_INT + 1];
char aretry[3 + DEC_DIGS_PER_INT + 1];
- int status = 0;
+ int status;
- av[ac++] = l->data.external.name ?
- l->data.external.name : MU_LOCKER_EXTERNAL_PROGRAM;
+ av[ac++] = lck->data.external.name;
- if (l->flags & MU_LOCKER_TIME)
+ if (lck->flags & MU_LOCKER_FLAG_EXPIRE_TIME)
{
- snprintf (aforce, sizeof (aforce), "-f%d", l->expire_time);
+ snprintf (aforce, sizeof (aforce), "-f%d", lck->expire_time);
aforce[sizeof (aforce) - 1] = 0;
av[ac++] = aforce;
}
- if (l->flags & MU_LOCKER_RETRY)
+ if (lck->flags & MU_LOCKER_FLAG_RETRY)
{
- snprintf (aretry, sizeof (aretry), "-r%d", l->retries);
+ snprintf (aretry, sizeof (aretry), "-r%d", lck->retry_count);
aretry[sizeof (aretry) - 1] = 0;
av[ac++] = aretry;
}
@@ -983,13 +468,17 @@ external_locker (mu_locker_t l, int lock)
if (!lock)
av[ac++] = "-u";
- av[ac++] = l->file;
+ av[ac++] = lck->file;
av[ac++] = NULL;
if ((err = mu_spawnvp (av[0], av, &status)))
- return err;
-
+ {
+ perror ("mu_spawnvp");
+ fprintf (stderr, "errcode %d\n", err);
+ return err;
+ }
+
if (!WIFEXITED (status))
{
err = MU_ERR_LOCK_EXT_KILLED;
@@ -1004,7 +493,7 @@ external_locker (mu_locker_t l, int lock)
case MU_DL_EX_OK:
err = 0;
- l->refcnt = lock;
+ lck->refcnt = lock;
break;
case MU_DL_EX_NEXIST:
@@ -1030,14 +519,591 @@ external_locker (mu_locker_t l, int lock)
}
static int
-lock_external (mu_locker_t locker, enum mu_locker_mode mode)
+lock_external (mu_locker_t lck, enum mu_locker_mode mode)
+{
+ return external_locker (lck, 1);
+}
+
+static int
+unlock_external (mu_locker_t lck)
+{
+ return external_locker (lck, 0);
+}
+
+mu_locker_hints_t mu_locker_defaults = {
+ .flags = MU_LOCKER_FLAG_TYPE | MU_LOCKER_FLAG_RETRY,
+ .type = MU_LOCKER_TYPE_DEFAULT,
+ .retry_count = MU_LOCKER_DEFAULT_RETRY_COUNT,
+ .retry_sleep = MU_LOCKER_DEFAULT_RETRY_SLEEP
+};
+
+struct locker_tab
+{
+ int (*init) (mu_locker_t, mu_locker_hints_t *);
+ void (*destroy) (mu_locker_t);
+ int (*prelock) (mu_locker_t);
+ int (*lock) (mu_locker_t, enum mu_locker_mode);
+ int (*unlock) (mu_locker_t);
+};
+
+static struct locker_tab locker_tab[] = {
+ /* MU_LOCKER_TYPE_DOTLOCK */
+ { init_dotlock, destroy_dotlock, prelock_common,
+ lock_dotlock, unlock_dotlock },
+ /* MU_LOCKER_TYPE_EXTERNAL */
+ { init_external, destroy_external, prelock_common,
+ lock_external, unlock_external },
+ /* MU_LOCKER_TYPE_KERNEL */
+ { NULL, NULL, NULL, lock_kernel, unlock_kernel },
+ /* MU_LOCKER_TYPE_NULL */
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+#define MU_LOCKER_NTYPES (sizeof (locker_tab) / sizeof (locker_tab[0]))
+
+int
+mu_locker_create_ext (mu_locker_t *plocker, const char *fname,
+ mu_locker_hints_t *user_hints)
+{
+ mu_locker_t lck;
+ char *filename;
+ int err = 0;
+ mu_locker_hints_t hints;
+
+ if (plocker == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (fname == NULL)
+ return EINVAL;
+
+ if ((err = mu_unroll_symlink (fname, &filename)))
+ {
+ if (err == ENOENT)
+ {
+ /* Try the directory part. If it unrolls successfully (i.e.
+ all its components exist), tuck the filename part back in
+ the resulting path and use it as the lock filename. */
+ char *p, *new_name, *tmp = strdup (fname);
+ if (!tmp)
+ return ENOMEM;
+ p = strrchr (tmp, '/');
+ if (!p)
+ filename = tmp;
+ else
+ {
+ *p = 0;
+ err = mu_unroll_symlink (tmp, &filename);
+ if (err)
+ {
+ free (tmp);
+ return err;
+ }
+
+ new_name = mu_make_file_name_suf (filename, p + 1, NULL);
+ free (tmp);
+ free (filename);
+ if (!new_name)
+ return ENOMEM;
+ filename = new_name;
+ }
+ }
+ else
+ return err;
+ }
+
+ lck = calloc (1, sizeof (*lck));
+
+ if (lck == NULL)
+ {
+ free (filename);
+ return ENOMEM;
+ }
+
+ lck->file = filename;
+
+ hints = user_hints ? *user_hints : mu_locker_defaults;
+ if ((hints.flags & MU_LOCKER_FLAG_TYPE) == 0)
+ {
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_DEFAULT;
+ }
+ err = mu_locker_modify (lck, &hints);
+ if (err)
+ mu_locker_destroy (&lck);
+ else
+ *plocker = lck;
+
+ return err;
+}
+
+int
+mu_locker_modify (mu_locker_t lck, mu_locker_hints_t *hints)
+{
+ if (!lck || !hints)
+ return EINVAL;
+
+ if (hints->flags & MU_LOCKER_FLAG_TYPE)
+ {
+ struct _mu_locker new_lck;
+ int type;
+
+ if (hints->type < 0 || hints->type >= MU_LOCKER_NTYPES)
+ return EINVAL;
+
+ if (lck->flags == 0 || hints->type != lck->type)
+ {
+ if (strcmp (lck->file, "/dev/null") == 0)
+ type = MU_LOCKER_TYPE_NULL;
+ else
+ type = hints->type;
+
+ memset (&new_lck, 0, sizeof (new_lck));
+ new_lck.type = type;
+ new_lck.file = lck->file;
+ if (locker_tab[type].init)
+ {
+ int rc = locker_tab[type].init (&new_lck, hints);
+ if (rc)
+ {
+ if (locker_tab[type].destroy)
+ locker_tab[type].destroy (&new_lck);
+ return rc;
+ }
+ }
+
+ if (lck->flags != 0 && locker_tab[lck->type].destroy)
+ locker_tab[lck->type].destroy (lck);
+
+ *lck = new_lck;
+ }
+ }
+
+ if (hints->flags & MU_LOCKER_FLAG_RETRY)
+ {
+ lck->retry_count = hints->retry_count > 0
+ ? hints->retry_count
+ : MU_LOCKER_DEFAULT_RETRY_COUNT;
+ lck->retry_sleep = hints->retry_sleep > 0
+ ? hints->retry_sleep
+ : MU_LOCKER_DEFAULT_RETRY_SLEEP;
+ }
+
+ if (hints->flags & MU_LOCKER_FLAG_EXPIRE_TIME)
+ lck->expire_time = hints->expire_time > 0 ? hints->expire_time
+ : MU_LOCKER_DEFAULT_EXPIRE_TIME;
+
+ lck->flags = hints->flags;
+
+ return 0;
+}
+
+void
+mu_locker_destroy (mu_locker_t *plocker)
+{
+ if (plocker && *plocker)
+ {
+ mu_locker_t lck = *plocker;
+ if (locker_tab[lck->type].destroy)
+ locker_tab[lck->type].destroy (lck);
+ free (lck->file);
+ free (lck);
+ *plocker = NULL;
+ }
+}
+
+int
+mu_locker_lock_mode (mu_locker_t lck, enum mu_locker_mode mode)
+{
+ int rc;
+ unsigned retries = 1;
+
+ if (!lck || lck->type < 0 || lck->type >= MU_LOCKER_NTYPES)
+ return EINVAL;
+
+ if (locker_tab[lck->type].prelock && (rc = locker_tab[lck->type].prelock (lck)))
+ return rc;
+
+ /* Is the lock already applied? */
+ if (lck->refcnt > 0)
+ {
+ lck->refcnt++;
+ if (mode == lck->mode)
+ return 0;
+ }
+
+ lck->mode = mode;
+
+ if (lck->flags & MU_LOCKER_FLAG_RETRY)
+ retries = lck->retry_count;
+
+ if (locker_tab[lck->type].lock)
+ {
+ while (retries--)
+ {
+ rc = locker_tab[lck->type].lock (lck, mode);
+ if (rc == EAGAIN && retries)
+ sleep (lck->retry_sleep);
+ else
+ break;
+ }
+
+ if (rc == EAGAIN)
+ rc = MU_ERR_LOCK_CONFLICT;
+ }
+ else
+ rc = 0;
+
+ if (rc == 0)
+ lck->refcnt++;
+
+ return rc;
+}
+
+int
+mu_locker_lock (mu_locker_t lck)
+{
+ return mu_locker_lock_mode (lck, mu_lck_exc);
+}
+
+int
+mu_locker_unlock (mu_locker_t lck)
+{
+ int rc = 0;
+
+ if (!lck)
+ return MU_ERR_LOCKER_NULL;
+
+ if (lck->refcnt == 0)
+ return MU_ERR_LOCK_NOT_HELD;
+
+ if ((rc = check_file_permissions (lck->file)))
+ return rc;
+
+ if (--lck->refcnt > 0)
+ return 0;
+
+ if (locker_tab[lck->type].unlock)
+ rc = locker_tab[lck->type].unlock (lck);
+ else
+ rc = 0;
+
+ return rc;
+}
+
+int
+mu_locker_remove_lock (mu_locker_t lck)
+{
+ if (!lck)
+ return MU_ERR_LOCKER_NULL;
+
+ /* Force the reference count to 1 to unlock the file. */
+ lck->refcnt = 1;
+ return mu_locker_unlock (lck);
+}
+
+int
+mu_locker_touchlock (mu_locker_t lck)
{
- return external_locker (locker, 1);
+ if (!lck)
+ return MU_ERR_LOCKER_NULL;
+
+ if (lck->type != MU_LOCKER_TYPE_DOTLOCK)
+ return 0;
+
+ if (lck->refcnt > 0)
+ return utime (lck->data.dot.dotlock, NULL);
+
+ return MU_ERR_LOCK_NOT_HELD;
+}
+
+int
+mu_locker_get_hints (mu_locker_t lck, mu_locker_hints_t *hints)
+{
+ if (!lck || !hints)
+ return EINVAL;
+
+ if (hints->flags & MU_LOCKER_FLAG_TYPE)
+ hints->type = lck->type;
+
+ hints->flags &= ~(lck->flags & hints->flags);
+
+ if (hints->flags & MU_LOCKER_FLAG_RETRY)
+ {
+ hints->retry_count = lck->retry_count;
+ hints->retry_sleep = lck->retry_sleep;
+ }
+ if (hints->flags & MU_LOCKER_FLAG_EXPIRE_TIME)
+ hints->expire_time = lck->expire_time;
+ if (hints->flags & MU_LOCKER_FLAG_EXT_LOCKER)
+ {
+ if (lck->type == MU_LOCKER_TYPE_EXTERNAL)
+ {
+ if ((hints->ext_locker = strdup (lck->data.external.name)) == NULL)
+ return errno;
+ }
+ else
+ hints->ext_locker = NULL;
+ }
+
+ return 0;
+}
+
+/* Deprecated interfaces */
+
+int
+mu_locker_create (mu_locker_t *lck, const char *filename, int flags)
+{
+ mu_locker_hints_t hints, *hp;
+
+ if (flags == 0)
+ hp = NULL;
+ else
+ {
+ hints.type = MU_LOCKER_FLAG_TO_TYPE(flags);
+ hints.flags = flags & MU_LOCKER_OPTION_MASK;
+ hp = &hints;
+ }
+ return mu_locker_create_ext (lck, filename, hp);
+}
+
+int
+mu_locker_set_default_flags (int flags, enum mu_locker_set_mode mode)
+{
+ int type = MU_LOCKER_FLAG_TO_TYPE (flags);
+ flags &= MU_LOCKER_OPTION_MASK;
+ switch (mode)
+ {
+ case mu_locker_assign:
+ mu_locker_defaults.flags = flags;
+ mu_locker_defaults.type = type;
+ break;
+
+ case mu_locker_set_bit:
+ mu_locker_defaults.flags |= flags;
+ mu_locker_defaults.type = type;
+ break;
+
+ case mu_locker_clear_bit:
+ mu_locker_defaults.flags &= flags;
+ if (type != MU_LOCKER_TYPE_DOTLOCK)
+ mu_locker_defaults.type = MU_LOCKER_TYPE_DOTLOCK;
+ break;
+ }
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_TYPE;
+ return 0;
+}
+
+void
+mu_locker_set_default_retry_timeout (time_t to)
+{
+ mu_locker_defaults.retry_sleep = to;
+}
+
+void
+mu_locker_set_default_retry_count (size_t n)
+{
+ mu_locker_defaults.retry_count = n;
+}
+
+void
+mu_locker_set_default_expire_timeout (time_t t)
+{
+ mu_locker_defaults.expire_time = t;
+}
+
+int
+mu_locker_set_default_external_program (char const *path)
+{
+ char *p = strdup (path);
+ if (!p)
+ return errno;
+ free (mu_locker_defaults.ext_locker);
+ mu_locker_defaults.ext_locker = p;
+ return 0;
}
static int
-unlock_external (mu_locker_t locker)
+legacy_locker_mod_flags (mu_locker_t lck, int flags,
+ enum mu_locker_set_mode mode)
{
- return external_locker (locker, 0);
+ mu_locker_hints_t hints;
+ int type = MU_LOCKER_FLAG_TO_TYPE (flags);
+
+ flags &= MU_LOCKER_OPTION_MASK;
+
+ switch (mode)
+ {
+ case mu_locker_assign:
+ hints.flags = flags | MU_LOCKER_FLAG_TYPE;
+ hints.type = type;
+ break;
+
+ case mu_locker_set_bit:
+ hints.flags = lck->flags | flags | MU_LOCKER_FLAG_TYPE;
+ hints.type = type;
+ break;
+
+ case mu_locker_clear_bit:
+ hints.flags = lck->flags & ~flags;
+ if (type != MU_LOCKER_TYPE_DOTLOCK)
+ {
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_DOTLOCK;
+ }
+ break;
+ }
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_set_flags (mu_locker_t lck, int flags)
+{
+ return legacy_locker_mod_flags (lck, flags, mu_locker_assign);
+}
+
+int
+mu_locker_mod_flags (mu_locker_t lck, int flags,
+ enum mu_locker_set_mode mode)
+{
+ return legacy_locker_mod_flags (lck, flags, mode);
+}
+
+int
+mu_locker_set_expire_time (mu_locker_t lck, int v)
+{
+ mu_locker_hints_t hints;
+
+ if (v < 0)
+ return EINVAL;
+ hints.flags = MU_LOCKER_FLAG_EXPIRE_TIME;
+ hints.expire_time = v;
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_set_retries (mu_locker_t lck, int v)
+{
+ mu_locker_hints_t hints;
+
+ if (v < 0)
+ return EINVAL;
+ hints.flags = MU_LOCKER_FLAG_RETRY;
+ hints.retry_count = v;
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_set_retry_sleep (mu_locker_t lck, int v)
+{
+ mu_locker_hints_t hints;
+
+ if (v < 0)
+ return EINVAL;
+ hints.flags = MU_LOCKER_FLAG_RETRY;
+ hints.retry_sleep = v;
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_set_external (mu_locker_t lck, const char *program)
+{
+ mu_locker_hints_t hints;
+
+ if (lck->type != MU_LOCKER_TYPE_EXTERNAL)
+ return EINVAL;
+
+ if (!program)
+ program = MU_LOCKER_DEFAULT_EXT_LOCKER;
+
+ hints.flags = MU_LOCKER_FLAG_EXT_LOCKER;
+ hints.ext_locker = (char*) program;
+ return mu_locker_modify (lck, &hints);
+}
+
+int
+mu_locker_get_flags (mu_locker_t lck, int *flags)
+{
+ mu_locker_hints_t hints;
+ int rc;
+
+ if (!flags)
+ return EINVAL;
+
+ hints.flags = MU_LOCKER_FLAGS_ALL;
+ if ((rc = mu_locker_get_hints (lck, &hints)) != 0)
+ return rc;
+ *flags = hints.flags | MU_LOCKER_TYPE_TO_FLAG (hints.type);
+ return 0;
+}
+
+int
+mu_locker_get_expire_time (mu_locker_t lck, int *pv)
+{
+ int rc;
+ mu_locker_hints_t hints;
+
+ if (!pv)
+ return EINVAL;
+
+ hints.flags = MU_LOCKER_FLAG_EXPIRE_TIME;
+ if ((rc = mu_locker_get_hints (lck, &hints)) != 0)
+ return rc;
+ if ((hints.flags & MU_LOCKER_FLAG_EXPIRE_TIME) == 0)
+ *pv = 0;
+ else
+ {
+ if (hints.expire_time > INT_MAX)
+ return ERANGE;
+ *pv = hints.expire_time;
+ }
+ return 0;
+}
+
+int
+mu_locker_get_retries (mu_locker_t lck, int *pv)
+{
+ int rc;
+ mu_locker_hints_t hints;
+
+ if (!pv)
+ return EINVAL;
+
+ hints.flags = MU_LOCKER_FLAG_RETRY;
+ if ((rc = mu_locker_get_hints (lck, &hints)) != 0)
+ return rc;
+ if ((hints.flags & MU_LOCKER_FLAG_RETRY) == 0)
+ *pv = 0;
+ else
+ {
+ if (hints.expire_time > INT_MAX)
+ return ERANGE;
+ *pv = hints.retry_count;
+ }
+ return 0;
}
+int
+mu_locker_get_retry_sleep (mu_locker_t lck, int *pv)
+{
+ int rc;
+ mu_locker_hints_t hints;
+
+ if (!pv)
+ return EINVAL;
+
+ hints.flags = MU_LOCKER_FLAG_RETRY;
+ if ((rc = mu_locker_get_hints (lck, &hints)) != 0)
+ return rc;
+ if ((hints.flags & MU_LOCKER_FLAG_RETRY) == 0)
+ *pv = 0;
+ else
+ {
+ if (hints.expire_time > INT_MAX)
+ return ERANGE;
+ *pv = hints.retry_sleep;
+ }
+ return 0;
+}
+
+/* mu_locker_get_external was never implemented */
+
diff --git a/libmailutils/base/spawnvp.c b/libmailutils/base/spawnvp.c
index ec0959a4c..6585a15dd 100644
--- a/libmailutils/base/spawnvp.c
+++ b/libmailutils/base/spawnvp.c
@@ -39,70 +39,89 @@ mu_spawnvp (const char *prog, char *av[], int *stat)
int err = 0;
int progstat;
struct sigaction ignore;
- struct sigaction saveintr;
- struct sigaction savequit;
+
+ struct sigsave {
+ int signo;
+ void *handler;
+ int saved;
+ struct sigaction act;
+ };
+
+ static struct sigsave sigsave[] = {
+ { SIGINT, SIG_IGN, 0 },
+ { SIGQUIT, SIG_IGN, 0 },
+ { SIGCHLD, SIG_DFL, 0 }
+ };
+ static int nsigsave = sizeof (sigsave) / sizeof (sigsave[0]);
+
+ int i;
+
sigset_t chldmask;
sigset_t savemask;
if (!prog || !av)
return EINVAL;
- ignore.sa_handler = SIG_IGN; /* ignore SIGINT and SIGQUIT */
ignore.sa_flags = 0;
sigemptyset (&ignore.sa_mask);
-
- if (sigaction (SIGINT, &ignore, &saveintr) < 0)
- return errno;
- if (sigaction (SIGQUIT, &ignore, &savequit) < 0)
+ for (i = 0; i < nsigsave; i++)
{
- sigaction (SIGINT, &saveintr, NULL);
- return errno;
+ ignore.sa_handler = sigsave[i].handler;
+ if (sigaction (sigsave[i].signo, &ignore, &sigsave[i].act) < 0)
+ {
+ err = errno;
+ break;
+ }
+ sigsave[i].saved = 1;
}
- sigemptyset (&chldmask); /* now block SIGCHLD */
- sigaddset (&chldmask, SIGCHLD);
-
- if (sigprocmask (SIG_BLOCK, &chldmask, &savemask) < 0)
+ if (err == 0)
{
- sigaction (SIGINT, &saveintr, NULL);
- sigaction (SIGQUIT, &savequit, NULL);
- return errno;
+ sigemptyset (&chldmask); /* now block SIGCHLD */
+ sigaddset (&chldmask, SIGCHLD);
+
+ if (sigprocmask (SIG_BLOCK, &chldmask, &savemask) < 0)
+ err = errno;
+ else
+ {
+ pid = fork ();
+
+ if (pid < 0)
+ {
+ err = errno;
+ }
+ else if (pid == 0)
+ { /* child */
+ for (i = 0; i < nsigsave; i++)
+ {
+ sigaction (sigsave[i].signo, &sigsave[i].act, NULL);
+ }
+ sigprocmask (SIG_SETMASK, &savemask, NULL);
+
+ execvp (prog, av);
+ _exit (127); /* exec error */
+ }
+ else
+ { /* parent */
+ while (waitpid (pid, &progstat, 0) < 0)
+ if (errno != EINTR)
+ {
+ err = errno; /* error other than EINTR from waitpid() */
+ break;
+ }
+ if (err == 0 && stat)
+ *stat = progstat;
+ }
+ }
}
-
- pid = fork ();
-
- if (pid < 0)
+ /* restore previous signal actions & reset signal mask */
+ for (i = 0; i < nsigsave; i++)
{
- err = errno;
- }
- else if (pid == 0)
- { /* child */
- /* restore previous signal actions & reset signal mask */
- sigaction (SIGINT, &saveintr, NULL);
- sigaction (SIGQUIT, &savequit, NULL);
- sigprocmask (SIG_SETMASK, &savemask, NULL);
-
- execvp (prog, av);
- _exit (127); /* exec error */
+ if (!sigsave[i].saved)
+ break;
+ if (sigaction (sigsave[i].signo, &sigsave[i].act, NULL) < 0)
+ err = err ? err : errno;
}
- else
- { /* parent */
- while (waitpid (pid, &progstat, 0) < 0)
- if (errno != EINTR)
- {
- err = errno; /* error other than EINTR from waitpid() */
- break;
- }
- if (err == 0 && stat)
- *stat = progstat;
- }
-
- /* restore previous signal actions & reset signal mask */
- /* preserve first error number, but still try and reset the signals */
- if (sigaction (SIGINT, &saveintr, NULL) < 0)
- err = err ? err : errno;
- if (sigaction (SIGQUIT, &savequit, NULL) < 0)
- err = err ? err : errno;
if (sigprocmask (SIG_SETMASK, &savemask, NULL) < 0)
err = err ? err : errno;
diff --git a/libmailutils/cli/stdcapa.c b/libmailutils/cli/stdcapa.c
index b72f024cb..1d496161c 100644
--- a/libmailutils/cli/stdcapa.c
+++ b/libmailutils/cli/stdcapa.c
@@ -307,10 +307,9 @@ static struct mu_cfg_param mailbox_cfg[] = {
static int
cb_locker_flags (void *data, mu_config_value_t *val)
{
- int flags = 0;
char const *s;
static struct mu_kwd flag_tab[] = {
- { "external-locker", 'E' },
+ { "type external", 'E' },
{ "retry-count", 'R' },
{ "expire-timeout", 'T' },
{ "pid-check", 'P' },
@@ -334,7 +333,7 @@ cb_locker_flags (void *data, mu_config_value_t *val)
mu_diag_output (MU_DIAG_WARNING,
_("applying legacy flag %c, use %s instead"),
*s, kw);
- flags |= MU_LOCKER_PID;
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_CHECK_PID;
}
else
{
@@ -345,13 +344,11 @@ cb_locker_flags (void *data, mu_config_value_t *val)
*s, kw);
}
}
- if (flags)
- mu_locker_set_default_flags (flags, mu_locker_assign);
return 0;
}
static int
-cb_locker_retry_timeout (void *data, mu_config_value_t *val)
+cb_locker_retry_sleep (void *data, mu_config_value_t *val)
{
int rc;
time_t t;
@@ -366,17 +363,24 @@ cb_locker_retry_timeout (void *data, mu_config_value_t *val)
mu_strerror (rc));
free (errmsg);
}
- else if (t == 0)
- mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_clear_bit);
else
{
- mu_locker_set_default_retry_timeout (t);
- mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_set_bit);
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_sleep = t;
}
return 0;
}
static int
+cb_locker_retry_timeout (void *data, mu_config_value_t *val)
+{
+ mu_diag_output (MU_DIAG_WARNING,
+ _("%s is deprecated, please use %s instead"),
+ "retry-timeout", "retry-sleep");
+ return cb_locker_retry_sleep (data, val);
+}
+
+static int
cb_locker_retry_count (void *data, mu_config_value_t *val)
{
int rc;
@@ -393,11 +397,11 @@ cb_locker_retry_count (void *data, mu_config_value_t *val)
free (errmsg);
}
else if (n == 0)
- mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_clear_bit);
+ mu_locker_defaults.flags &= ~MU_LOCKER_FLAG_RETRY;
else
{
- mu_locker_set_default_retry_count (n);
- mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_set_bit);
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_count = n;
}
return 0;
}
@@ -419,31 +423,58 @@ cb_locker_expire_timeout (void *data, mu_config_value_t *val)
free (errmsg);
}
else if (t == 0)
- mu_locker_set_default_flags (MU_LOCKER_TIME, mu_locker_clear_bit);
+ mu_locker_defaults.flags &= ~MU_LOCKER_FLAG_EXPIRE_TIME;
else
{
- mu_locker_set_default_expire_timeout (t);
- mu_locker_set_default_flags (MU_LOCKER_TIME, mu_locker_set_bit);
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_EXPIRE_TIME;
+ mu_locker_defaults.expire_time = t;
}
return 0;
}
static int
-cb_locker_external (void *data, mu_config_value_t *val)
+cb_locker_type (void *data, mu_config_value_t *val)
{
int t;
-
+ static struct mu_kwd ltab[] = {
+ { "dotlock", MU_LOCKER_TYPE_DOTLOCK },
+ { "default", MU_LOCKER_TYPE_DEFAULT },
+ { "external", MU_LOCKER_TYPE_EXTERNAL },
+ { "kernel", MU_LOCKER_TYPE_KERNEL },
+ { "null", MU_LOCKER_TYPE_NULL },
+ { NULL }
+ };
+
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
- if (mu_str_to_c (val->v.string, mu_c_bool, &t, NULL) == 0 && t == 0)
- {
- mu_locker_set_default_flags (MU_LOCKER_EXTERNAL, mu_locker_clear_bit);
- }
- else
+
+ if (mu_kwd_xlat_name (ltab, val->v.string, &t))
{
- mu_locker_set_default_external_program (val->v.string);
- mu_locker_set_default_flags (MU_LOCKER_EXTERNAL, mu_locker_set_bit);
+ mu_error (_("unrecognized locker type: %s"), val->v.string);
+ return 1;
}
+
+ free (mu_locker_defaults.ext_locker);
+ mu_locker_defaults.ext_locker = NULL;
+
+ mu_locker_defaults.type = t;
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_TYPE;
+
+ return 0;
+}
+
+static int
+cb_locker_external (void *data, mu_config_value_t *val)
+{
+ int t;
+
+ if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
+ return 1;
+
+ free (mu_locker_defaults.ext_locker);
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_EXT_LOCKER;
+ mu_locker_defaults.ext_locker = strdup (val->v.string);
+
return 0;
}
@@ -459,22 +490,26 @@ cb_locker_pid_check (void *data, mu_config_value_t *val)
mu_error ("%s", _("not a boolean"));
return 1;
}
- mu_locker_set_default_flags (MU_LOCKER_PID,
- t ? mu_locker_set_bit : mu_locker_clear_bit);
+ if (t)
+ mu_locker_defaults.flags |= MU_LOCKER_FLAG_CHECK_PID;
+ else
+ mu_locker_defaults.flags &= ~MU_LOCKER_FLAG_CHECK_PID;
return 0;
}
static struct mu_cfg_param locking_cfg[] = {
- /* FIXME: Flags are superfluous. */
- { "flags", mu_cfg_callback, NULL, 0, cb_locker_flags,
- N_("Default locker flags (E=external, R=retry, T=time, P=pid)."),
- N_("arg: string") },
- { "retry-timeout", mu_cfg_callback, NULL, 0, cb_locker_retry_timeout,
- N_("Set timeout for acquiring the lock."),
- N_("arg: interval")},
+ { "type", mu_cfg_callback, NULL, 0, cb_locker_type,
+ N_("Set locker type."),
+ N_("type: default | dotlock | external | kernel | null") },
{ "retry-count", mu_cfg_callback, NULL, 0, cb_locker_retry_count,
N_("Set the maximum number of times to retry acquiring the lock."),
N_("arg: integer") },
+ { "retry-sleep", mu_cfg_callback, NULL, 0, cb_locker_retry_sleep,
+ N_("Set the delay between two successive locking attempts."),
+ N_("arg: interval")},
+ { "retry-timeout", mu_cfg_callback, NULL, 0, cb_locker_retry_timeout,
+ N_("Deprecated alias of retry-sleep. Retained for backward compatibility."),
+ N_("arg: interval")},
{ "expire-timeout", mu_cfg_callback, NULL, 0, cb_locker_expire_timeout,
N_("Expire locks older than this amount of time."),
N_("arg: interval")},
@@ -484,6 +519,9 @@ static struct mu_cfg_param locking_cfg[] = {
{ "pid-check", mu_cfg_callback, NULL, 0, cb_locker_pid_check,
N_("Check if PID of the lock owner is active."),
N_("arg: bool") },
+ { "flags", mu_cfg_callback, NULL, 0, cb_locker_flags,
+ N_("Deprecated. Retained for backward compatibility."),
+ N_("arg: string") },
{ NULL, }
};
diff --git a/libmailutils/tests/lck.c b/libmailutils/tests/lck.c
index fe7ac9213..e1cce1da2 100644
--- a/libmailutils/tests/lck.c
+++ b/libmailutils/tests/lck.c
@@ -3,11 +3,11 @@
lck - mailutils locking test
SYNOPSIS
- lck [-akpu?] [-e COMMAND] [-H SECONDS] [-r N] [-t SECONDS] [-x SECONDS]
+ lck [-akpuv?] [-eCOMMAND] [-f SECONDS] [-H SECONDS] [-r N] [-t SECONDS]
[--abandon] [--delay=SECONDS] [--expire=SECONDS]
- [--external=COMMAND] [--help] [--hold=SECONDS] [--kernel]
+ [--external[=COMMAND]] [--help] [--hold=SECONDS] [--kernel]
[--pid-check] [--retry=N] [--show-config-options] [--unlock]
- [--usage] FILE
+ [--usage] [--verbose] FILE
DESCRIPTION
Tests the mailutils locking mechanism. Unless --hold (-H) or --abandon
@@ -24,13 +24,21 @@
The master waits for child to successfully lock the file and attempts to
obtain the lock. If successful, it exits with the 0 status. On errors,
- the termination status is 0.
+ the termination status is:
+
+ 1 An error other than described below occurred. This includes
+ usage errors.
+ 2 Unlock requested, but file is not locked.
+ 3 Lock requested, but file is already locked by another program.
+ 4 Insufficient permissions.
OPTIONS
Locking type (default: dotlock):
- -e, --external=COMMAND
- Use the external locker command.
+ -e, --external[=COMMAND]
+ Use the external locker command. If COMMAND is omitted,
+ the compiled-in default will be used (see
+ MU_LOCKER_DEFAULT_EXT_LOCKER in mailutils/locker.h.
-k, --kernel
Use kernel locking (fnctl).
@@ -46,7 +54,7 @@
-t, --delay=SECONDS
Delay between two successive locking attempts.
- -x, --expire=SECONDS
+ -f, --expire=SECONDS
Expire the lock after that many seconds.
Child operation modifiers
@@ -62,6 +70,11 @@
-u, --unlock
Release the existing lock.
+ -v, --verbose
+ If the lock (or unlock) operation fails, print the error
+ message on the stderr in addition to exiting with the error
+ status.
+
Informational options
--show-config-options
@@ -99,15 +112,12 @@
#include <signal.h>
#include <mailutils/mailutils.h>
-static int flags = 0;
+static mu_locker_hints_t hints = { .flags = 0 };
static int unlock;
-static unsigned retry_count;
-static unsigned retry_sleep = 0;
static int pid_check;
-static unsigned expire;
-static char const *extlocker;
static unsigned hold_time;
static int abandon_lock;
+static int verbose;
static void
cli_type (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
@@ -115,12 +125,18 @@ cli_type (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
switch (opt->opt_short)
{
case 'k':
- flags = MU_LOCKER_OPTIONS (flags) | MU_LOCKER_KERNEL;
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_KERNEL;
break;
case 'e':
- flags = MU_LOCKER_OPTIONS (flags) | MU_LOCKER_EXTERNAL;
- extlocker = arg;
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_EXTERNAL;
+ if (arg)
+ {
+ hints.flags |= MU_LOCKER_FLAG_EXT_LOCKER;
+ hints.ext_locker = strdup (arg);
+ }
break;
default:
@@ -133,22 +149,22 @@ struct mu_option options[] = {
{ "kernel", 'k', NULL, MU_OPTION_DEFAULT,
"use kernel locking", mu_c_void, NULL, cli_type },
- { "external", 'e', "COMMAND", MU_OPTION_DEFAULT,
+ { "external", 'e', "COMMAND", MU_OPTION_ARG_OPTIONAL,
"use external locker command", mu_c_void, NULL, cli_type },
MU_OPTION_GROUP ("Locking parameters"),
{ "retry", 'r', "N", MU_OPTION_DEFAULT,
"retry the lock N times",
- mu_c_uint, &retry_count },
+ mu_c_uint, &hints.retry_count },
{ "delay", 't', "SECONDS", MU_OPTION_DEFAULT,
"delay between two successive locking attempts (in seconds)",
- mu_c_uint, &retry_sleep },
+ mu_c_uint, &hints.retry_sleep },
{ "pid-check", 'p', NULL, MU_OPTION_DEFAULT,
"check if the PID of lock owner is still active",
mu_c_bool, &pid_check },
- { "expire", 'x', "SECONDS", MU_OPTION_DEFAULT,
+ { "expire", 'f', "SECONDS", MU_OPTION_DEFAULT,
"expire the lock after that many seconds",
- mu_c_uint, &expire },
+ mu_c_uint, &hints.expire_time },
MU_OPTION_GROUP ("Child operation modifiers"),
{ "hold", 'H', "SECONDS", MU_OPTION_DEFAULT,
@@ -161,6 +177,9 @@ struct mu_option options[] = {
MU_OPTION_GROUP ("Operation modifiers"),
{ "unlock", 'u', NULL, MU_OPTION_DEFAULT,
"unlock", mu_c_bool, &unlock },
+
+ { "verbose", 'v', NULL, MU_OPTION_DEFAULT,
+ "verbosely list errors", mu_c_bool, &verbose },
MU_OPTION_END
};
@@ -184,6 +203,29 @@ sighan (int sig)
}
}
+static void
+errcheck (int rc)
+{
+ switch (rc)
+ {
+ case 0:
+ break;
+
+ case MU_ERR_LOCK_CONFLICT:
+ exit (MU_DL_EX_EXIST);
+
+ case MU_ERR_LOCK_NOT_HELD:
+ exit (MU_DL_EX_NEXIST);
+
+ case EPERM:
+ case EACCES:
+ exit (MU_DL_EX_PERM);
+
+ default:
+ exit (MU_DL_EX_ERROR);
+ }
+}
+
int
main (int argc, char **argv)
{
@@ -202,26 +244,18 @@ main (int argc, char **argv)
if (argc != 1)
{
mu_error ("bad arguments; try %s --help for more info", mu_program_name);
- return 1;
+ return MU_DL_EX_ERROR;
}
file = argv[0];
- if (expire)
- flags |= MU_LOCKER_TIME;
- if (retry_count || retry_sleep)
- flags |= MU_LOCKER_RETRY;
+ if (hints.expire_time)
+ hints.flags |= MU_LOCKER_FLAG_EXPIRE_TIME;
+ if (hints.retry_count || hints.retry_sleep)
+ hints.flags |= MU_LOCKER_FLAG_RETRY;
if (pid_check)
- flags |= MU_LOCKER_PID;
+ hints.flags |= MU_LOCKER_FLAG_CHECK_PID;
- MU_ASSERT (mu_locker_create (&lck, file, flags));
- if (expire)
- mu_locker_set_expire_time (lck, expire);
- if (retry_count)
- mu_locker_set_retries (lck, retry_count);
- if (retry_sleep)
- mu_locker_set_retry_sleep (lck, retry_sleep);
- if (extlocker)
- mu_locker_set_external (lck, extlocker);
+ MU_ASSERT (mu_locker_create_ext (&lck, file, hints.flags > 0 ? &hints : NULL));
if (hold_time || abandon_lock)
{
@@ -232,14 +266,14 @@ main (int argc, char **argv)
if (pipe (p))
{
mu_diag_funcall (MU_DIAG_CRIT, "pipe", NULL, errno);
- return 1;
+ return MU_DL_EX_ERROR;
}
child_pid = fork ();
if (child_pid == -1)
{
mu_diag_funcall (MU_DIAG_CRIT, "fork", NULL, errno);
- return 1;
+ return MU_DL_EX_ERROR;
}
if (child_pid == 0)
@@ -253,8 +287,7 @@ main (int argc, char **argv)
rc = mu_locker_lock (lck);
fprintf (fp, "L%d\n", rc);
fclose (fp);
- if (rc)
- abort ();
+ errcheck (rc);
if (hold_time)
sleep (hold_time);
if (abandon_lock)
@@ -295,11 +328,13 @@ main (int argc, char **argv)
rc = mu_locker_remove_lock (lck);
else
rc = mu_locker_lock (lck);
- if (rc)
+ if (rc && verbose)
mu_diag_funcall (MU_DIAG_ERROR,
unlock ? "mu_locker_remove_lock" : "mu_locker_lock",
NULL, rc);
+ mu_locker_destroy (&lck);
+
if (child_pid > 0)
{
if (waitpid (child_pid, &child_status, WNOHANG) != child_pid)
@@ -317,19 +352,20 @@ main (int argc, char **argv)
if (status != 0)
{
mu_error ("child terminated with status %d", status);
- return 1;
+ return MU_DL_EX_ERROR;
}
}
else if (WIFSIGNALED (child_status))
{
mu_error ("child terminated on signal %d", WTERMSIG (child_status));
- return 1;
+ return MU_DL_EX_ERROR;
}
else
{
mu_error ("child terminated with unhandled status %d", child_status);
- return 1;
+ return MU_DL_EX_ERROR;
}
-
- return rc != 0;
+
+ errcheck (rc);
+ return 0;
}
diff --git a/libmailutils/tests/lock.at b/libmailutils/tests/lock.at
index 0886390b9..75fef24dd 100644
--- a/libmailutils/tests/lock.at
+++ b/libmailutils/tests/lock.at
@@ -33,15 +33,11 @@ LCK_TEST([retries], [--hold=4 --retry=10 --delay=1])
# Child holds lock for 4 seconds, master does two retries with one
# second interval and finishes before being able to acquire it.
LCK_TEST([conflict with previous locker], [--hold=4 --retry=2 --delay=1],
- [1],[],
- [lck: mu_locker_lock() failed: Conflict with previous locker
-])
+ [3])
# Child abandons the lock; master is not able to acquire it.
LCK_TEST([abandoned lock], [--abandon --retry=4 --delay=1],
- [1],[],
- [lck: mu_locker_lock() failed: Conflict with previous locker
-])
+ [3])
# Child abandons the lock; master asserts that its pid is not active and
# acquires it.
@@ -56,4 +52,88 @@ LCK_TEST([lock expiration],
# Default settings correspond to --retry=10 --delay=1 --expire=600
LCK_TEST([default settings], [--hold=2])
+AT_SETUP([external locker])
+AT_KEYWORDS([lock])
+
+AT_DATA([extlocker],
+[#!/bin/sh
+echo "$@" > extlocker.args
+])
+CWD=$(pwd)
+
+m4_pushdef([COND_SKIP],[test -f .skiptest && AT_SKIP_TEST])
+
+AT_CHECK([
+if chmod +x extlocker; then :; else touch .skiptest; fi >/dev/null 2>&1
+COND_SKIP
+],
+[ignore])
+
+AT_CHECK([COND_SKIP
+lck -e$CWD/extlocker file
+cat extlocker.args
+],
+[0],
+[file
+])
+
+AT_CHECK([COND_SKIP
+lck -e$CWD/extlocker --retry=4 --expire=35 file
+cat extlocker.args
+],
+[0],
+[-f35 -r4 file
+])
+
+AT_CHECK([COND_SKIP
+touch file
+lck -elck file
+])
+
+AT_CHECK([COND_SKIP
+lck -elck file
+],
+[3])
+
+AT_CHECK([COND_SKIP
+lck -elck -u file
+],
+[0])
+
+AT_CHECK([COND_SKIP
+lck -elck -u file
+],
+[2])
+
+# Child holds lock for 4 seconds, master acquires it when child has
+# released it.
+AT_CHECK([COND_SKIP
+lck -elck --hold=4 --retry=10 --delay=1 file
+])
+
+AT_CHECK([COND_SKIP
+lck -elck -u file
+])
+
+AT_CHECK([COND_SKIP
+# Child holds lock for 4 seconds, master does two retries with one
+# second interval and finishes before being able to acquire it.
+lck -elck --hold=4 --retry=2 --delay=1 file
+],
+[3])
+
+AT_CHECK([COND_SKIP
+# Master is not able to acquire abandoned lock.
+lck -elck --retry=4 --delay=1 file
+],
+[3])
+
+AT_CHECK([COND_SKIP
+# Master waits until lock has expired and acquires it.
+lck -elck --abandon --expire=3 --retry=10 --delay=1 file
+])
+
+m4_popdef([COND_SKIP])
+AT_CLEANUP
+
m4_popdef([LCK_TEST])

Return to:

Send suggestions and report system problems to the System administrator.