chiark / gitweb /
Expunge revision histories in files.
[misc] / locking.c
CommitLineData
f342fce2 1/* -*-c-*-
2 *
9a4b2474 3 * $Id: locking.c,v 1.3 2004/04/08 01:36:26 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
f342fce2 29/*----- Header files ------------------------------------------------------*/
30
31#include <errno.h>
32#include <setjmp.h>
33#include <signal.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38#include <sys/types.h>
39#include <sys/time.h>
40#include <unistd.h>
41#include <fcntl.h>
42#include <sys/wait.h>
43
44#include <mLib/mdwopt.h>
45#include <mLib/quis.h>
46#include <mLib/report.h>
47
48/*----- Static variables --------------------------------------------------*/
49
50static jmp_buf jmp;
51
52/*----- Main code ---------------------------------------------------------*/
53
54static void alrm(int s) { longjmp(jmp, 1); }
55
56static void usage(FILE *fp)
57{
58 pquis(fp,
59 "Usage: $ [-cfwx] [-p REALPROG] [-t TIMEOUT] FILE PROG ARGS...\n");
60}
61
62static void version(FILE *fp)
63{
5b11bb47 64 pquis(fp, "$ (version " VERSION ")\n");
f342fce2 65}
66
67static void help(FILE *fp)
68{
69 version(fp);
70 putchar('\n');
71 usage(fp);
72 pquis(fp, "\n\
73Lock FILE and run PROG, passing ARGS. Options are:\n\
74\n\
75-h, --help Show this help message.\n\
76-v, --version Show version string.\n\
77-u, --usage Show terse usage summary.\n\
78\n\
79-c, --[no-]create Create FILE if it doesn't exist [default: on].\n\
80-f, --[no-]fail Fail if the file is already locked [default: off].\n\
81-w, --[no-]wait Wait for the lock to be available [default: off].\n\
82-x, --[no-]exclusive Get an exclusive (writer) lock [default: on].\n\
83-p, --program=REALPROG Run REALPROG instead of PROG.\n\
84-t, --timeout=TIME Wait for TIME for lock to become available.\n\
85");
86}
87
88int main(int argc, char *argv[])
89{
90 const char *file = 0;
91 const char *prog = 0;
92 char *const *av;
93 void (*oalrm)(int) = 0;
94 int fd;
95 struct flock l;
96 char *p;
97 int t = -1;
98 unsigned int ot;
99 time_t nt;
100 pid_t kid;
101 int st;
102
103#define f_bogus 1u
104#define f_wait 2u
105#define f_fail 4u
106#define f_create 8u
107#define f_excl 16u
108
109 unsigned f = f_create | f_excl;
110
111 ego(argv[0]);
112
113 for (;;) {
114 static const struct option opts[] = {
115 { "help", 0, 0, 'h' },
116 { "version", 0, 0, 'v' },
117 { "usage", 0, 0, 'u' },
118 { "wait", OPTF_NEGATE, 0, 'w' },
119 { "fail", OPTF_NEGATE, 0, 'f' },
120 { "create", OPTF_NEGATE, 0, 'c' },
121 { "program", OPTF_ARGREQ, 0, 'p' },
122 { "timeout", OPTF_ARGREQ, 0, 't' },
123 { "exclusive", OPTF_NEGATE, 0, 'x' },
124 { 0, 0, 0, 0 }
125 };
126
127 int i = mdwopt(argc, argv, "-hvuw+f+c+x+p:t:", opts,
128 0, 0, OPTF_NEGATION);
129 if (i < 0)
130 break;
131 switch (i) {
132 case 'h':
133 help(stdout);
134 exit(0);
135 case 'v':
136 version(stdout);
137 exit(0);
138 case 'u':
139 usage(stdout);
140 exit(0);
141 case 'w':
142 f |= f_wait;
143 break;
144 case 'w' | OPTF_NEGATED:
145 f &= ~f_wait;
146 break;
147 case 'f':
148 f |= f_fail;
149 break;
150 case 'f' | OPTF_NEGATED:
151 f &= ~f_fail;
152 break;
153 case 'c':
154 f |= f_create;
155 break;
156 case 'c' | OPTF_NEGATED:
157 f &= ~f_create;
158 break;
159 case 'x':
160 f |= f_excl;
161 break;
162 case 'x' | OPTF_NEGATED:
163 f &= ~f_excl;
164 break;
165 case 't':
166 errno = 0;
167 t = strtol(optarg, &p, 0);
168 switch (*p) {
169 case 'd': t *= 24;
170 case 'h': t *= 60;
171 case 'm': t *= 60;
172 case 's': p++;
173 case 0: break;
174 default: die(111, "unknown time unit `%c'", *p);
175 }
176 if (*p || t < 0 || errno)
177 die(111, "bad time value `%s'", optarg);
178 f |= f_wait;
179 break;
180 case 'p':
181 prog = optarg;
182 break;
183 case 0:
184 if (file) {
185 optind--;
186 goto doneopts;
187 }
188 file = optarg;
189 break;
190 default:
191 f |= f_bogus;
192 break;
193 }
194 }
195
196doneopts:
197 if (f & f_bogus || argc - optind < 1) {
198 usage(stderr);
199 exit(EXIT_FAILURE);
200 }
201
202 av = &argv[optind];
203 if (!prog)
204 prog = av[0];
205 if ((fd = open(file,
206 ((f & f_create ? O_CREAT : 0) |
207 (f & f_excl ? O_RDWR : O_RDONLY)), 0666)) < 0)
208 die(111, "error opening `%s': %s", file, strerror(errno));
209 l.l_type = f & f_excl ? F_WRLCK : F_RDLCK;
210 l.l_whence = SEEK_SET;
211 l.l_start = 0;
212 l.l_len = 0;
213 nt = time(0);
214 if (setjmp(jmp)) {
215 errno = EAGAIN;
216 nt = t;
217 } else {
218 ot = alarm(0);
219 oalrm = signal(SIGALRM, alrm);
220 if (t >= 0)
221 alarm(t);
222 if (fcntl(fd, f & f_wait ? F_SETLKW : F_SETLK, &l) >= 0)
223 errno = 0;
224 }
225 signal(SIGALRM, oalrm);
226 if (ot) {
227 nt = time(0) - nt;
228 if (nt > ot)
229 raise(SIGALRM);
230 else
231 alarm(ot - nt);
232 }
233 if (errno &&
234 ((errno != EAGAIN && errno != EWOULDBLOCK && errno != EACCES) ||
235 (f & f_fail)))
236 die(111, "error locking `%s': %s", file, strerror(errno));
237 if (errno)
238 exit(0);
239
240 if ((kid = fork()) < 0)
241 die(111, "error from fork: %s", strerror(errno));
242 if (!kid) {
243 close(fd);
244 execvp(prog, av);
245 die(111, "couldn't exec `%s': %s", prog, strerror(errno));
246 }
247 if (waitpid(kid, &st, 0) < 0)
248 die(EXIT_FAILURE, "error from wait: %s", strerror(errno));
249 l.l_type = F_UNLCK;
250 l.l_whence = SEEK_SET;
251 l.l_start = 0;
252 l.l_len = 0;
253 fcntl(fd, F_SETLK, &l);
254 close(fd);
255 if (WIFEXITED(st))
256 exit(WEXITSTATUS(st));
257 else
258 exit(255);
259}
260
261/*----- That's all, folks -------------------------------------------------*/