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