Line data Source code
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 5 : 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 5 : struct tevent_context *ev = NULL;
37 5 : struct messaging_context *msg = NULL;
38 5 : struct db_context *backend = NULL;
39 5 : struct db_context *db = NULL;
40 :
41 5 : ev = samba_tevent_context_init(mem_ctx);
42 5 : if (ev == NULL) {
43 0 : fprintf(stderr, "tevent_context_init failed\n");
44 0 : goto fail;
45 : }
46 :
47 5 : msg = messaging_init(ev, ev);
48 5 : if (msg == NULL) {
49 0 : fprintf(stderr, "messaging_init failed\n");
50 0 : goto fail;
51 : }
52 :
53 5 : 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 5 : if (backend == NULL) {
63 0 : fprintf(stderr, "db_open failed: %s\n", strerror(errno));
64 0 : goto fail;
65 : }
66 :
67 : {
68 5 : struct db_context *backend_copy = backend;
69 :
70 5 : db = db_open_watched(ev, &backend_copy, msg);
71 5 : if (db == NULL) {
72 0 : fprintf(stderr, "db_open_watched failed\n");
73 0 : goto fail;
74 : }
75 : }
76 :
77 5 : if (pev != NULL) {
78 5 : *pev = ev;
79 : }
80 5 : if (pmsg != NULL) {
81 5 : *pmsg = msg;
82 : }
83 5 : if (pbackend != NULL) {
84 5 : *pbackend = backend;
85 : }
86 5 : if (pdb != NULL) {
87 5 : *pdb = db;
88 : }
89 0 : return true;
90 :
91 0 : fail:
92 0 : TALLOC_FREE(backend);
93 0 : TALLOC_FREE(msg);
94 0 : TALLOC_FREE(ev);
95 0 : return false;
96 : }
97 :
98 1 : bool run_dbwrap_watch1(int dummy)
99 : {
100 1 : struct tevent_context *ev = NULL;
101 1 : struct messaging_context *msg = NULL;
102 1 : struct db_context *backend = NULL;
103 1 : struct db_context *db = NULL;
104 1 : const char *keystr = "key";
105 1 : TDB_DATA key = string_term_tdb_data(keystr);
106 1 : struct db_record *rec = NULL;
107 1 : struct tevent_req *req = NULL;
108 : NTSTATUS status;
109 1 : bool ret = false;
110 :
111 1 : ret = test_dbwrap_watch_init(
112 : talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
113 1 : if (!ret) {
114 0 : goto fail;
115 : }
116 :
117 1 : rec = dbwrap_fetch_locked(db, db, key);
118 1 : if (rec == NULL) {
119 0 : fprintf(stderr, "dbwrap_fetch_locked failed\n");
120 0 : goto fail;
121 : }
122 1 : req = dbwrap_watched_watch_send(talloc_tos(), ev, rec,
123 1 : (struct server_id){0});
124 1 : if (req == NULL) {
125 0 : fprintf(stderr, "dbwrap_record_watch_send failed\n");
126 0 : goto fail;
127 : }
128 1 : TALLOC_FREE(rec);
129 :
130 1 : status = dbwrap_store_int32_bystring(db, "different_key", 1);
131 1 : if (!NT_STATUS_IS_OK(status)) {
132 0 : fprintf(stderr, "dbwrap_store_int32 failed: %s\n",
133 : nt_errstr(status));
134 0 : goto fail;
135 : }
136 :
137 1 : status = dbwrap_store_int32_bystring(db, keystr, 1);
138 1 : if (!NT_STATUS_IS_OK(status)) {
139 0 : fprintf(stderr, "dbwrap_store_int32 failed: %s\n",
140 : nt_errstr(status));
141 0 : goto fail;
142 : }
143 :
144 1 : if (!tevent_req_poll(req, ev)) {
145 0 : fprintf(stderr, "tevent_req_poll failed\n");
146 0 : goto fail;
147 : }
148 :
149 1 : status = dbwrap_watched_watch_recv(req, NULL, NULL);
150 1 : if (!NT_STATUS_IS_OK(status)) {
151 0 : fprintf(stderr, "dbwrap_record_watch_recv failed: %s\n",
152 : nt_errstr(status));
153 0 : goto fail;
154 : }
155 :
156 1 : (void)unlink("test_watch.tdb");
157 1 : ret = true;
158 1 : fail:
159 1 : TALLOC_FREE(req);
160 1 : TALLOC_FREE(rec);
161 1 : TALLOC_FREE(db);
162 1 : TALLOC_FREE(msg);
163 1 : TALLOC_FREE(ev);
164 1 : return ret;
165 : }
166 :
167 : /*
168 : * Make sure dbwrap_parse_record does not return NT_STATUS_OK on
169 : * invalid data
170 : */
171 :
172 1 : bool run_dbwrap_watch2(int dummy)
173 : {
174 1 : struct tevent_context *ev = NULL;
175 1 : struct messaging_context *msg = NULL;
176 1 : struct db_context *backend = NULL;
177 1 : struct db_context *db = NULL;
178 1 : const char *keystr = "key";
179 1 : TDB_DATA key = string_term_tdb_data(keystr);
180 : NTSTATUS status;
181 1 : bool ret = false;
182 :
183 1 : ret = test_dbwrap_watch_init(
184 : talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
185 1 : if (!ret) {
186 0 : goto fail;
187 : }
188 :
189 : /*
190 : * Store invalid data (from the dbwrap_watch point of view)
191 : * directly into the backend database
192 : */
193 1 : status = dbwrap_store_uint32_bystring(backend, keystr, UINT32_MAX);
194 1 : if (!NT_STATUS_IS_OK(status)) {
195 0 : fprintf(stderr, "dbwrap_store_uint32_bystring failed: %s\n",
196 : nt_errstr(status));
197 0 : goto fail;
198 : }
199 :
200 1 : status = dbwrap_parse_record(db, key, NULL, NULL);
201 1 : if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
202 0 : fprintf(stderr, "dbwrap_parse_record returned %s, expected "
203 : "NT_STATUS_NOT_FOUND\n", nt_errstr(status));
204 0 : goto fail;
205 : }
206 :
207 1 : (void)unlink("test_watch.tdb");
208 1 : ret = true;
209 1 : fail:
210 1 : TALLOC_FREE(db);
211 1 : TALLOC_FREE(msg);
212 1 : TALLOC_FREE(ev);
213 1 : return ret;
214 : }
215 :
216 : /*
217 : * Test autocleanup of dead watchers
218 : */
219 :
220 1 : bool run_dbwrap_watch3(int dummy)
221 : {
222 1 : struct tevent_context *ev = NULL;
223 1 : struct messaging_context *msg = NULL;
224 1 : struct db_context *backend = NULL;
225 1 : struct db_context *db = NULL;
226 1 : const char *keystr = "key";
227 1 : TDB_DATA key = string_term_tdb_data(keystr);
228 : NTSTATUS status;
229 1 : bool ret = false;
230 : pid_t child, waited;
231 : int wstatus, exit_status;
232 :
233 1 : BlockSignals(true, SIGCHLD);
234 :
235 1 : child = fork();
236 2 : if (child == -1) {
237 0 : fprintf(stderr,
238 : "fork failed: %s\n",
239 0 : strerror(errno));
240 0 : goto fail;
241 : }
242 :
243 2 : ret = test_dbwrap_watch_init(
244 : talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
245 2 : if (!ret) {
246 0 : goto fail;
247 : }
248 :
249 2 : if (child == 0) {
250 1 : struct db_record *rec = dbwrap_fetch_locked(db, db, key);
251 1 : struct tevent_req *req = NULL;
252 :
253 1 : if (rec == NULL) {
254 0 : fprintf(stderr, "dbwrap_fetch_locked failed\n");
255 0 : exit(1);
256 : }
257 :
258 1 : req = dbwrap_watched_watch_send(
259 1 : db, ev, rec, (struct server_id) { 0 });
260 1 : if (req == NULL) {
261 0 : fprintf(stderr, "dbwrap_watched_watch_send failed\n");
262 0 : exit(2);
263 : }
264 :
265 1 : exit(0);
266 : }
267 :
268 1 : waited = waitpid(child, &wstatus, 0);
269 1 : if (waited == -1) {
270 0 : fprintf(stderr, "waitpid failed: %s\n", strerror(errno));
271 0 : goto fail;
272 : }
273 1 : if (!WIFEXITED(wstatus)) {
274 0 : fprintf(stderr, "child did not exit normally\n");
275 0 : goto fail;
276 : }
277 :
278 1 : exit_status = WEXITSTATUS(wstatus);
279 1 : if (exit_status != 0) {
280 0 : fprintf(stderr, "exit status is %d\n", exit_status);
281 0 : goto fail;
282 : }
283 :
284 1 : status = dbwrap_store_uint32_bystring(db, keystr, 1);
285 1 : if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
286 0 : fprintf(stderr,
287 : "dbwrap_store_uint32 returned %s\n",
288 : nt_errstr(status));
289 0 : goto fail;
290 : }
291 :
292 1 : (void)unlink("test_watch.tdb");
293 1 : ret = true;
294 1 : fail:
295 1 : TALLOC_FREE(db);
296 1 : TALLOC_FREE(msg);
297 1 : TALLOC_FREE(ev);
298 1 : return ret;
299 : }
300 :
301 : /*
302 : * Test that we can't add two watchers in the same
303 : * fetch_lock/do_locked round
304 : */
305 :
306 : struct dbwrap_watch4_state {
307 : TALLOC_CTX *mem_ctx;
308 : struct tevent_context *ev;
309 : struct db_context *db;
310 : TDB_DATA key;
311 :
312 : NTSTATUS status;
313 :
314 : struct tevent_req *req1;
315 : NTSTATUS status1;
316 :
317 : struct tevent_req *req2;
318 : NTSTATUS status2;
319 : };
320 :
321 : static void dbwrap_watch4_done1(struct tevent_req *subreq);
322 : static void dbwrap_watch4_done2(struct tevent_req *subreq);
323 :
324 1 : static void dbwrap_watch4_fn(struct db_record *rec,
325 : TDB_DATA value,
326 : void *private_data)
327 : {
328 1 : struct dbwrap_watch4_state *state = private_data;
329 : bool ok;
330 :
331 1 : state->req1 = dbwrap_watched_watch_send(
332 1 : state->mem_ctx, state->ev, rec, (struct server_id) { .pid=0 });
333 1 : if (state->req1 == NULL) {
334 0 : goto nomem;
335 : }
336 1 : tevent_req_set_callback(state->req1, dbwrap_watch4_done1, state);
337 1 : state->status1 = NT_STATUS_EVENT_PENDING;
338 :
339 1 : ok = tevent_req_set_endtime(
340 : state->req1, state->ev, timeval_current_ofs(1, 0));
341 1 : if (!ok) {
342 0 : goto nomem;
343 : }
344 :
345 1 : state->req2 = dbwrap_watched_watch_send(
346 1 : state->mem_ctx, state->ev, rec, (struct server_id) { .pid=0 });
347 1 : if (state->req2 == NULL) {
348 0 : goto nomem;
349 : }
350 1 : tevent_req_set_callback(state->req2, dbwrap_watch4_done2, state);
351 1 : state->status2 = NT_STATUS_EVENT_PENDING;
352 :
353 1 : ok = tevent_req_set_endtime(
354 : state->req2, state->ev, timeval_current_ofs(1, 0));
355 1 : if (!ok) {
356 0 : goto nomem;
357 : }
358 :
359 1 : state->status = NT_STATUS_OK;
360 1 : return;
361 :
362 0 : nomem:
363 0 : state->status = NT_STATUS_NO_MEMORY;
364 : }
365 :
366 1 : static void dbwrap_watch4_done1(struct tevent_req *subreq)
367 : {
368 1 : struct dbwrap_watch4_state *state = tevent_req_callback_data_void(subreq);
369 1 : state->status1 = dbwrap_watched_watch_recv(subreq, NULL, NULL);
370 1 : TALLOC_FREE(subreq);
371 2 : printf("req1 finished: %s\n", nt_errstr(state->status1));
372 1 : state->req1 = NULL;
373 1 : }
374 :
375 1 : static void dbwrap_watch4_done2(struct tevent_req *subreq)
376 : {
377 1 : struct dbwrap_watch4_state *state = tevent_req_callback_data_void(subreq);
378 1 : state->status2 = dbwrap_watched_watch_recv(subreq, NULL, NULL);
379 1 : TALLOC_FREE(subreq);
380 2 : printf("req2 finished: %s\n", nt_errstr(state->status2));
381 1 : state->req2 = NULL;
382 1 : }
383 :
384 1 : bool run_dbwrap_watch4(int dummy)
385 : {
386 1 : struct tevent_context *ev = NULL;
387 1 : struct messaging_context *msg = NULL;
388 1 : struct db_context *backend = NULL;
389 1 : struct db_context *db = NULL;
390 1 : const char *keystr = "key";
391 1 : TDB_DATA key = string_term_tdb_data(keystr);
392 1 : struct dbwrap_watch4_state state = { 0 };
393 : NTSTATUS status;
394 1 : bool ret = false;
395 : bool ok;
396 :
397 1 : ok = test_dbwrap_watch_init(
398 : talloc_tos(), "test_watch.tdb", &ev, &msg, &backend, &db);
399 1 : if (!ok) {
400 0 : goto fail;
401 : }
402 :
403 1 : state = (struct dbwrap_watch4_state) {
404 1 : .mem_ctx = talloc_tos(),
405 : .ev = ev,
406 : .db = db,
407 : .key = key,
408 : };
409 :
410 1 : status = dbwrap_do_locked(db, key, dbwrap_watch4_fn, &state);
411 1 : if (!NT_STATUS_IS_OK(status)) {
412 0 : fprintf(stderr,
413 : "dbwrap_do_locked failed: %s\n",
414 : nt_errstr(status));
415 0 : goto fail;
416 : }
417 1 : if (!NT_STATUS_IS_OK(state.status)) {
418 0 : fprintf(stderr,
419 : "dbwrap_watch4_fn failed: %s\n",
420 : nt_errstr(status));
421 0 : goto fail;
422 : }
423 :
424 1 : status = dbwrap_store(db, key, key, 0);
425 1 : if (!NT_STATUS_IS_OK(status)) {
426 0 : fprintf(stderr,
427 : "dbwrap_store failed: %s\n",
428 : nt_errstr(status));
429 0 : goto fail;
430 : }
431 :
432 5 : while (NT_STATUS_EQUAL(state.status1, NT_STATUS_EVENT_PENDING) ||
433 1 : NT_STATUS_EQUAL(state.status2, NT_STATUS_EVENT_PENDING)) {
434 3 : int res = tevent_loop_once(ev);
435 3 : if (res != 0) {
436 0 : fprintf(stderr,
437 : "tevent_loop_once failed: %s\n",
438 0 : strerror(errno));
439 0 : goto fail;
440 : }
441 : }
442 :
443 1 : if (!NT_STATUS_IS_OK(state.status1)) {
444 0 : fprintf(stderr,
445 : "req1 returned %s\n",
446 : nt_errstr(state.status1));
447 0 : goto fail;
448 : }
449 :
450 1 : if (!NT_STATUS_EQUAL(state.status2, NT_STATUS_REQUEST_NOT_ACCEPTED)) {
451 0 : fprintf(stderr,
452 : "req2 returned %s\n",
453 : nt_errstr(state.status2));
454 0 : goto fail;
455 : }
456 :
457 1 : (void)unlink("test_watch.tdb");
458 1 : ret = true;
459 1 : fail:
460 1 : TALLOC_FREE(state.req2);
461 1 : TALLOC_FREE(state.req1);
462 1 : TALLOC_FREE(db);
463 1 : TALLOC_FREE(msg);
464 1 : TALLOC_FREE(ev);
465 1 : return ret;
466 : }
|