2734742571255f300a8f83be33f0953677631ba9
[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;
23
24         if (fork() == 0) {
25                 /* We expect this to fail. */
26                 if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
27                         exit(1);
28
29                 if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
30                         exit(1);
31
32                 if (tap_log_messages != 2)
33                         exit(2);
34
35                 tdb_close(tdb);
36                 if (tap_log_messages != 2)
37                         exit(3);
38                 exit(0);
39         }
40         wait(&status);
41         ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
42         return TDB_SUCCESS;
43 }
44
45 int main(int argc, char *argv[])
46 {
47         unsigned int i;
48         struct tdb_context *tdb;
49         int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
50                         TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
51         struct tdb_data key = tdb_mkdata("key", 3);
52         struct tdb_data data = tdb_mkdata("data", 4);
53
54         plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
55         for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
56                 int status;
57
58                 tap_log_messages = 0;
59
60                 tdb = tdb_open("run-fork-test.tdb", flags[i],
61                                O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
62                 if (!ok1(tdb))
63                         continue;
64
65                 /* Put a record in here. */
66                 ok1(tdb_store(tdb, key, data, TDB_REPLACE) == TDB_SUCCESS);
67
68                 ok1(tdb_chainlock(tdb, key) == TDB_SUCCESS);
69                 if (fork() == 0) {
70                         /* We expect this to fail. */
71                         if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
72                                 return 1;
73
74                         if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
75                                 return 1;
76
77                         if (tap_log_messages != 2)
78                                 return 2;
79
80                         tdb_chainunlock(tdb, key);
81                         if (tap_log_messages != 3)
82                                 return 3;
83                         tdb_close(tdb);
84                         if (tap_log_messages != 3)
85                                 return 4;
86                         return 0;
87                 }
88                 wait(&status);
89                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
90                 tdb_chainunlock(tdb, key);
91
92                 ok1(tdb_lockall(tdb) == TDB_SUCCESS);
93                 if (fork() == 0) {
94                         /* We expect this to fail. */
95                         if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
96                                 return 1;
97
98                         if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
99                                 return 1;
100
101                         if (tap_log_messages != 2)
102                                 return 2;
103
104                         tdb_unlockall(tdb);
105                         if (tap_log_messages != 2)
106                                 return 3;
107                         tdb_close(tdb);
108                         if (tap_log_messages != 2)
109                                 return 4;
110                         return 0;
111                 }
112                 wait(&status);
113                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
114                 tdb_unlockall(tdb);
115
116                 ok1(tdb_lockall_read(tdb) == TDB_SUCCESS);
117                 if (fork() == 0) {
118                         /* We expect this to fail. */
119                         /* This would always fail anyway... */
120                         if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
121                                 return 1;
122
123                         if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
124                                 return 1;
125
126                         if (tap_log_messages != 2)
127                                 return 2;
128
129                         tdb_unlockall_read(tdb);
130                         if (tap_log_messages != 2)
131                                 return 3;
132                         tdb_close(tdb);
133                         if (tap_log_messages != 2)
134                                 return 4;
135                         return 0;
136                 }
137                 wait(&status);
138                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
139                 tdb_unlockall_read(tdb);
140
141                 ok1(tdb_transaction_start(tdb) == TDB_SUCCESS);
142                 /* If transactions is empty, noop "commit" succeeds. */
143                 ok1(tdb_delete(tdb, key) == TDB_SUCCESS);
144                 if (fork() == 0) {
145                         /* We expect this to fail. */
146                         if (tdb_store(tdb, key, data, TDB_REPLACE) != TDB_ERR_LOCK)
147                                 return 1;
148
149                         if (tdb_fetch(tdb, key, &data) != TDB_ERR_LOCK)
150                                 return 1;
151
152                         if (tap_log_messages != 2)
153                                 return 2;
154
155                         if (tdb_transaction_commit(tdb) != TDB_ERR_LOCK)
156                                 return 3;
157
158                         tdb_close(tdb);
159                         if (tap_log_messages < 3)
160                                 return 4;
161                         return 0;
162                 }
163                 wait(&status);
164                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
165                 tdb_transaction_cancel(tdb);
166
167                 ok1(tdb_parse_record(tdb, key, fork_in_parse, tdb)
168                     == TDB_SUCCESS);
169                 tdb_close(tdb);
170                 ok1(tap_log_messages == 0);
171         }
172         return exit_status();
173 }