summaryrefslogtreecommitdiff
path: root/libmailutils
diff options
context:
space:
mode:
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)
{