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