- fix
[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 ((GNUNET_YES == pc->transport_connected) && (NULL == pc->th_ping))
456     pc->th_ping = GNUNET_TRANSPORT_notify_transmit_ready(th, &pc->id,
457         sizeof (struct PING), UINT_MAX,
458         GNUNET_TIME_relative_get_forever(), &send_transport_ping_cb, pc);
459   else
460     GNUNET_break(0);
461
462   if ((GNUNET_YES == pc->core_connected) && (NULL == pc->ch_ping))
463     pc->ch_ping = GNUNET_CORE_notify_transmit_ready(ch,
464                                              GNUNET_NO, UINT_MAX,
465                                              GNUNET_TIME_relative_get_forever(),
466                                              &pc->id,
467                                              sizeof (struct PING),
468                                              send_core_ping_cb, pc);
469   else
470     GNUNET_break (0);
471
472   return GNUNET_OK;
473 }
474
475
476 static void
477 stats_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
478 {
479   statistics_task = GNUNET_SCHEDULER_NO_TASK;
480
481   if (GNUNET_YES == stat_check_running)
482   {
483     statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
484   }
485
486   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_ping_it, NULL);
487
488   stat_check_running = GNUNET_YES;
489
490   statistics_transport_connections = 0 ;
491   statistics_core_entries_session_map = 0;
492   statistics_core_neighbour_entries = 0;
493
494   GNUNET_STATISTICS_get (stats, "transport", "# peers connected", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_transport_connections);
495   GNUNET_STATISTICS_get (stats, "core", "# neighbour entries allocated", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_core_neighbour_entries);
496   GNUNET_STATISTICS_get (stats, "core", "# entries in session map", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_core_entries_session_map);
497
498   /* TCP plugin specific checks */
499   if (GNUNET_YES == have_tcp)
500     GNUNET_STATISTICS_get (stats, "transport", "# TCP sessions active", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_transport_tcp_connections);
501 }
502
503
504
505 size_t send_transport_pong_cb (void *cls, size_t size, void *buf)
506 {
507  struct PeerContainer * pc = cls;
508  struct PING ping;
509  size_t mlen = sizeof (struct PING);
510
511  if (size < mlen)
512  {
513    GNUNET_break (0);
514    return 0;
515  }
516
517  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518       "Sending transport pong to `%s'\n", GNUNET_i2s  (&pc->id));
519  ping.header.size = htons (mlen);
520  ping.header.type = htons (4321);
521  ping.src = htons (0);
522
523  pc->th_pong = NULL;
524
525  memcpy (buf, &ping, mlen);
526  return mlen;
527 }
528
529 size_t send_core_pong_cb (void *cls, size_t size, void *buf)
530 {
531 struct PeerContainer * pc = cls;
532 struct PING ping;
533 size_t mlen = sizeof (struct PING);
534
535 if (size < mlen)
536 {
537   GNUNET_break (0);
538   return 0;
539 }
540
541 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
542      "Sending core pong to `%s'\n", GNUNET_i2s  (&pc->id));
543 ping.header.size = htons (mlen);
544 ping.header.type = htons (4321);
545 ping.src = htons (1);
546
547 pc->ch_pong = NULL;
548
549 memcpy (buf, &ping, mlen);
550 return mlen;
551 }
552
553
554 static void
555 map_connect (const struct GNUNET_PeerIdentity *peer, void * source)
556 {
557   struct PeerContainer * pc;
558   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
559   {
560     pc = GNUNET_malloc (sizeof (struct PeerContainer));
561     pc->id = *peer;
562     pc->core_connected = GNUNET_NO;
563     pc->transport_connected = GNUNET_NO;
564     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(peers, &peer->hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
565   }
566
567   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
568   if (source == th)
569   {
570     if (GNUNET_NO == pc->transport_connected)
571     {
572       pc->transport_connected = GNUNET_YES;
573       if (GNUNET_YES == ping)
574       {
575         if (NULL == pc->th_ping)
576           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);
577         else
578           GNUNET_break(0);
579       }
580     }
581     else
582     {
583       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
584            "%s notified multiple times about for peers `%s' (%s : %s)\n",
585            "TRANSPORT",
586            GNUNET_i2s (&pc->id),
587            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
588       GNUNET_break (0);
589     }
590   }
591   if (source == ch)
592   {
593     if (GNUNET_NO == pc->core_connected)
594     {
595       pc->core_connected = GNUNET_YES;
596       if (GNUNET_YES == ping)
597       {
598         if (NULL == pc->ch_ping)
599           pc->ch_ping = GNUNET_CORE_notify_transmit_ready(ch,
600                                                  GNUNET_NO, UINT_MAX,
601                                                  GNUNET_TIME_relative_get_forever(),
602                                                  peer,
603                                                  sizeof (struct PING),
604                                                  send_core_ping_cb, pc);
605         else
606           GNUNET_break (0);
607       }
608     }
609     else
610     {
611       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
612            "%s notified multiple times about for peers `%s' (%s : %s)\n",
613            "CORE",
614            GNUNET_i2s (&pc->id),
615                "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
616       GNUNET_break (0);
617     }
618   }
619   if (GNUNET_SCHEDULER_NO_TASK != check_task)
620     GNUNET_SCHEDULER_cancel(check_task);
621   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
622
623   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
624     GNUNET_SCHEDULER_cancel(statistics_task);
625   statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
626 }
627
628
629 static void
630 map_disconnect (const struct GNUNET_PeerIdentity * peer, void * source)
631 {
632
633   struct PeerContainer * pc;
634   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
635   {
636     if (source == th)
637     {
638       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
639          "%s disconnect notification for unknown peer `%s'\n",
640          "TRANSPORT", GNUNET_i2s (peer));
641       GNUNET_break (0);
642       return;
643     }
644     if (source == ch)
645     {
646       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
647          "%s disconnect notification for unknown peer `%s'\n",
648          "CORE", GNUNET_i2s (peer));
649       return;
650     }
651   }
652
653   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
654   if (source == th)
655   {
656     if (NULL != pc->th_ping)
657     {
658       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_ping);
659       pc->th_ping = NULL;
660     }
661     if (NULL != pc->th_pong)
662     {
663       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_pong);
664       pc->th_pong = NULL;
665     }
666
667     if (GNUNET_YES == pc->transport_connected)
668     {
669       pc->transport_connected = GNUNET_NO;
670     }
671     else
672     {
673       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
674            "%s notified for not connected peer `%s' (%s : %s)\n",
675            "TRANSPORT",
676            GNUNET_i2s (&pc->id),
677            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
678       GNUNET_break (0);
679     }
680   }
681   if (source == ch)
682   {
683     if (NULL != pc->ch_ping)
684     {
685       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_ping);
686       pc->ch_ping = NULL;
687     }
688     if (NULL != pc->ch_pong)
689     {
690       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_pong);
691       pc->ch_pong = NULL;
692     }
693
694     if (GNUNET_YES == pc->core_connected)
695     {
696       pc->core_connected = GNUNET_NO;
697     }
698     else
699     {
700       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
701            "%s notified for not connected peer `%s' (%s : %s)\n",
702            "CORE",
703            GNUNET_i2s (&pc->id),
704            "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
705       GNUNET_break (0);
706     }
707   }
708
709   if ((GNUNET_NO == pc->core_connected) && (GNUNET_NO == pc->transport_connected))
710   {
711     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing peer `%s'\n", GNUNET_i2s (&pc->id));
712     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey, pc));
713
714
715     GNUNET_free (pc);
716   }
717
718   if (GNUNET_SCHEDULER_NO_TASK != check_task)
719     GNUNET_SCHEDULER_cancel(check_task);
720   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
721
722   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
723     GNUNET_SCHEDULER_cancel(statistics_task);
724   statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
725 }
726
727
728 static void
729 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
730 {
731   struct TransportPlugin * cur = phead;
732
733   if (NULL != th)
734   {
735     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from transport service\n");
736     GNUNET_TRANSPORT_disconnect (th);
737     th = NULL;
738   }
739
740
741   if (NULL != ch)
742   {
743     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from core service\n");
744     GNUNET_CORE_disconnect (ch);
745     ch = NULL;
746   }
747
748   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
749   {
750     GNUNET_SCHEDULER_cancel(statistics_task);
751     statistics_task = GNUNET_SCHEDULER_NO_TASK;
752   }
753
754   if (GNUNET_SCHEDULER_NO_TASK != check_task)
755   {
756     GNUNET_SCHEDULER_cancel(check_task);
757     check_task = GNUNET_SCHEDULER_NO_TASK;
758   }
759
760   for (cur = phead; cur != NULL; cur = phead)
761   {
762     GNUNET_CONTAINER_DLL_remove(phead, ptail, cur);
763     GNUNET_free (cur->short_name);
764     GNUNET_free (cur);
765   }
766
767   check_task = GNUNET_SCHEDULER_add_now (&map_check, &map_cleanup);
768 }
769
770 void
771 transport_notify_connect_cb (void *cls,
772                 const struct GNUNET_PeerIdentity
773                 * peer,
774                 const struct
775                 GNUNET_ATS_Information * ats,
776                 uint32_t ats_count)
777 {
778   transport_connections ++;
779   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT connect for peer `%s' (%u total)\n",
780       GNUNET_i2s (peer), transport_connections);
781   map_connect (peer, th);
782 }
783
784 /**
785  * Function called to notify transport users that another
786  * peer disconnected from us.
787  *
788  * @param cls closure
789  * @param peer the peer that disconnected
790  */
791 void
792 transport_notify_disconnect_cb (void *cls,
793                                const struct
794                                GNUNET_PeerIdentity * peer)
795 {
796   GNUNET_assert (transport_connections > 0);
797   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT disconnect for peer `%s' (%u total)\n",
798       GNUNET_i2s (peer), transport_connections) ;
799   map_disconnect (peer, th);
800   transport_connections --;
801
802 }
803
804 static void
805 transport_notify_receive_cb (void *cls,
806                             const struct
807                             GNUNET_PeerIdentity * peer,
808                             const struct
809                             GNUNET_MessageHeader *
810                             message,
811                             const struct
812                             GNUNET_ATS_Information * ats,
813                             uint32_t ats_count)
814 {
815
816
817   struct PeerContainer *pc = NULL;
818
819   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
820
821   if (NULL == pc)
822   {
823     GNUNET_break (0);
824     return;
825   }
826
827   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
828   {
829     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
830         "TRANSPORT",
831         "PING",
832         GNUNET_i2s (peer)) ;
833     if (GNUNET_YES == ping)
834     {
835       if (NULL == pc->th_pong)
836         pc->th_pong = GNUNET_TRANSPORT_notify_transmit_ready(th,
837           peer, sizeof (struct PONG),
838           UINT_MAX, GNUNET_TIME_relative_get_forever(),
839           &send_transport_pong_cb, pc);
840       else
841         GNUNET_break (0);
842     }
843
844   }
845   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
846   {
847     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
848         "TRANSPORT",
849         "PONG",
850         GNUNET_i2s (peer));
851   }
852 }
853
854 int core_notify_receive_cb (void *cls,
855                                 const struct GNUNET_PeerIdentity * peer,
856                                 const struct GNUNET_MessageHeader * message,
857                                 const struct GNUNET_ATS_Information* atsi,
858                                 unsigned int atsi_count)
859 {
860   struct PeerContainer *pc = NULL;
861
862   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
863
864   if (NULL == pc)
865   {
866     GNUNET_break (0);
867     return GNUNET_OK;
868   }
869
870   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
871   {
872     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
873         "CORE",
874         "PING",
875         GNUNET_i2s (peer));
876     if (GNUNET_YES == ping)
877     {
878       if (NULL == pc->ch_pong)
879         pc->ch_pong = GNUNET_CORE_notify_transmit_ready(ch,
880                                                GNUNET_NO, UINT_MAX,
881                                                GNUNET_TIME_relative_get_forever(),
882                                                peer,
883                                                sizeof (struct PONG),
884                                                send_core_pong_cb, pc);
885       else
886         GNUNET_break (0);
887     }
888   }
889
890   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
891   {
892     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
893         "CORE",
894         "PONG",
895         GNUNET_i2s (peer));
896
897   }
898
899   return GNUNET_OK;
900 }
901
902 static void
903 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
904                       const struct GNUNET_ATS_Information *atsi,
905                       unsigned int atsi_count)
906 {
907   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
908   {
909     core_connections ++;
910     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for peer `%s' (%u total)\n",
911       GNUNET_i2s (peer), core_connections);
912     map_connect (peer, ch);
913   }
914   else
915   {
916     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for myself `%s' (%u total)\n",
917       GNUNET_i2s (peer), core_connections);
918   }
919 }
920
921 static void
922 core_disconnect_cb (void *cls,
923                       const struct
924                       GNUNET_PeerIdentity * peer)
925 {
926   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
927   {
928     GNUNET_assert (core_connections >= 0);
929     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for peer `%s' (%u total)\n",
930       GNUNET_i2s (peer), core_connections);
931     map_disconnect (peer, ch);
932     core_connections --;
933   }
934   else
935   {
936     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for myself `%s' (%u total)\n",
937       GNUNET_i2s (peer), core_connections);
938   }
939
940 }
941
942 static void
943 core_init_cb (void *cls, struct GNUNET_CORE_Handle *server,
944                    const struct GNUNET_PeerIdentity *my_identity)
945 {
946   my_peer_id = *my_identity;
947   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to core service\n");
948 }
949
950
951 static void
952 init ()
953 {
954   struct TransportPlugin * cur;
955   char *plugs;
956   char *pos;
957   char *secname;
958   int counter;
959   long long unsigned int port;
960
961   have_tcp = GNUNET_NO;
962   have_udp = GNUNET_NO;
963   have_http = GNUNET_NO;
964   have_https = GNUNET_NO;
965   have_unix = GNUNET_NO;
966
967   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (mycfg, "TRANSPORT", "PLUGINS", &plugs))
968     return;
969   counter = 0;
970   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
971   {
972     counter++;
973
974     GNUNET_asprintf(&secname, "transport-%s", pos);
975
976     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (mycfg, secname, "PORT", &port))
977     {
978       GNUNET_free (secname);
979       continue;
980     }
981
982     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport plugin: `%s' port %llu\n"), pos, port);
983     cur = GNUNET_malloc(sizeof (struct TransportPlugin));
984     cur->short_name = strdup (pos);
985     cur->port = port;
986     if (0 == strcmp("tcp", pos))
987     {
988       have_tcp = GNUNET_YES;
989       cur->protocol = tcp;
990     }
991     if (0 == strcmp("udp", pos))
992     {
993       have_udp = GNUNET_YES;
994       cur->protocol = udp;
995     }
996     if (0 == strcmp("http", pos))
997     {
998       have_http = GNUNET_YES;
999       cur->protocol = tcp;
1000     }
1001     if (0 == strcmp("https", pos))
1002     {
1003       have_https = GNUNET_YES;
1004       cur->protocol = tcp;
1005     }
1006     if (0 == strcmp("unix", pos))
1007     {
1008       have_unix = GNUNET_YES;
1009       cur->protocol = unixdomain;
1010     }
1011
1012     GNUNET_CONTAINER_DLL_insert(phead, ptail, cur);
1013     GNUNET_free (secname);
1014   }
1015   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Found %u transport plugins: `%s'\n"),
1016               counter, plugs);
1017
1018   GNUNET_free (plugs);
1019 }
1020
1021 /**
1022  * Main function that will be run by the scheduler.
1023  *
1024  * @param cls closure
1025  * @param args remaining command-line arguments
1026  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1027  * @param cfg configuration
1028  */
1029 static void
1030 run (void *cls, char *const *args, const char *cfgfile,
1031      const struct GNUNET_CONFIGURATION_Handle *cfg)
1032 {
1033   transport_connections = 0;
1034   core_connections = 0;
1035   mycfg = cfg;
1036
1037   init();
1038
1039   stats = GNUNET_STATISTICS_create ("watchdog", cfg);
1040   peers = GNUNET_CONTAINER_multihashmap_create (20);
1041
1042   th = GNUNET_TRANSPORT_connect(cfg, NULL, NULL,
1043                                 &transport_notify_receive_cb,
1044                                 &transport_notify_connect_cb,
1045                                 &transport_notify_disconnect_cb);
1046   GNUNET_assert (th != NULL);
1047   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to transport service\n");
1048   ch =  GNUNET_CORE_connect (cfg, 1, NULL,
1049                              &core_init_cb,
1050                              &core_connect_cb,
1051                              &core_disconnect_cb,
1052                              &core_notify_receive_cb, GNUNET_NO,
1053                              NULL, GNUNET_NO,
1054                              NULL);
1055   GNUNET_assert (ch != NULL);
1056
1057   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, NULL);
1058
1059 }
1060
1061
1062 /**
1063  * The main function.
1064  *
1065  * @param argc number of arguments from the command line
1066  * @param argv command line arguments
1067  * @return 0 ok, 1 on error
1068  */
1069 int
1070 main (int argc, char *const *argv)
1071 {
1072   ping = GNUNET_NO;
1073   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1074    {'p', "ping", NULL, gettext_noop ("Send ping messages to test connectivity (default == NO)"),
1075     GNUNET_NO, &GNUNET_GETOPT_set_one, &ping},
1076     GNUNET_GETOPT_OPTION_END
1077   };
1078   return (GNUNET_OK ==
1079           GNUNET_PROGRAM_run (argc, argv, "cn",
1080                               gettext_noop ("help text"), options, &run,
1081                               NULL)) ? ret : 1;
1082 }
1083
1084 /* end of connection_watchdog.c */