tdb2: test: try (almost) all tests with TDB_VERSION1 flag.
[ccan] / ccan / tdb2 / test / run-fork-test.c
1 /* Test forking while holding lock.
2  *
3  * There are only five ways to do this currently:
4  * (1) grab a tdb_chainlock, then fork.
5  * (2) grab a tdb_lockall, then fork.
6  * (3) grab a tdb_lockall_read, then fork.
7  * (4) start a transaction, then fork.
8  * (5) fork from inside a tdb_parse() callback.
9  *
10  * Note that we don't hold a lock across tdb_traverse callbacks, so
11  * that doesn't matter.
12  */
13 #include "tdb2-source.h"
14 #include <ccan/tap/tap.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include "logging.h"
18
19 static enum TDB_ERROR fork_in_parse(TDB_DATA key, TDB_DATA data,
20                                     struct tdb_context *tdb)
21 {
22         int status, extra_messages;
23
24         if (tdb_get_flags(tdb) & TDB_VERSION1) {
25                 extra_messages = 1;
26         } else {
27                 extra_messages = 0;
28         }
29
30         if (fork() == 0) {
31                 /* We expect this to fail. */
32                 if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
33                         exit(1);
34                 tap_log_messages -= extra_messages;
35
36                 if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
37                         exit(1);
38
39                 tap_log_messages -= extra_messages;
40                 if (tap_log_messages != 2)
41                         exit(2);
42
43                 tdb_close(tdb);
44                 if (tap_log_messages != 2)
45                         exit(3);
46                 exit(0);
47         }
48         wait(&status);
49         ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
50         return TDB_SUCCESS;
51 }
52
53 int main(int argc, char *argv[])
54 {
55         unsigned int i;
56         struct tdb_context *tdb;
57         int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
58                         TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT,
59                         TDB_VERSION1, TDB_NOMMAP|TDB_VERSION1,
60                         TDB_CONVERT|TDB_VERSION1,
61                         TDB_NOMMAP|TDB_CONVERT|TDB_VERSION1 };
62         struct tdb_data key = tdb_mkdata("key", 3);
63         struct tdb_data data = tdb_mkdata("data", 4);
64
65         plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
66         for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
67                 int status, extra_messages;
68
69                 if (flags[i] & TDB_VERSION1) {
70                         extra_messages = 1;
71                 } else {
72                         extra_messages = 0;
73                 }
74
75                 tap_log_messages = 0;
76
77                 tdb = tdb_open("run-fork-test.tdb", flags[i],
78                                O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
79                 if (!ok1(tdb))
80                         continue;
81
82                 /* Put a record in here. */
83                 ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_SUCCESS);
84
85                 ok1(tdb_chainlock(tdb, key) == TDB_SUCCESS);
86                 if (fork() == 0) {
87                         /* We expect this to fail. */
88                         if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
89                                 return 1;
90                         tap_log_messages -= extra_messages;
91
92                         if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
93                                 return 1;
94                         tap_log_messages -= extra_messages;
95
96                         if (tap_log_messages != 2)
97                                 return 2;
98
99                         tdb_chainunlock(tdb, key);
100                         if (tap_log_messages != 3)
101                                 return 3;
102                         tdb_close(tdb);
103                         if (tap_log_messages != 3)
104                                 return 4;
105                         return 0;
106                 }
107                 wait(&status);
108                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
109                 tdb_chainunlock(tdb, key);
110
111                 ok1(tdb_lockall(tdb) == TDB_SUCCESS);
112                 if (fork() == 0) {
113                         /* We expect this to fail. */
114                         if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
115                                 return 1;
116                         tap_log_messages -= extra_messages;
117
118                         if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
119                                 return 1;
120                         tap_log_messages -= extra_messages;
121
122                         if (tap_log_messages != 2)
123                                 return 2;
124
125                         tdb_unlockall(tdb);
126                         if (tap_log_messages != 2)
127                                 return 3;
128                         tdb_close(tdb);
129                         if (tap_log_messages != 2)
130                                 return 4;
131                         return 0;
132                 }
133                 wait(&status);
134                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
135                 tdb_unlockall(tdb);
136
137                 ok1(tdb_lockall_read(tdb) == TDB_SUCCESS);
138                 if (fork() == 0) {
139                         /* We expect this to fail. */
140                         /* This would always fail anyway... */
141                         if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
142                                 return 1;
143                         tap_log_messages -= extra_messages;
144
145                         if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
146                                 return 1;
147                         tap_log_messages -= extra_messages;
148
149                         if (tap_log_messages != 2)
150                                 return 2;
151
152                         tdb_unlockall_read(tdb);
153                         if (tap_log_messages != 2)
154                                 return 3;
155                         tdb_close(tdb);
156                         if (tap_log_messages != 2)
157                                 return 4;
158                         return 0;
159                 }
160                 wait(&status);
161                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
162                 tdb_unlockall_read(tdb);
163
164                 ok1(tdb_transaction_start(tdb) == TDB_SUCCESS);
165                 /* If transactions is empty, noop "commit" succeeds. */
166                 ok1(tdb_delete(tdb, key) == TDB_SUCCESS);
167                 if (fork() == 0) {
168                         /* We expect this to fail. */
169                         if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
170                                 return 1;
171                         tap_log_messages -= extra_messages;
172
173                         if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
174                                 return 1;
175                         tap_log_messages -= extra_messages;
176
177                         if (tap_log_messages != 2)
178                                 return 2;
179
180                         if (tdb_transaction_commit(tdb) != TDB_ERR_LOCK)
181                                 return 3;
182                         tap_log_messages -= extra_messages;
183
184                         tdb_close(tdb);
185                         if (tap_log_messages < 3)
186                                 return 4;
187                         return 0;
188                 }
189                 wait(&status);
190                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
191                 tdb_transaction_cancel(tdb);
192
193                 ok1(tdb_parse_record(tdb, key, fork_in_parse, tdb)
194                     == TDB_SUCCESS);
195                 tdb_close(tdb);
196                 ok1(tap_log_messages == 0);
197         }
198         return exit_status();
199 }