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