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