]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/flock.c
whereis: tell when mandatory option is missing
[thirdparty/util-linux.git] / sys-utils / flock.c
CommitLineData
75332f0f 1/* Copyright 2003-2005 H. Peter Anvin - All Rights Reserved
baf39af1
KZ
2 *
3 * Permission is hereby granted, free of charge, to any person
4 * obtaining a copy of this software and associated documentation
5 * files (the "Software"), to deal in the Software without
6 * restriction, including without limitation the rights to use,
7 * copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following
10 * conditions:
db1749cf 11 *
baf39af1
KZ
12 * The above copyright notice and this permission notice shall
13 * be included in all copies or substantial portions of the Software.
db1749cf 14 *
baf39af1
KZ
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
75332f0f 23 */
baf39af1 24
75332f0f 25#include <ctype.h>
baf39af1 26#include <errno.h>
75332f0f 27#include <fcntl.h>
baf39af1 28#include <getopt.h>
75332f0f 29#include <paths.h>
baf39af1 30#include <signal.h>
75332f0f
SK
31#include <stdio.h>
32#include <stdlib.h>
baf39af1 33#include <string.h>
baf39af1 34#include <sysexits.h>
baf39af1 35#include <sys/file.h>
75332f0f 36#include <sys/stat.h>
baf39af1 37#include <sys/time.h>
75332f0f 38#include <sys/types.h>
d162fcb5 39#include <sys/wait.h>
75332f0f 40#include <unistd.h>
d162fcb5 41
d4eafec4 42#include "c.h"
db1749cf 43#include "nls.h"
37bb0ce8 44#include "strutils.h"
efb8854f 45#include "closestream.h"
907624eb 46#include "timer.h"
db1749cf 47
e8cea669 48static void __attribute__((__noreturn__)) usage(int ex)
baf39af1 49{
dc36ee2a 50 fprintf(stderr, USAGE_HEADER);
75332f0f 51 fprintf(stderr,
7a853ebc
BS
52 _(" %1$s [options] <file>|<directory> <command> [<argument>...]\n"
53 " %1$s [options] <file>|<directory> -c <command>\n"
295dd902 54 " %1$s [options] <file descriptor number>\n"),
d4eafec4 55 program_invocation_short_name);
451dbcfa
BS
56
57 fputs(USAGE_SEPARATOR, stderr);
58 fputs(_("Manage file locks from shell scripts.\n"), stderr);
59
dc36ee2a 60 fputs(USAGE_OPTIONS, stderr);
7a853ebc
BS
61 fputs(_( " -s, --shared get a shared lock\n"), stderr);
62 fputs(_( " -x, --exclusive get an exclusive lock (default)\n"), stderr);
63 fputs(_( " -u, --unlock remove a lock\n"), stderr);
64 fputs(_( " -n, --nonblock fail rather than wait\n"), stderr);
65 fputs(_( " -w, --timeout <secs> wait for a limited amount of time\n"), stderr);
66 fputs(_( " -E, --conflict-exit-code <number> exit code after conflict or timeout\n"), stderr);
67 fputs(_( " -o, --close close file descriptor before running command\n"), stderr);
68 fputs(_( " -c, --command <command> run a single command string through the shell\n"), stderr);
dc36ee2a
SK
69 fprintf(stderr, USAGE_SEPARATOR);
70 fprintf(stderr, USAGE_HELP);
71 fprintf(stderr, USAGE_VERSION);
72 fprintf(stderr, USAGE_MAN_TAIL("flock(1)"));
75332f0f 73 exit(ex);
baf39af1
KZ
74}
75
baf39af1
KZ
76static sig_atomic_t timeout_expired = 0;
77
e8cea669 78static void timeout_handler(int sig __attribute__((__unused__)))
baf39af1 79{
75332f0f 80 timeout_expired = 1;
baf39af1
KZ
81}
82
eb742a1f
KZ
83static int open_file(const char *filename, int *flags)
84{
85
86 int fd;
87 int fl = *flags == 0 ? O_RDONLY : *flags;
88
89 errno = 0;
90 fl |= O_NOCTTY | O_CREAT;
91 fd = open(filename, fl, 0666);
92
93 /* Linux doesn't like O_CREAT on a directory, even though it
94 * should be a no-op; POSIX doesn't allow O_RDWR or O_WRONLY
95 */
96 if (fd < 0 && errno == EISDIR) {
97 fl = O_RDONLY | O_NOCTTY;
98 fd = open(filename, fl);
99 }
100 if (fd < 0) {
101 warn(_("cannot open lock file %s"), filename);
102 if (errno == ENOMEM || errno == EMFILE || errno == ENFILE)
103 exit(EX_OSERR);
104 if (errno == EROFS || errno == ENOSPC)
105 exit(EX_CANTCREAT);
106 exit(EX_NOINPUT);
107 }
108 *flags = fl;
109 return fd;
110}
111
baf39af1 112int main(int argc, char *argv[])
d162fcb5 113{
75332f0f
SK
114 struct itimerval timeout, old_timer;
115 int have_timeout = 0;
116 int type = LOCK_EX;
117 int block = 0;
eb742a1f 118 int open_flags = 0;
75332f0f
SK
119 int fd = -1;
120 int opt, ix;
121 int do_close = 0;
75332f0f 122 int status;
827b1cee
JYK
123 /*
124 * The default exit code for lock conflict or timeout
125 * is specified in man flock.1
126 */
127 int conflict_exit_code = 1;
75332f0f
SK
128 char **cmd_argv = NULL, *sh_c_argv[4];
129 const char *filename = NULL;
907624eb 130 struct sigaction old_sa;
75332f0f 131
d89bfedf
SK
132 static const struct option long_options[] = {
133 {"shared", no_argument, NULL, 's'},
134 {"exclusive", no_argument, NULL, 'x'},
135 {"unlock", no_argument, NULL, 'u'},
136 {"nonblocking", no_argument, NULL, 'n'},
137 {"nb", no_argument, NULL, 'n'},
138 {"timeout", required_argument, NULL, 'w'},
139 {"wait", required_argument, NULL, 'w'},
827b1cee 140 {"conflict-exit-code", required_argument, NULL, 'E'},
d89bfedf
SK
141 {"close", no_argument, NULL, 'o'},
142 {"help", no_argument, NULL, 'h'},
143 {"version", no_argument, NULL, 'V'},
144 {NULL, 0, NULL, 0}
145 };
146
75332f0f
SK
147 setlocale(LC_ALL, "");
148 bindtextdomain(PACKAGE, LOCALEDIR);
149 textdomain(PACKAGE);
efb8854f 150 atexit(close_stdout);
75332f0f 151
75332f0f
SK
152 if (argc < 2)
153 usage(EX_USAGE);
154
155 memset(&timeout, 0, sizeof timeout);
156
157 optopt = 0;
158 while ((opt =
827b1cee 159 getopt_long(argc, argv, "+sexnouw:E:hV?", long_options,
75332f0f
SK
160 &ix)) != EOF) {
161 switch (opt) {
162 case 's':
163 type = LOCK_SH;
164 break;
165 case 'e':
166 case 'x':
167 type = LOCK_EX;
168 break;
169 case 'u':
170 type = LOCK_UN;
171 break;
172 case 'o':
173 do_close = 1;
174 break;
175 case 'n':
176 block = LOCK_NB;
177 break;
178 case 'w':
179 have_timeout = 1;
880c4045
KZ
180 strtotimeval_or_err(optarg, &timeout.it_value,
181 _("invalid timeout value"));
75332f0f 182 break;
827b1cee
JYK
183 case 'E':
184 conflict_exit_code = strtos32_or_err(optarg,
185 _("invalid exit code"));
186 break;
75332f0f 187 case 'V':
0c6625a1 188 printf(UTIL_LINUX_VERSION);
56d45cfa 189 exit(EX_OK);
75332f0f
SK
190 default:
191 /* optopt will be set if this was an unrecognized
192 * option, i.e. *not* 'h' or '?
193 */
194 usage(optopt ? EX_USAGE : 0);
195 break;
196 }
197 }
198
199 if (argc > optind + 1) {
200 /* Run command */
201 if (!strcmp(argv[optind + 1], "-c") ||
202 !strcmp(argv[optind + 1], "--command")) {
d4eafec4
SK
203 if (argc != optind + 3)
204 errx(EX_USAGE,
205 _("%s requires exactly one command argument"),
206 argv[optind + 1]);
75332f0f
SK
207 cmd_argv = sh_c_argv;
208 cmd_argv[0] = getenv("SHELL");
209 if (!cmd_argv[0] || !*cmd_argv[0])
210 cmd_argv[0] = _PATH_BSHELL;
211 cmd_argv[1] = "-c";
212 cmd_argv[2] = argv[optind + 2];
213 cmd_argv[3] = 0;
214 } else {
215 cmd_argv = &argv[optind + 1];
216 }
217
218 filename = argv[optind];
eb742a1f
KZ
219 fd = open_file(filename, &open_flags);
220
75332f0f
SK
221 } else if (optind < argc) {
222 /* Use provided file descriptor */
c5c056e7 223 fd = strtos32_or_err(argv[optind], _("bad file descriptor"));
75332f0f
SK
224 } else {
225 /* Bad options */
d4eafec4 226 errx(EX_USAGE, _("requires file descriptor, file or directory"));
75332f0f
SK
227 }
228
229 if (have_timeout) {
230 if (timeout.it_value.tv_sec == 0 &&
231 timeout.it_value.tv_usec == 0) {
232 /* -w 0 is equivalent to -n; this has to be
233 * special-cased because setting an itimer to zero
234 * means disabled!
235 */
236 have_timeout = 0;
237 block = LOCK_NB;
bc3ae4c6 238 } else
907624eb 239 setup_timer(&timeout, &old_timer, &old_sa, timeout_handler);
75332f0f
SK
240 }
241
242 while (flock(fd, type | block)) {
d4eafec4 243 switch (errno) {
75332f0f 244 case EWOULDBLOCK:
827b1cee
JYK
245 /* -n option set and failed to lock. */
246 exit(conflict_exit_code);
75332f0f
SK
247 case EINTR:
248 /* Signal received */
249 if (timeout_expired)
827b1cee
JYK
250 /* -w option set and failed to lock. */
251 exit(conflict_exit_code);
75332f0f
SK
252 /* otherwise try again */
253 continue;
eb742a1f 254 case EIO:
caf1ba11 255 case EBADF: /* since Linux 3.4 (commit 55725513) */
eb742a1f
KZ
256 /* Probably NFSv4 where flock() is emulated by fcntl().
257 * Let's try to reopen in read-write mode.
258 */
259 if (!(open_flags & O_RDWR) &&
260 type != LOCK_SH &&
3c7fbc82 261 filename &&
eb742a1f
KZ
262 access(filename, R_OK | W_OK) == 0) {
263
264 close(fd);
265 open_flags = O_RDWR;
266 fd = open_file(filename, &open_flags);
267
268 if (open_flags & O_RDWR)
269 break;
270 }
271 /* go through */
75332f0f
SK
272 default:
273 /* Other errors */
274 if (filename)
d4eafec4 275 warn("%s", filename);
75332f0f 276 else
d4eafec4
SK
277 warn("%d", fd);
278 exit((errno == ENOLCK
279 || errno == ENOMEM) ? EX_OSERR : EX_DATAERR);
75332f0f
SK
280 }
281 }
282
bc3ae4c6
KZ
283 if (have_timeout)
284 cancel_timer(&old_timer, &old_sa);
75332f0f 285
56d45cfa 286 status = EX_OK;
75332f0f
SK
287
288 if (cmd_argv) {
289 pid_t w, f;
290 /* Clear any inherited settings */
291 signal(SIGCHLD, SIG_DFL);
292 f = fork();
293
294 if (f < 0) {
d4eafec4 295 err(EX_OSERR, _("fork failed"));
75332f0f
SK
296 } else if (f == 0) {
297 if (do_close)
298 close(fd);
299 execvp(cmd_argv[0], cmd_argv);
75332f0f 300 /* execvp() failed */
07ff972e 301 warn(_("failed to execute %s"), cmd_argv[0]);
d4eafec4 302 _exit((errno == ENOMEM) ? EX_OSERR : EX_UNAVAILABLE);
75332f0f
SK
303 } else {
304 do {
305 w = waitpid(f, &status, 0);
306 if (w == -1 && errno != EINTR)
307 break;
308 } while (w != f);
309
310 if (w == -1) {
75332f0f 311 status = EXIT_FAILURE;
d4eafec4 312 warn(_("waitpid failed"));
75332f0f
SK
313 } else if (WIFEXITED(status))
314 status = WEXITSTATUS(status);
315 else if (WIFSIGNALED(status))
316 status = WTERMSIG(status) + 128;
317 else
318 /* WTF? */
319 status = EX_OSERR;
320 }
321 }
322
323 return status;
d162fcb5 324}