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