tdb: fix tbdtool list freelist output
[samba.git] / lib / tdb / test / run-mutex-die.c
1 #include "../common/tdb_private.h"
2 #include "lock-tracking.h"
3 static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset);
4 static ssize_t write_check(int fd, const void *buf, size_t count);
5 static int ftruncate_check(int fd, off_t length);
6
7 #define pwrite pwrite_check
8 #define write write_check
9 #define fcntl fcntl_with_lockcheck
10 #define ftruncate ftruncate_check
11
12 #include "../common/io.c"
13 #include "../common/tdb.c"
14 #include "../common/lock.c"
15 #include "../common/freelist.c"
16 #include "../common/traverse.c"
17 #include "../common/transaction.c"
18 #include "../common/error.c"
19 #include "../common/open.c"
20 #include "../common/check.c"
21 #include "../common/hash.c"
22 #include "../common/mutex.c"
23 #include "tap-interface.h"
24 #include <stdlib.h>
25 #include <stdbool.h>
26 #include <stdarg.h>
27 #include "external-agent.h"
28 #include "logging.h"
29
30 #undef write
31 #undef pwrite
32 #undef fcntl
33 #undef ftruncate
34
35 static int target, current;
36 #define TEST_DBNAME "run-mutex-die.tdb"
37 #define KEY_STRING "helloworld"
38
39 static void maybe_die(int fd)
40 {
41         if (target == 0) {
42                 return;
43         }
44         current += 1;
45         if (current == target) {
46                 _exit(1);
47         }
48 }
49
50 static ssize_t pwrite_check(int fd,
51                             const void *buf, size_t count, off_t offset)
52 {
53         ssize_t ret;
54
55         maybe_die(fd);
56
57         ret = pwrite(fd, buf, count, offset);
58         if (ret != count)
59                 return ret;
60
61         maybe_die(fd);
62         return ret;
63 }
64
65 static ssize_t write_check(int fd, const void *buf, size_t count)
66 {
67         ssize_t ret;
68
69         maybe_die(fd);
70
71         ret = write(fd, buf, count);
72         if (ret != count)
73                 return ret;
74
75         maybe_die(fd);
76         return ret;
77 }
78
79 static int ftruncate_check(int fd, off_t length)
80 {
81         int ret;
82
83         maybe_die(fd);
84
85         ret = ftruncate(fd, length);
86
87         maybe_die(fd);
88         return ret;
89 }
90
91 static enum agent_return flakey_ops(struct agent *a)
92 {
93         enum agent_return ret;
94
95         /*
96          * Run in the external agent child
97          */
98
99         ret = external_agent_operation(a, OPEN_WITH_CLEAR_IF_FIRST, TEST_DBNAME);
100         if (ret != SUCCESS) {
101                 fprintf(stderr, "Agent failed to open: %s\n",
102                         agent_return_name(ret));
103                 return ret;
104         }
105         ret = external_agent_operation(a, UNMAP, "");
106         if (ret != SUCCESS) {
107                 fprintf(stderr, "Agent failed to unmap: %s\n",
108                         agent_return_name(ret));
109                 return ret;
110         }
111         ret = external_agent_operation(a, STORE, "xyz");
112         if (ret != SUCCESS) {
113                 fprintf(stderr, "Agent failed to store: %s\n",
114                         agent_return_name(ret));
115                 return ret;
116         }
117         ret = external_agent_operation(a, STORE, KEY_STRING);
118         if (ret != SUCCESS) {
119                 fprintf(stderr, "Agent failed store: %s\n",
120                         agent_return_name(ret));
121                 return ret;
122         }
123         ret = external_agent_operation(a, FETCH, KEY_STRING);
124         if (ret != SUCCESS) {
125                 fprintf(stderr, "Agent failed find key: %s\n",
126                         agent_return_name(ret));
127                 return ret;
128         }
129         ret = external_agent_operation(a, PING, "");
130         if (ret != SUCCESS) {
131                 fprintf(stderr, "Agent failed ping: %s\n",
132                         agent_return_name(ret));
133                 return ret;
134         }
135         return ret;
136 }
137
138 static bool prep_db(void) {
139         struct tdb_context *tdb;
140         TDB_DATA key;
141         TDB_DATA data;
142
143         key.dptr = discard_const_p(uint8_t, KEY_STRING);
144         key.dsize = strlen((char *)key.dptr);
145         data.dptr = discard_const_p(uint8_t, "foo");
146         data.dsize = strlen((char *)data.dptr);
147
148         unlink(TEST_DBNAME);
149
150         tdb = tdb_open_ex(
151                 TEST_DBNAME, 2,
152                 TDB_INCOMPATIBLE_HASH|TDB_MUTEX_LOCKING|TDB_CLEAR_IF_FIRST,
153                 O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL);
154         if (tdb == NULL) {
155                 return false;
156         }
157
158         if (tdb_store(tdb, key, data, TDB_INSERT) != 0) {
159                 return false;
160         }
161
162         tdb_close(tdb);
163         tdb = NULL;
164
165         forget_locking();
166
167         return true;
168 }
169
170 static bool test_db(void) {
171         struct tdb_context *tdb;
172         int ret;
173
174         tdb = tdb_open_ex(
175                 TEST_DBNAME, 1024, TDB_INCOMPATIBLE_HASH,
176                 O_RDWR, 0600, &taplogctx, NULL);
177
178         if (tdb == NULL) {
179                 perror("tdb_open_ex failed");
180                 return false;
181         }
182
183         ret = tdb_traverse(tdb, NULL, NULL);
184         if (ret == -1) {
185                 perror("traverse failed");
186                 goto fail;
187         }
188
189         tdb_close(tdb);
190
191         forget_locking();
192
193         return true;
194
195 fail:
196         tdb_close(tdb);
197         return false;
198 }
199
200 static bool test_one(void)
201 {
202         enum agent_return ret;
203
204         ret = AGENT_DIED;
205         target = 19;
206
207         while (ret != SUCCESS) {
208                 struct agent *agent;
209
210                 {
211                         int child_target = target;
212                         bool pret;
213                         target = 0;
214                         pret = prep_db();
215                         ok1(pret);
216                         target = child_target;
217                 }
218
219                 agent = prepare_external_agent();
220
221                 ret = flakey_ops(agent);
222
223                 diag("Agent (target=%d) returns %s",
224                      target, agent_return_name(ret));
225
226                 if (ret == SUCCESS) {
227                         ok((target > 19), "At least one AGENT_DIED expected");
228                 } else {
229                         ok(ret == AGENT_DIED, "AGENT_DIED expected");
230                 }
231
232                 shutdown_agent(agent);
233
234                 {
235                         int child_target = target;
236                         bool tret;
237                         target = 0;
238                         tret = test_db();
239                         ok1(tret);
240                         target = child_target;
241                 }
242
243                 target += 1;
244         }
245
246         return true;
247 }
248
249 int main(int argc, char *argv[])
250 {
251         bool ret;
252         bool runtime_support;
253
254         runtime_support = tdb_runtime_check_for_robust_mutexes();
255
256         if (!runtime_support) {
257                 skip(1, "No robust mutex support");
258                 return exit_status();
259         }
260
261         plan_tests(12);
262         unlock_callback = maybe_die;
263
264         ret = test_one();
265         ok1(ret);
266
267         diag("done");
268         return exit_status();
269 }