summaryrefslogtreecommitdiff
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
parent5daca02feaa90dcac68c03e479a0a61e51d2c7ca (diff)
downloadmailutils-master.tar.gz
mailutils-master.tar.bz2
Revise the mailbox locking functions.HEADmaster
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.
-rw-r--r--NEWS31
-rw-r--r--configure.ac2
-rw-r--r--doc/texinfo/Makefile.am3
-rw-r--r--doc/texinfo/programs.texi140
-rw-r--r--doc/texinfo/programs/dotlock.texi62
-rw-r--r--dotlock/dotlock.c28
-rw-r--r--include/mailutils/locker.h198
-rw-r--r--lib/manlock.c20
-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
-rw-r--r--libmu_dbm/berkeley.c11
-rw-r--r--libmu_sieve/extensions/vacation.c2
-rw-r--r--libproto/dotmail/dotmail.c4
-rw-r--r--libproto/mbox/mboxrd.c4
-rw-r--r--mail/copy.c7
-rw-r--r--mda/lmtpd/lmtpd.c7
-rw-r--r--mda/mda/mda.c9
-rw-r--r--mda/putmail/putmail.c7
22 files changed, 1331 insertions, 923 deletions
diff --git a/NEWS b/NEWS
index 4f0825bf2..98bc1a233 100644
--- a/NEWS
+++ b/NEWS
@@ -1,8 +1,37 @@
-GNU mailutils NEWS -- history of user-visible changes. 2021-02-13
+GNU mailutils NEWS -- history of user-visible changes. 2021-05-01
See the end of file for copying conditions.
Please send mailutils bug reports to <bug-mailutils@gnu.org>.
+Version 3.12.90 (git)
+
+* Improved mailbox locking
+
+This fixes several bugs.
+
+* Changes in the 'locking' configuration statement.
+
+New statement 'type' configures the type of locking to use. Possible
+values are: 'default', 'dotlock', 'external', 'kernel', and 'null'.
+
+New statement 'pid-check' instructs mailutils to check if the PID of
+the process owning the lock still exists in the process table. If it
+doesn't the lock is considered stale and is removed.
+
+The 'flags' statement is deprecated. If used, an error message is
+issued along with a suggestion on what statement to use instead:
+
+ Flag Statement to use
+ --------+-------------------
+ E type external
+ R retry-count N
+ T expire-timeout N
+ P pid-check true
+
+The 'retry-timeout' statement is a misnomer and is therefore
+deprecated. Use 'retry-sleep' instead.
+
+
Version 3.12, 2021-02-13
* Fix expunging of mailboxes in mbox and dotmail format in a read-only directory
diff --git a/configure.ac b/configure.ac
index 479360a99..a6f361444 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,7 +16,7 @@ dnl You should have received a copy of the GNU General Public License along
dnl with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ(2.63)
-AC_INIT([GNU Mailutils], [3.12], [bug-mailutils@gnu.org], [mailutils],
+AC_INIT([GNU Mailutils], [3.12.90], [bug-mailutils@gnu.org], [mailutils],
[http://mailutils.org])
AC_CONFIG_SRCDIR([libmailutils/mailbox/mailbox.c])
AC_CONFIG_AUX_DIR([build-aux])
diff --git a/doc/texinfo/Makefile.am b/doc/texinfo/Makefile.am
index 92a3c4d5c..186415d0d 100644
--- a/doc/texinfo/Makefile.am
+++ b/doc/texinfo/Makefile.am
@@ -44,7 +44,8 @@ programs_TEXINFOS = \
programs/pop3d.texi\
programs/imap4d.texi\
programs/comsatd.texi\
- programs/mailutils.texi
+ programs/mailutils.texi\
+ programs/dotlock.texi
clean-local:
rm -rf manual
diff --git a/doc/texinfo/programs.texi b/doc/texinfo/programs.texi
index 5e4a07694..4ce1526a0 100644
--- a/doc/texinfo/programs.texi
+++ b/doc/texinfo/programs.texi
@@ -46,6 +46,7 @@ syntax.
* mh:: The MH Message Handling System.
* mailutils:: The Mailutils Multi-Purpose Tool.
+* dotlock:: The External Locker Utility.
@end menu
@node command line
@@ -1281,74 +1282,128 @@ pattern list, which contains:
@example
locking @{
# @r{Default locker flags.}
- flags @var{arg};
-
- # @r{Set timeout for acquiring the lock.}
- retry-timeout @var{arg};
+ type @code{default} | @code{dotlock} | @code{external} | @code{kernel} | @code{null};
# @r{Set the maximum number of times to retry acquiring the lock.}
retry-count @var{number};
+ # @r{Set the delay between two successive locking attempts.}
+ retry-sleep @var{arg};
+
# @r{Expire locks older than this amount of time.}
expire-timeout @var{number};
-
+
+ # @r{Check if PID of the lock owner is active, steal the lock if it not.}
+ pid-check @var{bool};
+
# @r{Use @var{prog} as external locker program.}
external-locker @var{prog};
@}
@end example
@subheading Description
-This block statement configures various parameters used when locking
+This compound statement configures various parameters used when locking
UNIX mailboxes in order to prevent simultaneous writes.
-It is important to note, that locking applies only to traditional
-UNIX mailboxes (@pxref{mbox}). All other
-mailbox types don't require locking.
+It is important to note, that locking applies only to monolithic
+mailboxes, i.e. mailboxes of @samp{mbox} and @samp{dotmail} types
+(@pxref{mbox}). Other mailbox types don't require locking.
-@deffn {Configuration} flags @var{string}
-Set locking flags. Argument is a string consisting of one or more of
-the following letters:
+@deffn {Configuration} type @var{string}
+Set locking type. Allowed arguments are:
-@table @asis
-@item E
-Use an external program to manage locks. The program is given by the
-@code{external-locker} statement (see below).
-
-@item R
-If the locking attempt failed, retry it. This is the default. The
-number of retries, and time interval between the two successive
-attempts is given by @code{retry-count} and @code{retry-timeout}
-statements, correspondingly.
-
-@item T
-If a lock file exists, check its modification time and, if it is
-older than a predefined amount of time, remove the lock. The amount
-of time is specified by @code{expire-timeout} statement.
-
-@item P
-Store the PID of the locking process in a lock file.
+@table @code
+@item default
+Default locking type. As of @command{mailutils} version
+@value{VERSION}, this is equivalent to @code{dotlock}.
+
+@item dotlock
+@anchor{dotlock locking type}
+A @samp{dotlock}-style locking. To lock a mailbox named @var{X}
+a @dfn{lock file} named @file{@var{X}.lock} is created. If
+@code{pid-check yes} is set, this file will contain the PID of the
+locking process, so that another process wishing to acquire the lock
+could verify if the lock is still in use.
+
+@item external
+@anchor{external locking type}
+Run external program to perform locking/unlocking operations. The
+name of the program is given by the @code{external-locker} statement
+(see below). If it is not given, the built-in default @samp{dotlock}
+is used.
+
+The locker program is invoked as follows:
+
+@example
+# To lock @var{mbox}:
+@var{locker} -f@var{expire_timeout} -r@var{retry_count} @var{mbox}
+# To unlock it:
+@var{locker} -u -f@var{expire_timeout} -r@var{retry_count} @var{mbox}
+@end example
+
+@noindent
+Here, @var{expire_timeout} is the value supplied with the
+@code{expire-timeout} configuration statement, and @var{retry_count}
+is the value supplied with the @code{retry-count} statement (see below).
+
+To properly interact with @command{mailutils}, the external locker
+program must use the following exit codes:
+
+@multitable @columnfractions 0.3 0.6
+@headitem Exit code @tab Meaning
+@item 0 @tab Success.
+@item 1 @tab Failed due to an error.
+@item 2 @tab Unlock requested (@option{-u}), but file is not locked.
+@item 3 @tab Lock requested, but file is already locked.
+@item 4 @tab Insufficient permissions.
+@end multitable
+
+@xref{dotlock}, for the description of the default external locker,
+shipped with mailutils.
+
+@item kernel
+Use kernel locking mechanism (@code{fcntl}(2)).
+
+@item null
+No locking at all. The statements below are silently ignored.
@end table
@end deffn
@deffn {Configuration} retry-count @var{number}
-Number of locking attempts. The @samp{P} flag must be set for this to
-take effect.
+Number of locking attempts. The default is 10.
@end deffn
-@deffn {Configuration} retry-timeout @var{seconds}
-Time interval, in seconds, between the two successive locking
-attempts. The @samp{P} flag must be set for this to take effect.
+@deffn {Configuration} retry-sleep @var{seconds}
+@deffnx {Configuration} retry-timeout @var{seconds}
+Time interval, in seconds, between two successive locking
+attempts. The default is 1 second. The @code{retry-timeout}
+statement is deprecated because of its misleading name.
@end deffn
@deffn {Configuration} expire-timeout @var{seconds}
-Remove existing lock file, if it is created more than this number of
-seconds ago. The @samp{T} flag must be set for this to take effect.
+Sets the expiration timeout. The existing lock file will be removed,
+if it was created more than this number of seconds ago. The default
+is 600.
+@end deffn
+
+@deffn {Configuration} pid-check @var{bool}
+This statement can be used if locking type is set to @code{dotlock}.
+If set to @code{true}, it instructs the locking algorithm to check
+if the PID of the lock owner is still running by the time when it
+tries to acquire the lock. This works as follows. When the lock file
+is created, the PID of the creating process is written to it. If
+another process tries to acquire the lock and sees that the lock file
+already exists, it reads the PID from the file and checks if a process
+with that PID still exists in the process table. If it does not, the
+process considers the lock file to be stale, removes it and locks the
+mailbox.
@end deffn
@deffn {Configuration} external-locker @var{string}
-Determines the external locker program to use. The @var{string}
-argument is the valid command line, starting with the full program
-name. The @samp{E} flag must be set for this to take effect.
+Sets the name of the external locker program to use, instead of the
+default @samp{dotlock}.
+
+This statement is in effect only when used together with @code{type external}.
@end deffn
@node mailer statement
@@ -2990,3 +3045,8 @@ Show payload information
@node mailutils
@section mailutils
@include programs/mailutils.texi
+
+@page
+@node dotlock
+@section dotlock
+@include programs/dotlock.texi
diff --git a/doc/texinfo/programs/dotlock.texi b/doc/texinfo/programs/dotlock.texi
new file mode 100644
index 000000000..2f7be2e8a
--- /dev/null
+++ b/doc/texinfo/programs/dotlock.texi
@@ -0,0 +1,62 @@
+@pindex dotlock
+
+A stand-alone mailbox-locking utility. It is the default program used
+by @command{mailutils} if the @code{locking.type} configuration
+statement is set to @code{external} (@pxref{external locking type}).
+
+The program usage syntax is:
+
+@example
+@group
+# To lock @var{mbox}:
+dotlock @var{options} @var{mbox}
+# To unlock it:
+dotlock -u @var{options} @var{mbox}
+@end group
+@end example
+
+By default the program implements the @samp{dotlock} locking
+(@pxref{dotlock locking type}). This can be changed either in the
+configuration file, or via the command line options.
+
+The following common configuration statements affect the behavior of
+@command{dotlock}:
+
+@multitable @columnfractions 0.3 0.6
+@headitem Statement @tab Reference
+@item debug @tab @xref{Debug Statement}.
+@item locking @tab @xref{Locking Statement}.
+@end multitable
+
+The program understands the following command line options:
+
+@table @option
+@item -d
+@itemx --debug
+Print details of failure reasons to stderr.
+
+@item -f[@var{n}]
+@itemx --force[=@var{n}]
+If a lock file exists and is more than @var{n} minutes old, forcibly
+remove it and re-lock the mailbox. Default @var{n} is 10 minutes.
+
+@item -p
+@itemx --pid-check
+Check if the PID of lock owner is still active. If not, break the
+lock.
+
+@item -r @var{n}
+@item --retry=@var{n}
+Number of times to retry acquiring the lock, if it is held by another
+process. The default is 10 times.
+
+@item -t @var{n}
+@itemx --delay=@var{n}
+Sets delay in seconds between two successive locking attempts.
+The default is 1 second.
+
+@item -u
+@itemx --unlock
+Unlock the mailbox.
+@end table
+
diff --git a/dotlock/dotlock.c b/dotlock/dotlock.c
index 1bb11590b..303f6d943 100644
--- a/dotlock/dotlock.c
+++ b/dotlock/dotlock.c
@@ -28,7 +28,6 @@
static const char *file;
static int unlock;
-static int flags;
static unsigned retries;
static unsigned force;
static int debug;
@@ -119,6 +118,7 @@ int
main (int argc, char *argv[])
{
mu_locker_t locker = 0;
+ mu_locker_hints_t hints = { .flags = 0 };
int err = 0;
pid_t usergid = getgid ();
pid_t mailgid = getegid ();
@@ -149,31 +149,27 @@ main (int argc, char *argv[])
if (force)
{
- force *= 60;
- flags |= MU_LOCKER_TIME;
+ hints.flags |= MU_LOCKER_FLAG_EXPIRE_TIME;
+ hints.expire_time = force * 60;
}
- if (retries || retry_sleep)
- flags |= MU_LOCKER_RETRY;
+ if (retries)
+ {
+ hints.flags |= MU_LOCKER_FLAG_RETRY;
+ hints.retry_count = retries;
+ hints.retry_sleep = retry_sleep;
+ }
if (pid_check)
- flags |= MU_LOCKER_PID;
+ hints.flags |= MU_LOCKER_FLAG_CHECK_PID;
- if ((err = mu_locker_create (&locker, file, flags)))
+ if ((err = mu_locker_create_ext (&locker, file, hints.flags != 0 ? &hints : NULL)))
{
if (debug)
- mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create", NULL, err);
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create_ext", NULL, err);
return MU_DL_EX_ERROR;
}
- if (force != 0)
- mu_locker_set_expire_time (locker, force);
-
- if (retries)
- mu_locker_set_retries (locker, retries);
- if (retry_sleep)
- mu_locker_set_retry_sleep (locker, retry_sleep);
-
if (setegid (mailgid) < 0)
return MU_DL_EX_ERROR;
diff --git a/include/mailutils/locker.h b/include/mailutils/locker.h
index 21aed9de9..082bbd942 100644
--- a/include/mailutils/locker.h
+++ b/include/mailutils/locker.h
@@ -25,105 +25,56 @@ extern "C" {
#endif
/* lock expiry time */
-#define MU_LOCKER_EXPIRE_TIME (10 * 60)
-#define MU_LOCKER_RETRIES (10)
-#define MU_LOCKER_RETRY_SLEEP (1)
-#define MU_LOCKER_EXTERNAL_PROGRAM "dotlock"
+#define MU_LOCKER_DEFAULT_EXPIRE_TIME (10 * 60)
+#define MU_LOCKER_DEFAULT_RETRY_COUNT 10
+#define MU_LOCKER_DEFAULT_RETRY_SLEEP 1
+#define MU_LOCKER_DEFAULT_EXT_LOCKER "dotlock"
/* return codes for the external locker */
-#define MU_DL_EX_PERM 4 /* insufficient permissions */
-#define MU_DL_EX_EXIST 3 /* lock requested, but file is already locked */
-#define MU_DL_EX_NEXIST 2 /* unlock requested, but file is not locked */
-#define MU_DL_EX_ERROR 1 /* failed due to some other error */
-#define MU_DL_EX_OK 0 /* success */
-
-enum mu_locker_set_mode
+enum
{
- mu_locker_assign,
- mu_locker_set_bit,
- mu_locker_clear_bit
+ MU_DL_EX_OK = 0, /* success */
+ MU_DL_EX_ERROR = 1, /* failed due to some other error */
+ MU_DL_EX_NEXIST = 2, /* unlock requested, but file is not locked */
+ MU_DL_EX_EXIST = 3, /* lock requested, but file is already locked */
+ MU_DL_EX_PERM = 4, /* insufficient permissions */
};
-
-/* mu_locker_create() flags */
/* Locker types */
+enum
+ {
+ MU_LOCKER_TYPE_DOTLOCK = 0, /* Dotlock-style locking. The default. */
+ MU_LOCKER_TYPE_EXTERNAL = 1, /* Use external program to lock the file. */
+ MU_LOCKER_TYPE_KERNEL = 2, /* Use kernel locking (flock,lockf,ioctl) */
+ MU_LOCKER_TYPE_NULL = 3, /* No locking at all. */
+ };
-#define MU_LOCKER_TYPE_DOTLOCK 0
-#define MU_LOCKER_TYPE_EXTERNAL 1
- /* Use an external program to lock the file. This is necessary
- for programs having permission to access a file, but do not
- have write permission on the directory that contains that file. */
-#define MU_LOCKER_TYPE_KERNEL 2
- /* Use kernel locking (flock, lockf or ioctl) */
-#define MU_LOCKER_TYPE_NULL 3
- /* Special locker type: means no lock. This is to be used with
- temporary mailboxes stored in memory. */
-
-#define MU_LOCKER_TYPE_TO_FLAG(t) ((t) << 8)
-#define MU_LOCKER_FLAG_TO_TYPE(f) ((f) >> 8)
-#define MU_LOCKER_IS_TYPE(f,t) (MU_LOCKER_FLAG_TO_TYPE(f) == (t))
-#define MU_LOCKER_SET_TYPE(f,t) ((f) = MU_LOCKER_TYPE_TO_FLAG(t) | MU_LOCKER_OPTIONS(f))
-#define MU_LOCKER_TYPE_MASK 0xff00
-#define MU_LOCKER_OPTION_MASK 0x00ff
-#define MU_LOCKER_OPTIONS(f) ((f) & MU_LOCKER_OPTION_MASK)
+#define MU_LOCKER_TYPE_DEFAULT MU_LOCKER_TYPE_DOTLOCK
-#define MU_LOCKER_NULL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_NULL)
-#define MU_LOCKER_DOTLOCK MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_DOTLOCK)
-#define MU_LOCKER_EXTERNAL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_EXTERNAL)
-#define MU_LOCKER_KERNEL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_KERNEL)
-
-/* Options */
-
-#define MU_LOCKER_SIMPLE 0x0000
- /* Just try and dotlock the file, not the default because its usually
- better to retry. */
-#define MU_LOCKER_RETRY 0x0001
- /* This requests that we loop retries times, sleeping retry_sleep
- seconds in between trying to obtain the lock before failing with
- MU_LOCK_CONFLICT. */
-#define MU_LOCKER_TIME 0x0002
- /* This mode checks the last update time of the lock, then removes
- it if older than MU_LOCKER_EXPIRE_TIME. If a client uses this,
- then the servers better periodically update the lock on the
- file... do they? */
-#define MU_LOCKER_PID 0x0004
- /* PID locking is only useful for programs that aren't using
- an external dotlocker, non-setgid programs will use a dotlocker,
- which locks and exits imediately. This is a protection against
- a server crashing, it's not generally useful. */
+typedef struct
+{
+ int flags;
+ int type;
+ unsigned retry_count;
+ unsigned retry_sleep;
+ unsigned expire_time;
+ char *ext_locker;
+} mu_locker_hints_t;
+
+/* Locker hint flags */
+#define MU_LOCKER_FLAG_RETRY 0x0001 /* retry_count and retry_sleep are set */
+#define MU_LOCKER_FLAG_EXPIRE_TIME 0x0002 /* expire_time is set */
+#define MU_LOCKER_FLAG_CHECK_PID 0x0004 /* check if lock owner PID is active */
+#define MU_LOCKER_FLAG_EXT_LOCKER 0x0008 /* ext_locker is set */
+#define MU_LOCKER_FLAG_TYPE 0x0010 /* type is set */
+
+#define MU_LOCKER_FLAGS_ALL (\
+ MU_LOCKER_FLAG_TYPE | \
+ MU_LOCKER_FLAG_RETRY | \
+ MU_LOCKER_FLAG_EXPIRE_TIME | \
+ MU_LOCKER_FLAG_EXT_LOCKER | \
+ MU_LOCKER_FLAG_CHECK_PID )
-#define MU_LOCKER_DEFAULT (MU_LOCKER_DOTLOCK | MU_LOCKER_RETRY)
-
-/* Use these flags for as the default locker flags (the default defaults
- * to MU_LOCKER_DEFAULT). A flags of 0 resets the flags back to the
- * the default.
- */
-extern int mu_locker_set_default_flags (int flags, enum mu_locker_set_mode mode);
-extern void mu_locker_set_default_retry_timeout (time_t to);
-extern void mu_locker_set_default_retry_count (size_t n);
-extern void mu_locker_set_default_expire_timeout (time_t t);
-extern int mu_locker_set_default_external_program (char const *path);
-
-/* A flags of 0 means that the default will be used. */
-extern int mu_locker_create (mu_locker_t *, const char *filename, int flags);
-extern void mu_locker_destroy (mu_locker_t *);
-
-/* Time is measured in seconds. */
-
-extern int mu_locker_set_flags (mu_locker_t, int);
-extern int mu_locker_mod_flags (mu_locker_t locker, int flags,
- enum mu_locker_set_mode mode);
-extern int mu_locker_set_expire_time (mu_locker_t, int);
-extern int mu_locker_set_retries (mu_locker_t, int);
-extern int mu_locker_set_retry_sleep (mu_locker_t, int);
-extern int mu_locker_set_external (mu_locker_t, const char* program);
-
-extern int mu_locker_get_flags (mu_locker_t, int*);
-extern int mu_locker_get_expire_time (mu_locker_t, int*);
-extern int mu_locker_get_retries (mu_locker_t, int*);
-extern int mu_locker_get_retry_sleep (mu_locker_t, int*);
-extern int mu_locker_get_external (mu_locker_t, char**);
-
enum mu_locker_mode
{
mu_lck_shr, /* Shared (advisory) lock */
@@ -132,12 +83,79 @@ enum mu_locker_mode
locking otherwise */
};
+#define MU_LOCKFILE_MODE 0644
+
+extern int mu_locker_create_ext (mu_locker_t *, const char *, mu_locker_hints_t *);
+extern int mu_locker_modify (mu_locker_t, mu_locker_hints_t *);
+extern void mu_locker_destroy (mu_locker_t *);
+
extern int mu_locker_lock_mode (mu_locker_t, enum mu_locker_mode);
extern int mu_locker_lock (mu_locker_t);
extern int mu_locker_touchlock (mu_locker_t);
extern int mu_locker_unlock (mu_locker_t);
extern int mu_locker_remove_lock (mu_locker_t);
+extern int mu_locker_get_hints (mu_locker_t lck, mu_locker_hints_t *hints);
+
+extern mu_locker_hints_t mu_locker_defaults;
+
+/*
+ * Deprecated defines and interfaces.
+ */
+
+/* Legacy definitions for locker defaults */
+#define MU_LOCKER_EXPIRE_TIME MU_LOCKER_DEFAULT_EXPIRE_TIME
+#define MU_LOCKER_RETRIES MU_LOCKER_DEFAULT_RETRY_COUNT
+#define MU_LOCKER_RETRY_SLEEP MU_LOCKER_DEFAULT_RETRY_SLEEP
+#define MU_LOCKER_EXTERNAL_PROGRAM MU_LOCKER_DEFAULT_EXT_LOCKER
+
+/* Legacy definitions of locker types */
+#define MU_LOCKER_DOTLOCK (MU_LOCKER_TYPE_DOTLOCK << 8)
+#define MU_LOCKER_EXTERNAL (MU_LOCKER_TYPE_EXTERNAL << 8)
+#define MU_LOCKER_KERNEL (MU_LOCKER_TYPE_KERNEL << 8)
+#define MU_LOCKER_NULL (MU_LOCKER_TYPE_NULL << 8)
+
+/* Legacy definitions of locker flags (a.k.a. options). */
+#define MU_LOCKER_SIMPLE 0x0000
+#define MU_LOCKER_RETRY MU_LOCKER_FLAG_RETRY
+#define MU_LOCKER_TIME MU_LOCKER_FLAG_EXPIRE_TIME
+#define MU_LOCKER_PID MU_LOCKER_FLAG_CHECK_PID
+
+#define MU_LOCKER_DEFAULT (MU_LOCKER_DOTLOCK | MU_LOCKER_RETRY)
+
+/* The following was used to pack/unpack flags and locker type: */
+#define MU_LOCKER_TYPE_TO_FLAG(t) ((t) << 8)
+#define MU_LOCKER_FLAG_TO_TYPE(f) ((f) >> 8)
+#define MU_LOCKER_TYPE_MASK 0xff00
+#define MU_LOCKER_OPTION_MASK 0x00ff
+
+enum mu_locker_set_mode
+ {
+ mu_locker_assign,
+ mu_locker_set_bit,
+ mu_locker_clear_bit
+ };
+
+extern int mu_locker_create (mu_locker_t *, const char *, int) MU_DEPRECATED;
+
+extern int mu_locker_set_default_flags (int, enum mu_locker_set_mode) MU_DEPRECATED;
+extern void mu_locker_set_default_retry_timeout (time_t) MU_DEPRECATED;
+extern void mu_locker_set_default_retry_count (size_t) MU_DEPRECATED;
+extern void mu_locker_set_default_expire_timeout (time_t) MU_DEPRECATED;
+extern int mu_locker_set_default_external_program (char const *) MU_DEPRECATED;
+
+extern int mu_locker_set_flags (mu_locker_t, int) MU_DEPRECATED;
+extern int mu_locker_mod_flags (mu_locker_t, int, enum mu_locker_set_mode) MU_DEPRECATED;
+extern int mu_locker_set_expire_time (mu_locker_t, int) MU_DEPRECATED;
+extern int mu_locker_set_retries (mu_locker_t, int) MU_DEPRECATED;
+extern int mu_locker_set_retry_sleep (mu_locker_t, int) MU_DEPRECATED;
+extern int mu_locker_set_external (mu_locker_t, const char *) MU_DEPRECATED;
+
+extern int mu_locker_get_flags (mu_locker_t, int *) MU_DEPRECATED;
+extern int mu_locker_get_expire_time (mu_locker_t, int *) MU_DEPRECATED;
+extern int mu_locker_get_retries (mu_locker_t, int *) MU_DEPRECATED;
+extern int mu_locker_get_retry_sleep (mu_locker_t, int *) MU_DEPRECATED;
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/manlock.c b/lib/manlock.c
index 0c32115cc..e06387807 100644
--- a/lib/manlock.c
+++ b/lib/manlock.c
@@ -128,14 +128,17 @@ mailbox_open_and_lock (mu_mailbox_t mbox, int flags)
if (lock)
{
- status = mu_locker_get_flags (lock, &flags);
+ mu_locker_hints_t hints;
+
+ hints.flags = MU_LOCKER_FLAG_TYPE;
+ status = mu_locker_get_hints (lock, &hints);
if (status)
{
- mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_get_flags", urlstr,
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_get_hints", urlstr,
status);
return MU_ERR_FAILURE;
}
- if (flags & MU_LOCKER_NULL)
+ if (hints.type == MU_LOCKER_TYPE_NULL)
lock = NULL;
}
@@ -157,10 +160,10 @@ mailbox_open_and_lock (mu_mailbox_t mbox, int flags)
if (!fname)
return MU_ERR_FAILURE;
- status = mu_locker_create (&lock, fname, 0);
+ status = mu_locker_create_ext (&lock, fname, NULL);
if (status)
{
- mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create", fname, status);
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create_ext", fname, status);
free (fname);
return MU_ERR_FAILURE;
}
@@ -245,13 +248,14 @@ manlock_lock (mu_mailbox_t mbox)
mu_locker_t lock = NULL;
const char *name;
int status;
-
+ mu_locker_hints_t hints = { .flags = MU_LOCKER_FLAG_CHECK_PID };
+
if (!manlock_mandatory_locking)
return 0;
mu_mailbox_get_url (mbox, &url);
name = mu_url_to_string (url);
- mu_mailbox_get_locker (mbox, &lock);
- mu_locker_mod_flags (lock, MU_LOCKER_PID, mu_locker_set_bit);
+ mu_mailbox_get_locker (mbox, &lock);
+ mu_locker_modify (lock, &hints);
if ((status = mu_locker_lock (lock)))
{
mu_diag_output (MU_DIAG_NOTICE, _("locking mailbox `%s' failed: %s"),
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])
diff --git a/libmu_dbm/berkeley.c b/libmu_dbm/berkeley.c
index d14082f59..7651980fc 100644
--- a/libmu_dbm/berkeley.c
+++ b/libmu_dbm/berkeley.c
@@ -69,8 +69,9 @@ static int
do_bdb_open (mu_dbm_file_t mdb, int flags, int mode)
{
struct bdb_file *bdb_file = mdb->db_descr;
- int f, rc, locker_flags;
+ int f, rc;
enum mu_locker_mode locker_mode;
+ mu_locker_hints_t hints = { .flags = MU_LOCKER_FLAG_RETRY };
int tfd = -1;
switch (flags)
@@ -96,12 +97,10 @@ do_bdb_open (mu_dbm_file_t mdb, int flags, int mode)
#ifdef DB_FCNTL_LOCKING
f |= DB_FCNTL_LOCKING;
- locker_flags = MU_LOCKER_KERNEL;
-#else
- locker_flags = 0;
+ hints.flags |= MU_LOCKER_FLAG_TYPE;
+ hints.type = MU_LOCKER_TYPE_KERNEL;
#endif
- rc = mu_locker_create (&bdb_file->locker, mdb->db_name,
- locker_flags|MU_LOCKER_RETRY);
+ rc = mu_locker_create_ext (&bdb_file->locker, mdb->db_name, &hints);
if (rc)
return rc;
diff --git a/libmu_sieve/extensions/vacation.c b/libmu_sieve/extensions/vacation.c
index 91887e258..aaf71d734 100644
--- a/libmu_sieve/extensions/vacation.c
+++ b/libmu_sieve/extensions/vacation.c
@@ -365,7 +365,7 @@ check_db (mu_sieve_machine_t mach, char *from)
mu_sieve_abort (mach);
}
- rc = mu_locker_create (&locker, file, 0);
+ rc = mu_locker_create_ext (&locker, file, NULL);
if (rc)
{
mu_sieve_error (mach, _("%lu: cannot lock %s: %s"),
diff --git a/libproto/dotmail/dotmail.c b/libproto/dotmail/dotmail.c
index 533032032..56ae0a9f6 100644
--- a/libproto/dotmail/dotmail.c
+++ b/libproto/dotmail/dotmail.c
@@ -119,11 +119,11 @@ dotmail_open (mu_mailbox_t mailbox, int flags)
if (mailbox->locker == NULL
&& (flags & (MU_STREAM_WRITE | MU_STREAM_APPEND | MU_STREAM_CREAT)))
{
- rc = mu_locker_create (&mailbox->locker, dmp->name, 0);
+ rc = mu_locker_create_ext (&mailbox->locker, dmp->name, NULL);
if (rc)
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("%s:%s (%s): %s",
- __func__, "mu_locker_create", dmp->name,
+ __func__, "mu_locker_create_ext", dmp->name,
mu_strerror (rc)));
}
diff --git a/libproto/mbox/mboxrd.c b/libproto/mbox/mboxrd.c
index aec3ddf9b..ccf3f186f 100644
--- a/libproto/mbox/mboxrd.c
+++ b/libproto/mbox/mboxrd.c
@@ -126,11 +126,11 @@ mboxrd_open (mu_mailbox_t mailbox, int flags)
&& mailbox->locker == NULL
&& (flags & (MU_STREAM_WRITE | MU_STREAM_APPEND | MU_STREAM_CREAT)))
{
- rc = mu_locker_create (&mailbox->locker, dmp->name, 0);
+ rc = mu_locker_create_ext (&mailbox->locker, dmp->name, NULL);
if (rc)
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("%s:%s (%s): %s",
- __func__, "mu_locker_create", dmp->name,
+ __func__, "mu_locker_create_ext", dmp->name,
mu_strerror (rc)));
}
diff --git a/mail/copy.c b/mail/copy.c
index 0d29bc679..8a2d2771c 100644
--- a/mail/copy.c
+++ b/mail/copy.c
@@ -97,6 +97,10 @@ append_to_file (char const *filename, msgset_t *msglist, int mark,
mu_stream_t ostr, mstr;
mu_message_t msg;
mu_locker_t locker;
+ mu_locker_hints_t hints = {
+ .flags = MU_LOCKER_FLAG_TYPE | MU_LOCKER_FLAG_RETRY,
+ .type = MU_LOCKER_TYPE_KERNEL
+ };
mu_stream_stat_buffer stat;
status = mu_file_stream_create (&ostr, filename,
@@ -108,8 +112,7 @@ append_to_file (char const *filename, msgset_t *msglist, int mark,
return 1;
}
- status = mu_locker_create (&locker, filename,
- MU_LOCKER_KERNEL|MU_LOCKER_RETRY);
+ status = mu_locker_create_ext (&locker, filename, &hints);
if (status)
{
mu_error (_("Cannot create locker %s: %s"),
diff --git a/mda/lmtpd/lmtpd.c b/mda/lmtpd/lmtpd.c
index 8e64eb5a7..6fae4a9db 100644
--- a/mda/lmtpd/lmtpd.c
+++ b/mda/lmtpd/lmtpd.c
@@ -183,10 +183,9 @@ main (int argc, char **argv)
MU_APP_INIT_NLS ();
/* Default locker settings */
- mu_locker_set_default_flags (MU_LOCKER_PID|MU_LOCKER_RETRY,
- mu_locker_assign);
- mu_locker_set_default_retry_timeout (1);
- mu_locker_set_default_retry_count (300);
+ mu_locker_defaults.flags = MU_LOCKER_FLAG_CHECK_PID | MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_sleep = 1;
+ mu_locker_defaults.retry_count = 300;
/* Register needed modules */
MU_AUTH_REGISTER_ALL_MODULES ();
diff --git a/mda/mda/mda.c b/mda/mda/mda.c
index d96ae03d4..76f4fc762 100644
--- a/mda/mda/mda.c
+++ b/mda/mda/mda.c
@@ -95,11 +95,10 @@ main (int argc, char **argv)
/* Native Language Support */
MU_APP_INIT_NLS ();
- /* Default locker settings */
- mu_locker_set_default_flags (MU_LOCKER_PID|MU_LOCKER_RETRY,
- mu_locker_assign);
- mu_locker_set_default_retry_timeout (1);
- mu_locker_set_default_retry_count (300);
+ /* Set locker defaults. */
+ mu_locker_defaults.flags = MU_LOCKER_FLAG_CHECK_PID | MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_sleep = 1;
+ mu_locker_defaults.retry_count = 300;
/* Register needed modules */
MU_AUTH_REGISTER_ALL_MODULES ();
diff --git a/mda/putmail/putmail.c b/mda/putmail/putmail.c
index 3fa6a57ea..8174a68b6 100644
--- a/mda/putmail/putmail.c
+++ b/mda/putmail/putmail.c
@@ -44,10 +44,9 @@ main (int argc, char **argv)
MU_APP_INIT_NLS ();
/* Default locker settings */
- mu_locker_set_default_flags (MU_LOCKER_PID|MU_LOCKER_RETRY,
- mu_locker_assign);
- mu_locker_set_default_retry_timeout (1);
- mu_locker_set_default_retry_count (300);
+ mu_locker_defaults.flags = MU_LOCKER_FLAG_CHECK_PID | MU_LOCKER_FLAG_RETRY;
+ mu_locker_defaults.retry_sleep = 1;
+ mu_locker_defaults.retry_count = 300;
/* Register needed modules */
MU_AUTH_REGISTER_ALL_MODULES ();

Return to:

Send suggestions and report system problems to the System administrator.