messaging4: Pass fds to messaging handlers
[samba.git] / source4 / lib / messaging / tests / messaging.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    local test for messaging code
5
6    Copyright (C) Andrew Tridgell 2004
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "lib/events/events.h"
24 #include "lib/messaging/irpc.h"
25 #include "torture/torture.h"
26 #include "cluster/cluster.h"
27 #include "param/param.h"
28 #include "torture/local/proto.h"
29 #include "system/select.h"
30 #include "system/filesys.h"
31
32 #include <gnutls/gnutls.h>
33 #include <gnutls/crypto.h>
34
35 static uint32_t msg_pong;
36
37 static void ping_message(struct imessaging_context *msg,
38                          void *private_data,
39                          uint32_t msg_type,
40                          struct server_id src,
41                          size_t num_fds,
42                          int *fds,
43                          DATA_BLOB *data)
44 {
45         NTSTATUS status;
46
47         if (num_fds != 0) {
48                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
49                 return;
50         }
51
52         status = imessaging_send(msg, src, msg_pong, data);
53         if (!NT_STATUS_IS_OK(status)) {
54                 printf("pong failed - %s\n", nt_errstr(status));
55         }
56 }
57
58 static void pong_message(struct imessaging_context *msg,
59                          void *private_data,
60                          uint32_t msg_type,
61                          struct server_id src,
62                          size_t num_fds,
63                          int *fds,
64                          DATA_BLOB *data)
65 {
66         int *count = (int *)private_data;
67
68         if (num_fds != 0) {
69                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
70                 return;
71         }
72
73         (*count)++;
74 }
75
76 static void exit_message(struct imessaging_context *msg,
77                          void *private_data,
78                          uint32_t msg_type,
79                          struct server_id src,
80                          size_t num_fds,
81                          int *fds,
82                          DATA_BLOB *data)
83 {
84         if (num_fds != 0) {
85                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
86                 return;
87         }
88
89         talloc_free(private_data);
90         exit(0);
91 }
92
93 /*
94   test ping speed
95 */
96 static bool test_ping_speed(struct torture_context *tctx)
97 {
98         struct tevent_context *ev;
99         struct imessaging_context *msg_client_ctx;
100         struct imessaging_context *msg_server_ctx;
101         int ping_count = 0;
102         int pong_count = 0;
103         struct timeval tv;
104         int timelimit = torture_setting_int(tctx, "timelimit", 10);
105         uint32_t msg_ping, msg_exit;
106
107         lpcfg_set_cmdline(tctx->lp_ctx, "pid directory", "piddir.tmp");
108
109         ev = tctx->ev;
110
111         msg_server_ctx = imessaging_init(tctx,
112                                          tctx->lp_ctx, cluster_id(0, 1),
113                                          ev);
114         
115         torture_assert(tctx, msg_server_ctx != NULL, "Failed to init ping messaging context");
116                 
117         imessaging_register_tmp(msg_server_ctx, NULL, ping_message, &msg_ping);
118         imessaging_register_tmp(msg_server_ctx, tctx, exit_message, &msg_exit);
119
120         msg_client_ctx = imessaging_init(tctx,
121                                          tctx->lp_ctx,
122                                          cluster_id(0, 2),
123                                          ev);
124
125         torture_assert(tctx, msg_client_ctx != NULL, 
126                        "msg_client_ctx imessaging_init() failed");
127
128         imessaging_register_tmp(msg_client_ctx, &pong_count, pong_message, &msg_pong);
129
130         tv = timeval_current();
131
132         torture_comment(tctx, "Sending pings for %d seconds\n", timelimit);
133         while (timeval_elapsed(&tv) < timelimit) {
134                 DATA_BLOB data;
135                 NTSTATUS status1, status2;
136
137                 data.data = discard_const_p(uint8_t, "testing");
138                 data.length = strlen((const char *)data.data);
139
140                 status1 = imessaging_send(msg_client_ctx, cluster_id(0, 1), msg_ping, &data);
141                 status2 = imessaging_send(msg_client_ctx, cluster_id(0, 1), msg_ping, NULL);
142
143                 torture_assert_ntstatus_ok(tctx, status1, "msg1 failed");
144                 ping_count++;
145
146                 torture_assert_ntstatus_ok(tctx, status2, "msg2 failed");
147                 ping_count++;
148
149                 while (ping_count > pong_count + 20) {
150                         tevent_loop_once(ev);
151                 }
152         }
153
154         torture_comment(tctx, "waiting for %d remaining replies (done %d)\n", 
155                ping_count - pong_count, pong_count);
156         while (timeval_elapsed(&tv) < 30 && pong_count < ping_count) {
157                 tevent_loop_once(ev);
158         }
159
160         torture_comment(tctx, "sending exit\n");
161         imessaging_send(msg_client_ctx, cluster_id(0, 1), msg_exit, NULL);
162
163         torture_assert_int_equal(tctx, ping_count, pong_count, "ping test failed");
164
165         torture_comment(tctx, "ping rate of %.0f messages/sec\n", 
166                (ping_count+pong_count)/timeval_elapsed(&tv));
167
168         talloc_free(msg_client_ctx);
169         talloc_free(msg_server_ctx);
170
171         return true;
172 }
173
174 static bool test_messaging_overflow(struct torture_context *tctx)
175 {
176         struct imessaging_context *msg_ctx;
177         ssize_t nwritten, nread;
178         pid_t child;
179         char c = 0;
180         int up_pipe[2], down_pipe[2];
181         int i, ret, child_status;
182
183         ret = pipe(up_pipe);
184         torture_assert(tctx, ret == 0, "pipe failed");
185         ret = pipe(down_pipe);
186         torture_assert(tctx, ret == 0, "pipe failed");
187
188         child = fork();
189         if (child < 0) {
190                 torture_fail(tctx, "fork failed");
191         }
192
193         if (child == 0) {
194                 ret = tevent_re_initialise(tctx->ev);
195                 torture_assert(tctx, ret == 0, "tevent_re_initialise failed");
196
197                 msg_ctx = imessaging_init(tctx, tctx->lp_ctx,
198                                           cluster_id(getpid(), 0),
199                                           tctx->ev);
200                 torture_assert(tctx, msg_ctx != NULL,
201                                "imessaging_init failed");
202
203                 do {
204                         nwritten = write(up_pipe[1], &c, 1);
205                 } while ((nwritten == -1) && (errno == EINTR));
206
207                 ret = close(down_pipe[1]);
208                 torture_assert(tctx, ret == 0, "close failed");
209
210                 do {
211                         nread = read(down_pipe[0], &c, 1);
212                 } while ((nread == -1) && (errno == EINTR));
213
214                 exit(0);
215         }
216
217         do {
218                 nread = read(up_pipe[0], &c, 1);
219         } while ((nread == -1) && (errno == EINTR));
220
221         msg_ctx = imessaging_init(tctx, tctx->lp_ctx, cluster_id(getpid(), 0),
222                                   tctx->ev);
223         torture_assert(tctx, msg_ctx != NULL, "imessaging_init failed");
224
225         for (i=0; i<1000; i++) {
226                 NTSTATUS status;
227                 status = imessaging_send(msg_ctx, cluster_id(child, 0),
228                                          MSG_PING, NULL);
229                 torture_assert_ntstatus_ok(tctx, status,
230                                            "imessaging_send failed");
231         }
232
233         tevent_loop_once(tctx->ev);
234
235         talloc_free(msg_ctx);
236
237         ret = close(down_pipe[1]);
238         torture_assert(tctx, ret == 0, "close failed");
239
240         ret = waitpid(child, &child_status, 0);
241         torture_assert(tctx, ret == child, "wrong child exited");
242         torture_assert(tctx, child_status == 0, "child failed");
243
244         poll(NULL, 0, 500);
245
246         return true;
247 }
248
249 struct overflow_parent_child {
250         gnutls_hash_hd_t md5_hash_hnd;
251         bool done;
252 };
253
254 static void overflow_md5_child_handler(struct imessaging_context *msg,
255                                        void *private_data,
256                                        uint32_t msg_type,
257                                        struct server_id server_id,
258                                        size_t num_fds,
259                                        int *fds,
260                                        DATA_BLOB *data)
261 {
262         struct overflow_parent_child *state = private_data;
263
264         if (num_fds != 0) {
265                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
266                 return;
267         }
268
269         if (data->length == 0) {
270                 state->done = true;
271                 return;
272         }
273
274         gnutls_hash(state->md5_hash_hnd, data->data, data->length);
275 }
276
277 struct overflow_child_parent {
278         uint8_t final[16];
279         bool done;
280 };
281
282 static void overflow_md5_parent_handler(struct imessaging_context *msg_ctx,
283                                         void *private_data,
284                                         uint32_t msg_type,
285                                         struct server_id server_id,
286                                         size_t num_fds,
287                                         int *fds,
288                                         DATA_BLOB *data)
289 {
290         struct overflow_child_parent *state = private_data;
291
292         if (num_fds != 0) {
293                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
294                 return;
295         }
296
297         if (data->length != sizeof(state->final)) {
298                 memset(state->final, 0, sizeof(state->final));
299                 state->done = true;
300                 return;
301         }
302         memcpy(state->final, data->data, 16);
303         state->done = true;
304 }
305
306 static bool test_messaging_overflow_check(struct torture_context *tctx)
307 {
308         struct imessaging_context *msg_ctx;
309         ssize_t nwritten, nread;
310         pid_t child;
311         char c = 0;
312         int up_pipe[2], down_pipe[2];
313         int i, ret, child_status;
314         gnutls_hash_hd_t hash_hnd;
315         uint8_t final[16];
316         struct overflow_child_parent child_msg = { .done = false };
317         NTSTATUS status;
318
319         ret = pipe(up_pipe);
320         torture_assert(tctx, ret == 0, "pipe failed");
321         ret = pipe(down_pipe);
322         torture_assert(tctx, ret == 0, "pipe failed");
323
324         child = fork();
325         if (child < 0) {
326                 torture_fail(tctx, "fork failed");
327         }
328
329         if (child == 0) {
330                 struct overflow_parent_child child_state = { .done = false };
331                 DATA_BLOB retblob = { .data = final, .length = sizeof(final) };
332
333                 ret = tevent_re_initialise(tctx->ev);
334                 torture_assert(tctx, ret == 0, "tevent_re_initialise failed");
335
336                 gnutls_hash_init(&child_state.md5_hash_hnd, GNUTLS_DIG_MD5);
337
338                 msg_ctx = imessaging_init(tctx, tctx->lp_ctx,
339                                           cluster_id(getpid(), 0),
340                                           tctx->ev);
341                 torture_assert(tctx, msg_ctx != NULL,
342                                "imessaging_init failed");
343
344                 status = imessaging_register(msg_ctx, &child_state,
345                                              MSG_TMP_BASE-1,
346                                              overflow_md5_child_handler);
347                 torture_assert(tctx, NT_STATUS_IS_OK(status),
348                                "imessaging_register failed");
349
350                 do {
351                         nwritten = write(up_pipe[1], &c, 1);
352                 } while ((nwritten == -1) && (errno == EINTR));
353
354                 ret = close(down_pipe[1]);
355                 torture_assert(tctx, ret == 0, "close failed");
356
357                 do {
358                         nread = read(down_pipe[0], &c, 1);
359                 } while ((nread == -1) && (errno == EINTR));
360
361                 while (!child_state.done) {
362                         tevent_loop_once(tctx->ev);
363                 }
364
365                 gnutls_hash_deinit(child_state.md5_hash_hnd, final);
366
367                 status = imessaging_send(msg_ctx,
368                                          cluster_id(getppid(), 0),
369                                          MSG_TMP_BASE-2,
370                                          &retblob);
371                 torture_assert(tctx, NT_STATUS_IS_OK(status),
372                                "imessaging_send failed");
373
374                 exit(0);
375         }
376
377         do {
378                 nread = read(up_pipe[0], &c, 1);
379         } while ((nread == -1) && (errno == EINTR));
380
381         msg_ctx = imessaging_init(tctx, tctx->lp_ctx, cluster_id(getpid(), 0),
382                                   tctx->ev);
383         torture_assert(tctx, msg_ctx != NULL, "imessaging_init failed");
384
385         status = imessaging_register(msg_ctx,
386                                      &child_msg,
387                                      MSG_TMP_BASE-2,
388                                      overflow_md5_parent_handler);
389         torture_assert(tctx,
390                        NT_STATUS_IS_OK(status),
391                        "imessaging_register failed");
392
393         gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
394
395         for (i=0; i<1000; i++) {
396                 size_t len = ((random() % 100) + 1);
397                 uint8_t buf[len];
398                 DATA_BLOB blob = { .data = buf, .length = len };
399
400                 generate_random_buffer(buf, len);
401
402                 gnutls_hash(hash_hnd, buf, len);
403
404                 status = imessaging_send(msg_ctx, cluster_id(child, 0),
405                                          MSG_TMP_BASE-1, &blob);
406                 torture_assert_ntstatus_ok(tctx, status,
407                                            "imessaging_send failed");
408         }
409
410         status = imessaging_send(msg_ctx, cluster_id(child, 0),
411                                  MSG_TMP_BASE-1, NULL);
412         torture_assert_ntstatus_ok(tctx, status,
413                                    "imessaging_send failed");
414
415         gnutls_hash_deinit(hash_hnd, final);
416
417         do {
418                 nwritten = write(down_pipe[1], &c, 1);
419         } while ((nwritten == -1) && (errno == EINTR));
420
421         while (!child_msg.done) {
422                 tevent_loop_once(tctx->ev);
423         }
424
425         ret = close(down_pipe[1]);
426         torture_assert(tctx, ret == 0, "close failed");
427
428         talloc_free(msg_ctx);
429
430         ret = waitpid(child, &child_status, 0);
431         torture_assert(tctx, ret == child, "wrong child exited");
432         torture_assert(tctx, child_status == 0, "child failed");
433
434         if (memcmp(final, child_msg.final, 16) != 0) {
435                 dump_data_file(final, 16, false, stderr);
436                 dump_data_file(child_msg.final, 16, false, stderr);
437                 fflush(stderr);
438                 torture_fail(tctx, "checksum comparison failed");
439         }
440
441         return true;
442 }
443
444 struct test_multi_ctx {
445         struct torture_context *tctx;
446         struct imessaging_context *server_ctx;
447         struct imessaging_context *client_ctx[4];
448         size_t num_missing;
449         bool got_server;
450         bool got_client_0_1;
451         bool got_client_2_3;
452         bool ok;
453 };
454
455 static void multi_ctx_server_handler(struct imessaging_context *msg,
456                                      void *private_data,
457                                      uint32_t msg_type,
458                                      struct server_id server_id,
459                                      size_t num_fds,
460                                      int *fds,
461                                      DATA_BLOB *data)
462 {
463         struct test_multi_ctx *state = private_data;
464         char *str = NULL;
465
466         if (num_fds != 0) {
467                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
468                 return;
469         }
470
471         torture_assert_goto(state->tctx, state->num_missing >= 1,
472                             state->ok, fail,
473                             "num_missing should be at least 1.");
474         state->num_missing -= 1;
475
476         torture_assert_goto(state->tctx, !state->got_server,
477                             state->ok, fail,
478                             "already got server.");
479         state->got_server = true;
480
481         /*
482          * We free the context itself and most likely reuse
483          * the memory immediately.
484          */
485         TALLOC_FREE(state->server_ctx);
486         str = generate_random_str(state->tctx, 128);
487         torture_assert_goto(state->tctx, str != NULL,
488                             state->ok, fail,
489                             "generate_random_str()");
490
491 fail:
492         return;
493 }
494
495 static void multi_ctx_client_0_1_handler(struct imessaging_context *msg,
496                                          void *private_data,
497                                          uint32_t msg_type,
498                                          struct server_id server_id,
499                                          size_t num_fds,
500                                          int *fds,
501                                          DATA_BLOB *data)
502 {
503         struct test_multi_ctx *state = private_data;
504         char *str = NULL;
505
506         if (num_fds != 0) {
507                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
508                 return;
509         }
510
511         torture_assert_goto(state->tctx, state->num_missing >= 2,
512                             state->ok, fail,
513                             "num_missing should be at least 2.");
514         state->num_missing -= 2;
515
516         torture_assert_goto(state->tctx, !state->got_client_0_1,
517                             state->ok, fail,
518                             "already got client_0_1.");
519         state->got_client_0_1 = true;
520
521         /*
522          * We free two contexts and most likely reuse
523          * the memory immediately.
524          */
525         TALLOC_FREE(state->client_ctx[0]);
526         str = generate_random_str(state->tctx, 128);
527         torture_assert_goto(state->tctx, str != NULL,
528                             state->ok, fail,
529                             "generate_random_str()");
530         TALLOC_FREE(state->client_ctx[1]);
531         str = generate_random_str(state->tctx, 128);
532         torture_assert_goto(state->tctx, str != NULL,
533                             state->ok, fail,
534                             "generate_random_str()");
535
536 fail:
537         return;
538 }
539
540 static void multi_ctx_client_2_3_handler(struct imessaging_context *msg,
541                                          void *private_data,
542                                          uint32_t msg_type,
543                                          struct server_id server_id,
544                                          size_t num_fds,
545                                          int *fds,
546                                          DATA_BLOB *data)
547 {
548         struct test_multi_ctx *state = private_data;
549         char *str = NULL;
550
551         if (num_fds != 0) {
552                 DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
553                 return;
554         }
555
556         torture_assert_goto(state->tctx, state->num_missing >= 2,
557                             state->ok, fail,
558                             "num_missing should be at least 2.");
559         state->num_missing -= 2;
560
561         torture_assert_goto(state->tctx, !state->got_client_2_3,
562                             state->ok, fail,
563                             "already got client_2_3.");
564         state->got_client_2_3 = true;
565
566         /*
567          * We free two contexts and most likely reuse
568          * the memory immediately.
569          */
570         TALLOC_FREE(state->client_ctx[2]);
571         str = generate_random_str(state->tctx, 128);
572         torture_assert_goto(state->tctx, str != NULL,
573                             state->ok, fail,
574                             "generate_random_str()");
575         TALLOC_FREE(state->client_ctx[3]);
576         str = generate_random_str(state->tctx, 128);
577         torture_assert_goto(state->tctx, str != NULL,
578                             state->ok, fail,
579                             "generate_random_str()");
580
581 fail:
582         return;
583 }
584
585 static bool test_multi_ctx(struct torture_context *tctx)
586 {
587         struct test_multi_ctx state = {
588                 .tctx = tctx,
589                 .ok = true,
590         };
591         struct timeval tv;
592         NTSTATUS status;
593
594         lpcfg_set_cmdline(tctx->lp_ctx, "pid directory", "piddir.tmp");
595
596         /*
597          * We use cluster_id(0, 0) as that gets for
598          * all task ids.
599          */
600         state.server_ctx = imessaging_init(tctx,
601                                            tctx->lp_ctx,
602                                            cluster_id(0, 0),
603                                            tctx->ev);
604         torture_assert(tctx, state.server_ctx != NULL,
605                        "Failed to init messaging context");
606
607         status = imessaging_register(state.server_ctx, &state,
608                                      MSG_TMP_BASE-1,
609                                      multi_ctx_server_handler);
610         torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
611
612         state.client_ctx[0] = imessaging_init(tctx,
613                                               tctx->lp_ctx,
614                                               cluster_id(0, 0),
615                                               tctx->ev);
616         torture_assert(tctx, state.client_ctx[0] != NULL,
617                        "msg_client_ctx imessaging_init() failed");
618         status = imessaging_register(state.client_ctx[0], &state,
619                                      MSG_TMP_BASE-1,
620                                      multi_ctx_client_0_1_handler);
621         torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
622         state.client_ctx[1] = imessaging_init(tctx,
623                                               tctx->lp_ctx,
624                                               cluster_id(0, 0),
625                                               tctx->ev);
626         torture_assert(tctx, state.client_ctx[1] != NULL,
627                        "msg_client_ctx imessaging_init() failed");
628         status = imessaging_register(state.client_ctx[1], &state,
629                                      MSG_TMP_BASE-1,
630                                      multi_ctx_client_0_1_handler);
631         torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
632         state.client_ctx[2] = imessaging_init(tctx,
633                                               tctx->lp_ctx,
634                                               cluster_id(0, 0),
635                                               tctx->ev);
636         torture_assert(tctx, state.client_ctx[2] != NULL,
637                        "msg_client_ctx imessaging_init() failed");
638         status = imessaging_register(state.client_ctx[2], &state,
639                                      MSG_TMP_BASE-1,
640                                      multi_ctx_client_2_3_handler);
641         torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
642         state.client_ctx[3] = imessaging_init(tctx,
643                                               tctx->lp_ctx,
644                                               cluster_id(0, 0),
645                                               tctx->ev);
646         torture_assert(tctx, state.client_ctx[3] != NULL,
647                        "msg_client_ctx imessaging_init() failed");
648         status = imessaging_register(state.client_ctx[3], &state,
649                                      MSG_TMP_BASE-1,
650                                      multi_ctx_client_2_3_handler);
651         torture_assert(tctx, NT_STATUS_IS_OK(status), "imessaging_register failed");
652
653         /*
654          * Send one message that need to arrive on 3 ( 5 - 2 ) handlers.
655          */
656         state.num_missing = 5;
657
658         status = imessaging_send(state.server_ctx,
659                                  cluster_id(0, 0),
660                                  MSG_TMP_BASE-1, NULL);
661         torture_assert_ntstatus_ok(tctx, status, "msg failed");
662
663         tv = timeval_current();
664         while (timeval_elapsed(&tv) < 30 && state.num_missing > 0 && state.ok) {
665                 int ret;
666
667                 ret = tevent_loop_once(tctx->ev);
668                 torture_assert_int_equal(tctx, ret, 0, "tevent_loop_once()");
669         }
670
671         if (!state.ok) {
672                 return false;
673         }
674
675         torture_assert_int_equal(tctx, state.num_missing, 0,
676                                  "wrong message count");
677
678         torture_assert(tctx, state.got_client_0_1, "got_client_0_1");
679         torture_assert(tctx, state.got_client_2_3, "got_client_2_3");
680         torture_assert(tctx, state.got_server, "got_server");
681
682         return true;
683 }
684
685 struct torture_suite *torture_local_messaging(TALLOC_CTX *mem_ctx)
686 {
687         struct torture_suite *s = torture_suite_create(mem_ctx, "messaging");
688         torture_suite_add_simple_test(s, "overflow", test_messaging_overflow);
689         torture_suite_add_simple_test(s, "overflow_check",
690                                       test_messaging_overflow_check);
691         torture_suite_add_simple_test(s, "ping_speed", test_ping_speed);
692         torture_suite_add_simple_test(s, "multi_ctx", test_multi_ctx);
693         return s;
694 }