9c4017c36f2b2b6906de89fbb865a540af52157e
[obnox/samba/samba-obnox.git] / source3 / torture / test_messaging_read.c
1 /*
2    Unix SMB/CIFS implementation.
3    Test for a messaging_read bug
4    Copyright (C) Volker Lendecke 2014
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 "lib/util/tevent_unix.h"
23 #include "messages.h"
24
25 struct msg_count_state {
26         struct tevent_context *ev;
27         struct messaging_context *msg_ctx;
28         uint32_t msg_type;
29         unsigned *count;
30 };
31
32 static void msg_count_done(struct tevent_req *subreq);
33
34 static struct tevent_req *msg_count_send(TALLOC_CTX *mem_ctx,
35                                          struct tevent_context *ev,
36                                          struct messaging_context *msg_ctx,
37                                          uint32_t msg_type,
38                                          unsigned *count)
39 {
40         struct tevent_req *req, *subreq;
41         struct msg_count_state *state;
42
43         req = tevent_req_create(mem_ctx, &state, struct msg_count_state);
44         if (req == NULL) {
45                 return NULL;
46         }
47         state->ev = ev;
48         state->msg_ctx = msg_ctx;
49         state->msg_type = msg_type;
50         state->count = count;
51
52         subreq = messaging_read_send(state, state->ev, state->msg_ctx,
53                                      state->msg_type);
54         if (tevent_req_nomem(subreq, req)) {
55                 return tevent_req_post(req, ev);
56         }
57         tevent_req_set_callback(subreq, msg_count_done, req);
58         return req;
59 }
60
61 static void msg_count_done(struct tevent_req *subreq)
62 {
63         struct tevent_req *req = tevent_req_callback_data(
64                 subreq, struct tevent_req);
65         struct msg_count_state *state = tevent_req_data(
66                 req, struct msg_count_state);
67         int ret;
68
69         ret = messaging_read_recv(subreq, NULL, NULL);
70         TALLOC_FREE(subreq);
71         if (tevent_req_error(req, ret)) {
72                 return;
73         }
74         *state->count += 1;
75
76         subreq = messaging_read_send(state, state->ev, state->msg_ctx,
77                                      state->msg_type);
78         if (tevent_req_nomem(subreq, req)) {
79                 return;
80         }
81         tevent_req_set_callback(subreq, msg_count_done, req);
82 }
83
84 bool run_messaging_read1(int dummy)
85 {
86         struct tevent_context *ev = NULL;
87         struct messaging_context *msg_ctx = NULL;
88         struct tevent_req *req1 = NULL;
89         unsigned count1 = 0;
90         struct tevent_req *req2 = NULL;
91         unsigned count2 = 0;
92         NTSTATUS status;
93         bool retval = false;
94         int i;
95
96         ev = samba_tevent_context_init(talloc_tos());
97         if (ev == NULL) {
98                 fprintf(stderr, "tevent_context_init failed\n");
99                 goto fail;
100         }
101         msg_ctx = messaging_init(ev, ev);
102         if (msg_ctx == NULL) {
103                 fprintf(stderr, "messaging_init failed\n");
104                 goto fail;
105         }
106
107         req1 = msg_count_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &count1);
108         if (req1 == NULL) {
109                 fprintf(stderr, "msg_count_send failed\n");
110                 goto fail;
111         }
112         req2 = msg_count_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &count2);
113         if (req1 == NULL) {
114                 fprintf(stderr, "msg_count_send failed\n");
115                 goto fail;
116         }
117         status = messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx),
118                                     MSG_SMB_NOTIFY, NULL, 0);
119         if (!NT_STATUS_IS_OK(status)) {
120                 fprintf(stderr, "messaging_send_buf failed: %s\n",
121                         nt_errstr(status));
122                 goto fail;
123         }
124
125         for (i=0; i<2; i++) {
126                 if (tevent_loop_once(ev) != 0) {
127                         fprintf(stderr, "tevent_loop_once failed\n");
128                         goto fail;
129                 }
130         }
131
132         printf("%u/%u\n", count1, count2);
133
134         if ((count1 != 1) || (count2 != 1)){
135                 fprintf(stderr, "Got %u/%u msgs, expected 1 each\n",
136                         count1, count2);
137                 goto fail;
138         }
139
140         retval = true;
141 fail:
142         TALLOC_FREE(req1);
143         TALLOC_FREE(req2);
144         TALLOC_FREE(msg_ctx);
145         TALLOC_FREE(ev);
146         return retval;
147 }
148
149 struct msg_free_state {
150         struct tevent_req **to_free;
151 };
152
153 static void msg_free_done(struct tevent_req *subreq);
154
155 static struct tevent_req *msg_free_send(TALLOC_CTX *mem_ctx,
156                                         struct tevent_context *ev,
157                                         struct messaging_context *msg_ctx,
158                                         uint32_t msg_type,
159                                         struct tevent_req **to_free)
160 {
161         struct tevent_req *req, *subreq;
162         struct msg_free_state *state;
163
164         req = tevent_req_create(mem_ctx, &state, struct msg_free_state);
165         if (req == NULL) {
166                 return NULL;
167         }
168         state->to_free = to_free;
169
170         subreq = messaging_read_send(state, ev, msg_ctx, msg_type);
171         if (tevent_req_nomem(subreq, req)) {
172                 return tevent_req_post(req, ev);
173         }
174         tevent_req_set_callback(subreq, msg_free_done, req);
175         return req;
176 }
177
178 static void msg_free_done(struct tevent_req *subreq)
179 {
180         struct tevent_req *req = tevent_req_callback_data(
181                 subreq, struct tevent_req);
182         struct msg_free_state *state = tevent_req_data(
183                 req, struct msg_free_state);
184         int ret;
185
186         ret = messaging_read_recv(subreq, NULL, NULL);
187         TALLOC_FREE(subreq);
188         if (tevent_req_error(req, ret)) {
189                 return;
190         }
191         TALLOC_FREE(*state->to_free);
192         tevent_req_done(req);
193 }
194
195 bool run_messaging_read2(int dummy)
196 {
197         struct tevent_context *ev = NULL;
198         struct messaging_context *msg_ctx = NULL;
199         struct tevent_req *req1 = NULL;
200         struct tevent_req *req2 = NULL;
201         unsigned count = 0;
202         NTSTATUS status;
203         bool retval = false;
204
205         ev = samba_tevent_context_init(talloc_tos());
206         if (ev == NULL) {
207                 fprintf(stderr, "tevent_context_init failed\n");
208                 goto fail;
209         }
210         msg_ctx = messaging_init(ev, ev);
211         if (msg_ctx == NULL) {
212                 fprintf(stderr, "messaging_init failed\n");
213                 goto fail;
214         }
215
216         req1 = msg_free_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &req2);
217         if (req1 == NULL) {
218                 fprintf(stderr, "msg_count_send failed\n");
219                 goto fail;
220         }
221         req2 = msg_count_send(ev, ev, msg_ctx, MSG_SMB_NOTIFY, &count);
222         if (req1 == NULL) {
223                 fprintf(stderr, "msg_count_send failed\n");
224                 goto fail;
225         }
226         status = messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx),
227                                     MSG_SMB_NOTIFY, NULL, 0);
228         if (!NT_STATUS_IS_OK(status)) {
229                 fprintf(stderr, "messaging_send_buf failed: %s\n",
230                         nt_errstr(status));
231                 goto fail;
232         }
233
234         if (!tevent_req_poll(req1, ev) != 0) {
235                 fprintf(stderr, "tevent_req_poll failed\n");
236                 goto fail;
237         }
238
239         if (count != 0) {
240                 fprintf(stderr, "Got %u msgs, expected none\n", count);
241                 goto fail;
242         }
243
244         retval = true;
245 fail:
246         TALLOC_FREE(req1);
247         TALLOC_FREE(msg_ctx);
248         TALLOC_FREE(ev);
249         return retval;
250 }
251
252 struct msg_pingpong_state {
253         uint8_t dummy;
254 };
255
256 static void msg_pingpong_done(struct tevent_req *subreq);
257
258 static struct tevent_req *msg_pingpong_send(TALLOC_CTX *mem_ctx,
259                                             struct tevent_context *ev,
260                                             struct messaging_context *msg_ctx,
261                                             struct server_id dst)
262 {
263         struct tevent_req *req, *subreq;
264         struct msg_pingpong_state *state;
265         NTSTATUS status;
266
267         req = tevent_req_create(mem_ctx, &state, struct msg_pingpong_state);
268         if (req == NULL) {
269                 return NULL;
270         }
271
272         status = messaging_send_buf(msg_ctx, dst, MSG_PING, NULL, 0);
273         if (!NT_STATUS_IS_OK(status)) {
274                 tevent_req_error(req, map_errno_from_nt_status(status));
275                 return tevent_req_post(req, ev);
276         }
277
278         subreq = messaging_read_send(state, ev, msg_ctx, MSG_PONG);
279         if (tevent_req_nomem(subreq, req)) {
280                 return tevent_req_post(req, ev);
281         }
282         tevent_req_set_callback(subreq, msg_pingpong_done, req);
283         return req;
284 }
285
286 static void msg_pingpong_done(struct tevent_req *subreq)
287 {
288         struct tevent_req *req = tevent_req_callback_data(
289                 subreq, struct tevent_req);
290         int ret;
291
292         ret = messaging_read_recv(subreq, NULL, NULL);
293         TALLOC_FREE(subreq);
294         if (ret != 0) {
295                 tevent_req_error(req, ret);
296                 return;
297         }
298         tevent_req_done(req);
299 }
300
301 static int msg_pingpong_recv(struct tevent_req *req)
302 {
303         int err;
304
305         if (tevent_req_is_unix_error(req, &err)) {
306                 return err;
307         }
308         return 0;
309 }
310
311 static int msg_pingpong(struct messaging_context *msg_ctx,
312                         struct server_id dst)
313 {
314         struct tevent_context *ev;
315         struct tevent_req *req;
316         int ret = ENOMEM;
317
318         ev = tevent_context_init(msg_ctx);
319         if (ev == NULL) {
320                 goto fail;
321         }
322         req = msg_pingpong_send(ev, ev, msg_ctx, dst);
323         if (req == NULL) {
324                 goto fail;
325         }
326         if (!tevent_req_poll(req, ev)) {
327                 ret = errno;
328                 goto fail;
329         }
330         ret = msg_pingpong_recv(req);
331 fail:
332         TALLOC_FREE(ev);
333         return ret;
334 }
335
336 static void ping_responder_exit(struct tevent_context *ev,
337                                 struct tevent_fd *fde,
338                                 uint16_t flags,
339                                 void *private_data)
340 {
341         bool *done = private_data;
342
343         printf("Child: received write on exit-pipe\n");
344
345         *done = true;
346 }
347
348 static void ping_responder(int ready_pipe, int exit_pipe)
349 {
350         struct tevent_context *ev;
351         struct messaging_context *msg_ctx;
352         struct tevent_fd *exit_handler;
353         char c = 0;
354         bool done = false;
355
356         ev = samba_tevent_context_init(talloc_tos());
357         if (ev == NULL) {
358                 fprintf(stderr, "child tevent_context_init failed\n");
359                 exit(1);
360         }
361         msg_ctx = messaging_init(ev, ev);
362         if (msg_ctx == NULL) {
363                 fprintf(stderr, "child messaging_init failed\n");
364                 exit(1);
365         }
366         exit_handler = tevent_add_fd(ev, ev, exit_pipe, TEVENT_FD_READ,
367                                      ping_responder_exit, &done);
368         if (exit_handler == NULL) {
369                 fprintf(stderr, "child tevent_add_fd failed\n");
370                 exit(1);
371         }
372
373         if (write(ready_pipe, &c, 1) != 1) {
374                 fprintf(stderr, "child messaging_init failed\n");
375                 exit(1);
376         }
377
378         while (!done) {
379                 int ret;
380                 ret = tevent_loop_once(ev);
381                 if (ret != 0) {
382                         fprintf(stderr, "child tevent_loop_once failed\n");
383                         exit(1);
384                 }
385         }
386
387         printf("Child: done, exiting...\n");
388
389         TALLOC_FREE(msg_ctx);
390         TALLOC_FREE(ev);
391 }
392
393 bool run_messaging_read3(int dummy)
394 {
395         struct tevent_context *ev = NULL;
396         struct messaging_context *msg_ctx = NULL;
397         bool retval = false;
398         pid_t child;
399         int ready_pipe[2];
400         int exit_pipe[2];
401         int ret;
402         char c;
403         struct server_id dst;
404         ssize_t written;
405
406         if ((pipe(ready_pipe) != 0) || (pipe(exit_pipe) != 0)) {
407                 perror("pipe failed");
408                 return false;
409         }
410
411         child = fork();
412         if (child == -1) {
413                 perror("fork failed");
414                 return false;
415         }
416
417         if (child == 0) {
418                 close(ready_pipe[0]);
419                 close(exit_pipe[1]);
420                 ping_responder(ready_pipe[1], exit_pipe[0]);
421                 exit(0);
422         }
423         close(ready_pipe[1]);
424         close(exit_pipe[0]);
425
426         if (read(ready_pipe[0], &c, 1) != 1) {
427                 perror("read failed");
428                 return false;
429         }
430
431         ev = samba_tevent_context_init(talloc_tos());
432         if (ev == NULL) {
433                 fprintf(stderr, "tevent_context_init failed\n");
434                 goto fail;
435         }
436         msg_ctx = messaging_init(ev, ev);
437         if (msg_ctx == NULL) {
438                 fprintf(stderr, "messaging_init failed\n");
439                 goto fail;
440         }
441
442         dst = messaging_server_id(msg_ctx);
443         dst.pid = child;
444
445         ret = msg_pingpong(msg_ctx, dst);
446         if (ret != 0){
447                 fprintf(stderr, "msg_pingpong failed\n");
448                 goto fail;
449         }
450
451         printf("Parent: telling child to exit\n");
452
453         written = write(exit_pipe[1], &c, 1);
454         if (written != 1) {
455                 perror("write to exit_pipe failed");
456                 goto fail;
457         }
458
459         ret = waitpid(child, NULL, 0);
460         if (ret == -1) {
461                 perror("waitpid failed");
462                 goto fail;
463         }
464
465         printf("Parent: child exited. Done\n");
466
467         retval = true;
468 fail:
469         TALLOC_FREE(msg_ctx);
470         TALLOC_FREE(ev);
471         return retval;
472 }
473
474 /**
475  * read4:
476  *
477  * test transferring a big payload.
478  */
479
480 #define MSG_TORTURE_READ4 0xF104
481
482 static bool read4_child(int ready_fd)
483 {
484         struct tevent_context *ev = NULL;
485         struct messaging_context *msg_ctx = NULL;
486         TALLOC_CTX *frame = talloc_stackframe();
487         bool retval = false;
488         uint8_t c = 1;
489         struct tevent_req *subreq;
490         int ret;
491         ssize_t bytes;
492         struct messaging_rec *rec;
493         bool ok;
494
495         ev = samba_tevent_context_init(frame);
496         if (ev == NULL) {
497                 fprintf(stderr, "child: tevent_context_init failed\n");
498                 goto done;
499         }
500
501         msg_ctx = messaging_init(ev, ev);
502         if (msg_ctx == NULL) {
503                 fprintf(stderr, "child: messaging_init failed\n");
504                 goto done;
505         }
506
507         printf("child: telling parent we're ready to receive messages\n");
508
509         /* Tell the parent we are ready to receive mesages. */
510         bytes = write(ready_fd, &c, 1);
511         if (bytes != 1) {
512                 perror("child: failed to write to ready_fd");
513                 goto done;
514         }
515
516         printf("child: waiting for messages\n");
517
518         subreq = messaging_read_send(frame, /* TALLOC_CTX */
519                                      ev, msg_ctx,
520                                      MSG_TORTURE_READ4);
521         if (subreq == NULL) {
522                 fprintf(stderr, "child: messaging_read_send failed\n");
523                 goto done;
524         }
525
526         ok = tevent_req_poll(subreq, ev);
527         if (!ok) {
528                 fprintf(stderr, "child: tevent_req_poll failed\n");
529                 goto done;
530         }
531
532         printf("child: receiving message\n");
533
534         ret = messaging_read_recv(subreq, frame, &rec);
535         TALLOC_FREE(subreq);
536         if (ret != 0) {
537                 fprintf(stderr, "child: messaging_read_recv failed\n");
538                 goto done;
539         }
540
541         printf("child: received message\n");
542
543         /* Tell the parent we are done. */
544         bytes = write(ready_fd, &c, 1);
545         if (bytes != 1) {
546                 perror("child: failed to write to ready_fd");
547                 goto done;
548         }
549
550         printf("child: done\n");
551
552         retval = true;
553
554 done:
555         TALLOC_FREE(frame);
556         return retval;
557 }
558
559 struct child_done_state {
560         int fd;
561         bool done;
562 };
563
564 static void child_done_cb(struct tevent_context *ev,
565                           struct tevent_fd *fde,
566                           uint16_t flags,
567                           void *private_data)
568 {
569         struct child_done_state *state =
570                         (struct child_done_state *)private_data;
571         char c = 0;
572         ssize_t bytes;
573
574         bytes = read(state->fd, &c, 1);
575         if (bytes != 1) {
576                 perror("parent: read from ready_fd failed");
577         }
578
579         state->done = true;
580 }
581
582 static bool read4_parent(pid_t child_pid, int ready_fd)
583 {
584         struct tevent_context *ev = NULL;
585         struct messaging_context *msg_ctx = NULL;
586         bool retval = false;
587         int ret;
588         NTSTATUS status;
589         struct server_id dst;
590         TALLOC_CTX *frame = talloc_stackframe();
591         uint8_t c;
592         ssize_t bytes;
593         struct iovec iov;
594         DATA_BLOB blob;
595         struct tevent_fd *child_done_fde;
596         struct child_done_state child_state;
597
598         /* wait until the child is ready to receive messages */
599         bytes = read(ready_fd, &c, 1);
600         if (bytes != 1) {
601                 perror("parent: read from ready_fd failed");
602                 goto done;
603         }
604
605         printf("parent: child is ready to receive messages\n");
606
607         ev = samba_tevent_context_init(frame);
608         if (ev == NULL) {
609                 fprintf(stderr, "parent: tevent_context_init failed\n");
610                 goto done;
611         }
612
613         msg_ctx = messaging_init(ev, ev);
614         if (msg_ctx == NULL) {
615                 fprintf(stderr, "parent: messaging_init failed\n");
616                 goto done;
617         }
618
619         child_state.fd = ready_fd;
620         child_state.done = false;
621
622         child_done_fde = tevent_add_fd(ev, ev, ready_fd, TEVENT_FD_READ,
623                                        child_done_cb, &child_state);
624         if (child_done_fde == NULL) {
625                 fprintf(stderr,
626                         "parent: failed tevent_add_fd for child done\n");
627                 goto done;
628         }
629
630         /*
631          * Send a 1M payload with the message.
632          */
633         blob = data_blob_talloc_zero(frame, 1000*1000);
634         iov.iov_base = blob.data;
635         iov.iov_len  = blob.length;
636
637         dst = messaging_server_id(msg_ctx);
638         dst.pid = child_pid;
639
640         printf("parent: sending message to child\n");
641
642         status = messaging_send_iov(msg_ctx, dst, MSG_TORTURE_READ4, &iov, 1,
643                                     NULL, 0);
644         if (!NT_STATUS_IS_OK(status)) {
645                 fprintf(stderr, "parent: messaging_send_iov failed: %s\n",
646                         nt_errstr(status));
647                 goto done;
648         }
649
650         printf("parent: waiting for child to confirm\n");
651
652         while (!child_state.done) {
653                 ret = tevent_loop_once(ev);
654                 if (ret != 0) {
655                         fprintf(stderr, "parent: tevent_loop_once failed\n");
656                         goto done;
657                 }
658         }
659
660         printf("parent: child confirmed\n");
661
662         ret = waitpid(child_pid, NULL, 0);
663         if (ret == -1) {
664                 perror("parent: waitpid failed");
665                 goto done;
666         }
667
668         printf("parent: done\n");
669
670         retval = true;
671
672 done:
673         TALLOC_FREE(frame);
674         return retval;
675 }
676
677 bool run_messaging_read4(int dummy)
678 {
679         bool retval = false;
680         pid_t child_pid;
681         int ready_pipe[2];
682         int ret;
683
684         ret = pipe(ready_pipe);
685         if (ret != 0) {
686                 perror("parent: pipe failed for ready_pipe");
687                 return retval;
688         }
689
690         child_pid = fork();
691         if (child_pid == -1) {
692                 perror("fork failed");
693         } else if (child_pid == 0) {
694                 close(ready_pipe[0]);
695                 retval = read4_child(ready_pipe[1]);
696         } else {
697                 close(ready_pipe[1]);
698                 retval = read4_parent(child_pid, ready_pipe[0]);
699         }
700
701         return retval;
702 }