api changes
[oweals/gnunet.git] / src / integration-tests / connection_watchdog.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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 integration-tests/connection_watchdog.c
22  * @brief tool to monitor core and transport connections for consistency
23  * @author Matthias Wachs
24  */
25 #include "platform.h"
26 #include "gnunet_common.h"
27 #include "gnunet_constants.h"
28 #include "gnunet_arm_service.h"
29 #include "gnunet_core_service.h"
30 #include "gnunet_getopt_lib.h"
31 #include "gnunet_os_lib.h"
32 #include "gnunet_program_lib.h"
33 #include "gnunet_scheduler_lib.h"
34 #include "gnunet_transport_service.h"
35 #include "gnunet_statistics_service.h"
36
37
38 #define CHECK_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
39 #define STATS_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
40 #define REPEATED_STATS_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
41 #define STATS_VALUES 4
42
43 /**
44  * Final status code.
45  */
46 static int ret;
47 static int ping;
48
49 static int have_tcp;
50 static int have_udp;
51 static int have_http;
52 static int have_https;
53 static int have_unix;
54
55 static struct GNUNET_TRANSPORT_Handle *th;
56 static struct GNUNET_CORE_Handle *ch;
57 static struct GNUNET_PeerIdentity my_peer_id;
58 static const struct GNUNET_CONFIGURATION_Handle *mycfg;
59 static struct GNUNET_STATISTICS_Handle *stats;
60
61
62 static unsigned int transport_connections;
63 static unsigned int core_connections;
64
65 static GNUNET_SCHEDULER_TaskIdentifier check_task;
66 static GNUNET_SCHEDULER_TaskIdentifier statistics_task;
67
68 static uint64_t statistics_transport_connections;
69 static uint64_t statistics_transport_tcp_connections;
70 static uint64_t statistics_core_neighbour_entries;
71 static uint64_t statistics_core_entries_session_map;
72
73 int stat_check_running;
74
75 static struct GNUNET_CONTAINER_MultiHashMap *peers;
76
77 struct PeerContainer
78 {
79   struct GNUNET_PeerIdentity id;
80   int transport_connected;
81   int core_connected;
82   struct GNUNET_TRANSPORT_TransmitHandle *th_ping;
83   struct GNUNET_CORE_TransmitHandle *ch_ping;
84
85   struct GNUNET_TRANSPORT_TransmitHandle *th_pong;
86   struct GNUNET_CORE_TransmitHandle *ch_pong;
87 };
88
89
90 enum protocol
91 {
92   tcp,
93   udp,
94   unixdomain
95 };
96
97 struct TransportPlugin
98 {
99   /**
100    * This is a doubly-linked list.
101    */
102   struct TransportPlugin *next;
103
104   /**
105    * This is a doubly-linked list.
106    */
107   struct TransportPlugin *prev;
108
109   /**
110    * Short name for the plugin (i.e. "tcp").
111    */
112   char *short_name;
113
114   int port;
115
116   int protocol;
117 };
118
119 struct TransportPlugin *phead;
120 struct TransportPlugin *ptail;
121
122 int map_check_it (void *cls,
123                   const GNUNET_HashCode * key,
124                   void *value)
125 {
126   int *fail = cls;
127   struct PeerContainer *pc = value;
128   if (pc->core_connected != pc->transport_connected)
129   {
130     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
131      "Inconsistent peer `%s': TRANSPORT %s <-> CORE %s\n",
132      GNUNET_i2s (&pc->id),
133      (GNUNET_YES == pc->transport_connected) ? "YES" : "NO",
134      (GNUNET_YES == pc->core_connected) ? "YES" : "NO");
135     (*fail) ++;
136   }
137
138   return GNUNET_OK;
139 }
140
141
142 int map_cleanup_it (void *cls,
143                   const GNUNET_HashCode * key,
144                   void *value)
145 {
146   struct PeerContainer *pc = value;
147   GNUNET_CONTAINER_multihashmap_remove(peers, key, value);
148   if (NULL != pc->th_ping)
149   {
150     GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_ping);
151     pc->th_ping = NULL;
152   }
153   if (NULL != pc->th_pong)
154   {
155     GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_pong);
156     pc->th_pong = NULL;
157   }
158   if (NULL != pc->ch_ping)
159   {
160     GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_ping);
161     pc->ch_ping = NULL;
162   }
163   if (NULL != pc->ch_pong)
164   {
165     GNUNET_CORE_notify_transmit_ready_cancel(pc->ch_pong);
166     pc->ch_pong = NULL;
167   }
168   GNUNET_free (pc);
169   return GNUNET_OK;
170 }
171
172 static void
173 map_cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
174 {
175   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_cleanup_it, NULL);
176   GNUNET_CONTAINER_multihashmap_destroy(peers);
177 }
178
179 static void
180 map_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
181 {
182   int fail = 0;
183   check_task = GNUNET_SCHEDULER_NO_TASK;
184   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_check_it, &fail);
185   if (0 > fail)
186     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
187        "Inconsistent peers after connection consistency check: %u\n", fail);
188   else
189     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
190        "Inconsistent peers after connection consistency check: %u\n", fail);
191
192
193   if (NULL != cls)
194   {
195     GNUNET_SCHEDULER_add_now (cls, NULL);
196   }
197 }
198
199
200 static void
201 stats_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
202
203 static int
204 check_lowlevel_connections (int port, int protocol)
205 {
206   FILE *f;
207   char * cmdline;
208   char * proto;
209   char line[1024];
210   int count = -1;
211 #ifdef MINGW
212   /* not supported */
213   return count;
214 #else
215
216   switch (protocol) {
217     case tcp:
218       proto = "-t";
219       break;
220     case udp:
221       proto = "-u";
222       break;
223     case unixdomain:
224       proto = "-x";
225       break;
226     default:
227       proto = "";
228       break;
229   }
230
231
232   GNUNET_asprintf(&cmdline, "netstat -n %s | grep %u", proto, port);
233
234   if (system ("netstat -n > /dev/null 2> /dev/null"))
235     if (system ("netstat -n > /dev/null 2> /dev/null") == 0)
236       f = popen (cmdline, "r");
237     else
238       f = NULL;
239   else
240     f = popen (cmdline, "r");
241   if (!f)
242   {
243     GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "ss");
244     GNUNET_free (cmdline);
245     return -1;
246   }
247
248   count = 0;
249   while (NULL != fgets (line, sizeof (line), f))
250   {
251     /* read */
252     //printf ("%s", line);
253     count ++;
254   }
255
256   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%i TCP connections established with port %u\n",
257        count, port);
258
259   pclose (f);
260   GNUNET_free (cmdline);
261   return count;
262 #endif
263 }
264
265
266 static struct TransportPlugin *
267 find_plugin (char * name)
268 {
269   struct TransportPlugin *cur = NULL;
270
271   for (cur = phead; cur != NULL; cur = phead)
272   {
273     if (0 == strcmp(name, cur->short_name))
274       return cur;
275   }
276   return cur;
277 }
278
279 int stats_check_cb (void *cls, const char *subsystem,
280                    const char *name, uint64_t value,
281                    int is_persistent)
282 {
283   static int counter;
284
285   uint64_t *val = cls;
286
287   if (NULL != val)
288     (*val) = value;
289
290   counter ++;
291   if ((STATS_VALUES == counter) || ((GNUNET_NO == have_tcp) && (STATS_VALUES - 1 == counter)))
292   {
293     int fail = GNUNET_NO;
294
295
296
297     int low_level_connections_udp = check_lowlevel_connections (2086, udp);
298
299     if (transport_connections != core_connections)
300     {
301       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
302            "%u transport notifications <-> %u core notifications\n",
303            transport_connections, core_connections);
304       fail = GNUNET_YES;
305     }
306
307     if (transport_connections != statistics_transport_connections)
308     {
309       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
310            "%u transport notifications <-> %u in statistics (peers connected)\n",
311            transport_connections, statistics_transport_connections);
312       fail = GNUNET_YES;
313     }
314
315     if (core_connections != statistics_core_entries_session_map)
316     {
317       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
318            "%u core notifications <-> %u in statistics (entries session map)\n",
319            core_connections, statistics_core_entries_session_map);
320       fail = GNUNET_YES;
321     }
322
323     if (core_connections != statistics_core_neighbour_entries)
324     {
325       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
326            "%u core notifications <-> %u in statistics (neighbour entries allocated)\n",
327            core_connections, statistics_core_neighbour_entries);
328       fail = GNUNET_YES;
329     }
330
331     if (GNUNET_NO == fail)
332       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
333          "Check successful : (%u transport / %u core) connections established\n", transport_connections, core_connections);
334
335     /* TCP plugin specific checks */
336     if (GNUNET_YES == have_tcp)
337     {
338       struct TransportPlugin * p = find_plugin ("tcp");
339       int low_level_connections_tcp = check_lowlevel_connections (p->port, p->protocol);
340
341       if (low_level_connections_tcp != -1)
342       {
343         if (statistics_transport_tcp_connections > low_level_connections_tcp)
344         {
345           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
346                "%u transport tcp sessions <-> %i established tcp connections\n",
347                statistics_transport_tcp_connections, low_level_connections_tcp);
348           fail = GNUNET_YES;
349         }
350         else if (low_level_connections_tcp != -1)
351         {
352           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
353                "%u TCP connections, %u UDP connections \n",
354                low_level_connections_tcp, low_level_connections_udp);
355         }
356       }
357       if (transport_connections > statistics_transport_tcp_connections)
358       {
359         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
360              "%u transport notifications <-> %u in statistics (statistics_transport_tcp_connections)\n",
361              transport_connections, statistics_transport_tcp_connections);
362         fail = GNUNET_YES;
363       }
364       else
365       {
366         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
367              " %u transport notifications <-> %u in statistics (statistics_transport_tcp_connections)\n",
368              transport_connections, statistics_transport_tcp_connections);
369       }
370     }
371
372     if (GNUNET_SCHEDULER_NO_TASK == statistics_task)
373       statistics_task = GNUNET_SCHEDULER_add_delayed(REPEATED_STATS_DELAY, &stats_check, NULL);
374
375     stat_check_running = GNUNET_NO;
376     counter = 0;
377   }
378
379   return GNUNET_OK;
380 }
381
382 GNUNET_NETWORK_STRUCT_BEGIN
383
384 struct PING
385 {
386   struct GNUNET_MessageHeader header;
387
388   uint16_t src;
389 };
390
391 struct PONG
392 {
393   struct GNUNET_MessageHeader header;
394
395   uint16_t src;
396 };
397 GNUNET_NETWORK_STRUCT_END
398
399
400 size_t send_transport_ping_cb (void *cls, size_t size, void *buf)
401 {
402  struct PeerContainer * pc = cls;
403  struct PING ping;
404  size_t mlen = sizeof (struct PING);
405
406  if (size < mlen)
407  {
408    GNUNET_break (0);
409    return 0;
410  }
411
412  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413       "Sending transport ping to `%s'\n", GNUNET_i2s  (&pc->id));
414  ping.header.size = htons (mlen);
415  ping.header.type = htons (1234);
416  ping.src = htons (0);
417
418  pc->th_ping = NULL;
419
420  memcpy (buf, &ping, mlen);
421  return mlen;
422 }
423
424 size_t send_core_ping_cb (void *cls, size_t size, void *buf)
425 {
426 struct PeerContainer * pc = cls;
427 struct PING ping;
428 size_t mlen = sizeof (struct PING);
429
430 if (size < mlen)
431 {
432   GNUNET_break (0);
433   return 0;
434 }
435
436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
437      "Sending core ping to `%s'\n", GNUNET_i2s  (&pc->id));
438 ping.header.size = htons (mlen);
439 ping.header.type = htons (1234);
440 ping.src = htons (1);
441
442 pc->ch_ping = NULL;
443
444 memcpy (buf, &ping, mlen);
445 return mlen;
446 }
447
448
449 int map_ping_it (void *cls,
450                   const GNUNET_HashCode * key,
451                   void *value)
452 {
453   struct PeerContainer *pc = value;
454
455   if (ping == GNUNET_YES)
456   {
457     if ((GNUNET_YES == pc->transport_connected) && (NULL == pc->th_ping))
458       pc->th_ping = GNUNET_TRANSPORT_notify_transmit_ready(th, &pc->id,
459           sizeof (struct PING), UINT_MAX,
460           GNUNET_TIME_relative_get_forever(), &send_transport_ping_cb, pc);
461     else
462       GNUNET_break(0);
463
464     if ((GNUNET_YES == pc->core_connected) && (NULL == pc->ch_ping))
465       pc->ch_ping = GNUNET_CORE_notify_transmit_ready(ch,
466                                                GNUNET_NO, UINT_MAX,
467                                                GNUNET_TIME_relative_get_forever(),
468                                                &pc->id,
469                                                sizeof (struct PING),
470                                                send_core_ping_cb, pc);
471     else
472       GNUNET_break (0);
473   }
474   return GNUNET_OK;
475 }
476
477
478 static void
479 stats_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
480 {
481   statistics_task = GNUNET_SCHEDULER_NO_TASK;
482
483   if (GNUNET_YES == stat_check_running)
484   {
485     statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
486   }
487
488   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_ping_it, NULL);
489
490   stat_check_running = GNUNET_YES;
491
492   statistics_transport_connections = 0 ;
493   statistics_core_entries_session_map = 0;
494   statistics_core_neighbour_entries = 0;
495
496   GNUNET_STATISTICS_get (stats, "transport", "# peers connected", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_transport_connections);
497   GNUNET_STATISTICS_get (stats, "core", "# neighbour entries allocated", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_core_neighbour_entries);
498   GNUNET_STATISTICS_get (stats, "core", "# entries in session map", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_core_entries_session_map);
499
500   /* TCP plugin specific checks */
501   if (GNUNET_YES == have_tcp)
502     GNUNET_STATISTICS_get (stats, "transport", "# TCP sessions active", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_transport_tcp_connections);
503 }
504
505
506
507 size_t send_transport_pong_cb (void *cls, size_t size, void *buf)
508 {
509  struct PeerContainer * pc = cls;
510  struct PING ping;
511  size_t mlen = sizeof (struct PING);
512
513  if (size < mlen)
514  {
515    GNUNET_break (0);
516    return 0;
517  }
518
519  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
520       "Sending transport pong to `%s'\n", GNUNET_i2s  (&pc->id));
521  ping.header.size = htons (mlen);
522  ping.header.type = htons (4321);
523  ping.src = htons (0);
524
525  pc->th_pong = NULL;
526
527  memcpy (buf, &ping, mlen);
528  return mlen;
529 }
530
531 size_t send_core_pong_cb (void *cls, size_t size, void *buf)
532 {
533 struct PeerContainer * pc = cls;
534 struct PING ping;
535 size_t mlen = sizeof (struct PING);
536
537 if (size < mlen)
538 {
539   GNUNET_break (0);
540   return 0;
541 }
542
543 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
544      "Sending core pong to `%s'\n", GNUNET_i2s  (&pc->id));
545 ping.header.size = htons (mlen);
546 ping.header.type = htons (4321);
547 ping.src = htons (1);
548
549 pc->ch_pong = NULL;
550
551 memcpy (buf, &ping, mlen);
552 return mlen;
553 }
554
555
556 static void
557 map_connect (const struct GNUNET_PeerIdentity *peer, void * source)
558 {
559   struct PeerContainer * pc;
560   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
561   {
562     pc = GNUNET_malloc (sizeof (struct PeerContainer));
563     pc->id = *peer;
564     pc->core_connected = GNUNET_NO;
565     pc->transport_connected = GNUNET_NO;
566     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(peers, &peer->hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
567   }
568
569   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
570   if (source == th)
571   {
572     if (GNUNET_NO == pc->transport_connected)
573     {
574       pc->transport_connected = GNUNET_YES;
575       if (GNUNET_YES == ping)
576       {
577         if (NULL == pc->th_ping)
578           pc->th_ping = GNUNET_TRANSPORT_notify_transmit_ready(th, peer, sizeof (struct PING), UINT_MAX, GNUNET_TIME_relative_get_forever(), &send_transport_ping_cb, pc);
579         else
580           GNUNET_break(0);
581       }
582     }
583     else
584     {
585       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
586            "%s notified multiple times about for peers `%s' (%s : %s)\n",
587            "TRANSPORT",
588            GNUNET_i2s (&pc->id),
589            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
590       GNUNET_break (0);
591     }
592   }
593   if (source == ch)
594   {
595     if (GNUNET_NO == pc->core_connected)
596     {
597       pc->core_connected = GNUNET_YES;
598       if (GNUNET_YES == ping)
599       {
600         if (NULL == pc->ch_ping)
601           pc->ch_ping = GNUNET_CORE_notify_transmit_ready(ch,
602                                                  GNUNET_NO, UINT_MAX,
603                                                  GNUNET_TIME_relative_get_forever(),
604                                                  peer,
605                                                  sizeof (struct PING),
606                                                  send_core_ping_cb, pc);
607         else
608           GNUNET_break (0);
609       }
610     }
611     else
612     {
613       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
614            "%s notified multiple times about for peers `%s' (%s : %s)\n",
615            "CORE",
616            GNUNET_i2s (&pc->id),
617                "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
618       GNUNET_break (0);
619     }
620   }
621   if (GNUNET_SCHEDULER_NO_TASK != check_task)
622     GNUNET_SCHEDULER_cancel(check_task);
623   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
624
625   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
626     GNUNET_SCHEDULER_cancel(statistics_task);
627   statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
628 }
629
630
631 static void
632 map_disconnect (const struct GNUNET_PeerIdentity * peer, void * source)
633 {
634
635   struct PeerContainer * pc;
636   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
637   {
638     if (source == th)
639     {
640       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
641          "%s disconnect notification for unknown peer `%s'\n",
642          "TRANSPORT", GNUNET_i2s (peer));
643       GNUNET_break (0);
644       return;
645     }
646     if (source == ch)
647     {
648       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
649          "%s disconnect notification for unknown peer `%s'\n",
650          "CORE", GNUNET_i2s (peer));
651       return;
652     }
653   }
654
655   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
656   if (source == th)
657   {
658     if (NULL != pc->th_ping)
659     {
660       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_ping);
661       pc->th_ping = NULL;
662     }
663     if (NULL != pc->th_pong)
664     {
665       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_pong);
666       pc->th_pong = NULL;
667     }
668
669     if (GNUNET_YES == pc->transport_connected)
670     {
671       pc->transport_connected = GNUNET_NO;
672     }
673     else
674     {
675       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
676            "%s notified for not connected peer `%s' (%s : %s)\n",
677            "TRANSPORT",
678            GNUNET_i2s (&pc->id),
679            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
680       GNUNET_break (0);
681     }
682   }
683   if (source == ch)
684   {
685     if (NULL != pc->ch_ping)
686     {
687       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_ping);
688       pc->ch_ping = NULL;
689     }
690     if (NULL != pc->ch_pong)
691     {
692       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_pong);
693       pc->ch_pong = NULL;
694     }
695
696     if (GNUNET_YES == pc->core_connected)
697     {
698       pc->core_connected = GNUNET_NO;
699     }
700     else
701     {
702       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
703            "%s notified for not connected peer `%s' (%s : %s)\n",
704            "CORE",
705            GNUNET_i2s (&pc->id),
706            "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
707       GNUNET_break (0);
708     }
709   }
710
711   if ((GNUNET_NO == pc->core_connected) && (GNUNET_NO == pc->transport_connected))
712   {
713     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing peer `%s'\n", GNUNET_i2s (&pc->id));
714     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey, pc));
715
716
717     GNUNET_free (pc);
718   }
719
720   if (GNUNET_SCHEDULER_NO_TASK != check_task)
721     GNUNET_SCHEDULER_cancel(check_task);
722   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
723
724   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
725     GNUNET_SCHEDULER_cancel(statistics_task);
726   statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
727 }
728
729
730 static void
731 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
732 {
733   struct TransportPlugin * cur = phead;
734
735   if (NULL != th)
736   {
737     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from transport service\n");
738     GNUNET_TRANSPORT_disconnect (th);
739     th = NULL;
740   }
741
742
743   if (NULL != ch)
744   {
745     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from core service\n");
746     GNUNET_CORE_disconnect (ch);
747     ch = NULL;
748   }
749
750   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
751   {
752     GNUNET_SCHEDULER_cancel(statistics_task);
753     statistics_task = GNUNET_SCHEDULER_NO_TASK;
754   }
755
756   if (GNUNET_SCHEDULER_NO_TASK != check_task)
757   {
758     GNUNET_SCHEDULER_cancel(check_task);
759     check_task = GNUNET_SCHEDULER_NO_TASK;
760   }
761
762   for (cur = phead; cur != NULL; cur = phead)
763   {
764     GNUNET_CONTAINER_DLL_remove(phead, ptail, cur);
765     GNUNET_free (cur->short_name);
766     GNUNET_free (cur);
767   }
768
769   check_task = GNUNET_SCHEDULER_add_now (&map_check, &map_cleanup);
770 }
771
772 void
773 transport_notify_connect_cb (void *cls,
774                 const struct GNUNET_PeerIdentity
775                 * peer,
776                 const struct
777                 GNUNET_ATS_Information * ats,
778                 uint32_t ats_count)
779 {
780   transport_connections ++;
781   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT connect for peer `%s' (%u total)\n",
782       GNUNET_i2s (peer), transport_connections);
783   map_connect (peer, th);
784 }
785
786 /**
787  * Function called to notify transport users that another
788  * peer disconnected from us.
789  *
790  * @param cls closure
791  * @param peer the peer that disconnected
792  */
793 void
794 transport_notify_disconnect_cb (void *cls,
795                                const struct
796                                GNUNET_PeerIdentity * peer)
797 {
798   GNUNET_assert (transport_connections > 0);
799   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT disconnect for peer `%s' (%u total)\n",
800       GNUNET_i2s (peer), transport_connections) ;
801   map_disconnect (peer, th);
802   transport_connections --;
803
804 }
805
806 static void
807 transport_notify_receive_cb (void *cls,
808                             const struct
809                             GNUNET_PeerIdentity * peer,
810                             const struct
811                             GNUNET_MessageHeader *
812                             message,
813                             const struct
814                             GNUNET_ATS_Information * ats,
815                             uint32_t ats_count)
816 {
817
818
819   struct PeerContainer *pc = NULL;
820
821   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
822
823   if (NULL == pc)
824   {
825     GNUNET_break (0);
826     return;
827   }
828
829   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
830   {
831     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
832         "TRANSPORT",
833         "PING",
834         GNUNET_i2s (peer)) ;
835     if (GNUNET_YES == ping)
836     {
837       if (NULL == pc->th_pong)
838         pc->th_pong = GNUNET_TRANSPORT_notify_transmit_ready(th,
839           peer, sizeof (struct PONG),
840           UINT_MAX, GNUNET_TIME_relative_get_forever(),
841           &send_transport_pong_cb, pc);
842       else
843         GNUNET_break (0);
844     }
845
846   }
847   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
848   {
849     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
850         "TRANSPORT",
851         "PONG",
852         GNUNET_i2s (peer));
853   }
854 }
855
856 int core_notify_receive_cb (void *cls,
857                                 const struct GNUNET_PeerIdentity * peer,
858                                 const struct GNUNET_MessageHeader * message,
859                                 const struct GNUNET_ATS_Information* atsi,
860                                 unsigned int atsi_count)
861 {
862   struct PeerContainer *pc = NULL;
863
864   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
865
866   if (NULL == pc)
867   {
868     GNUNET_break (0);
869     return GNUNET_OK;
870   }
871
872   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
873   {
874     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
875         "CORE",
876         "PING",
877         GNUNET_i2s (peer));
878     if (GNUNET_YES == ping)
879     {
880       if (NULL == pc->ch_pong)
881         pc->ch_pong = GNUNET_CORE_notify_transmit_ready(ch,
882                                                GNUNET_NO, UINT_MAX,
883                                                GNUNET_TIME_relative_get_forever(),
884                                                peer,
885                                                sizeof (struct PONG),
886                                                send_core_pong_cb, pc);
887       else
888         GNUNET_break (0);
889     }
890   }
891
892   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
893   {
894     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
895         "CORE",
896         "PONG",
897         GNUNET_i2s (peer));
898
899   }
900
901   return GNUNET_OK;
902 }
903
904 static void
905 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
906                       const struct GNUNET_ATS_Information *atsi,
907                       unsigned int atsi_count)
908 {
909   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
910   {
911     core_connections ++;
912     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for peer `%s' (%u total)\n",
913       GNUNET_i2s (peer), core_connections);
914     map_connect (peer, ch);
915   }
916   else
917   {
918     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for myself `%s' (%u total)\n",
919       GNUNET_i2s (peer), core_connections);
920   }
921 }
922
923 static void
924 core_disconnect_cb (void *cls,
925                       const struct
926                       GNUNET_PeerIdentity * peer)
927 {
928   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
929   {
930     GNUNET_assert (core_connections >= 0);
931     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for peer `%s' (%u total)\n",
932       GNUNET_i2s (peer), core_connections);
933     map_disconnect (peer, ch);
934     core_connections --;
935   }
936   else
937   {
938     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for myself `%s' (%u total)\n",
939       GNUNET_i2s (peer), core_connections);
940   }
941
942 }
943
944 static void
945 core_init_cb (void *cls, struct GNUNET_CORE_Handle *server,
946                    const struct GNUNET_PeerIdentity *my_identity)
947 {
948   my_peer_id = *my_identity;
949   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to core service\n");
950 }
951
952
953 static void
954 init ()
955 {
956   struct TransportPlugin * cur;
957   char *plugs;
958   char *pos;
959   char *secname;
960   int counter;
961   long long unsigned int port;
962
963   have_tcp = GNUNET_NO;
964   have_udp = GNUNET_NO;
965   have_http = GNUNET_NO;
966   have_https = GNUNET_NO;
967   have_unix = GNUNET_NO;
968
969   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (mycfg, "TRANSPORT", "PLUGINS", &plugs))
970     return;
971   counter = 0;
972   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
973   {
974     counter++;
975
976     GNUNET_asprintf(&secname, "transport-%s", pos);
977
978     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (mycfg, secname, "PORT", &port))
979     {
980       GNUNET_free (secname);
981       continue;
982     }
983
984     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport plugin: `%s' port %llu\n"), pos, port);
985     cur = GNUNET_malloc(sizeof (struct TransportPlugin));
986     cur->short_name = strdup (pos);
987     cur->port = port;
988     if (0 == strcmp("tcp", pos))
989     {
990       have_tcp = GNUNET_YES;
991       cur->protocol = tcp;
992     }
993     if (0 == strcmp("udp", pos))
994     {
995       have_udp = GNUNET_YES;
996       cur->protocol = udp;
997     }
998     if (0 == strcmp("http", pos))
999     {
1000       have_http = GNUNET_YES;
1001       cur->protocol = tcp;
1002     }
1003     if (0 == strcmp("https", pos))
1004     {
1005       have_https = GNUNET_YES;
1006       cur->protocol = tcp;
1007     }
1008     if (0 == strcmp("unix", pos))
1009     {
1010       have_unix = GNUNET_YES;
1011       cur->protocol = unixdomain;
1012     }
1013
1014     GNUNET_CONTAINER_DLL_insert(phead, ptail, cur);
1015     GNUNET_free (secname);
1016   }
1017   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Found %u transport plugins: `%s'\n"),
1018               counter, plugs);
1019
1020   GNUNET_free (plugs);
1021 }
1022
1023 /**
1024  * Main function that will be run by the scheduler.
1025  *
1026  * @param cls closure
1027  * @param args remaining command-line arguments
1028  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1029  * @param cfg configuration
1030  */
1031 static void
1032 run (void *cls, char *const *args, const char *cfgfile,
1033      const struct GNUNET_CONFIGURATION_Handle *cfg)
1034 {
1035   transport_connections = 0;
1036   core_connections = 0;
1037   mycfg = cfg;
1038
1039   init();
1040
1041   stats = GNUNET_STATISTICS_create ("watchdog", cfg);
1042   peers = GNUNET_CONTAINER_multihashmap_create (20);
1043
1044   th = GNUNET_TRANSPORT_connect(cfg, NULL, NULL,
1045                                 &transport_notify_receive_cb,
1046                                 &transport_notify_connect_cb,
1047                                 &transport_notify_disconnect_cb);
1048   GNUNET_assert (th != NULL);
1049   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to transport service\n");
1050   ch =  GNUNET_CORE_connect (cfg, 1, NULL,
1051                              &core_init_cb,
1052                              &core_connect_cb,
1053                              &core_disconnect_cb,
1054                              &core_notify_receive_cb, GNUNET_NO,
1055                              NULL, GNUNET_NO,
1056                              NULL);
1057   GNUNET_assert (ch != NULL);
1058
1059   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, NULL);
1060
1061 }
1062
1063
1064 /**
1065  * The main function.
1066  *
1067  * @param argc number of arguments from the command line
1068  * @param argv command line arguments
1069  * @return 0 ok, 1 on error
1070  */
1071 int
1072 main (int argc, char *const *argv)
1073 {
1074   ping = GNUNET_NO;
1075   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1076    {'p', "ping", NULL, gettext_noop ("Send ping messages to test connectivity (default == NO)"),
1077     GNUNET_NO, &GNUNET_GETOPT_set_one, &ping},
1078     GNUNET_GETOPT_OPTION_END
1079   };
1080   return (GNUNET_OK ==
1081           GNUNET_PROGRAM_run (argc, argv, "cn",
1082                               gettext_noop ("help text"), options, &run,
1083                               NULL)) ? ret : 1;
1084 }
1085
1086 /* end of connection_watchdog.c */