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