1*03c86659Smrg /* $NetBSD: flock.c,v 1.12 2019/10/04 16:27:00 mrg Exp $ */
2dcdc302cSchristos
3dcdc302cSchristos /*-
4dcdc302cSchristos * Copyright (c) 2012 The NetBSD Foundation, Inc.
5dcdc302cSchristos * All rights reserved.
6dcdc302cSchristos *
7dcdc302cSchristos * This code is derived from software contributed to The NetBSD Foundation
8dcdc302cSchristos * by Christos Zoulas.
9dcdc302cSchristos *
10dcdc302cSchristos * Redistribution and use in source and binary forms, with or without
11dcdc302cSchristos * modification, are permitted provided that the following conditions
12dcdc302cSchristos * are met:
13dcdc302cSchristos * 1. Redistributions of source code must retain the above copyright
14dcdc302cSchristos * notice, this list of conditions and the following disclaimer.
15dcdc302cSchristos * 2. Redistributions in binary form must reproduce the above copyright
16dcdc302cSchristos * notice, this list of conditions and the following disclaimer in the
17dcdc302cSchristos * documentation and/or other materials provided with the distribution.
18dcdc302cSchristos * from this software without specific prior written permission.
19dcdc302cSchristos *
20dcdc302cSchristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21dcdc302cSchristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22dcdc302cSchristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23dcdc302cSchristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24dcdc302cSchristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25dcdc302cSchristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26dcdc302cSchristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27dcdc302cSchristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28dcdc302cSchristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29dcdc302cSchristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30dcdc302cSchristos * POSSIBILITY OF SUCH DAMAGE.
31dcdc302cSchristos */
32dcdc302cSchristos
33dcdc302cSchristos #include <sys/cdefs.h>
34*03c86659Smrg __RCSID("$NetBSD: flock.c,v 1.12 2019/10/04 16:27:00 mrg Exp $");
35dcdc302cSchristos
36dcdc302cSchristos #include <stdio.h>
37dcdc302cSchristos #include <string.h>
38dcdc302cSchristos #include <fcntl.h>
39dcdc302cSchristos #include <stdlib.h>
40dcdc302cSchristos #include <signal.h>
41dcdc302cSchristos #include <unistd.h>
42dcdc302cSchristos #include <err.h>
43dcdc302cSchristos #include <errno.h>
44dcdc302cSchristos #include <getopt.h>
45dcdc302cSchristos #include <paths.h>
46519e6f92Schristos #include <limits.h>
479abb2d20Schristos #include <time.h>
48dcdc302cSchristos
492e03ba11Schristos static struct option flock_longopts[] = {
50dcdc302cSchristos { "debug", no_argument, 0, 'd' },
51dcdc302cSchristos { "help", no_argument, 0, 'h' },
52dcdc302cSchristos { "nonblock", no_argument, 0, 'n' },
53dcdc302cSchristos { "nb", no_argument, 0, 'n' },
54dcdc302cSchristos { "close", no_argument, 0, 'o' },
55dcdc302cSchristos { "shared", no_argument, 0, 's' },
56dcdc302cSchristos { "exclusive", no_argument, 0, 'x' },
57dcdc302cSchristos { "unlock", no_argument, 0, 'u' },
58dcdc302cSchristos { "verbose", no_argument, 0, 'v' },
59dcdc302cSchristos { "command", required_argument, 0, 'c' },
60dcdc302cSchristos { "wait", required_argument, 0, 'w' },
61dcdc302cSchristos { "timeout", required_argument, 0, 'w' },
62dcdc302cSchristos { NULL, 0, 0, 0 },
63dcdc302cSchristos };
64dcdc302cSchristos
65ac881fe5Schristos static sig_atomic_t timeout_expired;
66dcdc302cSchristos
67d9c7ee5bSjoerg static __dead __printflike(1, 2) void
usage(const char * fmt,...)682e03ba11Schristos usage(const char *fmt, ...)
69dcdc302cSchristos {
702e03ba11Schristos if (fmt) {
712e03ba11Schristos va_list ap;
722e03ba11Schristos va_start(ap, fmt);
732e03ba11Schristos fprintf(stderr, "%s: ", getprogname());
742e03ba11Schristos vfprintf(stderr, fmt, ap);
752e03ba11Schristos fputc('\n', stderr);
762e03ba11Schristos va_end(ap);
772e03ba11Schristos }
782e03ba11Schristos
799abb2d20Schristos fprintf(stderr, "Usage: %s [-dnosvx] [-w timeout] lockfile|lockdir "
809abb2d20Schristos "[-c command]|command ...\n\t%s [-dnsuvx] [-w timeout] lockfd\n",
819abb2d20Schristos getprogname(), getprogname());
82dcdc302cSchristos exit(EXIT_FAILURE);
83dcdc302cSchristos }
84dcdc302cSchristos
852e03ba11Schristos static void
sigalrm(int sig)86dcdc302cSchristos sigalrm(int sig)
87dcdc302cSchristos {
88ac881fe5Schristos timeout_expired++;
89dcdc302cSchristos }
90dcdc302cSchristos
91dcdc302cSchristos static const char *
lock2name(int l)92dcdc302cSchristos lock2name(int l)
93dcdc302cSchristos {
94dcdc302cSchristos static char buf[1024];
95dcdc302cSchristos int nb = l & LOCK_NB;
96dcdc302cSchristos
97dcdc302cSchristos l &= ~LOCK_NB;
98dcdc302cSchristos if (nb)
99dcdc302cSchristos strlcpy(buf, "LOCK_NB|", sizeof(buf));
100dcdc302cSchristos else
101dcdc302cSchristos buf[0] = '\0';
102dcdc302cSchristos
103dcdc302cSchristos switch (l) {
104dcdc302cSchristos case LOCK_SH:
105dcdc302cSchristos strlcat(buf, "LOCK_SH", sizeof(buf));
106dcdc302cSchristos return buf;
107dcdc302cSchristos case LOCK_EX:
108dcdc302cSchristos strlcat(buf, "LOCK_EX", sizeof(buf));
109dcdc302cSchristos return buf;
110dcdc302cSchristos case LOCK_UN:
111dcdc302cSchristos strlcat(buf, "LOCK_UN", sizeof(buf));
112dcdc302cSchristos return buf;
113dcdc302cSchristos default:
114dcdc302cSchristos snprintf(buf, sizeof(buf), "*%d*", l | nb);
115dcdc302cSchristos return buf;
116dcdc302cSchristos }
117dcdc302cSchristos }
118dcdc302cSchristos
1192e03ba11Schristos static char
lockchar(int l)1202e03ba11Schristos lockchar(int l)
1212e03ba11Schristos {
1222e03ba11Schristos switch (l & ~LOCK_NB) {
1232e03ba11Schristos case LOCK_SH:
1242e03ba11Schristos return 's';
1252e03ba11Schristos case LOCK_EX:
1262e03ba11Schristos return 'x';
1272e03ba11Schristos case LOCK_UN:
1282e03ba11Schristos return 'u';
1292e03ba11Schristos default:
1302e03ba11Schristos return '*';
1312e03ba11Schristos }
1322e03ba11Schristos }
1332e03ba11Schristos
1349abb2d20Schristos static char *
cmdline(char ** av)1359abb2d20Schristos cmdline(char **av)
1369abb2d20Schristos {
1379abb2d20Schristos char *v = NULL;
1389abb2d20Schristos while (*av)
1399abb2d20Schristos if (v) {
1409abb2d20Schristos if (asprintf(&v, "%s %s", v, *av++) < 0)
1419abb2d20Schristos err(EXIT_FAILURE, "malloc");
1429abb2d20Schristos } else {
1439abb2d20Schristos if ((v = strdup(*av++)) == NULL)
1449abb2d20Schristos err(EXIT_FAILURE, "strdup");
1459abb2d20Schristos }
1469abb2d20Schristos return v;
1479abb2d20Schristos }
1489abb2d20Schristos
149dcdc302cSchristos int
main(int argc,char * argv[])150dcdc302cSchristos main(int argc, char *argv[])
151dcdc302cSchristos {
152dcdc302cSchristos int c;
1534a734be0Schristos int lock = 0;
154dcdc302cSchristos double timeout = 0;
155dcdc302cSchristos int cls = 0;
156dcdc302cSchristos int fd = -1;
157dcdc302cSchristos int debug = 0;
1582e03ba11Schristos int verbose = 0;
159519e6f92Schristos long l;
160ac881fe5Schristos char *mcargv[] = {
161ac881fe5Schristos __UNCONST(_PATH_BSHELL), __UNCONST("-c"), NULL, NULL
162ac881fe5Schristos };
1639abb2d20Schristos char **cmdargv = NULL, *v;
1649abb2d20Schristos timer_t tm;
165dcdc302cSchristos
166dcdc302cSchristos setprogname(argv[0]);
167dcdc302cSchristos
1689abb2d20Schristos while ((c = getopt_long(argc, argv, "+dnosuvw:x", flock_longopts, NULL))
169dcdc302cSchristos != -1)
170dcdc302cSchristos switch (c) {
171dcdc302cSchristos case 'd':
172dcdc302cSchristos debug++;
173dcdc302cSchristos break;
174dcdc302cSchristos case 'x':
1754a734be0Schristos #define T(l) (lock & ~LOCK_NB) != (l) && (lock & ~LOCK_NB) != 0
1764a734be0Schristos if (T(LOCK_EX))
1772e03ba11Schristos goto badlock;
1782e03ba11Schristos lock |= LOCK_EX;
179dcdc302cSchristos break;
180dcdc302cSchristos case 'n':
181dcdc302cSchristos lock |= LOCK_NB;
182dcdc302cSchristos break;
183dcdc302cSchristos case 's':
1844a734be0Schristos if (T(LOCK_SH))
1852e03ba11Schristos goto badlock;
1862e03ba11Schristos lock |= LOCK_SH;
187dcdc302cSchristos break;
188dcdc302cSchristos case 'u':
1894a734be0Schristos if (T(LOCK_UN))
1902e03ba11Schristos goto badlock;
1912e03ba11Schristos lock |= LOCK_UN;
192dcdc302cSchristos break;
193dcdc302cSchristos case 'w':
194dcdc302cSchristos timeout = strtod(optarg, NULL);
195dcdc302cSchristos break;
196dcdc302cSchristos case 'v':
197dcdc302cSchristos verbose = 1;
198dcdc302cSchristos break;
199dcdc302cSchristos case 'o':
200dcdc302cSchristos cls = 1;
201dcdc302cSchristos break;
202dcdc302cSchristos default:
2032e03ba11Schristos usage("Invalid option '%c'", c);
2042e03ba11Schristos badlock:
2052e03ba11Schristos usage("-%c can't be used with -%c", c, lockchar(lock));
206dcdc302cSchristos }
207dcdc302cSchristos
208dcdc302cSchristos argc -= optind;
209dcdc302cSchristos argv += optind;
210dcdc302cSchristos
2114a734be0Schristos if ((lock & ~LOCK_NB) == 0)
212519e6f92Schristos lock |= LOCK_EX; /* default to exclusive like linux */
2134a734be0Schristos
214ac881fe5Schristos switch (argc) {
215ac881fe5Schristos case 0:
2162e03ba11Schristos usage("Missing lock file argument");
217ac881fe5Schristos case 1:
218dcdc302cSchristos if (cls)
219f3fb7b3cSchristos usage("Close is not valid for descriptors");
220519e6f92Schristos errno = 0;
221f3fb7b3cSchristos l = strtol(argv[0], &v, 0);
222519e6f92Schristos if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
223519e6f92Schristos err(EXIT_FAILURE, "Bad file descriptor `%s'", argv[0]);
224519e6f92Schristos if (l > INT_MAX || l < 0 || *v)
225519e6f92Schristos errx(EXIT_FAILURE, "Bad file descriptor `%s'", argv[0]);
226519e6f92Schristos fd = (int)l;
227b981f2c3Stron if (debug) {
228dcdc302cSchristos fprintf(stderr, "descriptor %s lock %s\n",
229dcdc302cSchristos argv[0], lock2name(lock));
230b981f2c3Stron }
231b981f2c3Stron break;
232b981f2c3Stron
233ac881fe5Schristos default:
234*03c86659Smrg if ((lock & ~LOCK_NB) == LOCK_UN)
2352e03ba11Schristos usage("Unlock is only valid for descriptors");
236ac881fe5Schristos if (strcmp(argv[1], "-c") == 0 ||
237ac881fe5Schristos strcmp(argv[1], "--command") == 0) {
238ac881fe5Schristos if (argc == 2)
2392e03ba11Schristos usage("Missing argument to %s", strcmp(argv[1],
2402e03ba11Schristos "-c") == 0 ? "-c" : "--command");
241ac881fe5Schristos mcargv[2] = argv[2];
242ac881fe5Schristos cmdargv = mcargv;
243ac881fe5Schristos } else
244ac881fe5Schristos cmdargv = argv + 1;
245ac881fe5Schristos
246ac881fe5Schristos if ((fd = open(argv[0], O_RDONLY)) == -1) {
247ac881fe5Schristos if (errno != ENOENT ||
248ac881fe5Schristos (fd = open(argv[0], O_RDWR|O_CREAT, 0600)) == -1)
249ac881fe5Schristos err(EXIT_FAILURE, "Cannot open `%s'", argv[0]);
250ac881fe5Schristos }
2519abb2d20Schristos if (debug) {
252ac881fe5Schristos fprintf(stderr, "file %s lock %s command %s ...\n",
2539abb2d20Schristos argv[0], lock2name(lock), v = cmdline(cmdargv));
2549abb2d20Schristos free(v);
2559abb2d20Schristos }
256ac881fe5Schristos break;
257dcdc302cSchristos }
258dcdc302cSchristos
259dcdc302cSchristos if (timeout) {
2609abb2d20Schristos struct sigevent ev;
2619abb2d20Schristos struct itimerspec it;
2629abb2d20Schristos struct sigaction sa;
2639abb2d20Schristos
2649abb2d20Schristos timespecclear(&it.it_interval);
2659abb2d20Schristos it.it_value.tv_sec = timeout;
2669abb2d20Schristos it.it_value.tv_nsec = (timeout - it.it_value.tv_sec) *
2679abb2d20Schristos 1000000000;
2689abb2d20Schristos
2699abb2d20Schristos memset(&ev, 0, sizeof(ev));
2709abb2d20Schristos ev.sigev_notify = SIGEV_SIGNAL;
2719abb2d20Schristos ev.sigev_signo = SIGALRM;
2729abb2d20Schristos
2739abb2d20Schristos if (timer_create(CLOCK_REALTIME, &ev, &tm) == -1)
2749abb2d20Schristos err(EXIT_FAILURE, "timer_create");
2759abb2d20Schristos
2769abb2d20Schristos if (timer_settime(tm, TIMER_RELTIME, &it, NULL) == -1)
2779abb2d20Schristos err(EXIT_FAILURE, "timer_settime");
2789abb2d20Schristos
2799abb2d20Schristos memset(&sa, 0, sizeof(sa));
2809abb2d20Schristos sa.sa_handler = sigalrm;
2819abb2d20Schristos sigemptyset(&sa.sa_mask);
2829abb2d20Schristos sa.sa_flags = 0;
2839abb2d20Schristos if (sigaction(SIGALRM, &sa, NULL) == -1)
2849abb2d20Schristos err(EXIT_FAILURE, "sigaction");
2859abb2d20Schristos
286dcdc302cSchristos if (debug)
2879abb2d20Schristos fprintf(stderr, "alarm %g\n", timeout);
288dcdc302cSchristos }
289dcdc302cSchristos
290ac881fe5Schristos while (flock(fd, lock) == -1) {
291ac881fe5Schristos if (errno == EINTR && timeout_expired == 0)
292ac881fe5Schristos continue;
293dcdc302cSchristos if (verbose)
294dcdc302cSchristos err(EXIT_FAILURE, "flock(%d, %s)", fd, lock2name(lock));
295dcdc302cSchristos else
296dcdc302cSchristos return EXIT_FAILURE;
297dcdc302cSchristos }
298dcdc302cSchristos
299dcdc302cSchristos if (timeout)
3009abb2d20Schristos timer_delete(tm);
301dcdc302cSchristos
302dcdc302cSchristos if (cls)
303dcdc302cSchristos (void)close(fd);
304dcdc302cSchristos
305ac881fe5Schristos if (cmdargv != NULL) {
306ac881fe5Schristos execvp(cmdargv[0], cmdargv);
3079abb2d20Schristos err(EXIT_FAILURE, "execvp '%s'", v = cmdline(cmdargv));
3089abb2d20Schristos free(v);
309ac881fe5Schristos }
310dcdc302cSchristos return 0;
311dcdc302cSchristos }
312