Commit d0769b4f d0769b4ffcfab56d74f68890c14bc5cfff3b3d3b by Sergey Poznyakoff

Implement optional mailbox-independent locking for pop3d and imap4d.

* lib/manlock.c: New file.
* lib/Makefile.am (libmuaux_a_SOURCES): Add manlock.c.
* lib/muaux.h (manlock_mandatory_locking)
(manlock_lock_dir): New externs.
(manlock_open_mailbox, manlock_lock)
(manlock_touchlock, manlock_unlock): New protos.

* libmailutils/base/amd.c (amd_cleanup): Remove unbalanced
call to mu_locker_unlock.
* libmailutils/base/locker.c (mu_locker_create): Bugfixes.

* imap4d/imap4d.c (imap4d_cfg_param): Add mandatory-locking
construct.
(main): Call manlock_cfg_init.
* imap4d/select.c (imap4d_select0): Use mandatory locking if
the mailbox is to be opened r/w.

* imap4d/rename.c (imap4d_rename): Note FIXME!

* imap4d/bye.c: Call manlock_unlock after closing the mailbox.
* imap4d/close.c: Likewise.
* imap4d/sync.c (imap4d_sync): Call manlock_touchlock.

* pop3d/lock.c: Remove.
* pop3d/Makefile.am (pop3d_SOURCES): Remove lock.c
* pop3d/extra.c (pop3d_abquit): Unlock mailbox.
* pop3d/pop3d.c (pop3d_cfg_param): Add mandatory-locking
construct.
(main): Call manlock_cfg_init.
(pop3d_mainloop): Call manlock_touchlock.
* pop3d/pop3d.h (pop3d_lock)
(pop3d_touchlock, pop3d_unlock): Remove.
* pop3d/quit.c (pop3d_quit): Unlock the mailbox.
* pop3d/user.c (pop3d_begin_session): Use manlock_open_mailbox.
1 parent a9667edc
......@@ -60,6 +60,7 @@ imap4d_bye0 (int reason, struct imap4d_command *command)
imap4d_enter_critical ();
mu_mailbox_flush (mbox, 0);
mu_mailbox_close (mbox);
manlock_unlock (mbox);
mu_mailbox_destroy (&mbox);
imap4d_leave_critical ();
}
......
......@@ -50,6 +50,7 @@ imap4d_close0 (struct imap4d_command *command, imap4d_tokbuf_t tok,
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_close", NULL, status);
msg = "closing mailbox failed";
}
manlock_unlock (mbox);
mu_mailbox_destroy (&mbox);
if (msg)
......
......@@ -284,6 +284,7 @@ static struct mu_cfg_param imap4d_cfg_param[] = {
N_("Use only encrypted ident responses.") },
{ "id-fields", MU_CFG_LIST_OF(mu_cfg_string), &imap4d_id_list, 0, NULL,
N_("List of fields to return in response to ID command.") },
{ "mandatory-locking", mu_cfg_section },
{ ".server", mu_cfg_section, NULL, 0, NULL,
N_("Server configuration.") },
{ "transcript", mu_cfg_bool, &imap4d_transcript, 0, NULL,
......@@ -556,6 +557,7 @@ main (int argc, char **argv)
mu_gocs_register ("gsasl", mu_gsasl_module_init);
#endif
mu_tcpwrapper_cfg_init ();
manlock_cfg_init ();
mu_acl_cfg_init ();
mu_m_server_cfg_init ();
......
......@@ -131,7 +131,8 @@ imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok)
exist. */
if (stat (newname, &newst) == 0)
{
if (!S_ISDIR(newst.st_mode))
/* FIXME: What if it's a maildir?!? */
if (!S_ISDIR (newst.st_mode))
{
free (newname);
return io_completion_response (command, RESP_NO,
......
......@@ -30,7 +30,7 @@ imap4d_select (struct imap4d_command *command, imap4d_tokbuf_t tok)
MU_STREAM_RDWR);
}
/* This code is share with EXAMINE. */
/* This code is shared with EXAMINE. */
int
imap4d_select0 (struct imap4d_command *command, const char *mboxname,
int flags)
......@@ -49,6 +49,7 @@ imap4d_select0 (struct imap4d_command *command, const char *mboxname,
imap4d_enter_critical ();
mu_mailbox_sync (mbox);
mu_mailbox_close (mbox);
manlock_unlock (mbox);
imap4d_leave_critical ();
mu_mailbox_destroy (&mbox);
/* Destroy the old uid table. */
......@@ -62,8 +63,31 @@ imap4d_select0 (struct imap4d_command *command, const char *mboxname,
if (!mailbox_name)
return io_completion_response (command, RESP_NO, "Couldn't open mailbox");
if ((status = mu_mailbox_create_default (&mbox, mailbox_name)) == 0
&& (status = mu_mailbox_open (mbox, flags)) == 0)
if (flags & MU_STREAM_RDWR)
{
status = manlock_open_mailbox (&mbox, mailbox_name, 1, flags);
}
else
{
status = mu_mailbox_create_default (&mbox, mailbox_name);
if (status)
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_create_default",
mailbox_name,
status);
else
{
status = mu_mailbox_open (mbox, flags);
if (status)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_open",
mailbox_name,
status);
mu_mailbox_destroy (&mbox);
}
}
}
if (status == 0)
{
select_flags = flags;
state = STATE_SEL;
......
......@@ -187,6 +187,7 @@ imap4d_set_observer (mu_mailbox_t mbox)
int
imap4d_sync (void)
{
manlock_touchlock (mbox);
/* If mbox --> NULL, it means to free all the resources.
It may be because of close or before select/examine a new mailbox.
If it was a close we do not send any notification. */
......
......@@ -23,6 +23,7 @@ libmuaux_a_SOURCES += \
argp_base.c\
daemon.c\
mailcap.c\
manlock.c\
mu_dbm.c\
signal.c\
strexit.c\
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999-2011 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Mailutils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <mailutils/types.h>
#include <mailutils/errno.h>
#include <mailutils/diag.h>
#include <mailutils/locker.h>
#include <mailutils/mailbox.h>
#include <mailutils/util.h>
#include <mailutils/url.h>
#include <mailutils/stream.h>
#include <mailutils/registrar.h>
#include <mailutils/nls.h>
#include <mailutils/cfg.h>
#include "muaux.h"
/* This file implements mandatory locking for pop3d and imap4d. Mandatory
locking applies to all mailbox formats. It is enabled by the following
configuration clause:
mandatory-locking {
enable yes;
}
If the underlying mailbox mechanism does not provide a locker (as is
the case, e.g., for maildir or MH mailboxes), a new locker object is
created and attached to the mailbox. This new locker complies with
the "logging" configuration settings. The name of the corresponding
lock file is created based on the mailbox URL. It is created in
a "lock directory", which is configured by `lock-directory' clause in
the `mandatory-locking' block.
For example, a lock file for "/var/spool/mail/g/r/gray" would
be "/var/lock/subsys/mail/%2Fvar%2Fspool%2Fmail%2Fg%2Fr%2Fgray.lock".
*/
int manlock_mandatory_locking;
char *manlock_lock_dir = "/var/lock/subsys/mail";
static char *
make_locker_file_name (const char *urlstr)
{
char *fname;
char *buf;
size_t size;
const char *p;
char *q;
static const char escapable_chars[] = "/%";
static const char xchr[] = "0123456789ABCDEF";
for (p = urlstr, size = 0; *p; p++, size++)
{
if (strchr (escapable_chars, *p))
size += 2;
}
buf = malloc (size + 1);
if (!buf)
{
mu_diag_funcall (MU_DIAG_ERROR, "malloc", NULL, errno);
return NULL;
}
for (p = urlstr, q = buf; *p; p++)
{
if (strchr (escapable_chars, *p))
{
unsigned char c = *p;
*q++ = '%';
*q++ = xchr[c >> 4];
*q++ = xchr[c & 0xf];
}
else
*q++ = *p;
}
*q = 0;
fname = mu_make_file_name_suf (manlock_lock_dir, buf, NULL);
if (!fname)
mu_diag_funcall (MU_DIAG_ERROR, "mu_make_file_name_suf", buf, errno);
free (buf);
return fname;
}
static int
mailbox_open_and_lock (mu_mailbox_t mbox, int flags)
{
mu_url_t url;
int status;
const char *urlstr;
mu_locker_t lock;
mu_mailbox_get_url (mbox, &url);
urlstr = mu_url_to_string (url);
status = mu_mailbox_get_locker (mbox, &lock);
if (status)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_locker", urlstr, status);
return MU_ERR_FAILURE;
}
if (!lock && manlock_mandatory_locking && manlock_lock_dir)
{
char *fname = NULL;
int res;
if (mu_registrar_test_local_url (url, &res) == 0 && res)
{
const char *path;
status = mu_url_sget_path (url, &path);
if (status == 0)
fname = make_locker_file_name (path);
}
if (!fname)
fname = make_locker_file_name (urlstr);
if (!fname)
return MU_ERR_FAILURE;
status = mu_locker_create (&lock, fname, 0);
if (status)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create", fname, status);
free (fname);
return MU_ERR_FAILURE;
}
mu_mailbox_set_locker (mbox, lock);
}
if ((status = mu_mailbox_open (mbox, flags)) != 0)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_open", urlstr, status);
return MU_ERR_FAILURE;
}
return manlock_lock (mbox);
}
int
manlock_open_mailbox (mu_mailbox_t *pmbox, const char *mailbox_name, int def,
int flags)
{
mu_mailbox_t mbox;
int status;
status = (def ? mu_mailbox_create_default : mu_mailbox_create)
(&mbox, mailbox_name);
if (status)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_create", mailbox_name,
status);
return 1;
}
status = mailbox_open_and_lock (mbox, flags);
if (status == 0)
*pmbox = mbox;
else
mu_mailbox_destroy (&mbox);
return status;
}
struct mu_cfg_param manlock_param[] = {
{ "enable", mu_cfg_bool, &manlock_mandatory_locking, 0, NULL,
N_("Enable mandatory locking.") },
{ "lock-directory", mu_cfg_string, &manlock_lock_dir, 0, NULL,
N_("Set directory for mandatory lock files.") },
{ NULL }
};
void
manlock_cfg_init ()
{
struct mu_cfg_section *section;
mu_create_canned_section ("mandatory-locking", &section);
mu_cfg_section_add_params (section, manlock_param);
}
int
manlock_lock (mu_mailbox_t mbox)
{
mu_url_t url = NULL;
mu_locker_t lock = NULL;
const char *name;
int status;
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);
if ((status = mu_locker_lock (lock)))
{
mu_diag_output (MU_DIAG_NOTICE, _("locking mailbox `%s' failed: %s"),
name ? name : "?", mu_strerror(status));
return status;
}
return 0;
}
int
manlock_touchlock (mu_mailbox_t mbox)
{
if (manlock_mandatory_locking)
{
mu_locker_t lock = NULL;
mu_mailbox_get_locker (mbox, &lock);
mu_locker_touchlock (lock);
}
return 0;
}
int
manlock_unlock (mu_mailbox_t mbox)
{
if (manlock_mandatory_locking)
{
mu_locker_t lock = NULL;
mu_mailbox_get_locker (mbox, &lock);
mu_locker_unlock (lock);
}
return 0;
}
......@@ -18,3 +18,14 @@ int mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups);
void mu_set_signals (RETSIGTYPE (*handler) (int signo), int *sigv, int sigc);
const char *mu_strexit (int code);
extern int manlock_mandatory_locking;
extern char *manlock_lock_dir;
int manlock_open_mailbox (mu_mailbox_t *pmbox, const char *mailbox_name,
int def, int flags);
int manlock_lock (mu_mailbox_t mbox);
int manlock_touchlock (mu_mailbox_t mbox);
int manlock_unlock (mu_mailbox_t mbox);
void manlock_cfg_init (void);
......
......@@ -1361,7 +1361,6 @@ amd_cleanup (void *arg)
{
mu_mailbox_t mailbox = arg;
mu_monitor_unlock (mailbox->monitor);
mu_locker_unlock (mailbox->locker);
}
int
......
......@@ -453,26 +453,28 @@ mu_locker_create (mu_locker_t *plocker, const char *fname, int flags)
/* 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, *tmp = strdup (fname);
char *p, *new_name, *tmp = strdup (fname);
if (!tmp)
return ENOMEM;
p = strchr (tmp, '/');
p = strrchr (tmp, '/');
if (!p)
filename = tmp;
else
{
*p = 0;
err = mu_unroll_symlink (tmp, &filename);
free (tmp);
if (err)
return err;
tmp = realloc (filename, strlen (filename) + strlen (p) + 1);
if (!tmp)
{
free (filename);
return ENOMEM;
free (tmp);
return err;
}
strcat (tmp, p);
filename = tmp;
new_name = mu_make_file_name_suf (filename, p + 1, NULL);
free (tmp);
free (filename);
if (!new_name)
return ENOMEM;
filename = new_name;
}
}
else
......
......@@ -32,7 +32,6 @@ pop3d_SOURCES =\
expire.c\
extra.c\
list.c\
lock.c\
logindelay.c\
noop.c\
pop3d.c\
......
......@@ -49,9 +49,9 @@ pop3d_abquit (int reason)
/* Unlock spool */
if (state != AUTHORIZATION)
{
pop3d_unlock ();
mu_mailbox_flush (mbox, 0);
mu_mailbox_close (mbox);
manlock_unlock (mbox);
mu_mailbox_destroy (&mbox);
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2002, 2005, 2007, 2008, 2009, 2010,
2011 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Mailutils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include "pop3d.h"
int
pop3d_lock ()
{
mu_url_t url = NULL;
mu_locker_t lock = NULL;
const char *name;
int status;
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);
if ((status = mu_locker_lock (lock)))
{
mu_diag_output (MU_DIAG_NOTICE, _("locking mailbox `%s' failed: %s"),
name ? name : "?", mu_strerror(status));
return ERR_MBOX_LOCK;
}
return 0;
}
int
pop3d_touchlock ()
{
mu_locker_t lock = NULL;
mu_mailbox_get_locker (mbox, &lock);
mu_locker_touchlock (lock);
return 0;
}
int
pop3d_unlock ()
{
mu_locker_t lock = NULL;
mu_mailbox_get_locker (mbox, &lock);
mu_locker_unlock (lock);
return 0;
}
......@@ -118,6 +118,7 @@ static struct mu_cfg_param pop3d_cfg_param[] = {
#endif
{ "output-buffer-size", mu_cfg_size, &pop3d_output_bufsize, 0, NULL,
N_("Size of the output buffer.") },
{ "mandatory-locking", mu_cfg_section },
{ ".server", mu_cfg_section, NULL, 0, NULL,
N_("Server configuration.") },
{ "transcript", mu_cfg_bool, &pop3d_transcript, 0, NULL,
......@@ -269,7 +270,7 @@ pop3d_mainloop (int ifd, int ofd)
}
/* Refresh the Lock. */
pop3d_touchlock ();
manlock_touchlock (mbox);
if ((handler = pop3d_find_command (cmd)) != NULL)
status = handler (arg);
......@@ -320,6 +321,7 @@ main (int argc, char **argv)
mu_gocs_register ("tls", mu_tls_module_init);
#endif /* WITH_TLS */
mu_tcpwrapper_cfg_init ();
manlock_cfg_init ();
mu_acl_cfg_init ();
mu_m_server_cfg_init ();
......
......@@ -220,7 +220,6 @@ void pop3d_send_payload (mu_stream_t stream, mu_stream_t linestr,
extern void pop3d_bye (void);
extern int pop3d_abquit (int);
extern char *pop3d_apopuser (const char *);
extern int pop3d_lock (void);
extern void process_cleanup (void);
extern void pop3d_parse_command (char *cmd, char **pcmd, char **parg);
......@@ -232,8 +231,6 @@ extern RETSIGTYPE pop3d_child_signal (int);
extern int pop3d_stls (char *);
extern void enable_stls (void);
#endif /* WITH_TLS */
extern int pop3d_touchlock (void);
extern int pop3d_unlock (void);
extern void pop3d_outf (const char *fmt, ...) MU_PRINTFLIKE(1,2);
extern void pop3d_setio (int, int);
......
......@@ -34,13 +34,13 @@ pop3d_quit (char *arg)
if (state == TRANSACTION)
{
pop3d_unlock ();
pop3d_fix_mark ();
if (mu_mailbox_flush (mbox, 1) != 0)
err = ERR_FILE;
if (mu_mailbox_close (mbox) != 0)
err = ERR_FILE;
manlock_unlock (mbox);
mu_mailbox_destroy (&mbox);
mu_diag_output (MU_DIAG_INFO, _("session ended for user: %s"), username);
}
......
......@@ -22,7 +22,8 @@ struct mu_auth_data *auth_data;
int
pop3d_begin_session ()
{
int status;
mu_url_t url = NULL;
size_t total = 0;
mu_diag_output (MU_DIAG_INFO, _("POP3 login: user `%s', source %s"),
auth_data->name, auth_data->source);
......@@ -40,19 +41,9 @@ pop3d_begin_session ()
if (auth_data->change_uid)
setuid (auth_data->uid);
if ((status = mu_mailbox_create (&mbox, auth_data->mailbox)) != 0
|| (status = mu_mailbox_open (mbox, MU_STREAM_CREAT | MU_STREAM_RDWR)) != 0)
if (manlock_open_mailbox (&mbox, auth_data->mailbox, 0,
MU_STREAM_CREAT | MU_STREAM_RDWR))
{
mu_mailbox_destroy (&mbox);
state = AUTHORIZATION;
mu_auth_data_destroy (&auth_data);
return ERR_MBOX_LOCK;
}
if (pop3d_lock ())
{
mu_mailbox_close (mbox);
mu_mailbox_destroy (&mbox);
mu_auth_data_destroy (&auth_data);
state = AUTHORIZATION;
return ERR_MBOX_LOCK;
......@@ -70,18 +61,14 @@ pop3d_begin_session ()
deliver_pending_bulletins ();
/* mailbox name */
{
mu_url_t url = NULL;
size_t total = 0;
mu_mailbox_get_url (mbox, &url);
mu_mailbox_messages_count (mbox, &total);
mu_diag_output (MU_DIAG_INFO,
/* log mailbox stats */
mu_mailbox_get_url (mbox, &url);
mu_mailbox_messages_count (mbox, &total);
mu_diag_output (MU_DIAG_INFO,
ngettext ("user `%s' logged in with mailbox `%s' (%s message)",
"user `%s' logged in with mailbox `%s' (%s messages)",
(unsigned long) total),
username, mu_url_to_string (url), mu_umaxtostr (0, total));
}
return OK;
}
......