lintian doesn't like orphan packages with uploaders...
[debian/amanda] / common-src / amflock-test.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27
28 #include "amanda.h"
29 #include "testutils.h"
30 #include "glib-util.h"
31
32 /* from amflock.c */
33 extern amflock_impl_t *amflock_impls[];
34
35 #define TEST_FILENAME "./amflocktest.file"
36
37 /* Test all amflock implementations available for basic 
38  * functionality
39  */
40 static gboolean
41 test_old_impls(void)
42 {
43     amflock_impl_t **imp = amflock_impls;
44     char *resource = "rez";
45     int fd;
46     int lock_ro;
47
48     /* set lnlock's lock directory to the current directory */
49     extern char *_lnlock_dir;
50     _lnlock_dir = ".";
51
52     while (*imp) {
53         tu_dbg("Testing amflock-%s\n", (*imp)->impl_name);
54
55         for (lock_ro = 0; lock_ro < 2; lock_ro++) { /* false (0) or true (1) */
56             if (unlink(TEST_FILENAME) == -1 && errno != ENOENT) {
57                 perror("unlink");
58                 return FALSE;
59             }
60
61             if ((fd = open(TEST_FILENAME, O_RDWR | O_CREAT | O_EXCL, 0600)) == -1) {
62                 perror("open");
63                 return FALSE;
64             }
65
66             if (lock_ro) {
67                 if ((*imp)->amroflock_impl(fd, resource) != 0) {
68                     perror("amroflock");
69                     return FALSE;
70                 }
71             } else {
72                 if ((*imp)->amflock_impl(fd, resource) != 0) {
73                     perror("amflock");
74                     return FALSE;
75                 }
76             }
77
78             if ((*imp)->amfunlock_impl(fd, resource) != 0) {
79                 perror("amfunlock");
80                 return FALSE;
81             }
82
83             close(fd); /* ignore error */
84             unlink(TEST_FILENAME); /* ignore error */
85         }
86
87         fprintf(stderr, "  PASS amflock-%s\n", (*imp)->impl_name);
88
89         imp++;
90     }
91
92     return TRUE;
93 }
94
95 /*
96  * Test lock and write_and_unlock
97  */
98 static gboolean
99 inc_counter(file_lock *lock)
100 {
101     char old_val = 'a';
102     char new_val;
103
104     if (lock->len) {
105         old_val = lock->data[0];
106     }
107
108     g_assert(old_val < 'z');
109
110     new_val = old_val + 1;
111     if (file_lock_write(lock, &new_val, 1) == -1) {
112         g_fprintf(stderr, "file_lock_write: %s\n",
113                         strerror(errno));
114         return FALSE;
115     }
116
117     return TRUE;
118 }
119
120 #define pipeget(fd, cp) \
121     (read((fd), (cp), 1) == 1)
122
123 #define pipeput(fd, s) \
124     g_assert(write((fd), s, 1) == 1);
125
126 static void
127 locking_slave(int in_fd, int out_fd)
128 {
129     char cmd;
130     int rv;
131     file_lock *lock = file_lock_new(TEST_FILENAME);
132     gboolean locked = 0;
133
134     while (1) {
135         if (!pipeget(in_fd, &cmd))
136             cmd = 'q';
137
138         switch (cmd) {
139             case 'q': /* q = quit */
140                 tu_dbg("slave: quitting\n");
141                 file_lock_free(lock);
142                 lock = NULL;
143                 return;
144
145             case 'l': /* l = try locking; reply with 'y' or 'n' */
146                 g_assert(!locked);
147                 rv = file_lock_lock(lock);
148                 if (rv == -1) {
149                     g_fprintf(stderr, "file_lock_lock: %s\n",
150                                     strerror(errno));
151                     return;
152                 }
153                 tu_dbg("slave: lock attempt => %s\n", (rv == 1)? "n" : "y");
154                 pipeput(out_fd, (rv == 1)? "n" : "y");
155                 if (rv != 1)
156                     locked = 1;
157                 break;
158
159             case 'i': /* i = increment counter, reply with new value */
160                 g_assert(locked);
161                 if (!inc_counter(lock))
162                     return;
163                 tu_dbg("slave: inc'd to %c\n", lock->data[0]);
164                 pipeput(out_fd, lock->data);
165                 break;
166
167             case 'u': /* u = try unlocking; reply with 'k' */
168                 g_assert(locked);
169                 rv = file_lock_unlock(lock);
170                 if (rv != 0) {
171                     g_fprintf(stderr, "file_lock_unlock: %s\n",
172                             strerror(errno));
173                     return;
174                 }
175                 tu_dbg("slave: unlocked\n");
176                 pipeput(out_fd, "k");
177                 locked = 0;
178                 break;
179
180             default:
181                 return;
182         }
183     }
184 }
185
186 static int
187 locking_master(int in_fd, int out_fd)
188 {
189     file_lock *lock = file_lock_new(TEST_FILENAME);
190     int rv;
191     char slaveres;
192
193     /* start by locking here and incrementing the value */
194     rv = file_lock_lock(lock);
195     if (rv == -1) {
196         g_fprintf(stderr, "file_lock_lock: %s\n", strerror(errno));
197         return 0;
198     }
199     g_assert(rv != 1); /* not already locked */
200     tu_dbg("master: locked\n");
201
202     if (!inc_counter(lock))
203         return 0;
204
205     g_assert(lock->data[0] == 'b');
206     tu_dbg("master: inc'd to b\n");
207
208     /* unlock and re-lock */
209     rv = file_lock_unlock(lock);
210     if (rv != 0) {
211         g_fprintf(stderr, "file_lock_unlock: %s\n", strerror(errno));
212         return 0;
213     }
214     tu_dbg("master: unlocked\n");
215
216     rv = file_lock_lock(lock);
217     if (rv == -1) {
218         g_fprintf(stderr, "file_lock_lock: %s\n", strerror(errno));
219         return 0;
220     }
221     g_assert(rv != 1); /* not already locked */
222     tu_dbg("master: locked\n");
223
224     /* inc it again */
225     g_assert(lock->data[0] == 'b');
226     inc_counter(lock);
227     g_assert(lock->data[0] == 'c');
228     tu_dbg("master: inc'd to c\n");
229
230     /* the slave should fail to get a lock now */
231     pipeput(out_fd, "l");
232     g_assert(pipeget(in_fd, &slaveres));
233     g_assert(slaveres == 'n');
234
235     /* and, finally unlock */
236     rv = file_lock_unlock(lock);
237     if (rv != 0) {
238         g_fprintf(stderr, "file_lock_unlock: %s\n", strerror(errno));
239         return 0;
240     }
241     tu_dbg("master: unlocked\n");
242
243     /* the slave should succeed now */
244     pipeput(out_fd, "l");
245     g_assert(pipeget(in_fd, &slaveres));
246     g_assert(slaveres == 'y');
247
248     pipeput(out_fd, "i");
249     g_assert(pipeget(in_fd, &slaveres));
250     g_assert(slaveres == 'd');
251
252     /* master shouldn't be able to lock now */
253     rv = file_lock_lock(lock);
254     if (rv == -1) {
255         g_fprintf(stderr, "file_lock_lock: %s\n", strerror(errno));
256         return 0;
257     }
258     g_assert(rv == 1); /* already locked */
259     tu_dbg("master: lock attempt failed (as expected)\n");
260
261     pipeput(out_fd, "i");
262     g_assert(pipeget(in_fd, &slaveres));
263     g_assert(slaveres == 'e');
264
265     /* get the slave to unlock */
266     pipeput(out_fd, "u");
267     g_assert(pipeget(in_fd, &slaveres));
268     g_assert(slaveres == 'k');
269
270     /* we should get a lock now */
271     rv = file_lock_lock(lock);
272     if (rv == -1) {
273         g_fprintf(stderr, "file_lock_lock: %s\n", strerror(errno));
274         return 0;
275     }
276     g_assert(rv != 1); /* not already locked */
277     tu_dbg("master: lock attempt succeeded\n");
278
279     g_assert(lock->data[0] == 'e');
280
281     /* leave it unlocked, just to see what happens */
282
283     return 1;
284 }
285
286 static gpointer
287 test_intra_proc_locking_thd(gpointer *fdptr)
288 {
289     int *fds = (int *)fdptr;
290     locking_slave(fds[0], fds[1]);
291     return NULL;
292 }
293
294 static gboolean
295 test_intra_proc_locking(void)
296 {
297     GThread *thd;
298     int outpipe[2], inpipe[2];
299     int thd_fds[2];
300     int rv;
301
302     unlink(TEST_FILENAME);
303
304     g_assert(pipe(outpipe) == 0);
305     g_assert(pipe(inpipe) == 0);
306
307     thd_fds[0] = outpipe[0];
308     thd_fds[1] = inpipe[1];
309     thd = g_thread_create((GThreadFunc)test_intra_proc_locking_thd, (gpointer)thd_fds, TRUE, NULL);
310
311     rv = locking_master(inpipe[0], outpipe[1]);
312
313     /* close the write end of the outgoing pipe, which should trigger an EOF on
314      * the slave if it's still running */
315     close(outpipe[1]);
316     g_thread_join(thd);
317     unlink(TEST_FILENAME);
318
319     /* caller will kill the remaining files */
320
321     return rv;
322 }
323
324 static gboolean
325 test_inter_proc_locking(void)
326 {
327     int outpipe[2], inpipe[2];
328     int pid;
329     int rv;
330
331     unlink(TEST_FILENAME);
332
333     g_assert(pipe(outpipe) == 0);
334     g_assert(pipe(inpipe) == 0);
335
336     if ((pid = fork()) == 0) {
337         close(outpipe[1]);
338         close(inpipe[0]);
339         locking_slave(outpipe[0], inpipe[1]);
340         exit(0);
341     }
342
343     close(outpipe[0]);
344     close(inpipe[1]);
345
346     rv = locking_master(inpipe[0], outpipe[1]);
347
348     /* close the write end of the outgoing pipe, which should trigger an EOF on
349      * the slave if it's still running */
350     close(outpipe[1]);
351     waitpid(pid, NULL, 0);
352     unlink(TEST_FILENAME);
353
354     /* caller will kill the remaining files */
355
356     return rv;
357 }
358
359 int
360 main(int argc, char **argv)
361 {
362     static TestUtilsTest tests[] = {
363         TU_TEST(test_old_impls, 90),
364         TU_TEST(test_inter_proc_locking, 60),
365         TU_TEST(test_intra_proc_locking, 60),
366         TU_END()
367     };
368
369     glib_init();
370     return testutils_run_tests(argc, argv, tests);
371 }