chiark / gitweb /
Missed a paren. Un-`toys'-ify.
[misc] / locking.c
CommitLineData
f342fce2 1/* -*-c-*-
2 *
5b11bb47 3 * $Id: locking.c,v 1.2 2003/10/09 15:15:42 mdw Exp $
f342fce2 4 *
5 * Lock a file, run a program
6 *
7 * (c) 2003 Mark Wooding
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the Toys utilties collection.
13 *
14 * Toys is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * Toys is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Toys; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: locking.c,v $
5b11bb47 32 * Revision 1.2 2003/10/09 15:15:42 mdw
33 * Missed a paren. Un-`toys'-ify.
34 *
f342fce2 35 * Revision 1.1 2003/10/09 15:05:34 mdw
36 * Lots of stuff.
37 *
38 * Revision 1.3 2003/09/24 14:58:08 mdw
39 * Fix options parsing again.
40 *
41 * Revision 1.2 2003/09/24 14:14:03 mdw
42 * Fix option handling behaviour.
43 *
44 * Revision 1.1 2003/05/11 13:30:04 mdw
45 * Initial checkin.
46 *
47 */
48
49/*----- Header files ------------------------------------------------------*/
50
51#include <errno.h>
52#include <setjmp.h>
53#include <signal.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57
58#include <sys/types.h>
59#include <sys/time.h>
60#include <unistd.h>
61#include <fcntl.h>
62#include <sys/wait.h>
63
64#include <mLib/mdwopt.h>
65#include <mLib/quis.h>
66#include <mLib/report.h>
67
68/*----- Static variables --------------------------------------------------*/
69
70static jmp_buf jmp;
71
72/*----- Main code ---------------------------------------------------------*/
73
74static void alrm(int s) { longjmp(jmp, 1); }
75
76static void usage(FILE *fp)
77{
78 pquis(fp,
79 "Usage: $ [-cfwx] [-p REALPROG] [-t TIMEOUT] FILE PROG ARGS...\n");
80}
81
82static void version(FILE *fp)
83{
5b11bb47 84 pquis(fp, "$ (version " VERSION ")\n");
f342fce2 85}
86
87static void help(FILE *fp)
88{
89 version(fp);
90 putchar('\n');
91 usage(fp);
92 pquis(fp, "\n\
93Lock FILE and run PROG, passing ARGS. Options are:\n\
94\n\
95-h, --help Show this help message.\n\
96-v, --version Show version string.\n\
97-u, --usage Show terse usage summary.\n\
98\n\
99-c, --[no-]create Create FILE if it doesn't exist [default: on].\n\
100-f, --[no-]fail Fail if the file is already locked [default: off].\n\
101-w, --[no-]wait Wait for the lock to be available [default: off].\n\
102-x, --[no-]exclusive Get an exclusive (writer) lock [default: on].\n\
103-p, --program=REALPROG Run REALPROG instead of PROG.\n\
104-t, --timeout=TIME Wait for TIME for lock to become available.\n\
105");
106}
107
108int main(int argc, char *argv[])
109{
110 const char *file = 0;
111 const char *prog = 0;
112 char *const *av;
113 void (*oalrm)(int) = 0;
114 int fd;
115 struct flock l;
116 char *p;
117 int t = -1;
118 unsigned int ot;
119 time_t nt;
120 pid_t kid;
121 int st;
122
123#define f_bogus 1u
124#define f_wait 2u
125#define f_fail 4u
126#define f_create 8u
127#define f_excl 16u
128
129 unsigned f = f_create | f_excl;
130
131 ego(argv[0]);
132
133 for (;;) {
134 static const struct option opts[] = {
135 { "help", 0, 0, 'h' },
136 { "version", 0, 0, 'v' },
137 { "usage", 0, 0, 'u' },
138 { "wait", OPTF_NEGATE, 0, 'w' },
139 { "fail", OPTF_NEGATE, 0, 'f' },
140 { "create", OPTF_NEGATE, 0, 'c' },
141 { "program", OPTF_ARGREQ, 0, 'p' },
142 { "timeout", OPTF_ARGREQ, 0, 't' },
143 { "exclusive", OPTF_NEGATE, 0, 'x' },
144 { 0, 0, 0, 0 }
145 };
146
147 int i = mdwopt(argc, argv, "-hvuw+f+c+x+p:t:", opts,
148 0, 0, OPTF_NEGATION);
149 if (i < 0)
150 break;
151 switch (i) {
152 case 'h':
153 help(stdout);
154 exit(0);
155 case 'v':
156 version(stdout);
157 exit(0);
158 case 'u':
159 usage(stdout);
160 exit(0);
161 case 'w':
162 f |= f_wait;
163 break;
164 case 'w' | OPTF_NEGATED:
165 f &= ~f_wait;
166 break;
167 case 'f':
168 f |= f_fail;
169 break;
170 case 'f' | OPTF_NEGATED:
171 f &= ~f_fail;
172 break;
173 case 'c':
174 f |= f_create;
175 break;
176 case 'c' | OPTF_NEGATED:
177 f &= ~f_create;
178 break;
179 case 'x':
180 f |= f_excl;
181 break;
182 case 'x' | OPTF_NEGATED:
183 f &= ~f_excl;
184 break;
185 case 't':
186 errno = 0;
187 t = strtol(optarg, &p, 0);
188 switch (*p) {
189 case 'd': t *= 24;
190 case 'h': t *= 60;
191 case 'm': t *= 60;
192 case 's': p++;
193 case 0: break;
194 default: die(111, "unknown time unit `%c'", *p);
195 }
196 if (*p || t < 0 || errno)
197 die(111, "bad time value `%s'", optarg);
198 f |= f_wait;
199 break;
200 case 'p':
201 prog = optarg;
202 break;
203 case 0:
204 if (file) {
205 optind--;
206 goto doneopts;
207 }
208 file = optarg;
209 break;
210 default:
211 f |= f_bogus;
212 break;
213 }
214 }
215
216doneopts:
217 if (f & f_bogus || argc - optind < 1) {
218 usage(stderr);
219 exit(EXIT_FAILURE);
220 }
221
222 av = &argv[optind];
223 if (!prog)
224 prog = av[0];
225 if ((fd = open(file,
226 ((f & f_create ? O_CREAT : 0) |
227 (f & f_excl ? O_RDWR : O_RDONLY)), 0666)) < 0)
228 die(111, "error opening `%s': %s", file, strerror(errno));
229 l.l_type = f & f_excl ? F_WRLCK : F_RDLCK;
230 l.l_whence = SEEK_SET;
231 l.l_start = 0;
232 l.l_len = 0;
233 nt = time(0);
234 if (setjmp(jmp)) {
235 errno = EAGAIN;
236 nt = t;
237 } else {
238 ot = alarm(0);
239 oalrm = signal(SIGALRM, alrm);
240 if (t >= 0)
241 alarm(t);
242 if (fcntl(fd, f & f_wait ? F_SETLKW : F_SETLK, &l) >= 0)
243 errno = 0;
244 }
245 signal(SIGALRM, oalrm);
246 if (ot) {
247 nt = time(0) - nt;
248 if (nt > ot)
249 raise(SIGALRM);
250 else
251 alarm(ot - nt);
252 }
253 if (errno &&
254 ((errno != EAGAIN && errno != EWOULDBLOCK && errno != EACCES) ||
255 (f & f_fail)))
256 die(111, "error locking `%s': %s", file, strerror(errno));
257 if (errno)
258 exit(0);
259
260 if ((kid = fork()) < 0)
261 die(111, "error from fork: %s", strerror(errno));
262 if (!kid) {
263 close(fd);
264 execvp(prog, av);
265 die(111, "couldn't exec `%s': %s", prog, strerror(errno));
266 }
267 if (waitpid(kid, &st, 0) < 0)
268 die(EXIT_FAILURE, "error from wait: %s", strerror(errno));
269 l.l_type = F_UNLCK;
270 l.l_whence = SEEK_SET;
271 l.l_start = 0;
272 l.l_len = 0;
273 fcntl(fd, F_SETLK, &l);
274 close(fd);
275 if (WIFEXITED(st))
276 exit(WEXITSTATUS(st));
277 else
278 exit(255);
279}
280
281/*----- That's all, folks -------------------------------------------------*/