diff options
Diffstat (limited to 'libmailutils')
-rw-r--r-- | libmailutils/base/amd.c | 2 | ||||
-rw-r--r-- | libmailutils/base/locker.c | 1276 | ||||
-rw-r--r-- | libmailutils/base/spawnvp.c | 117 | ||||
-rw-r--r-- | libmailutils/cli/stdcapa.c | 106 | ||||
-rw-r--r-- | libmailutils/tests/lck.c | 126 | ||||
-rw-r--r-- | libmailutils/tests/lock.at | 92 |
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) { |