flock.c revision 1.1
1/*	$NetBSD: flock.c,v 1.1 2012/11/01 23:30:19 christos 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.1 2012/11/01 23:30:19 christos 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;
64
65static __dead void usage(void)
66{
67	fprintf(stderr, "Usage: %s [-dnosxv] [-w <timeout>] file|directory [-c] "
68	    "<command>\n\t%s [-dnusxv] [-w <timeout>] fd\n", getprogname(),
69	    getprogname());
70	exit(EXIT_FAILURE);
71}
72
73static __dead void
74sigalrm(int sig)
75{
76	if (verbose) {
77		errno = ETIMEDOUT;
78		errx(EXIT_FAILURE, "");
79	} else
80		exit(EXIT_FAILURE);
81}
82
83static const char *
84lock2name(int l)
85{
86	static char buf[1024];
87	int nb = l & LOCK_NB;
88
89	l &= ~LOCK_NB;
90	if (nb)
91		strlcpy(buf, "LOCK_NB|", sizeof(buf));
92	else
93		buf[0] = '\0';
94
95	switch (l) {
96	case LOCK_SH:
97		strlcat(buf, "LOCK_SH", sizeof(buf));
98		return buf;
99	case LOCK_EX:
100		strlcat(buf, "LOCK_EX", sizeof(buf));
101		return buf;
102	case LOCK_UN:
103		strlcat(buf, "LOCK_UN", sizeof(buf));
104		return buf;
105	default:
106		snprintf(buf, sizeof(buf), "*%d*", l | nb);
107		return buf;
108	}
109}
110
111int
112main(int argc, char *argv[])
113{
114	int c;
115	int lock = 0;
116	double timeout = 0;
117	int cls = 0;
118	int fd = -1;
119	int debug = 0;
120	char *command = NULL;
121
122	setprogname(argv[0]);
123
124	while ((c = getopt_long(argc, argv, "c:dnosxuw:", flock_longopts, NULL))
125	    != -1)
126		switch (c) {
127		case 'c':
128			command = optarg;
129			break;
130		case 'd':
131			debug++;
132			break;
133		case 'e':
134		case 'x':
135			lock |= LOCK_EX;
136			break;
137		case 'n':
138			lock |= LOCK_NB;
139			break;
140		case 's':
141			lock |= LOCK_SH;
142			break;
143		case 'u':
144			lock |= LOCK_UN;
145			break;
146		case 'w':
147			timeout = strtod(optarg, NULL);
148			break;
149		case 'v':
150			verbose = 1;
151			break;
152		case 'o':
153			cls = 1;
154			break;
155		default:
156			usage();
157		}
158
159	argc -= optind;
160	argv += optind;
161
162	if (command) {
163		if (lock == LOCK_UN)
164			usage();
165		if ((fd = open(argv[0], O_RDONLY)) == -1) {
166			if (errno != ENOENT ||
167			    (fd = open(argv[0], O_RDWR|O_CREAT, 0600)) == -1)
168				err(EXIT_FAILURE, "Cannot open `%s'", argv[0]);
169		}
170		if (debug)
171			fprintf(stderr, "file %s lock %s command %s\n",
172			    argv[0], lock2name(lock), command);
173
174
175	} else {
176		if (cls)
177			usage();
178		fd = strtol(argv[0], NULL, 0);	// XXX: error checking
179		if (debug)
180			fprintf(stderr, "descriptor %s lock %s\n",
181			    argv[0], lock2name(lock));
182	}
183
184	if (timeout) {
185		signal(SIGALRM, sigalrm);
186		alarm((int)timeout);	// XXX: User timer_create()
187		if (debug)
188			fprintf(stderr, "alarm %d\n", (int)timeout);
189	}
190
191	if (flock(fd, lock) == -1) {
192		if (verbose)
193			err(EXIT_FAILURE, "flock(%d, %s)", fd, lock2name(lock));
194		else
195			return EXIT_FAILURE;
196	}
197
198	if (timeout)
199		alarm(0);
200
201	if (cls)
202		(void)close(fd);
203
204	if (command == NULL)
205		return 0;
206	if (debug)
207		fprintf(stderr, "execute %s -c '%s'\n", _PATH_BSHELL, command);
208	execlp(_PATH_BSHELL, "sh", "-c", command, NULL);
209	err(EXIT_FAILURE, "exec %s -c '%s'", _PATH_BSHELL, command);
210	return 0;
211}
212