/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999-2021 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 . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include "mailutils/cli.h"
static const char *file;
static int unlock;
static unsigned retries;
static unsigned force;
static int debug;
static unsigned retry_sleep = 0;
static int pid_check;
static void
cli_force (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
if (arg)
{
int rc;
char *errmsg;
rc = mu_str_to_c (arg, opt->opt_type, opt->opt_ptr, &errmsg);
if (rc)
{
if (opt->opt_long)
mu_parseopt_error (po, "--%s: %s", opt->opt_long,
errmsg ? errmsg : mu_strerror (rc));
else
mu_parseopt_error (po, "-%c: %s", opt->opt_short,
errmsg ? errmsg : mu_strerror (rc));
free (errmsg);
exit (po->po_exit_error);
}
}
else
*(unsigned*)opt->opt_ptr = 1;
}
static struct mu_option dotlock_options[] = {
{ "unlock", 'u', NULL, MU_OPTION_DEFAULT,
N_("unlock"),
mu_c_bool, &unlock },
{ "force", 'f', N_("MINUTES"), MU_OPTION_ARG_OPTIONAL,
N_("forcibly break an existing lock older than a certain time"),
mu_c_uint, &force, cli_force },
{ "retry", 'r', N_("RETRIES"), MU_OPTION_DEFAULT,
N_("retry the lock a few times"),
mu_c_uint, &retries },
{ "delay", 't', N_("SECONDS"), MU_OPTION_DEFAULT,
N_("delay between two successive locking attempts (in seconds)"),
mu_c_uint, &retry_sleep },
{ "pid-check", 'p', NULL, MU_OPTION_DEFAULT,
N_("check if the PID of lock owner is still active"),
mu_c_bool, &pid_check },
{ "debug", 'd', NULL, MU_OPTION_DEFAULT,
N_("print details of failure reasons to stderr"),
mu_c_bool, &debug },
MU_OPTION_END
}, *options[] = { dotlock_options, NULL };
struct mu_cfg_param dotlock_cfg_param[] = {
{ "force", mu_c_time, &force, 0, NULL,
N_("Forcibly break an existing lock older than the specified time.") },
{ "debug", mu_c_bool, &debug, 0, NULL,
N_("Print details of failure reasons to stderr.") },
{ NULL }
};
static struct mu_cli_setup cli = {
options,
dotlock_cfg_param,
N_("GNU dotlock -- lock mail spool files."),
N_("FILE"),
NULL,
N_("Returns 0 on success, 3 if locking the file fails because\
it's already locked, and 1 if some other kind of error occurred."),
MU_DL_EX_ERROR,
MU_DL_EX_ERROR
};
char *capa[] = {
"debug",
"locking",
NULL
};
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 ();
/* Native Language Support */
MU_APP_INIT_NLS ();
/* Drop permissions during argument parsing. */
if (setegid (usergid) < 0)
return MU_DL_EX_ERROR;
mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
switch (argc)
{
case 0:
mu_error (_("FILE must be specified"));
exit (MU_DL_EX_ERROR);
case 1:
file = argv[0];
break;
default:
mu_error (_("only one FILE can be specified"));
}
if (force)
{
hints.flags |= MU_LOCKER_FLAG_EXPIRE_TIME;
hints.expire_time = force * 60;
}
if (retries)
{
hints.flags |= MU_LOCKER_FLAG_RETRY;
hints.retry_count = retries;
hints.retry_sleep = retry_sleep;
}
if (pid_check)
hints.flags |= MU_LOCKER_FLAG_CHECK_PID;
if ((err = mu_locker_create_ext (&locker, file, hints.flags != 0 ? &hints : NULL)))
{
if (debug)
mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create_ext", NULL, err);
return MU_DL_EX_ERROR;
}
if (setegid (mailgid) < 0)
return MU_DL_EX_ERROR;
if (unlock)
err = mu_locker_remove_lock (locker);
else
err = mu_locker_lock (locker);
setegid (usergid);
mu_locker_destroy (&locker);
if (debug && err)
mu_error (unlock ? _("unlocking the file %s failed: %s") :
_("locking the file %s failed: %s"),
file, mu_strerror (err));
switch (err)
{
case 0:
err = MU_DL_EX_OK;
break;
case EPERM:
err = MU_DL_EX_PERM;
break;
case MU_ERR_LOCK_NOT_HELD:
err = MU_DL_EX_NEXIST;
break;
case MU_ERR_LOCK_CONFLICT:
err = MU_DL_EX_EXIST;
break;
default:
err = MU_DL_EX_ERROR;
break;
}
return err;
}