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