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