python:tests: Store keys as bytes rather than as lists of ints
[samba.git] / source3 / torture / test_dbwrap_watch.c
1 /*
2    Unix SMB/CIFS implementation.
3    Test dbwrap_watch API
4    Copyright (C) Volker Lendecke 2012
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "torture/proto.h"
22 #include "system/filesys.h"
23 #include "lib/dbwrap/dbwrap.h"
24 #include "lib/dbwrap/dbwrap_open.h"
25 #include "lib/dbwrap/dbwrap_watch.h"
26 #include "lib/util/util_tdb.h"
27
28 static bool test_dbwrap_watch_init(
29         TALLOC_CTX *mem_ctx,
30         const char *dbname,
31         struct tevent_context **pev,
32         struct messaging_context **pmsg,
33         struct db_context **pbackend,
34         struct db_context **pdb)
35 {
36         struct tevent_context *ev = NULL;
37         struct messaging_context *msg = NULL;
38         struct db_context *backend = NULL;
39         struct db_context *db = NULL;
40
41         ev = samba_tevent_context_init(mem_ctx);
42         if (ev == NULL) {
43                 fprintf(stderr, "tevent_context_init failed\n");
44                 goto fail;
45         }
46
47         msg = messaging_init(ev, ev);
48         if (msg == NULL) {
49                 fprintf(stderr, "messaging_init failed\n");
50                 goto fail;
51         }
52
53         backend = db_open(
54                 msg,
55                 dbname,
56                 0,
57                 TDB_CLEAR_IF_FIRST,
58                 O_CREAT|O_RDWR,
59                 0644,
60                 DBWRAP_LOCK_ORDER_1,
61                 DBWRAP_FLAG_NONE);
62         if (backend == NULL) {
63                 fprintf(stderr, "db_open failed: %s\n", strerror(errno));
64                 goto fail;
65         }
66
67         {
68                 struct db_context *backend_copy = backend;
69
70                 db = db_open_watched(ev, &backend_copy, msg);
71                 if (db == NULL) {
72                         fprintf(stderr, "db_open_watched failed\n");
73                         goto fail;
74                 }
75         }
76
77         if (pev != NULL) {
78                 *pev = ev;
79         }
80         if (pmsg != NULL) {
81                 *pmsg = msg;
82         }
83         if (pbackend != NULL) {
84                 *pbackend = backend;
85         }
86         if (pdb != NULL) {
87                 *pdb = db;
88         }
89         return true;
90
91 fail:
92         TALLOC_FREE(backend);
93         TALLOC_FREE(msg);
94         TALLOC_FREE(ev);
95         return false;
96 }
97
98 bool run_dbwrap_watch1(int dummy)
99 {
100         struct tevent_context *ev = NULL;
101         struct messaging_context *msg = NULL;
102         struct db_context *backend = NULL;
103         struct db_context *db = NULL;
104         const char *keystr = "key";
105         TDB_DATA key = string_term_tdb_data(keystr);
106         struct db_record *rec = NULL;
107         struct tevent_req *req = NULL;
108         NTSTATUS status;
109         bool ret = false;
110
111         ret = test_dbwrap_watch_init(
112                 talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
113         if (!ret) {
114                 goto fail;
115         }
116
117         rec = dbwrap_fetch_locked(db, db, key);
118         if (rec == NULL) {
119                 fprintf(stderr, "dbwrap_fetch_locked failed\n");
120                 goto fail;
121         }
122         req = dbwrap_watched_watch_send(talloc_tos(), ev, rec,
123                                         0, /* resume_instance */
124                                         (struct server_id){0});
125         if (req == NULL) {
126                 fprintf(stderr, "dbwrap_record_watch_send failed\n");
127                 goto fail;
128         }
129         TALLOC_FREE(rec);
130
131         status = dbwrap_store_int32_bystring(db, "different_key", 1);
132         if (!NT_STATUS_IS_OK(status)) {
133                 fprintf(stderr, "dbwrap_store_int32 failed: %s\n",
134                         nt_errstr(status));
135                 goto fail;
136         }
137
138         status = dbwrap_store_int32_bystring(db, keystr, 1);
139         if (!NT_STATUS_IS_OK(status)) {
140                 fprintf(stderr, "dbwrap_store_int32 failed: %s\n",
141                         nt_errstr(status));
142                 goto fail;
143         }
144
145         if (!tevent_req_poll(req, ev)) {
146                 fprintf(stderr, "tevent_req_poll failed\n");
147                 goto fail;
148         }
149
150         status = dbwrap_watched_watch_recv(req, NULL, NULL, NULL);
151         if (!NT_STATUS_IS_OK(status)) {
152                 fprintf(stderr, "dbwrap_record_watch_recv failed: %s\n",
153                         nt_errstr(status));
154                 goto fail;
155         }
156
157         (void)unlink("test_watch.tdb");
158         ret = true;
159 fail:
160         TALLOC_FREE(req);
161         TALLOC_FREE(rec);
162         TALLOC_FREE(db);
163         TALLOC_FREE(msg);
164         TALLOC_FREE(ev);
165         return ret;
166 }
167
168 /*
169  * Make sure dbwrap_parse_record does not return NT_STATUS_OK on
170  * invalid data
171  */
172
173 bool run_dbwrap_watch2(int dummy)
174 {
175         struct tevent_context *ev = NULL;
176         struct messaging_context *msg = NULL;
177         struct db_context *backend = NULL;
178         struct db_context *db = NULL;
179         const char *keystr = "key";
180         TDB_DATA key = string_term_tdb_data(keystr);
181         NTSTATUS status;
182         bool ret = false;
183
184         ret = test_dbwrap_watch_init(
185                 talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
186         if (!ret) {
187                 goto fail;
188         }
189
190         /*
191          * Store invalid data (from the dbwrap_watch point of view)
192          * directly into the backend database
193          */
194         status = dbwrap_store_uint32_bystring(backend, keystr, UINT32_MAX);
195         if (!NT_STATUS_IS_OK(status)) {
196                 fprintf(stderr, "dbwrap_store_uint32_bystring failed: %s\n",
197                         nt_errstr(status));
198                 goto fail;
199         }
200
201         status = dbwrap_parse_record(db, key, NULL, NULL);
202         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
203                 fprintf(stderr, "dbwrap_parse_record returned %s, expected "
204                         "NT_STATUS_NOT_FOUND\n", nt_errstr(status));
205                 goto fail;
206         }
207
208         (void)unlink("test_watch.tdb");
209         ret = true;
210 fail:
211         TALLOC_FREE(db);
212         TALLOC_FREE(msg);
213         TALLOC_FREE(ev);
214         return ret;
215 }
216
217 /*
218  * Test autocleanup of dead watchers
219  */
220
221 bool run_dbwrap_watch3(int dummy)
222 {
223         struct tevent_context *ev = NULL;
224         struct messaging_context *msg = NULL;
225         struct db_context *backend = NULL;
226         struct db_context *db = NULL;
227         const char *keystr = "key";
228         TDB_DATA key = string_term_tdb_data(keystr);
229         NTSTATUS status;
230         bool ret = false;
231         pid_t child, waited;
232         int wstatus, exit_status;
233
234         BlockSignals(true, SIGCHLD);
235
236         child = fork();
237         if (child == -1) {
238                 fprintf(stderr,
239                         "fork failed: %s\n",
240                         strerror(errno));
241                 goto fail;
242         }
243
244         ret = test_dbwrap_watch_init(
245                 talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
246         if (!ret) {
247                 goto fail;
248         }
249
250         if (child == 0) {
251                 struct db_record *rec = dbwrap_fetch_locked(db, db, key);
252                 struct tevent_req *req = NULL;
253
254                 if (rec == NULL) {
255                         fprintf(stderr, "dbwrap_fetch_locked failed\n");
256                         exit(1);
257                 }
258
259                 req = dbwrap_watched_watch_send(
260                         db, ev, rec, 0, (struct server_id) { 0 });
261                 if (req == NULL) {
262                         fprintf(stderr, "dbwrap_watched_watch_send failed\n");
263                         exit(2);
264                 }
265
266                 exit(0);
267         }
268
269         waited = waitpid(child, &wstatus, 0);
270         if (waited == -1) {
271                 fprintf(stderr, "waitpid failed: %s\n", strerror(errno));
272                 goto fail;
273         }
274         if (!WIFEXITED(wstatus)) {
275                 fprintf(stderr, "child did not exit normally\n");
276                 goto fail;
277         }
278
279         exit_status = WEXITSTATUS(wstatus);
280         if (exit_status != 0) {
281                 fprintf(stderr, "exit status is %d\n", exit_status);
282                 goto fail;
283         }
284
285         status = dbwrap_store_uint32_bystring(db, keystr, 1);
286         if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
287                 fprintf(stderr,
288                         "dbwrap_store_uint32 returned %s\n",
289                         nt_errstr(status));
290                 goto fail;
291         }
292
293         (void)unlink("test_watch.tdb");
294         ret = true;
295 fail:
296         TALLOC_FREE(db);
297         TALLOC_FREE(msg);
298         TALLOC_FREE(ev);
299         return ret;
300 }
301
302 /*
303  * Test that we can't add two watchers in the same
304  * fetch_lock/do_locked round
305  */
306
307 struct dbwrap_watch4_state {
308         TALLOC_CTX *mem_ctx;
309         struct tevent_context *ev;
310         struct db_context *db;
311         TDB_DATA key;
312
313         NTSTATUS status;
314
315         struct tevent_req *req1;
316         NTSTATUS status1;
317
318         struct tevent_req *req2;
319         NTSTATUS status2;
320 };
321
322 static void dbwrap_watch4_done1(struct tevent_req *subreq);
323 static void dbwrap_watch4_done2(struct tevent_req *subreq);
324
325 static void dbwrap_watch4_fn(struct db_record *rec,
326                              TDB_DATA value,
327                              void *private_data)
328 {
329         struct dbwrap_watch4_state *state = private_data;
330         bool ok;
331
332         state->req1 = dbwrap_watched_watch_send(
333                 state->mem_ctx, state->ev, rec, 0, (struct server_id) { .pid=0 });
334         if (state->req1 == NULL) {
335                 goto nomem;
336         }
337         tevent_req_set_callback(state->req1, dbwrap_watch4_done1, state);
338         state->status1 = NT_STATUS_EVENT_PENDING;
339
340         ok = tevent_req_set_endtime(
341                 state->req1, state->ev, timeval_current_ofs(1, 0));
342         if (!ok) {
343                 goto nomem;
344         }
345
346         state->req2 = dbwrap_watched_watch_send(
347                 state->mem_ctx, state->ev, rec, 0, (struct server_id) { .pid=0 });
348         if (state->req2 == NULL) {
349                 goto nomem;
350         }
351         tevent_req_set_callback(state->req2, dbwrap_watch4_done2, state);
352         state->status2 = NT_STATUS_EVENT_PENDING;
353
354         ok = tevent_req_set_endtime(
355                 state->req2, state->ev, timeval_current_ofs(1, 0));
356         if (!ok) {
357                 goto nomem;
358         }
359
360         state->status = NT_STATUS_OK;
361         return;
362
363         nomem:
364         state->status = NT_STATUS_NO_MEMORY;
365 }
366
367 static void dbwrap_watch4_done1(struct tevent_req *subreq)
368 {
369         struct dbwrap_watch4_state *state = tevent_req_callback_data_void(subreq);
370         state->status1 = dbwrap_watched_watch_recv(subreq, NULL, NULL, NULL);
371         TALLOC_FREE(subreq);
372         printf("req1 finished: %s\n", nt_errstr(state->status1));
373         state->req1 = NULL;
374 }
375
376 static void dbwrap_watch4_done2(struct tevent_req *subreq)
377 {
378         struct dbwrap_watch4_state *state = tevent_req_callback_data_void(subreq);
379         state->status2 = dbwrap_watched_watch_recv(subreq, NULL, NULL, NULL);
380         TALLOC_FREE(subreq);
381         printf("req2 finished: %s\n", nt_errstr(state->status2));
382         state->req2 = NULL;
383 }
384
385 bool run_dbwrap_watch4(int dummy)
386 {
387         struct tevent_context *ev = NULL;
388         struct messaging_context *msg = NULL;
389         struct db_context *backend = NULL;
390         struct db_context *db = NULL;
391         const char *keystr = "key";
392         TDB_DATA key = string_term_tdb_data(keystr);
393         struct dbwrap_watch4_state state = { 0 };
394         NTSTATUS status;
395         bool ret = false;
396         bool ok;
397
398         ok = test_dbwrap_watch_init(
399                 talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
400         if (!ok) {
401                 goto fail;
402         }
403
404         state = (struct dbwrap_watch4_state) {
405                 .mem_ctx = talloc_tos(),
406                 .ev = ev,
407                 .db = db,
408                 .key = key,
409         };
410
411         status = dbwrap_do_locked(db, key, dbwrap_watch4_fn, &state);
412         if (!NT_STATUS_IS_OK(status)) {
413                 fprintf(stderr,
414                         "dbwrap_do_locked failed: %s\n",
415                         nt_errstr(status));
416                 goto fail;
417         }
418         if (!NT_STATUS_IS_OK(state.status)) {
419                 fprintf(stderr,
420                         "dbwrap_watch4_fn failed: %s\n",
421                         nt_errstr(status));
422                 goto fail;
423         }
424
425         status = dbwrap_store(db, key, key, 0);
426         if (!NT_STATUS_IS_OK(status)) {
427                 fprintf(stderr,
428                         "dbwrap_store failed: %s\n",
429                         nt_errstr(status));
430                 goto fail;
431         }
432
433         while (NT_STATUS_EQUAL(state.status1, NT_STATUS_EVENT_PENDING) ||
434                NT_STATUS_EQUAL(state.status2, NT_STATUS_EVENT_PENDING)) {
435                 int res = tevent_loop_once(ev);
436                 if (res != 0) {
437                         fprintf(stderr,
438                                 "tevent_loop_once failed: %s\n",
439                                 strerror(errno));
440                         goto fail;
441                 }
442         }
443
444         if (!NT_STATUS_IS_OK(state.status1)) {
445                 fprintf(stderr,
446                         "req1 returned %s\n",
447                         nt_errstr(state.status1));
448                 goto fail;
449         }
450
451         if (!NT_STATUS_EQUAL(state.status2, NT_STATUS_REQUEST_NOT_ACCEPTED)) {
452                 fprintf(stderr,
453                         "req2 returned %s\n",
454                         nt_errstr(state.status2));
455                 goto fail;
456         }
457
458         (void)unlink("test_watch.tdb");
459         ret = true;
460 fail:
461         TALLOC_FREE(state.req2);
462         TALLOC_FREE(state.req1);
463         TALLOC_FREE(db);
464         TALLOC_FREE(msg);
465         TALLOC_FREE(ev);
466         return ret;
467 }