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