xref: /netbsd-src/usr.bin/flock/flock.c (revision 03c86659dee305833bf41787a0a3a0044b89750c)
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