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