flock.c revision 1.3
1/*	$NetBSD: flock.c,v 1.3 2012/11/02 02:03:18 wiz Exp $	*/
2
3/*-
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
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.
19 *
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.
31 */
32
33#include <sys/cdefs.h>
34__RCSID("$NetBSD: flock.c,v 1.3 2012/11/02 02:03:18 wiz Exp $");
35
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
47struct option flock_longopts[] = {
48	{ "debug",		no_argument,		0, 'd' },
49	{ "help",		no_argument,		0, 'h' },
50	{ "nonblock",		no_argument,		0, 'n' },
51	{ "nb",			no_argument,		0, 'n' },
52	{ "close",		no_argument,		0, 'o' },
53	{ "shared",		no_argument,		0, 's' },
54	{ "exclusive",		no_argument,		0, 'x' },
55	{ "unlock",		no_argument,		0, 'u' },
56	{ "verbose",		no_argument,		0, 'v' },
57	{ "command",		required_argument,	0, 'c' },
58	{ "wait",		required_argument,	0, 'w' },
59	{ "timeout",		required_argument,	0, 'w' },
60	{ NULL,			0,			0, 0   },
61};
62
63static int verbose = 0;
64static sig_atomic_t timeout_expired;
65
66static __dead void usage(void)
67{
68	fprintf(stderr, "Usage: %s [-dnosvx] [-w timeout] lockfile|lockdir [-c] "
69	    "command\n\t%s [-dnsuvx] [-w timeout] lockfd\n", getprogname(),
70	    getprogname());
71	exit(EXIT_FAILURE);
72}
73
74static __dead void
75sigalrm(int sig)
76{
77	timeout_expired++;
78}
79
80static const char *
81lock2name(int l)
82{
83	static char buf[1024];
84	int nb = l & LOCK_NB;
85
86	l &= ~LOCK_NB;
87	if (nb)
88		strlcpy(buf, "LOCK_NB|", sizeof(buf));
89	else
90		buf[0] = '\0';
91
92	switch (l) {
93	case LOCK_SH:
94		strlcat(buf, "LOCK_SH", sizeof(buf));
95		return buf;
96	case LOCK_EX:
97		strlcat(buf, "LOCK_EX", sizeof(buf));
98		return buf;
99	case LOCK_UN:
100		strlcat(buf, "LOCK_UN", sizeof(buf));
101		return buf;
102	default:
103		snprintf(buf, sizeof(buf), "*%d*", l | nb);
104		return buf;
105	}
106}
107
108int
109main(int argc, char *argv[])
110{
111	int c;
112	int lock = LOCK_EX;
113	double timeout = 0;
114	int cls = 0;
115	int fd = -1;
116	int debug = 0;
117	char *mcargv[] = {
118	    __UNCONST(_PATH_BSHELL), __UNCONST("-c"), NULL, NULL
119	};
120	char **cmdargv = NULL;
121
122	setprogname(argv[0]);
123
124	while ((c = getopt_long(argc, argv, "+dnosuw:x", flock_longopts, NULL))
125	    != -1)
126		switch (c) {
127		case 'd':
128			debug++;
129			break;
130		case 'x':
131			lock = LOCK_EX | (lock & ~LOCK_NB);
132			break;
133		case 'n':
134			lock |= LOCK_NB;
135			break;
136		case 's':
137			lock = LOCK_SH | (lock & ~LOCK_NB);
138			break;
139		case 'u':
140			lock = LOCK_UN | (lock & ~LOCK_NB);
141			break;
142		case 'w':
143			timeout = strtod(optarg, NULL);
144			break;
145		case 'v':
146			verbose = 1;
147			break;
148		case 'o':
149			cls = 1;
150			break;
151		default:
152			usage();
153		}
154
155	argc -= optind;
156	argv += optind;
157
158	switch (argc) {
159	case 0:
160		usage();
161	case 1:
162		if (cls)
163			usage();
164		fd = strtol(argv[0], NULL, 0);	// XXX: error checking
165		if (debug)
166			fprintf(stderr, "descriptor %s lock %s\n",
167			    argv[0], lock2name(lock));
168		if (lock == LOCK_UN)
169			usage();
170	default:
171		if (strcmp(argv[1], "-c") == 0 ||
172		    strcmp(argv[1], "--command") == 0) {
173			if (argc == 2)
174				usage();
175			mcargv[2] = argv[2];
176			cmdargv = mcargv;
177		} else
178			cmdargv = argv + 1;
179
180		if ((fd = open(argv[0], O_RDONLY)) == -1) {
181			if (errno != ENOENT ||
182			    (fd = open(argv[0], O_RDWR|O_CREAT, 0600)) == -1)
183				err(EXIT_FAILURE, "Cannot open `%s'", argv[0]);
184		}
185		if (debug)
186			fprintf(stderr, "file %s lock %s command %s ...\n",
187			    argv[0], lock2name(lock), cmdargv[0]);
188		break;
189	}
190
191	if (timeout) {
192		signal(SIGALRM, sigalrm);
193		alarm((int)timeout);	// XXX: User timer_create()
194		if (debug)
195			fprintf(stderr, "alarm %d\n", (int)timeout);
196	}
197
198	while (flock(fd, lock) == -1) {
199		if (errno == EINTR && timeout_expired == 0)
200			continue;
201		if (verbose)
202			err(EXIT_FAILURE, "flock(%d, %s)", fd, lock2name(lock));
203		else
204			return EXIT_FAILURE;
205	}
206
207	if (timeout)
208		alarm(0);
209
210	if (cls)
211		(void)close(fd);
212
213	if (cmdargv != NULL) {
214		execvp(cmdargv[0], cmdargv);
215		err(EXIT_FAILURE, "execvp '%s'", cmdargv[0]);
216	}
217	return 0;
218}
219