Drop main() prototype. Syncs with NetBSD-8
[minix.git] / usr.bin / flock / flock.c
blobeb2a6689c7a4a8efa71c2eb5718dfd0598ca9296
1 /* $NetBSD: flock.c,v 1.11 2014/08/18 09:16:35 christos Exp $ */
3 /*-
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
33 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: flock.c,v 1.11 2014/08/18 09:16:35 christos Exp $");
36 #include <stdio.h>
37 #include <string.h>
38 #include <fcntl.h>
39 #include <stdlib.h>
40 #include <signal.h>
41 #include <unistd.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <getopt.h>
45 #include <paths.h>
46 #include <limits.h>
47 #include <time.h>
49 static struct option flock_longopts[] = {
50 { "debug", no_argument, 0, 'd' },
51 { "help", no_argument, 0, 'h' },
52 { "nonblock", no_argument, 0, 'n' },
53 { "nb", no_argument, 0, 'n' },
54 { "close", no_argument, 0, 'o' },
55 { "shared", no_argument, 0, 's' },
56 { "exclusive", no_argument, 0, 'x' },
57 { "unlock", no_argument, 0, 'u' },
58 { "verbose", no_argument, 0, 'v' },
59 { "command", required_argument, 0, 'c' },
60 { "wait", required_argument, 0, 'w' },
61 { "timeout", required_argument, 0, 'w' },
62 { NULL, 0, 0, 0 },
65 static sig_atomic_t timeout_expired;
67 static __dead __printflike(1, 2) void
68 usage(const char *fmt, ...)
70 if (fmt) {
71 va_list ap;
72 va_start(ap, fmt);
73 fprintf(stderr, "%s: ", getprogname());
74 vfprintf(stderr, fmt, ap);
75 fputc('\n', stderr);
76 va_end(ap);
79 fprintf(stderr, "Usage: %s [-dnosvx] [-w timeout] lockfile|lockdir "
80 "[-c command]|command ...\n\t%s [-dnsuvx] [-w timeout] lockfd\n",
81 getprogname(), getprogname());
82 exit(EXIT_FAILURE);
85 static void
86 sigalrm(int sig)
88 timeout_expired++;
91 static const char *
92 lock2name(int l)
94 static char buf[1024];
95 int nb = l & LOCK_NB;
97 l &= ~LOCK_NB;
98 if (nb)
99 strlcpy(buf, "LOCK_NB|", sizeof(buf));
100 else
101 buf[0] = '\0';
103 switch (l) {
104 case LOCK_SH:
105 strlcat(buf, "LOCK_SH", sizeof(buf));
106 return buf;
107 case LOCK_EX:
108 strlcat(buf, "LOCK_EX", sizeof(buf));
109 return buf;
110 case LOCK_UN:
111 strlcat(buf, "LOCK_UN", sizeof(buf));
112 return buf;
113 default:
114 snprintf(buf, sizeof(buf), "*%d*", l | nb);
115 return buf;
119 static char
120 lockchar(int l)
122 switch (l & ~LOCK_NB) {
123 case LOCK_SH:
124 return 's';
125 case LOCK_EX:
126 return 'x';
127 case LOCK_UN:
128 return 'u';
129 default:
130 return '*';
134 static char *
135 cmdline(char **av)
137 char *v = NULL;
138 while (*av)
139 if (v) {
140 if (asprintf(&v, "%s %s", v, *av++) < 0)
141 err(EXIT_FAILURE, "malloc");
142 } else {
143 if ((v = strdup(*av++)) == NULL)
144 err(EXIT_FAILURE, "strdup");
146 return v;
150 main(int argc, char *argv[])
152 int c;
153 int lock = 0;
154 double timeout = 0;
155 int cls = 0;
156 int fd = -1;
157 int debug = 0;
158 int verbose = 0;
159 long l;
160 char *mcargv[] = {
161 __UNCONST(_PATH_BSHELL), __UNCONST("-c"), NULL, NULL
163 char **cmdargv = NULL, *v;
164 #if !defined(__minix)
165 timer_t tm;
166 #else
167 struct itimerval it;
168 #endif /* !defined(__minix) */
170 setprogname(argv[0]);
172 while ((c = getopt_long(argc, argv, "+dnosuvw:x", flock_longopts, NULL))
173 != -1)
174 switch (c) {
175 case 'd':
176 debug++;
177 break;
178 case 'x':
179 #define T(l) (lock & ~LOCK_NB) != (l) && (lock & ~LOCK_NB) != 0
180 if (T(LOCK_EX))
181 goto badlock;
182 lock |= LOCK_EX;
183 break;
184 case 'n':
185 lock |= LOCK_NB;
186 break;
187 case 's':
188 if (T(LOCK_SH))
189 goto badlock;
190 lock |= LOCK_SH;
191 break;
192 case 'u':
193 if (T(LOCK_UN))
194 goto badlock;
195 lock |= LOCK_UN;
196 break;
197 case 'w':
198 timeout = strtod(optarg, NULL);
199 break;
200 case 'v':
201 verbose = 1;
202 break;
203 case 'o':
204 cls = 1;
205 break;
206 default:
207 usage("Invalid option '%c'", c);
208 badlock:
209 usage("-%c can't be used with -%c", c, lockchar(lock));
212 argc -= optind;
213 argv += optind;
215 if ((lock & ~LOCK_NB) == 0)
216 lock |= LOCK_EX; /* default to exclusive like linux */
218 switch (argc) {
219 case 0:
220 usage("Missing lock file argument");
221 case 1:
222 if (cls)
223 usage("Close is not valid for descriptors");
224 errno = 0;
225 l = strtol(argv[0], &v, 0);
226 if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
227 err(EXIT_FAILURE, "Bad file descriptor `%s'", argv[0]);
228 if (l > INT_MAX || l < 0 || *v)
229 errx(EXIT_FAILURE, "Bad file descriptor `%s'", argv[0]);
230 fd = (int)l;
231 if (debug) {
232 fprintf(stderr, "descriptor %s lock %s\n",
233 argv[0], lock2name(lock));
235 break;
237 default:
238 if ((lock & LOCK_NB) == LOCK_UN)
239 usage("Unlock is only valid for descriptors");
240 if (strcmp(argv[1], "-c") == 0 ||
241 strcmp(argv[1], "--command") == 0) {
242 if (argc == 2)
243 usage("Missing argument to %s", strcmp(argv[1],
244 "-c") == 0 ? "-c" : "--command");
245 mcargv[2] = argv[2];
246 cmdargv = mcargv;
247 } else
248 cmdargv = argv + 1;
250 if ((fd = open(argv[0], O_RDONLY)) == -1) {
251 if (errno != ENOENT ||
252 (fd = open(argv[0], O_RDWR|O_CREAT, 0600)) == -1)
253 err(EXIT_FAILURE, "Cannot open `%s'", argv[0]);
255 if (debug) {
256 fprintf(stderr, "file %s lock %s command %s ...\n",
257 argv[0], lock2name(lock), v = cmdline(cmdargv));
258 free(v);
260 break;
263 if (timeout) {
264 #if !defined(__minix)
265 struct sigevent ev;
266 struct itimerspec it;
267 #endif /* !defined(__minix) */
268 struct sigaction sa;
270 #if !defined(__minix)
271 timespecclear(&it.it_interval);
272 it.it_value.tv_sec = timeout;
273 it.it_value.tv_nsec = (timeout - it.it_value.tv_sec) *
274 1000000000;
276 memset(&ev, 0, sizeof(ev));
277 ev.sigev_notify = SIGEV_SIGNAL;
278 ev.sigev_signo = SIGALRM;
280 if (timer_create(CLOCK_REALTIME, &ev, &tm) == -1)
281 err(EXIT_FAILURE, "timer_create");
283 if (timer_settime(tm, TIMER_RELTIME, &it, NULL) == -1)
284 err(EXIT_FAILURE, "timer_settime");
285 #else
286 memset(&it.it_interval, 0, sizeof(it.it_interval));
287 it.it_value.tv_sec = timeout;
288 it.it_value.tv_usec = (timeout - it.it_value.tv_sec) * 1000000;
290 if (setitimer(ITIMER_REAL, &it, NULL) == -1)
291 err(EXIT_FAILURE, "setitimer");
293 memset(&it, 0, sizeof(it)); /* for the reset later */
294 #endif /* !defined(__minix) */
296 memset(&sa, 0, sizeof(sa));
297 sa.sa_handler = sigalrm;
298 sigemptyset(&sa.sa_mask);
299 sa.sa_flags = 0;
300 if (sigaction(SIGALRM, &sa, NULL) == -1)
301 err(EXIT_FAILURE, "sigaction");
303 if (debug)
304 fprintf(stderr, "alarm %g\n", timeout);
307 while (flock(fd, lock) == -1) {
308 if (errno == EINTR && timeout_expired == 0)
309 continue;
310 if (verbose)
311 err(EXIT_FAILURE, "flock(%d, %s)", fd, lock2name(lock));
312 else
313 return EXIT_FAILURE;
316 if (timeout)
317 #if !defined(__minix)
318 timer_delete(tm);
319 #else
320 setitimer(ITIMER_REAL, &it, NULL);
321 #endif /* !defined(__minix) */
323 if (cls)
324 (void)close(fd);
326 if (cmdargv != NULL) {
327 execvp(cmdargv[0], cmdargv);
328 err(EXIT_FAILURE, "execvp '%s'", v = cmdline(cmdargv));
329 free(v);
331 return 0;