- inital mesh_profiler commit
[oweals/gnunet.git] / src / mesh / mesh_profiler.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file mesh/mesh_profiler.c
22  *
23  * @brief Profiler for mesh experiments.
24  */
25 #include <stdio.h>
26 #include "platform.h"
27 #include "mesh_test_lib.h"
28 #include "gnunet_mesh_service.h"
29 #include "gnunet_statistics_service.h"
30 #include <gauger.h>
31
32
33 /**
34  * How namy messages to send
35  */
36 #define TOTAL_PACKETS 1000
37
38 /**
39  * How namy peers to run
40  */
41 #define TOTAL_PEERS 1000
42
43 /**
44  * How long until we give up on connecting the peers?
45  */
46 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
47
48 /**
49  * Time to wait for stuff that should be rather fast
50  */
51 #define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
52
53
54 /**
55  * How many events have happened
56  */
57 static int ok;
58
59 /**
60  * Number of events expected to conclude the test successfully.
61  */
62 int ok_goal;
63
64 /**
65  * Size of each test packet
66  */
67 size_t size_payload = sizeof (struct GNUNET_MessageHeader) + sizeof (uint32_t);
68
69 /**
70  * Operation to get peer ids.
71  */
72 struct GNUNET_TESTBED_Operation *t_op[TOTAL_PEERS];
73
74 /**
75  * Peer ids.
76  */
77 struct GNUNET_PeerIdentity *p_id[TOTAL_PEERS];
78
79 /**
80  * Mesh handle for the root peer
81  */
82 static struct GNUNET_MESH_Handle *mesh_h[TOTAL_PEERS];
83
84 /**
85  * Channel handle for the root peer
86  */
87 static struct GNUNET_MESH_Channel *ch;
88
89 /**
90  * Channel handle for the dest peer
91  */
92 static struct GNUNET_MESH_Channel *incoming_ch;
93
94 /**
95  * Peer ids counter.
96  */
97 unsigned int p_ids;
98
99 /**
100  * Is the setup initialized?
101  */
102 static int initialized;
103
104 /**
105  * Number of payload packes sent
106  */
107 static int data_sent;
108
109 /**
110  * Number of payload packets received
111  */
112 static int data_received;
113
114 /**
115  * Number of payload packed explicitly (app level) acknowledged
116  */
117 static int data_ack;
118
119 /**
120  * Total number of currently running peers.
121  */
122 static unsigned long long peers_running;
123
124 /**
125  * Test context (to shut down).
126  */
127 struct GNUNET_MESH_TEST_Context *test_ctx;
128
129 /**
130  * Task called to shutdown test.
131  */
132 static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle;
133
134 /**
135  * Task called to disconnect peers, before shutdown.
136  */
137 static GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
138
139 /**
140  * Task to perform tests
141  */
142 static GNUNET_SCHEDULER_TaskIdentifier test_task;
143
144 /**
145  * Time we started the data transmission (after channel has been established
146  * and initilized).
147  */
148 static struct GNUNET_TIME_Absolute start_time;
149
150 static struct GNUNET_TESTBED_Peer **testbed_peers;
151
152 /**
153  *
154  */
155 static struct GNUNET_STATISTICS_Handle *stats;
156 static struct GNUNET_STATISTICS_GetHandle *stats_get;
157 static struct GNUNET_TESTBED_Operation *stats_op;
158 static unsigned int ka_sent;
159 static unsigned int ka_received;
160
161
162 /**
163  * Show the results of the test (banwidth acheived) and log them to GAUGER
164  */
165 static void
166 show_end_data (void)
167 {
168   static struct GNUNET_TIME_Absolute end_time;
169   static struct GNUNET_TIME_Relative total_time;
170
171   end_time = GNUNET_TIME_absolute_get();
172   total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time);
173   FPRINTF (stderr, "\nResults of test \"%s\"\n", test_name);
174   FPRINTF (stderr, "Test time %s\n",
175            GNUNET_STRINGS_relative_time_to_string (total_time,
176                                                    GNUNET_YES));
177   FPRINTF (stderr, "Test bandwidth: %f kb/s\n",
178            4 * TOTAL_PACKETS * 1.0 / (total_time.rel_value_us / 1000)); // 4bytes * ms
179   FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
180            TOTAL_PACKETS * 1000.0 / (total_time.rel_value_us / 1000)); // packets * ms
181   GAUGER ("MESH", test_name,
182           TOTAL_PACKETS * 1000.0 / (total_time.rel_value_us / 1000),
183           "packets/s");
184 }
185
186
187 /**
188  * Shut down peergroup, clean up.
189  *
190  * @param cls Closure (unused).
191  * @param tc Task Context.
192  */
193 static void
194 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
195 {
196   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n");
197   shutdown_handle = GNUNET_SCHEDULER_NO_TASK;
198 }
199
200
201 /**
202  * Disconnect from mesh services af all peers, call shutdown.
203  *
204  * @param cls Closure (unused).
205  * @param tc Task Context.
206  */
207 static void
208 disconnect_mesh_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
209 {
210   long line = (long) cls;
211   unsigned int i;
212
213   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
214               "disconnecting mesh service of peers, called from line %ld\n",
215               line);
216   disconnect_task = GNUNET_SCHEDULER_NO_TASK;
217   for (i = 0; i < 2; i++)
218   {
219     GNUNET_TESTBED_operation_done (t_op[i]);
220   }
221   if (NULL != ch)
222   {
223     GNUNET_MESH_channel_destroy (ch);
224     ch = NULL;
225   }
226   if (NULL != incoming_ch)
227   {
228     GNUNET_MESH_channel_destroy (incoming_ch);
229     incoming_ch = NULL;
230   }
231   GNUNET_MESH_TEST_cleanup (test_ctx);
232   if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle)
233   {
234     GNUNET_SCHEDULER_cancel (shutdown_handle);
235   }
236   if (NULL != stats_get)
237     GNUNET_STATISTICS_get_cancel (stats_get);
238   shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
239 }
240
241
242 /**
243  * Abort test: schedule disconnect and shutdown immediately
244  *
245  * @param line Line in the code the abort is requested from (__LINE__).
246  */
247 static void
248 abort_test (long line)
249 {
250   if (disconnect_task != GNUNET_SCHEDULER_NO_TASK)
251   {
252     GNUNET_SCHEDULER_cancel (disconnect_task);
253     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers,
254                                                 (void *) line);
255   }
256 }
257
258 /**
259  * Transmit ready callback.
260  *
261  * @param cls Closure (message type).
262  * @param size Size of the tranmist buffer.
263  * @param buf Pointer to the beginning of the buffer.
264  *
265  * @return Number of bytes written to buf.
266  */
267 static size_t
268 tmt_rdy (void *cls, size_t size, void *buf);
269
270
271 /**
272  * Task to schedule a new data transmission.
273  *
274  * @param cls Closure (peer #).
275  * @param tc Task Context.
276  */
277 static void
278 data_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
279 {
280   struct GNUNET_MESH_TransmitHandle *th;
281   struct GNUNET_MESH_Channel *channel;
282
283   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
284     return;
285
286   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data task\n");
287   if (GNUNET_YES == test_backwards)
288   {
289     channel = incoming_ch;
290   }
291   else
292   {
293     channel = ch;
294   }
295   th = GNUNET_MESH_notify_transmit_ready (channel, GNUNET_NO,
296                                           GNUNET_TIME_UNIT_FOREVER_REL,
297                                           size_payload, &tmt_rdy, (void *) 1L);
298   if (NULL == th)
299   {
300     unsigned long i = (unsigned long) cls;
301
302     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Retransmission\n");
303     if (0 == i)
304     {
305       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "  in 1 ms\n");
306       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
307                                     &data_task, (void *)1UL);
308     }
309     else
310     {
311       i++;
312       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "in %u ms\n", i);
313       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(
314                                       GNUNET_TIME_UNIT_MILLISECONDS,
315                                       i),
316                                     &data_task, (void *)i);
317     }
318   }
319 }
320
321
322 /**
323  * Transmit ready callback
324  *
325  * @param cls Closure (message type).
326  * @param size Size of the buffer we have.
327  * @param buf Buffer to copy data to.
328  */
329 size_t
330 tmt_rdy (void *cls, size_t size, void *buf)
331 {
332   struct GNUNET_MessageHeader *msg = buf;
333   uint32_t *data;
334
335   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336               "tmt_rdy called, filling buffer\n");
337   if (size < size_payload || NULL == buf)
338   {
339     GNUNET_break (ok >= ok_goal - 2);
340     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341                 "size %u, buf %p, data_sent %u, data_received %u\n",
342                 size, buf, data_sent, data_received);
343     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ok %u, ok goal %u\n", ok, ok_goal);
344
345     return 0;
346   }
347   msg->size = htons (size);
348   msg->type = htons ((long) cls);
349   data = (uint32_t *) &msg[1];
350   *data = htonl (data_sent);
351   if (GNUNET_NO == initialized)
352   {
353     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354                 "sending initializer\n");
355   }
356   else if (SPEED == test)
357   {
358     data_sent++;
359     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360               " Sent packet %d\n", data_sent);
361     if (data_sent < TOTAL_PACKETS)
362     {
363       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364               " Scheduling packet %d\n", data_sent + 1);
365       GNUNET_SCHEDULER_add_now (&data_task, NULL);
366     }
367   }
368
369   return size_payload;
370 }
371
372
373 /**
374  * Function is called whenever a message is received.
375  *
376  * @param cls closure (set from GNUNET_MESH_connect)
377  * @param channel connection to the other end
378  * @param channel_ctx place to store local state associated with the channel
379  * @param message the actual message
380  * @return GNUNET_OK to keep the connection open,
381  *         GNUNET_SYSERR to close it (signal serious error)
382  */
383 int
384 data_callback (void *cls, struct GNUNET_MESH_Channel *channel,
385                void **channel_ctx,
386                const struct GNUNET_MessageHeader *message)
387 {
388   long client = (long) cls;
389   long expected_target_client;
390   uint32_t *data;
391
392   ok++;
393
394   GNUNET_MESH_receive_done (channel);
395
396   if ((ok % 20) == 0)
397   {
398     if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
399     {
400       GNUNET_SCHEDULER_cancel (disconnect_task);
401       disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
402                                                       &disconnect_mesh_peers,
403                                                       (void *) __LINE__);
404     }
405   }
406
407   switch (client)
408   {
409   case 0L:
410     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message!\n");
411     break;
412   case 4L:
413     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
414                 "Leaf client %li got a message.\n",
415                 client);
416     break;
417   default:
418     GNUNET_assert (0);
419     break;
420   }
421   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: (%d/%d)\n", ok, ok_goal);
422   data = (uint32_t *) &message[1];
423   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " payload: (%u)\n", ntohl (*data));
424   if (SPEED == test && GNUNET_YES == test_backwards)
425   {
426     expected_target_client = 0L;
427   }
428   else
429   {
430     expected_target_client = 4L;
431   }
432
433   if (GNUNET_NO == initialized)
434   {
435     initialized = GNUNET_YES;
436     start_time = GNUNET_TIME_absolute_get ();
437     if (SPEED == test)
438     {
439       GNUNET_assert (4L == client);
440       GNUNET_SCHEDULER_add_now (&data_task, NULL);
441       return GNUNET_OK;
442     }
443   }
444
445   if (client == expected_target_client) // Normally 4
446   {
447     data_received++;
448     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " received data %u\n", data_received);
449     if (SPEED != test || (ok_goal - 2) == ok)
450     {
451       GNUNET_MESH_notify_transmit_ready (channel, GNUNET_NO,
452                                          GNUNET_TIME_UNIT_FOREVER_REL,
453                                          size_payload, &tmt_rdy, (void *) 1L);
454       return GNUNET_OK;
455     }
456     else
457     {
458       if (data_received < TOTAL_PACKETS)
459         return GNUNET_OK;
460     }
461   }
462   else // Normally 0
463   {
464     if (test == SPEED_ACK || test == SPEED)
465     {
466       data_ack++;
467       GNUNET_log (GNUNET_ERROR_TYPE_INFO, " received ack %u\n", data_ack);
468       GNUNET_MESH_notify_transmit_ready (channel, GNUNET_NO,
469                                          GNUNET_TIME_UNIT_FOREVER_REL,
470                                          size_payload, &tmt_rdy, (void *) 1L);
471       if (data_ack < TOTAL_PACKETS && SPEED != test)
472         return GNUNET_OK;
473       if (ok == 2 && SPEED == test)
474         return GNUNET_OK;
475       show_end_data();
476     }
477     if (test == P2P_SIGNAL)
478     {
479       GNUNET_MESH_channel_destroy (incoming_ch);
480       incoming_ch = NULL;
481     }
482     else
483     {
484       GNUNET_MESH_channel_destroy (ch);
485       ch = NULL;
486     }
487   }
488
489   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
490   {
491     GNUNET_SCHEDULER_cancel (disconnect_task);
492     disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
493                                                     &disconnect_mesh_peers,
494                                                     (void *) __LINE__);
495   }
496
497   return GNUNET_OK;
498 }
499
500
501 /**
502  * Adapter function called to establish a connection to the statistics service.
503  *
504  * @param cls closure
505  * @param cfg configuration of the peer to connect to; will be available until
506  *          GNUNET_TESTBED_operation_done() is called on the operation returned
507  *          from GNUNET_TESTBED_service_connect()
508  * @return service handle to return in 'op_result', NULL on error
509  */
510 static void *
511 stats_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
512 {
513   return GNUNET_STATISTICS_create ("<test_mesh>", cfg);
514 }
515
516
517 /**
518  * Adapter function called to destroy a connection to
519  * statistics service.
520  *
521  * @param cls Closure (unused).
522  * @param op_result service handle returned from the connect adapter
523  */
524 static void
525 stats_da (void *cls, void *op_result)
526 {
527   GNUNET_assert (op_result == stats);
528   GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
529   stats = NULL;
530 }
531
532
533 /**
534  * Function called by testbed once we are connected to stats
535  * service. Get the statistics of interest.
536  *
537  * @param cls Closure (unused).
538  * @param op connect operation handle
539  * @param ca_result handle to stats service
540  * @param emsg error message on failure
541  */
542 static void
543 stats_connect_cb (void *cls,
544                   struct GNUNET_TESTBED_Operation *op,
545                   void *ca_result,
546                   const char *emsg);
547
548 /**
549  * Stats callback. Finish the stats testbed operation and when all stats have
550  * been iterated, shutdown the test.
551  *
552  * @param cls closure
553  * @param success GNUNET_OK if statistics were
554  *        successfully obtained, GNUNET_SYSERR if not.
555  */
556 static void
557 stats_cont (void *cls, int success)
558 {
559   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "stats_cont for peer %u\n", cls);
560   GNUNET_TESTBED_operation_done (stats_op);
561   stats_get = NULL;
562   if (NULL == cls)
563   {
564     stats_op = GNUNET_TESTBED_service_connect (NULL,
565                                                testbed_peers[4],
566                                                "statistics",
567                                                &stats_connect_cb,
568                                                (void *)4,
569                                                &stats_ca,
570                                                &stats_da,
571                                                (void *)4);
572   }
573   else
574   {
575     if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
576       GNUNET_SCHEDULER_cancel (disconnect_task);
577     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers,
578                                                 (void *) __LINE__);
579   }
580 }
581
582
583 /**
584  * Process statistic values.
585  *
586  * @param cls closure
587  * @param subsystem name of subsystem that created the statistic
588  * @param name the name of the datum
589  * @param value the current value
590  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
591  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
592  */
593 static int
594 stats_iterator (void *cls, const char *subsystem, const char *name,
595                 uint64_t value, int is_persistent)
596 {
597   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  %u - %s [%s]: %llu\n",
598               cls, subsystem, name, value);
599   if (0 == strncmp("# keepalives sent", name,
600                    strlen("# keepalives sent"))
601       && 0 == (long) cls)
602     ka_sent = value;
603
604   if (0 == strncmp("# keepalives received", name,
605                    strlen ("# keepalives received"))
606       && 4 == (long) cls)
607   {
608     ka_received = value;
609     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " sent: %u, received: %u\n",
610                 ka_sent, ka_received);
611     if (ka_sent < 2 || ka_sent > ka_received + 1)
612       ok--;
613   }
614
615   return GNUNET_OK;
616 }
617
618
619 /**
620  * Function called by testbed once we are connected to stats
621  * service. Get the statistics of interest.
622  *
623  * @param cls Closure (unused).
624  * @param op connect operation handle
625  * @param ca_result handle to stats service
626  * @param emsg error message on failure
627  */
628 static void
629 stats_connect_cb (void *cls,
630                   struct GNUNET_TESTBED_Operation *op,
631                   void *ca_result,
632                   const char *emsg)
633 {
634   if (NULL == ca_result || NULL != emsg)
635   {
636     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
637                 "Failed to connect to statistics service: %s\n", emsg);
638     return;
639   }
640
641   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "stats for peer %u\n", cls);
642   stats = ca_result;
643
644   stats_get = GNUNET_STATISTICS_get (stats, "mesh", NULL,
645                                      GNUNET_TIME_UNIT_FOREVER_REL,
646                                      &stats_cont, &stats_iterator, cls);
647   if (NULL == stats_get)
648   {
649     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
650                 "Could not get statistics of peer %u!\n", cls);
651   }
652 }
653
654
655 /**
656  * Task check that keepalives were sent and received.
657  *
658  * @param cls Closure (NULL).
659  * @param tc Task Context.
660  */
661 static void
662 check_keepalives (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
663 {
664   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
665     return;
666
667   disconnect_task = GNUNET_SCHEDULER_NO_TASK;
668   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "check keepalives\n");
669   GNUNET_MESH_channel_destroy (ch);
670   stats_op = GNUNET_TESTBED_service_connect (NULL,
671                                              testbed_peers[0],
672                                              "statistics",
673                                              &stats_connect_cb,
674                                              NULL,
675                                              &stats_ca,
676                                              &stats_da,
677                                              NULL);
678 }
679
680
681 /**
682  * Handlers, for diverse services
683  */
684 static struct GNUNET_MESH_MessageHandler handlers[] = {
685   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
686   {NULL, 0, 0}
687 };
688
689
690 /**
691  * Method called whenever another peer has added us to a channel
692  * the other peer initiated.
693  *
694  * @param cls Closure.
695  * @param channel New handle to the channel.
696  * @param initiator Peer that started the channel.
697  * @param port Port this channel is connected to.
698  * @param options channel option flags
699  * @return Initial channel context for the channel
700  *         (can be NULL -- that's not an error).
701  */
702 static void *
703 incoming_channel (void *cls, struct GNUNET_MESH_Channel *channel,
704                  const struct GNUNET_PeerIdentity *initiator,
705                  uint32_t port, enum GNUNET_MESH_ChannelOption options)
706 {
707   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
708               "Incoming channel from %s to peer %d\n",
709               GNUNET_i2s (initiator), (long) cls);
710   ok++;
711   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
712   if ((long) cls == 4L)
713     incoming_ch = channel;
714   else
715   {
716     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
717                 "Incoming channel for unknown client %lu\n", (long) cls);
718     GNUNET_break(0);
719   }
720   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
721   {
722     GNUNET_SCHEDULER_cancel (disconnect_task);
723     if (KEEPALIVE == test)
724     {
725       struct GNUNET_TIME_Relative delay;
726       delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS , 5);
727       disconnect_task =
728         GNUNET_SCHEDULER_add_delayed (delay, &check_keepalives, NULL);
729     }
730     else
731       disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
732                                                       &disconnect_mesh_peers,
733                                                       (void *) __LINE__);
734   }
735
736   return NULL;
737 }
738
739 /**
740  * Function called whenever an inbound channel is destroyed.  Should clean up
741  * any associated state.
742  *
743  * @param cls closure (set from GNUNET_MESH_connect)
744  * @param channel connection to the other end (henceforth invalid)
745  * @param channel_ctx place where local state associated
746  *                   with the channel is stored
747  */
748 static void
749 channel_cleaner (void *cls, const struct GNUNET_MESH_Channel *channel,
750                  void *channel_ctx)
751 {
752   long i = (long) cls;
753
754   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
755               "Incoming channel disconnected at peer %d\n",
756               i);
757   if (4L == i)
758   {
759     ok++;
760     GNUNET_break (channel == incoming_ch);
761     incoming_ch = NULL;
762   }
763   else if (0L == i)
764   {
765     if (P2P_SIGNAL == test)
766     {
767       ok ++;
768     }
769     GNUNET_break (channel == ch);
770     ch = NULL;
771   }
772   else
773     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
774                 "Unknown peer! %d\n", i);
775   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
776
777   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
778   {
779     GNUNET_SCHEDULER_cancel (disconnect_task);
780     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers,
781                                                 (void *) __LINE__);
782   }
783
784   return;
785 }
786
787
788 /**
789  * START THE TESTCASE ITSELF, AS WE ARE CONNECTED TO THE MESH SERVICES.
790  *
791  * Testcase continues when the root receives confirmation of connected peers,
792  * on callback funtion ch.
793  *
794  * @param cls Closure (unsued).
795  * @param tc Task Context.
796  */
797 static void
798 do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
799 {
800   enum GNUNET_MESH_ChannelOption flags;
801
802   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
803     return;
804
805   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test_task\n");
806
807   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
808   {
809     GNUNET_SCHEDULER_cancel (disconnect_task);
810   }
811
812   flags = GNUNET_MESH_OPTION_DEFAULT;
813   if (SPEED_REL == test)
814   {
815     test = SPEED;
816     flags |= GNUNET_MESH_OPTION_RELIABLE;
817   }
818   ch = GNUNET_MESH_channel_create (h1, NULL, p_id[1], 1, flags);
819
820   disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
821                                                   &disconnect_mesh_peers,
822                                                   (void *) __LINE__);
823   if (KEEPALIVE == test)
824     return; /* Don't send any data. */
825
826   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
827               "Sending data initializer...\n");
828   data_ack = 0;
829   data_received = 0;
830   data_sent = 0;
831   GNUNET_MESH_notify_transmit_ready (ch, GNUNET_NO,
832                                      GNUNET_TIME_UNIT_FOREVER_REL,
833                                      size_payload, &tmt_rdy, (void *) 1L);
834 }
835
836 /**
837  * Callback to be called when the requested peer information is available
838  *
839  * @param cls the closure from GNUNET_TESTBED_peer_get_information()
840  * @param op the operation this callback corresponds to
841  * @param pinfo the result; will be NULL if the operation has failed
842  * @param emsg error message if the operation has failed;
843  *             NULL if the operation is successfull
844  */
845 static void
846 pi_cb (void *cls,
847        struct GNUNET_TESTBED_Operation *op,
848        const struct GNUNET_TESTBED_PeerInformation *pinfo,
849        const char *emsg)
850 {
851   long i = (long) cls;
852
853   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "id callback for %ld\n", i);
854
855   if (NULL == pinfo || NULL != emsg)
856   {
857     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "pi_cb: %s\n", emsg);
858     abort_test (__LINE__);
859     return;
860   }
861   p_id[i] = pinfo->result.id;
862   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  id: %s\n", GNUNET_i2s (p_id[i]));
863   p_ids++;
864   if (p_ids < 2)
865     return;
866   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got all IDs, starting test\n");
867   test_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
868                                             &do_test, NULL);
869 }
870
871 /**
872  * test main: start test when all peers are connected
873  *
874  * @param cls Closure.
875  * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end.
876  * @param num_peers Number of peers that are running.
877  * @param peers Array of peers.
878  * @param meshes Handle to each of the MESHs of the peers.
879  */
880 static void
881 tmain (void *cls,
882        struct GNUNET_MESH_TEST_Context *ctx,
883        unsigned int num_peers,
884        struct GNUNET_TESTBED_Peer **peers,
885        struct GNUNET_MESH_Handle **meshes)
886 {
887   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test main\n");
888   ok = 0;
889   test_ctx = ctx;
890   peers_running = num_peers;
891   testbed_peers = peers;
892   h1 = meshes[0];
893   h2 = meshes[num_peers - 1];
894   disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME,
895                                                   &disconnect_mesh_peers,
896                                                   (void *) __LINE__);
897   shutdown_handle = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
898                                                   &shutdown_task, NULL);
899   t_op[0] = GNUNET_TESTBED_peer_get_information (peers[0],
900                                                  GNUNET_TESTBED_PIT_IDENTITY,
901                                                  &pi_cb, (void *) 0L);
902   t_op[1] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 1],
903                                                  GNUNET_TESTBED_PIT_IDENTITY,
904                                                  &pi_cb, (void *) 1L);
905   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requested peer ids\n");
906 }
907
908
909 /**
910  * Main: start test
911  */
912 int
913 main (int argc, char *argv[])
914 {
915   initialized = GNUNET_NO;
916   uint32_t ports[2];
917   const char *config_file;
918
919   GNUNET_log_setup ("test", "DEBUG", NULL);
920   config_file = "test_mesh.conf";
921
922   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n");
923   if (strstr (argv[0], "_small_forward") != NULL)
924   {
925     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FORWARD\n");
926     test = FORWARD;
927     test_name = "unicast";
928     ok_goal = 4;
929   }
930   else if (strstr (argv[0], "_small_signal") != NULL)
931   {
932     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SIGNAL\n");
933     test = P2P_SIGNAL;
934     test_name = "signal";
935     ok_goal = 4;
936   }
937   else if (strstr (argv[0], "_small_speed_ack") != NULL)
938   {
939     /* Test is supposed to generate the following callbacks:
940      * 1 incoming channel (@dest)
941      * TOTAL_PACKETS received data packet (@dest)
942      * TOTAL_PACKETS received data packet (@orig)
943      * 1 received channel destroy (@dest)
944      */
945     ok_goal = TOTAL_PACKETS * 2 + 2;
946     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n");
947     test = SPEED_ACK;
948     test_name = "speed ack";
949   }
950   else if (strstr (argv[0], "_small_speed") != NULL)
951   {
952     /* Test is supposed to generate the following callbacks:
953      * 1 incoming channel (@dest)
954      * 1 initial packet (@dest)
955      * TOTAL_PACKETS received data packet (@dest)
956      * 1 received data packet (@orig)
957      * 1 received channel destroy (@dest)
958      */
959     ok_goal = TOTAL_PACKETS + 4;
960     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n");
961     if (strstr (argv[0], "_reliable") != NULL)
962     {
963       test = SPEED_REL;
964       test_name = "speed reliable";
965       config_file = "test_mesh_drop.conf";
966     }
967     else
968     {
969       test = SPEED;
970       test_name = "speed";
971     }
972   }
973   else if (strstr (argv[0], "_keepalive") != NULL)
974   {
975     test = KEEPALIVE;
976     /* Test is supposed to generate the following callbacks:
977      * 1 incoming channel (@dest)
978      * [wait]
979      * 1 received channel destroy (@dest)
980      */
981     ok_goal = 2;
982   }
983   else
984   {
985     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNKNOWN\n");
986     test = SETUP;
987     ok_goal = 0;
988   }
989
990   if (strstr (argv[0], "backwards") != NULL)
991   {
992     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BACKWARDS (LEAF TO ROOT)\n");
993     test_backwards = GNUNET_YES;
994     GNUNET_asprintf (&test_name, "backwards %s", test_name);
995   }
996
997   p_ids = 0;
998   ports[0] = 1;
999   ports[1] = 0;
1000   GNUNET_MESH_TEST_run ("test_mesh_small",
1001                         config_file,
1002                         5,
1003                         &tmain,
1004                         NULL, /* tmain cls */
1005                         &incoming_channel,
1006                         &channel_cleaner,
1007                         handlers,
1008                         ports);
1009
1010   if (ok_goal > ok)
1011   {
1012     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1013                 "FAILED! (%d/%d)\n", ok, ok_goal);
1014     return 1;
1015   }
1016   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n");
1017   return 0;
1018 }
1019
1020 /* end of test_mesh_small.c */
1021