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