]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/flock.c
lsns: rename notruns to no_trunc
[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"
59dc9f28 46#include "monotonic.h"
26e8964b 47#include "timer.h"
db1749cf 48
6d7bee26 49static void __attribute__((__noreturn__)) usage(void)
baf39af1 50{
6d7bee26
RM
51 fputs(USAGE_HEADER, stdout);
52 printf(
7a853ebc
BS
53 _(" %1$s [options] <file>|<directory> <command> [<argument>...]\n"
54 " %1$s [options] <file>|<directory> -c <command>\n"
295dd902 55 " %1$s [options] <file descriptor number>\n"),
d4eafec4 56 program_invocation_short_name);
451dbcfa 57
6d7bee26
RM
58 fputs(USAGE_SEPARATOR, stdout);
59 fputs(_("Manage file locks from shell scripts.\n"), stdout);
60
61 fputs(USAGE_OPTIONS, stdout);
62 fputs(_( " -s, --shared get a shared lock\n"), stdout);
63 fputs(_( " -x, --exclusive get an exclusive lock (default)\n"), stdout);
64 fputs(_( " -u, --unlock remove a lock\n"), stdout);
65 fputs(_( " -n, --nonblock fail rather than wait\n"), stdout);
66 fputs(_( " -w, --timeout <secs> wait for a limited amount of time\n"), stdout);
67 fputs(_( " -E, --conflict-exit-code <number> exit code after conflict or timeout\n"), stdout);
68 fputs(_( " -o, --close close file descriptor before running command\n"), stdout);
69 fputs(_( " -c, --command <command> run a single command string through the shell\n"), stdout);
70 fputs(_( " -F, --no-fork execute command without forking\n"), stdout);
71 fputs(_( " --verbose increase verbosity\n"), stdout);
72 fputs(USAGE_SEPARATOR, stdout);
f45f3ec3 73 printf(USAGE_HELP_OPTIONS(26));
6d7bee26
RM
74 printf(USAGE_MAN_TAIL("flock(1)"));
75 exit(EXIT_SUCCESS);
baf39af1
KZ
76}
77
baf39af1
KZ
78static sig_atomic_t timeout_expired = 0;
79
378543e1
SK
80static void timeout_handler(int sig __attribute__((__unused__)),
81 siginfo_t *info,
82 void *context __attribute__((__unused__)))
baf39af1 83{
378543e1
SK
84 if (info->si_code == SI_TIMER)
85 timeout_expired = 1;
baf39af1
KZ
86}
87
eb742a1f
KZ
88static int open_file(const char *filename, int *flags)
89{
90
91 int fd;
92 int fl = *flags == 0 ? O_RDONLY : *flags;
93
94 errno = 0;
95 fl |= O_NOCTTY | O_CREAT;
96 fd = open(filename, fl, 0666);
97
98 /* Linux doesn't like O_CREAT on a directory, even though it
99 * should be a no-op; POSIX doesn't allow O_RDWR or O_WRONLY
100 */
101 if (fd < 0 && errno == EISDIR) {
102 fl = O_RDONLY | O_NOCTTY;
103 fd = open(filename, fl);
104 }
105 if (fd < 0) {
106 warn(_("cannot open lock file %s"), filename);
107 if (errno == ENOMEM || errno == EMFILE || errno == ENFILE)
108 exit(EX_OSERR);
109 if (errno == EROFS || errno == ENOSPC)
110 exit(EX_CANTCREAT);
111 exit(EX_NOINPUT);
112 }
113 *flags = fl;
114 return fd;
115}
116
93fea49f 117static void __attribute__((__noreturn__)) run_program(char **cmd_argv)
391e675c 118{
391e675c
KZ
119 execvp(cmd_argv[0], cmd_argv);
120
121 warn(_("failed to execute %s"), cmd_argv[0]);
122 _exit((errno == ENOMEM) ? EX_OSERR : EX_UNAVAILABLE);
123}
124
baf39af1 125int main(int argc, char *argv[])
d162fcb5 126{
378543e1
SK
127 static timer_t t_id;
128 struct itimerval timeout;
75332f0f
SK
129 int have_timeout = 0;
130 int type = LOCK_EX;
131 int block = 0;
eb742a1f 132 int open_flags = 0;
75332f0f
SK
133 int fd = -1;
134 int opt, ix;
135 int do_close = 0;
70325140 136 int no_fork = 0;
75332f0f 137 int status;
59dc9f28
SK
138 int verbose = 0;
139 struct timeval time_start, time_done;
827b1cee
JYK
140 /*
141 * The default exit code for lock conflict or timeout
142 * is specified in man flock.1
143 */
144 int conflict_exit_code = 1;
75332f0f
SK
145 char **cmd_argv = NULL, *sh_c_argv[4];
146 const char *filename = NULL;
59dc9f28
SK
147 enum {
148 OPT_VERBOSE = CHAR_MAX + 1
149 };
d89bfedf
SK
150 static const struct option long_options[] = {
151 {"shared", no_argument, NULL, 's'},
152 {"exclusive", no_argument, NULL, 'x'},
153 {"unlock", no_argument, NULL, 'u'},
154 {"nonblocking", no_argument, NULL, 'n'},
155 {"nb", no_argument, NULL, 'n'},
156 {"timeout", required_argument, NULL, 'w'},
157 {"wait", required_argument, NULL, 'w'},
827b1cee 158 {"conflict-exit-code", required_argument, NULL, 'E'},
d89bfedf 159 {"close", no_argument, NULL, 'o'},
70325140 160 {"no-fork", no_argument, NULL, 'F'},
59dc9f28 161 {"verbose", no_argument, NULL, OPT_VERBOSE},
d89bfedf
SK
162 {"help", no_argument, NULL, 'h'},
163 {"version", no_argument, NULL, 'V'},
164 {NULL, 0, NULL, 0}
165 };
166
75332f0f
SK
167 setlocale(LC_ALL, "");
168 bindtextdomain(PACKAGE, LOCALEDIR);
169 textdomain(PACKAGE);
efb8854f 170 atexit(close_stdout);
75332f0f 171
0b2b32e8
RM
172 strutils_set_exitcode(EX_USAGE);
173
6d7bee26
RM
174 if (argc < 2) {
175 warnx(_("not enough arguments"));
176 errtryhelp(EX_USAGE);
177 }
75332f0f
SK
178
179 memset(&timeout, 0, sizeof timeout);
180
181 optopt = 0;
182 while ((opt =
70325140 183 getopt_long(argc, argv, "+sexnoFuw:E:hV?", long_options,
75332f0f
SK
184 &ix)) != EOF) {
185 switch (opt) {
186 case 's':
187 type = LOCK_SH;
188 break;
189 case 'e':
190 case 'x':
191 type = LOCK_EX;
192 break;
193 case 'u':
194 type = LOCK_UN;
195 break;
196 case 'o':
197 do_close = 1;
198 break;
70325140
TB
199 case 'F':
200 no_fork = 1;
201 break;
75332f0f
SK
202 case 'n':
203 block = LOCK_NB;
204 break;
205 case 'w':
206 have_timeout = 1;
880c4045
KZ
207 strtotimeval_or_err(optarg, &timeout.it_value,
208 _("invalid timeout value"));
75332f0f 209 break;
827b1cee
JYK
210 case 'E':
211 conflict_exit_code = strtos32_or_err(optarg,
212 _("invalid exit code"));
213 break;
59dc9f28
SK
214 case OPT_VERBOSE:
215 verbose = 1;
216 break;
75332f0f 217 case 'V':
0c6625a1 218 printf(UTIL_LINUX_VERSION);
56d45cfa 219 exit(EX_OK);
677ec86c 220 case 'h':
6d7bee26 221 usage();
75332f0f 222 default:
677ec86c 223 errtryhelp(EX_USAGE);
75332f0f
SK
224 }
225 }
226
70325140
TB
227 if (no_fork && do_close)
228 errx(EX_USAGE,
229 _("the --no-fork and --close options are incompatible"));
230
75332f0f
SK
231 if (argc > optind + 1) {
232 /* Run command */
233 if (!strcmp(argv[optind + 1], "-c") ||
234 !strcmp(argv[optind + 1], "--command")) {
d4eafec4
SK
235 if (argc != optind + 3)
236 errx(EX_USAGE,
237 _("%s requires exactly one command argument"),
238 argv[optind + 1]);
75332f0f
SK
239 cmd_argv = sh_c_argv;
240 cmd_argv[0] = getenv("SHELL");
241 if (!cmd_argv[0] || !*cmd_argv[0])
242 cmd_argv[0] = _PATH_BSHELL;
243 cmd_argv[1] = "-c";
244 cmd_argv[2] = argv[optind + 2];
87918040 245 cmd_argv[3] = NULL;
75332f0f
SK
246 } else {
247 cmd_argv = &argv[optind + 1];
248 }
249
250 filename = argv[optind];
eb742a1f
KZ
251 fd = open_file(filename, &open_flags);
252
75332f0f
SK
253 } else if (optind < argc) {
254 /* Use provided file descriptor */
c5c056e7 255 fd = strtos32_or_err(argv[optind], _("bad file descriptor"));
75332f0f
SK
256 } else {
257 /* Bad options */
d4eafec4 258 errx(EX_USAGE, _("requires file descriptor, file or directory"));
75332f0f
SK
259 }
260
261 if (have_timeout) {
262 if (timeout.it_value.tv_sec == 0 &&
263 timeout.it_value.tv_usec == 0) {
264 /* -w 0 is equivalent to -n; this has to be
265 * special-cased because setting an itimer to zero
266 * means disabled!
267 */
268 have_timeout = 0;
269 block = LOCK_NB;
bc3ae4c6 270 } else
378543e1 271 if (setup_timer(&t_id, &timeout, &timeout_handler))
62eea9ce 272 err(EX_OSERR, _("cannot set up timer"));
75332f0f
SK
273 }
274
59dc9f28
SK
275 if (verbose)
276 gettime_monotonic(&time_start);
75332f0f 277 while (flock(fd, type | block)) {
d4eafec4 278 switch (errno) {
75332f0f 279 case EWOULDBLOCK:
827b1cee 280 /* -n option set and failed to lock. */
59dc9f28
SK
281 if (verbose)
282 warnx(_("failed to get lock"));
827b1cee 283 exit(conflict_exit_code);
75332f0f
SK
284 case EINTR:
285 /* Signal received */
59dc9f28 286 if (timeout_expired) {
827b1cee 287 /* -w option set and failed to lock. */
59dc9f28
SK
288 if (verbose)
289 warnx(_("timeout while waiting to get lock"));
827b1cee 290 exit(conflict_exit_code);
59dc9f28 291 }
75332f0f
SK
292 /* otherwise try again */
293 continue;
eb742a1f 294 case EIO:
caf1ba11 295 case EBADF: /* since Linux 3.4 (commit 55725513) */
eb742a1f
KZ
296 /* Probably NFSv4 where flock() is emulated by fcntl().
297 * Let's try to reopen in read-write mode.
298 */
299 if (!(open_flags & O_RDWR) &&
300 type != LOCK_SH &&
3c7fbc82 301 filename &&
eb742a1f
KZ
302 access(filename, R_OK | W_OK) == 0) {
303
304 close(fd);
305 open_flags = O_RDWR;
306 fd = open_file(filename, &open_flags);
307
308 if (open_flags & O_RDWR)
309 break;
310 }
b1557fe9 311 /* fallthrough */
75332f0f
SK
312 default:
313 /* Other errors */
314 if (filename)
d4eafec4 315 warn("%s", filename);
75332f0f 316 else
d4eafec4
SK
317 warn("%d", fd);
318 exit((errno == ENOLCK
319 || errno == ENOMEM) ? EX_OSERR : EX_DATAERR);
75332f0f
SK
320 }
321 }
322
bc3ae4c6 323 if (have_timeout)
378543e1 324 cancel_timer(&t_id);
59dc9f28
SK
325 if (verbose) {
326 struct timeval delta;
75332f0f 327
59dc9f28
SK
328 gettime_monotonic(&time_done);
329 timersub(&time_done, &time_start, &delta);
330 printf(_("%s: getting lock took %ld.%06ld seconds\n"),
331 program_invocation_short_name, delta.tv_sec,
332 delta.tv_usec);
333 }
56d45cfa 334 status = EX_OK;
75332f0f
SK
335
336 if (cmd_argv) {
337 pid_t w, f;
338 /* Clear any inherited settings */
339 signal(SIGCHLD, SIG_DFL);
59dc9f28 340 if (verbose)
b5575bc0 341 printf(_("%s: executing %s\n"), program_invocation_short_name, cmd_argv[0]);
75332f0f 342
70325140
TB
343 if (!no_fork) {
344 f = fork();
391e675c 345 if (f < 0)
70325140 346 err(EX_OSERR, _("fork failed"));
391e675c
KZ
347
348 /* child */
93fea49f
KZ
349 else if (f == 0) {
350 if (do_close)
351 close(fd);
352 run_program(cmd_argv);
391e675c
KZ
353
354 /* parent */
93fea49f 355 } else {
391e675c
KZ
356 do {
357 w = waitpid(f, &status, 0);
358 if (w == -1 && errno != EINTR)
70325140 359 break;
391e675c 360 } while (w != f);
75332f0f 361
391e675c 362 if (w == -1) {
70325140
TB
363 status = EXIT_FAILURE;
364 warn(_("waitpid failed"));
365 } else if (WIFEXITED(status))
366 status = WEXITSTATUS(status);
367 else if (WIFSIGNALED(status))
368 status = WTERMSIG(status) + 128;
369 else
370 /* WTF? */
371 status = EX_OSERR;
70325140 372 }
391e675c
KZ
373
374 } else
375 /* no-fork execution */
93fea49f 376 run_program(cmd_argv);
75332f0f
SK
377 }
378
379 return status;
d162fcb5 380}