]>
Commit | Line | Data |
---|---|---|
ac718c9d | 1 | /* |
77c513a4 | 2 | * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. |
ac718c9d BP |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | ||
19 | #include "lockfile.h" | |
20 | ||
21 | #include <errno.h> | |
22 | #include <stdlib.h> | |
77c513a4 | 23 | #include <sys/stat.h> |
ac718c9d BP |
24 | #include <sys/wait.h> |
25 | #include <unistd.h> | |
26 | ||
27 | #include "process.h" | |
28 | #include "timeval.h" | |
29 | #include "util.h" | |
1e8cf0f7 | 30 | #include "vlog.h" |
ac718c9d | 31 | |
ac718c9d BP |
32 | struct test { |
33 | const char *name; | |
34 | void (*function)(void); | |
35 | }; | |
36 | ||
37 | static const struct test tests[]; | |
38 | ||
3d459fee BP |
39 | #define CHECK(A, B) check(A, B, #A, #B, __FILE__, __LINE__) |
40 | static void | |
41 | check(int a, int b, | |
42 | const char *a_string, const char *b_string, const char *file, int line) | |
43 | { | |
44 | if (a != b) { | |
45 | fprintf(stderr, "%s:%d: expected %s == %s but %d != %d\n", | |
46 | file, line, a_string, b_string, a, b); | |
47 | fflush(stderr); | |
48 | abort(); | |
49 | } | |
50 | } | |
51 | ||
ac718c9d BP |
52 | static void |
53 | run_lock_and_unlock(void) | |
54 | { | |
55 | struct lockfile *lockfile; | |
56 | ||
4770e795 | 57 | CHECK(lockfile_lock("file", &lockfile), 0); |
ac718c9d BP |
58 | lockfile_unlock(lockfile); |
59 | } | |
60 | ||
61 | static void | |
62 | run_lock_and_unlock_twice(void) | |
63 | { | |
64 | struct lockfile *lockfile; | |
65 | ||
4770e795 | 66 | CHECK(lockfile_lock("file", &lockfile), 0); |
ac718c9d BP |
67 | lockfile_unlock(lockfile); |
68 | ||
4770e795 | 69 | CHECK(lockfile_lock("file", &lockfile), 0); |
ac718c9d BP |
70 | lockfile_unlock(lockfile); |
71 | } | |
72 | ||
73 | static void | |
74 | run_lock_blocks_same_process(void) | |
75 | { | |
76 | struct lockfile *lockfile; | |
77 | ||
4770e795 LA |
78 | CHECK(lockfile_lock("file", &lockfile), 0); |
79 | CHECK(lockfile_lock("file", &lockfile), EDEADLK); | |
ac718c9d BP |
80 | lockfile_unlock(lockfile); |
81 | } | |
82 | ||
83 | static void | |
84 | run_lock_blocks_same_process_twice(void) | |
85 | { | |
86 | struct lockfile *lockfile; | |
87 | ||
4770e795 LA |
88 | CHECK(lockfile_lock("file", &lockfile), 0); |
89 | CHECK(lockfile_lock("file", &lockfile), EDEADLK); | |
90 | CHECK(lockfile_lock("file", &lockfile), EDEADLK); | |
ac718c9d BP |
91 | lockfile_unlock(lockfile); |
92 | } | |
93 | ||
94 | static enum { PARENT, CHILD } | |
95 | do_fork(void) | |
96 | { | |
97 | switch (fork()) { | |
98 | case 0: | |
99 | time_postfork(); | |
100 | lockfile_postfork(); | |
101 | return CHILD; | |
102 | ||
103 | default: | |
104 | return PARENT; | |
105 | ||
106 | case -1: | |
107 | /* Error. */ | |
108 | ovs_fatal(errno, "fork failed"); | |
109 | } | |
110 | } | |
111 | ||
112 | static void | |
113 | run_lock_blocks_other_process(void) | |
114 | { | |
93ff0290 BP |
115 | /* Making this static prevents a memory leak warning from valgrind for the |
116 | * parent process, which cannot easily unlock (and free) 'lockfile' because | |
117 | * it can only do so after the child has exited, and it's the caller of | |
118 | * this function that does the wait() call. */ | |
119 | static struct lockfile *lockfile; | |
ac718c9d | 120 | |
4770e795 | 121 | CHECK(lockfile_lock("file", &lockfile), 0); |
ac718c9d | 122 | if (do_fork() == CHILD) { |
93ff0290 | 123 | lockfile_unlock(lockfile); |
4770e795 | 124 | CHECK(lockfile_lock("file", &lockfile), EAGAIN); |
ac718c9d BP |
125 | exit(11); |
126 | } | |
127 | } | |
128 | ||
129 | static void | |
130 | run_lock_twice_blocks_other_process(void) | |
131 | { | |
132 | struct lockfile *lockfile, *dummy; | |
133 | ||
4770e795 LA |
134 | CHECK(lockfile_lock("file", &lockfile), 0); |
135 | CHECK(lockfile_lock("file", &dummy), EDEADLK); | |
ac718c9d | 136 | if (do_fork() == CHILD) { |
4770e795 | 137 | CHECK(lockfile_lock("file", &dummy), EAGAIN); |
ac718c9d BP |
138 | exit(11); |
139 | } | |
140 | } | |
141 | ||
142 | static void | |
143 | run_lock_and_unlock_allows_other_process(void) | |
144 | { | |
145 | struct lockfile *lockfile; | |
146 | ||
4770e795 | 147 | CHECK(lockfile_lock("file", &lockfile), 0); |
ac718c9d BP |
148 | lockfile_unlock(lockfile); |
149 | ||
150 | if (do_fork() == CHILD) { | |
4770e795 | 151 | CHECK(lockfile_lock("file", &lockfile), 0); |
ac718c9d BP |
152 | exit(11); |
153 | } | |
154 | } | |
155 | ||
ac718c9d BP |
156 | static void |
157 | run_lock_multiple(void) | |
158 | { | |
159 | struct lockfile *a, *b, *c, *dummy; | |
160 | ||
4770e795 LA |
161 | CHECK(lockfile_lock("a", &a), 0); |
162 | CHECK(lockfile_lock("b", &b), 0); | |
163 | CHECK(lockfile_lock("c", &c), 0); | |
ac718c9d BP |
164 | |
165 | lockfile_unlock(a); | |
4770e795 LA |
166 | CHECK(lockfile_lock("a", &a), 0); |
167 | CHECK(lockfile_lock("a", &dummy), EDEADLK); | |
ac718c9d BP |
168 | lockfile_unlock(a); |
169 | ||
170 | lockfile_unlock(b); | |
4770e795 | 171 | CHECK(lockfile_lock("a", &a), 0); |
ac718c9d BP |
172 | |
173 | lockfile_unlock(c); | |
174 | lockfile_unlock(a); | |
175 | } | |
176 | ||
77c513a4 BP |
177 | /* Checks that locking a dangling symlink works OK. (It used to hang.) */ |
178 | static void | |
179 | run_lock_symlink(void) | |
180 | { | |
181 | struct lockfile *a, *b, *dummy; | |
182 | struct stat s; | |
183 | ||
184 | /* Create a symlink .a.~lock~ pointing to .b.~lock~. */ | |
185 | CHECK(symlink(".b.~lock~", ".a.~lock~"), 0); | |
186 | CHECK(lstat(".a.~lock~", &s), 0); | |
187 | CHECK(S_ISLNK(s.st_mode) != 0, 1); | |
188 | CHECK(stat(".a.~lock~", &s), -1); | |
189 | CHECK(errno, ENOENT); | |
190 | CHECK(stat(".b.~lock~", &s), -1); | |
191 | CHECK(errno, ENOENT); | |
192 | ||
4770e795 LA |
193 | CHECK(lockfile_lock("a", &a), 0); |
194 | CHECK(lockfile_lock("a", &dummy), EDEADLK); | |
195 | CHECK(lockfile_lock("b", &dummy), EDEADLK); | |
77c513a4 BP |
196 | lockfile_unlock(a); |
197 | ||
4770e795 LA |
198 | CHECK(lockfile_lock("b", &b), 0); |
199 | CHECK(lockfile_lock("b", &dummy), EDEADLK); | |
200 | CHECK(lockfile_lock("a", &dummy), EDEADLK); | |
77c513a4 BP |
201 | lockfile_unlock(b); |
202 | ||
203 | CHECK(lstat(".a.~lock~", &s), 0); | |
204 | CHECK(S_ISLNK(s.st_mode) != 0, 1); | |
205 | CHECK(stat(".a.~lock~", &s), 0); | |
206 | CHECK(S_ISREG(s.st_mode) != 0, 1); | |
207 | CHECK(stat(".b.~lock~", &s), 0); | |
208 | CHECK(S_ISREG(s.st_mode) != 0, 1); | |
209 | } | |
210 | ||
b1fdc5fb BP |
211 | /* Checks that locking a file that is itself a symlink yields a lockfile in the |
212 | * directory that the symlink points to, named for the target of the | |
213 | * symlink. | |
214 | * | |
215 | * (That is, if "a" is a symlink to "dir/b", then "a"'s lockfile is named | |
216 | * "dir/.b.~lock".) */ | |
217 | static void | |
218 | run_lock_symlink_to_dir(void) | |
219 | { | |
220 | struct lockfile *a, *dummy; | |
221 | struct stat s; | |
222 | ||
223 | /* Create a symlink "a" pointing to "dir/b". */ | |
224 | CHECK(mkdir("dir", 0700), 0); | |
225 | CHECK(symlink("dir/b", "a"), 0); | |
226 | CHECK(lstat("a", &s), 0); | |
227 | CHECK(S_ISLNK(s.st_mode) != 0, 1); | |
228 | ||
229 | /* Lock 'a'. */ | |
4770e795 | 230 | CHECK(lockfile_lock("a", &a), 0); |
b1fdc5fb BP |
231 | CHECK(lstat("dir/.b.~lock~", &s), 0); |
232 | CHECK(S_ISREG(s.st_mode) != 0, 1); | |
233 | CHECK(lstat(".a.~lock~", &s), -1); | |
234 | CHECK(errno, ENOENT); | |
4770e795 | 235 | CHECK(lockfile_lock("dir/b", &dummy), EDEADLK); |
b1fdc5fb BP |
236 | |
237 | lockfile_unlock(a); | |
238 | } | |
239 | ||
ac718c9d BP |
240 | static void |
241 | run_help(void) | |
242 | { | |
243 | size_t i; | |
244 | ||
245 | printf("usage: %s TESTNAME\n" | |
246 | "where TESTNAME is one of the following:\n", | |
247 | program_name); | |
248 | for (i = 0; tests[i].name; i++) { | |
249 | fprintf(stderr, "\t%s\n", tests[i].name); | |
250 | } | |
251 | } | |
252 | ||
253 | static const struct test tests[] = { | |
254 | #define TEST(NAME) { #NAME, run_##NAME } | |
255 | TEST(lock_and_unlock), | |
256 | TEST(lock_and_unlock_twice), | |
257 | TEST(lock_blocks_same_process), | |
258 | TEST(lock_blocks_same_process_twice), | |
259 | TEST(lock_blocks_other_process), | |
260 | TEST(lock_twice_blocks_other_process), | |
261 | TEST(lock_and_unlock_allows_other_process), | |
ac718c9d | 262 | TEST(lock_multiple), |
77c513a4 | 263 | TEST(lock_symlink), |
b1fdc5fb | 264 | TEST(lock_symlink_to_dir), |
ac718c9d | 265 | TEST(help), |
e3c17733 | 266 | { NULL, NULL } |
ac718c9d BP |
267 | #undef TEST |
268 | }; | |
269 | ||
270 | int | |
271 | main(int argc, char *argv[]) | |
272 | { | |
273 | size_t i; | |
274 | ||
275 | set_program_name(argv[0]); | |
db90f940 BP |
276 | vlog_set_pattern(VLF_CONSOLE, "%c|%p|%m"); |
277 | vlog_set_levels(NULL, VLF_SYSLOG, VLL_OFF); | |
ac718c9d BP |
278 | |
279 | if (argc != 2) { | |
280 | ovs_fatal(0, "exactly one argument required; use \"%s help\" for help", | |
281 | program_name); | |
282 | return 1; | |
283 | } | |
284 | ||
285 | for (i = 0; tests[i].name; i++) { | |
286 | if (!strcmp(argv[1], tests[i].name)) { | |
287 | int n_children; | |
288 | int status; | |
289 | ||
290 | (tests[i].function)(); | |
291 | ||
292 | n_children = 0; | |
293 | while (wait(&status) > 0) { | |
294 | if (WIFEXITED(status) && WEXITSTATUS(status) == 11) { | |
295 | n_children++; | |
296 | } else { | |
297 | ovs_fatal(0, "child exited in unexpected way: %s", | |
298 | process_status_msg(status)); | |
299 | } | |
300 | } | |
301 | if (errno != ECHILD) { | |
302 | ovs_fatal(errno, "wait"); | |
303 | } | |
304 | ||
305 | printf("%s: success (%d child%s)\n", | |
306 | tests[i].name, n_children, n_children != 1 ? "ren" : ""); | |
307 | exit(0); | |
308 | } | |
309 | } | |
310 | ovs_fatal(0, "unknown test \"%s\"; use \"%s help\" for help", | |
311 | argv[1], program_name); | |
312 | } | |
313 |