1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20
21 #include <stdlib.h>
22 #include <unistd.h>
23
24 #include <mailutils/errno.h>
25 #include <mailutils/locker.h>
26 #include <mailutils/nls.h>
27 #include "mailutils/cli.h"
28
29 static const char *file;
30 static int unlock;
31 static unsigned retries;
32 static unsigned force;
33 static int debug;
34 static unsigned retry_sleep = 0;
35 static int pid_check;
36
37 static void
cli_force(struct mu_parseopt * po,struct mu_option * opt,char const * arg)38 cli_force (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
39 {
40 if (arg)
41 {
42 int rc;
43 char *errmsg;
44 rc = mu_str_to_c (arg, opt->opt_type, opt->opt_ptr, &errmsg);
45 if (rc)
46 {
47 if (opt->opt_long)
48 mu_parseopt_error (po, "--%s: %s", opt->opt_long,
49 errmsg ? errmsg : mu_strerror (rc));
50 else
51 mu_parseopt_error (po, "-%c: %s", opt->opt_short,
52 errmsg ? errmsg : mu_strerror (rc));
53 free (errmsg);
54 exit (po->po_exit_error);
55 }
56 }
57 else
58 *(unsigned*)opt->opt_ptr = 1;
59 }
60
61 static struct mu_option dotlock_options[] = {
62 { "unlock", 'u', NULL, MU_OPTION_DEFAULT,
63 N_("unlock"),
64 mu_c_bool, &unlock },
65
66 { "force", 'f', N_("MINUTES"), MU_OPTION_ARG_OPTIONAL,
67 N_("forcibly break an existing lock older than a certain time"),
68 mu_c_uint, &force, cli_force },
69
70 { "retry", 'r', N_("RETRIES"), MU_OPTION_DEFAULT,
71 N_("retry the lock a few times"),
72 mu_c_uint, &retries },
73
74 { "delay", 't', N_("SECONDS"), MU_OPTION_DEFAULT,
75 N_("delay between two successive locking attempts (in seconds)"),
76 mu_c_uint, &retry_sleep },
77
78 { "pid-check", 'p', NULL, MU_OPTION_DEFAULT,
79 N_("check if the PID of lock owner is still active"),
80 mu_c_bool, &pid_check },
81
82 { "debug", 'd', NULL, MU_OPTION_DEFAULT,
83 N_("print details of failure reasons to stderr"),
84 mu_c_bool, &debug },
85
86 MU_OPTION_END
87 }, *options[] = { dotlock_options, NULL };
88
89 struct mu_cfg_param dotlock_cfg_param[] = {
90 { "force", mu_c_time, &force, 0, NULL,
91 N_("Forcibly break an existing lock older than the specified time.") },
92 { "debug", mu_c_bool, &debug, 0, NULL,
93 N_("Print details of failure reasons to stderr.") },
94 { NULL }
95 };
96
97 static struct mu_cli_setup cli = {
98 options,
99 dotlock_cfg_param,
100 N_("GNU dotlock -- lock mail spool files."),
101 N_("FILE"),
102 NULL,
103 N_("Returns 0 on success, 3 if locking the file fails because\
104 it's already locked, and 1 if some other kind of error occurred."),
105 MU_DL_EX_ERROR,
106 MU_DL_EX_ERROR
107 };
108
109
110
111 char *capa[] = {
112 "debug",
113 "locking",
114 NULL
115 };
116
117 int
main(int argc,char * argv[])118 main (int argc, char *argv[])
119 {
120 mu_locker_t locker = 0;
121 mu_locker_hints_t hints = { .flags = 0 };
122 int err = 0;
123 pid_t usergid = getgid ();
124 pid_t mailgid = getegid ();
125
126 /* Native Language Support */
127 MU_APP_INIT_NLS ();
128
129 /* Drop permissions during argument parsing. */
130
131 if (setegid (usergid) < 0)
132 return MU_DL_EX_ERROR;
133
134 mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
135
136 switch (argc)
137 {
138 case 0:
139 mu_error (_("FILE must be specified"));
140 exit (MU_DL_EX_ERROR);
141
142 case 1:
143 file = argv[0];
144 break;
145
146 default:
147 mu_error (_("only one FILE can be specified"));
148 }
149
150 if (force)
151 {
152 hints.flags |= MU_LOCKER_FLAG_EXPIRE_TIME;
153 hints.expire_time = force * 60;
154 }
155
156 if (retries)
157 {
158 hints.flags |= MU_LOCKER_FLAG_RETRY;
159 hints.retry_count = retries;
160 hints.retry_sleep = retry_sleep;
161 }
162
163 if (pid_check)
164 hints.flags |= MU_LOCKER_FLAG_CHECK_PID;
165
166 if ((err = mu_locker_create_ext (&locker, file, hints.flags != 0 ? &hints : NULL)))
167 {
168 if (debug)
169 mu_diag_funcall (MU_DIAG_ERROR, "mu_locker_create_ext", NULL, err);
170 return MU_DL_EX_ERROR;
171 }
172
173 if (setegid (mailgid) < 0)
174 return MU_DL_EX_ERROR;
175
176 if (unlock)
177 err = mu_locker_remove_lock (locker);
178 else
179 err = mu_locker_lock (locker);
180
181 setegid (usergid);
182
183 mu_locker_destroy (&locker);
184
185 if (debug && err)
186 mu_error (unlock ? _("unlocking the file %s failed: %s") :
187 _("locking the file %s failed: %s"),
188 file, mu_strerror (err));
189
190 switch (err)
191 {
192 case 0:
193 err = MU_DL_EX_OK;
194 break;
195 case EPERM:
196 err = MU_DL_EX_PERM;
197 break;
198 case MU_ERR_LOCK_NOT_HELD:
199 err = MU_DL_EX_NEXIST;
200 break;
201 case MU_ERR_LOCK_CONFLICT:
202 err = MU_DL_EX_EXIST;
203 break;
204 default:
205 err = MU_DL_EX_ERROR;
206 break;
207 }
208
209 return err;
210 }
211
212