blob: f34c0ad8f0458a89d3f24b326b49e9e3c71cae98
1 | /* |
2 | * Copyright (C) 2010 Timo Teras <timo.teras@iki.fi> |
3 | * |
4 | * This is free software, licensed under the GNU General Public License v2. |
5 | */ |
6 | //config:config FLOCK |
7 | //config: bool "flock" |
8 | //config: default y |
9 | //config: help |
10 | //config: Manage locks from shell scripts |
11 | |
12 | //applet:IF_FLOCK(APPLET(flock, BB_DIR_USR_BIN, BB_SUID_DROP)) |
13 | |
14 | //kbuild:lib-$(CONFIG_FLOCK) += flock.o |
15 | |
16 | //usage:#define flock_trivial_usage |
17 | //usage: "[-sxun] FD|{FILE [-c] PROG ARGS}" |
18 | //usage:#define flock_full_usage "\n\n" |
19 | //usage: "[Un]lock file descriptor, or lock FILE, run PROG\n" |
20 | //usage: "\n -s Shared lock" |
21 | //usage: "\n -x Exclusive lock (default)" |
22 | //usage: "\n -u Unlock FD" |
23 | //usage: "\n -n Fail rather than wait" |
24 | |
25 | #include <sys/file.h> |
26 | #include "libbb.h" |
27 | |
28 | int flock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
29 | int flock_main(int argc UNUSED_PARAM, char **argv) |
30 | { |
31 | int mode, opt, fd; |
32 | enum { |
33 | OPT_s = (1 << 0), |
34 | OPT_x = (1 << 1), |
35 | OPT_n = (1 << 2), |
36 | OPT_u = (1 << 3), |
37 | OPT_c = (1 << 4), |
38 | }; |
39 | |
40 | #if ENABLE_LONG_OPTS |
41 | static const char getopt_longopts[] ALIGN1 = |
42 | "shared\0" No_argument "s" |
43 | "exclusive\0" No_argument "x" |
44 | "unlock\0" No_argument "u" |
45 | "nonblock\0" No_argument "n" |
46 | ; |
47 | applet_long_options = getopt_longopts; |
48 | #endif |
49 | opt_complementary = "-1"; |
50 | |
51 | opt = getopt32(argv, "+sxnu"); |
52 | argv += optind; |
53 | |
54 | if (argv[1]) { |
55 | fd = open(argv[0], O_RDONLY|O_NOCTTY|O_CREAT, 0666); |
56 | if (fd < 0 && errno == EISDIR) |
57 | fd = open(argv[0], O_RDONLY|O_NOCTTY); |
58 | if (fd < 0) |
59 | bb_perror_msg_and_die("can't open '%s'", argv[0]); |
60 | //TODO? close_on_exec_on(fd); |
61 | } else { |
62 | fd = xatoi_positive(argv[0]); |
63 | } |
64 | argv++; |
65 | |
66 | /* If it is "flock FILE -c PROG", then -c isn't caught by getopt32: |
67 | * we use "+" in order to support "flock -opt FILE PROG -with-opts", |
68 | * we need to remove -c by hand. |
69 | */ |
70 | if (argv[0] |
71 | && argv[0][0] == '-' |
72 | && ( (argv[0][1] == 'c' && !argv[0][2]) |
73 | || (ENABLE_LONG_OPTS && strcmp(argv[0] + 1, "-command") == 0) |
74 | ) |
75 | ) { |
76 | argv++; |
77 | if (argv[1]) |
78 | bb_error_msg_and_die("-c takes only one argument"); |
79 | opt |= OPT_c; |
80 | } |
81 | |
82 | if (OPT_s == LOCK_SH && OPT_x == LOCK_EX && OPT_n == LOCK_NB && OPT_u == LOCK_UN) { |
83 | /* With suitably matched constants, mode setting is much simpler */ |
84 | mode = opt & (LOCK_SH + LOCK_EX + LOCK_NB + LOCK_UN); |
85 | if (!(mode & ~LOCK_NB)) |
86 | mode |= LOCK_EX; |
87 | } else { |
88 | if (opt & OPT_u) |
89 | mode = LOCK_UN; |
90 | else if (opt & OPT_s) |
91 | mode = LOCK_SH; |
92 | else |
93 | mode = LOCK_EX; |
94 | if (opt & OPT_n) |
95 | mode |= LOCK_NB; |
96 | } |
97 | |
98 | if (flock(fd, mode) != 0) { |
99 | if (errno == EWOULDBLOCK) |
100 | return EXIT_FAILURE; |
101 | bb_perror_nomsg_and_die(); |
102 | } |
103 | |
104 | if (argv[0]) { |
105 | int rc; |
106 | if (opt & OPT_c) { |
107 | /* -c 'PROG ARGS' means "run sh -c 'PROG ARGS'" */ |
108 | argv -= 2; |
109 | argv[0] = (char*)get_shell_name(); |
110 | argv[1] = (char*)"-c"; |
111 | /* argv[2] = "PROG ARGS"; */ |
112 | /* argv[3] = NULL; */ |
113 | } |
114 | rc = spawn_and_wait(argv); |
115 | if (rc < 0) |
116 | bb_simple_perror_msg(argv[0]); |
117 | return rc; |
118 | } |
119 | |
120 | return EXIT_SUCCESS; |
121 | } |
122 |