-dead code elimination
[oweals/gnunet.git] / src / integration-tests / connection_watchdog.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file integration-tests/connection_watchdog.c
22  * @brief tool to monitor core and transport connections for consistency
23  * @author Matthias Wachs
24  */
25 #include "platform.h"
26 #include "gnunet_common.h"
27 #include "gnunet_constants.h"
28 #include "gnunet_arm_service.h"
29 #include "gnunet_core_service.h"
30 #include "gnunet_getopt_lib.h"
31 #include "gnunet_os_lib.h"
32 #include "gnunet_program_lib.h"
33 #include "gnunet_scheduler_lib.h"
34 #include "gnunet_transport_service.h"
35 #include "gnunet_statistics_service.h"
36
37
38 #define CHECK_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
39 #define STATS_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
40 #define REPEATED_STATS_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
41 #define STATS_VALUES 4
42
43 /**
44  * Final status code.
45  */
46 static int ret;
47 static int ping;
48
49 static int have_tcp;
50 static int have_udp;
51 static int have_http;
52 static int have_https;
53 static int have_unix;
54
55 static struct GNUNET_TRANSPORT_Handle *th;
56 static struct GNUNET_CORE_Handle *ch;
57 static struct GNUNET_PeerIdentity my_peer_id;
58 static const struct GNUNET_CONFIGURATION_Handle *mycfg;
59 static struct GNUNET_STATISTICS_Handle *stats;
60
61
62 static unsigned int transport_connections;
63 static unsigned int core_connections;
64
65 static GNUNET_SCHEDULER_TaskIdentifier check_task;
66 static GNUNET_SCHEDULER_TaskIdentifier statistics_task;
67
68 static uint64_t statistics_transport_connections;
69 static uint64_t statistics_transport_tcp_connections;
70 static uint64_t statistics_core_neighbour_entries;
71 static uint64_t statistics_core_entries_session_map;
72
73 int stat_check_running;
74
75 static struct GNUNET_CONTAINER_MultiHashMap *peers;
76
77 struct PeerContainer
78 {
79   struct GNUNET_PeerIdentity id;
80   int transport_connected;
81   int core_connected;
82   struct GNUNET_TRANSPORT_TransmitHandle *th_ping;
83   struct GNUNET_CORE_TransmitHandle *ch_ping;
84
85   struct GNUNET_TRANSPORT_TransmitHandle *th_pong;
86   struct GNUNET_CORE_TransmitHandle *ch_pong;
87 };
88
89
90 enum protocol
91 {
92   tcp,
93   udp,
94   unixdomain
95 };
96
97 struct TransportPlugin
98 {
99   /**
100    * This is a doubly-linked list.
101    */
102   struct TransportPlugin *next;
103
104   /**
105    * This is a doubly-linked list.
106    */
107   struct TransportPlugin *prev;
108
109   /**
110    * Short name for the plugin (i.e. "tcp").
111    */
112   char *short_name;
113
114   int port;
115
116   int protocol;
117 };
118
119 struct TransportPlugin *phead;
120 struct TransportPlugin *ptail;
121
122 static int 
123 map_check_it (void *cls,
124               const struct GNUNET_HashCode * key,
125               void *value)
126 {
127   int *fail = cls;
128   struct PeerContainer *pc = value;
129   if (pc->core_connected != pc->transport_connected)
130   {
131     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
132      "Inconsistent peer `%s': TRANSPORT %s <-> CORE %s\n",
133      GNUNET_i2s (&pc->id),
134      (GNUNET_YES == pc->transport_connected) ? "YES" : "NO",
135      (GNUNET_YES == pc->core_connected) ? "YES" : "NO");
136     (*fail) ++;
137   }
138
139   return GNUNET_OK;
140 }
141
142
143 static int 
144 map_cleanup_it (void *cls,
145                 const struct GNUNET_HashCode * key,
146                 void *value)
147 {
148   struct PeerContainer *pc = value;
149   GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove(peers, key, value));
150   if (NULL != pc->th_ping)
151   {
152     GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_ping);
153     pc->th_ping = NULL;
154   }
155   if (NULL != pc->th_pong)
156   {
157     GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_pong);
158     pc->th_pong = NULL;
159   }
160   if (NULL != pc->ch_ping)
161   {
162     GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_ping);
163     pc->ch_ping = NULL;
164   }
165   if (NULL != pc->ch_pong)
166   {
167     GNUNET_CORE_notify_transmit_ready_cancel(pc->ch_pong);
168     pc->ch_pong = NULL;
169   }
170   GNUNET_free (pc);
171   return GNUNET_OK;
172 }
173
174 static void
175 map_cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
176 {
177   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_cleanup_it, NULL);
178   GNUNET_CONTAINER_multihashmap_destroy(peers);
179 }
180
181 static void
182 map_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
183 {
184   int fail = 0;
185   check_task = GNUNET_SCHEDULER_NO_TASK;
186   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_check_it, &fail);
187   if (0 > fail)
188     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
189        "Inconsistent peers after connection consistency check: %u\n", fail);
190   else
191     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
192        "Inconsistent peers after connection consistency check: %u\n", fail);
193
194
195   if (NULL != cls)
196   {
197     GNUNET_SCHEDULER_add_now (cls, NULL);
198   }
199 }
200
201
202 static void
203 stats_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
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 size_t send_core_ping_cb (void *cls, size_t size, void *buf)
427 {
428 struct PeerContainer * pc = cls;
429 struct PING ping;
430 size_t mlen = sizeof (struct PING);
431
432 if (size < mlen)
433 {
434   GNUNET_break (0);
435   return 0;
436 }
437
438 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
439      "Sending core ping to `%s'\n", GNUNET_i2s  (&pc->id));
440 ping.header.size = htons (mlen);
441 ping.header.type = htons (1234);
442 ping.src = htons (1);
443
444 pc->ch_ping = NULL;
445
446 memcpy (buf, &ping, mlen);
447 return mlen;
448 }
449
450
451 int map_ping_it (void *cls,
452                   const struct GNUNET_HashCode * key,
453                   void *value)
454 {
455   struct PeerContainer *pc = value;
456
457   if (ping == GNUNET_YES)
458   {
459     if ((GNUNET_YES == pc->transport_connected) && (NULL == pc->th_ping))
460       pc->th_ping = GNUNET_TRANSPORT_notify_transmit_ready(th, &pc->id,
461           sizeof (struct PING), UINT_MAX,
462           GNUNET_TIME_UNIT_FOREVER_REL, &send_transport_ping_cb, pc);
463     else
464       GNUNET_break(0);
465
466     if ((GNUNET_YES == pc->core_connected) && (NULL == pc->ch_ping))
467       pc->ch_ping = GNUNET_CORE_notify_transmit_ready(ch,
468                                                GNUNET_NO, UINT_MAX,
469                                                       GNUNET_TIME_UNIT_FOREVER_REL,
470                                                &pc->id,
471                                                sizeof (struct PING),
472                                                send_core_ping_cb, pc);
473     else
474       GNUNET_break (0);
475   }
476   return GNUNET_OK;
477 }
478
479
480 static void
481 stats_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
482 {
483   statistics_task = GNUNET_SCHEDULER_NO_TASK;
484
485   if (GNUNET_YES == stat_check_running)
486   {
487     statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
488   }
489
490   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_ping_it, NULL);
491
492   stat_check_running = GNUNET_YES;
493
494   statistics_transport_connections = 0 ;
495   statistics_core_entries_session_map = 0;
496   statistics_core_neighbour_entries = 0;
497
498   GNUNET_STATISTICS_get (stats, "transport", "# peers connected", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_transport_connections);
499   GNUNET_STATISTICS_get (stats, "core", "# neighbour entries allocated", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_core_neighbour_entries);
500   GNUNET_STATISTICS_get (stats, "core", "# peers connected", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_core_entries_session_map);
501
502   /* TCP plugin specific checks */
503   if (GNUNET_YES == have_tcp)
504     GNUNET_STATISTICS_get (stats, "transport", "# TCP sessions active", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_transport_tcp_connections);
505 }
506
507
508
509 size_t send_transport_pong_cb (void *cls, size_t size, void *buf)
510 {
511  struct PeerContainer * pc = cls;
512  struct PING ping;
513  size_t mlen = sizeof (struct PING);
514
515  if (size < mlen)
516  {
517    GNUNET_break (0);
518    return 0;
519  }
520
521  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
522       "Sending transport pong to `%s'\n", GNUNET_i2s  (&pc->id));
523  ping.header.size = htons (mlen);
524  ping.header.type = htons (4321);
525  ping.src = htons (0);
526
527  pc->th_pong = NULL;
528
529  memcpy (buf, &ping, mlen);
530  return mlen;
531 }
532
533 static size_t 
534 send_core_pong_cb (void *cls, size_t size, void *buf)
535 {
536 struct PeerContainer * pc = cls;
537 struct PING ping;
538 size_t mlen = sizeof (struct PING);
539
540 if (size < mlen)
541 {
542   GNUNET_break (0);
543   return 0;
544 }
545
546 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
547      "Sending core pong to `%s'\n", GNUNET_i2s  (&pc->id));
548 ping.header.size = htons (mlen);
549 ping.header.type = htons (4321);
550 ping.src = htons (1);
551
552 pc->ch_pong = NULL;
553
554 memcpy (buf, &ping, mlen);
555 return mlen;
556 }
557
558
559 static void
560 map_connect (const struct GNUNET_PeerIdentity *peer, void * source)
561 {
562   struct PeerContainer * pc;
563   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
564   {
565     pc = GNUNET_malloc (sizeof (struct PeerContainer));
566     pc->id = *peer;
567     pc->core_connected = GNUNET_NO;
568     pc->transport_connected = GNUNET_NO;
569     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(peers, &peer->hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
570   }
571
572   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
573   GNUNET_assert (NULL != pc);
574
575   if (source == th)
576   {
577     if (GNUNET_NO == pc->transport_connected)
578     {
579       pc->transport_connected = GNUNET_YES;
580       if (GNUNET_YES == ping)
581       {
582         if (NULL == pc->th_ping)
583           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);
584         else
585           GNUNET_break(0);
586       }
587     }
588     else
589     {
590       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
591            "%s notified multiple times about for peers `%s' (%s : %s)\n",
592            "TRANSPORT",
593            GNUNET_i2s (&pc->id),
594            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
595       GNUNET_break (0);
596     }
597   }
598   if (source == ch)
599   {
600     if (GNUNET_NO == pc->core_connected)
601     {
602       pc->core_connected = GNUNET_YES;
603       if (GNUNET_YES == ping)
604       {
605         if (NULL == pc->ch_ping)
606           pc->ch_ping = GNUNET_CORE_notify_transmit_ready(ch,
607                                                  GNUNET_NO, UINT_MAX,
608                                                           GNUNET_TIME_UNIT_FOREVER_REL,
609                                                  peer,
610                                                  sizeof (struct PING),
611                                                  send_core_ping_cb, pc);
612         else
613           GNUNET_break (0);
614       }
615     }
616     else
617     {
618       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
619            "%s notified multiple times about for peers `%s' (%s : %s)\n",
620            "CORE",
621            GNUNET_i2s (&pc->id),
622                "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
623       GNUNET_break (0);
624     }
625   }
626   if (GNUNET_SCHEDULER_NO_TASK != check_task)
627     GNUNET_SCHEDULER_cancel(check_task);
628   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
629
630   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
631     GNUNET_SCHEDULER_cancel(statistics_task);
632   statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
633 }
634
635
636 static void
637 map_disconnect (const struct GNUNET_PeerIdentity * peer, void * source)
638 {
639
640   struct PeerContainer * pc;
641   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
642   {
643     if (source == th)
644     {
645       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
646          "%s disconnect notification for unknown peer `%s'\n",
647          "TRANSPORT", GNUNET_i2s (peer));
648       GNUNET_break (0);
649       return;
650     }
651     if (source == ch)
652     {
653       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
654          "%s disconnect notification for unknown peer `%s'\n",
655          "CORE", GNUNET_i2s (peer));
656       return;
657     }
658   }
659
660   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
661   GNUNET_assert (NULL != pc);
662
663   if (source == th)
664   {
665     if (NULL != pc->th_ping)
666     {
667       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_ping);
668       pc->th_ping = NULL;
669     }
670     if (NULL != pc->th_pong)
671     {
672       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_pong);
673       pc->th_pong = NULL;
674     }
675
676     if (GNUNET_YES == pc->transport_connected)
677     {
678       pc->transport_connected = GNUNET_NO;
679     }
680     else
681     {
682       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
683            "%s notified for not connected peer `%s' (%s : %s)\n",
684            "TRANSPORT",
685            GNUNET_i2s (&pc->id),
686            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
687       GNUNET_break (0);
688     }
689   }
690   if (source == ch)
691   {
692     if (NULL != pc->ch_ping)
693     {
694       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_ping);
695       pc->ch_ping = NULL;
696     }
697     if (NULL != pc->ch_pong)
698     {
699       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_pong);
700       pc->ch_pong = NULL;
701     }
702
703     if (GNUNET_YES == pc->core_connected)
704     {
705       pc->core_connected = GNUNET_NO;
706     }
707     else
708     {
709       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
710            "%s notified for not connected peer `%s' (%s : %s)\n",
711            "CORE",
712            GNUNET_i2s (&pc->id),
713            "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
714       GNUNET_break (0);
715     }
716   }
717
718   if ((GNUNET_NO == pc->core_connected) && (GNUNET_NO == pc->transport_connected))
719   {
720     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing peer `%s'\n", GNUNET_i2s (&pc->id));
721     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey, pc));
722
723
724     GNUNET_free (pc);
725   }
726
727   if (GNUNET_SCHEDULER_NO_TASK != check_task)
728     GNUNET_SCHEDULER_cancel(check_task);
729   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
730
731   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
732     GNUNET_SCHEDULER_cancel(statistics_task);
733   statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
734 }
735
736
737 static void
738 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
739 {
740   struct TransportPlugin * cur = phead;
741
742   if (NULL != th)
743   {
744     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from transport service\n");
745     GNUNET_TRANSPORT_disconnect (th);
746     th = NULL;
747   }
748
749
750   if (NULL != ch)
751   {
752     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from core service\n");
753     GNUNET_CORE_disconnect (ch);
754     ch = NULL;
755   }
756
757   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
758   {
759     GNUNET_SCHEDULER_cancel(statistics_task);
760     statistics_task = GNUNET_SCHEDULER_NO_TASK;
761   }
762
763   if (GNUNET_SCHEDULER_NO_TASK != check_task)
764   {
765     GNUNET_SCHEDULER_cancel(check_task);
766     check_task = GNUNET_SCHEDULER_NO_TASK;
767   }
768
769   for (cur = phead; cur != NULL; cur = phead)
770   {
771     GNUNET_CONTAINER_DLL_remove(phead, ptail, cur);
772     GNUNET_free (cur->short_name);
773     GNUNET_free (cur);
774   }
775
776   check_task = GNUNET_SCHEDULER_add_now (&map_check, &map_cleanup);
777 }
778
779 static void
780 transport_notify_connect_cb (void *cls,
781                 const struct GNUNET_PeerIdentity* peer)
782 {
783   transport_connections ++;
784   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT connect for peer `%s' (%u total)\n",
785       GNUNET_i2s (peer), transport_connections);
786   map_connect (peer, th);
787 }
788
789 /**
790  * Function called to notify transport users that another
791  * peer disconnected from us.
792  *
793  * @param cls closure
794  * @param peer the peer that disconnected
795  */
796 static void
797 transport_notify_disconnect_cb (void *cls,
798                                const struct
799                                GNUNET_PeerIdentity * peer)
800 {
801   GNUNET_assert (transport_connections > 0);
802   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT disconnect for peer `%s' (%u total)\n",
803       GNUNET_i2s (peer), transport_connections) ;
804   map_disconnect (peer, th);
805   transport_connections --;
806
807 }
808
809 static void
810 transport_notify_receive_cb (void *cls,
811                             const struct
812                             GNUNET_PeerIdentity * peer,
813                             const struct
814                             GNUNET_MessageHeader *
815                             message)
816 {
817
818
819   struct PeerContainer *pc = NULL;
820
821   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
822
823   if (NULL == pc)
824   {
825     GNUNET_break (0);
826     return;
827   }
828
829   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
830   {
831     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
832         "TRANSPORT",
833         "PING",
834         GNUNET_i2s (peer)) ;
835     if (GNUNET_YES == ping)
836     {
837       if (NULL == pc->th_pong)
838         pc->th_pong = GNUNET_TRANSPORT_notify_transmit_ready(th,
839           peer, sizeof (struct PONG),
840                                                              UINT_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
841           &send_transport_pong_cb, pc);
842       else
843         GNUNET_break (0);
844     }
845
846   }
847   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
848   {
849     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
850         "TRANSPORT",
851         "PONG",
852         GNUNET_i2s (peer));
853   }
854 }
855
856 static int 
857 core_notify_receive_cb (void *cls,
858                         const struct GNUNET_PeerIdentity * peer,
859                         const struct GNUNET_MessageHeader * message)
860 {
861   struct PeerContainer *pc = NULL;
862
863   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
864
865   if (NULL == pc)
866   {
867     if (0 == memcmp (peer, &my_peer_id, sizeof (my_peer_id)))
868         return GNUNET_OK;
869
870     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received unexpected message type %u from unknown peer `%s'\n",
871         ntohs (message->type),
872         GNUNET_i2s (peer));
873
874     GNUNET_break (0);
875     return GNUNET_OK;
876   }
877
878   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
879   {
880     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
881         "CORE",
882         "PING",
883         GNUNET_i2s (peer));
884     if (GNUNET_YES == ping)
885     {
886       if (NULL == pc->ch_pong)
887         pc->ch_pong = GNUNET_CORE_notify_transmit_ready(ch,
888                                                GNUNET_NO, UINT_MAX,
889                                                         GNUNET_TIME_UNIT_FOREVER_REL,
890                                                peer,
891                                                sizeof (struct PONG),
892                                                send_core_pong_cb, pc);
893       else
894         GNUNET_break (0);
895     }
896   }
897
898   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
899   {
900     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
901         "CORE",
902         "PONG",
903         GNUNET_i2s (peer));
904
905   }
906
907   return GNUNET_OK;
908 }
909
910 static void
911 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer)
912 {
913   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
914   {
915     core_connections ++;
916     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for peer `%s' (%u total)\n",
917       GNUNET_i2s (peer), core_connections);
918     map_connect (peer, ch);
919   }
920   else
921   {
922     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for myself `%s' (%u total)\n",
923       GNUNET_i2s (peer), core_connections);
924   }
925 }
926
927 static void
928 core_disconnect_cb (void *cls,
929                       const struct
930                       GNUNET_PeerIdentity * peer)
931 {
932   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
933   {
934     GNUNET_assert (core_connections > 0);
935     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for peer `%s' (%u total)\n",
936       GNUNET_i2s (peer), core_connections);
937     map_disconnect (peer, ch);
938     core_connections --;
939   }
940   else
941   {
942     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for myself `%s' (%u total)\n",
943       GNUNET_i2s (peer), core_connections);
944   }
945
946 }
947
948 static void
949 core_init_cb (void *cls, 
950               const struct GNUNET_PeerIdentity *my_identity)
951 {
952   my_peer_id = *my_identity;
953   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to core service\n");
954 }
955
956
957 static void
958 init ()
959 {
960   struct TransportPlugin * cur;
961   char *plugs;
962   char *pos;
963   char *secname;
964   int counter;
965   unsigned long long port;
966
967   have_tcp = GNUNET_NO;
968   have_udp = GNUNET_NO;
969   have_http = GNUNET_NO;
970   have_https = GNUNET_NO;
971   have_unix = GNUNET_NO;
972
973   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (mycfg, "TRANSPORT", "PLUGINS", &plugs))
974     return;
975   counter = 0;
976   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
977   {
978     counter++;
979
980     GNUNET_asprintf(&secname, "transport-%s", pos);
981
982     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (mycfg, secname, "PORT", &port))
983     {
984       GNUNET_free (secname);
985       continue;
986     }
987
988     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport plugin: `%s' port %llu\n"), pos, port);
989     cur = GNUNET_malloc(sizeof (struct TransportPlugin));
990     cur->short_name = GNUNET_strdup (pos);
991     cur->port = port;
992     if (0 == strcmp("tcp", pos))
993     {
994       have_tcp = GNUNET_YES;
995       cur->protocol = tcp;
996     }
997     if (0 == strcmp("udp", pos))
998     {
999       have_udp = GNUNET_YES;
1000       cur->protocol = udp;
1001     }
1002     if (0 == strcmp("http", pos))
1003     {
1004       have_http = GNUNET_YES;
1005       cur->protocol = tcp;
1006     }
1007     if (0 == strcmp("https", pos))
1008     {
1009       have_https = GNUNET_YES;
1010       cur->protocol = tcp;
1011     }
1012     if (0 == strcmp("unix", pos))
1013     {
1014       have_unix = GNUNET_YES;
1015       cur->protocol = unixdomain;
1016     }
1017
1018     GNUNET_CONTAINER_DLL_insert(phead, ptail, cur);
1019     GNUNET_free (secname);
1020   }
1021   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Found %u transport plugins: `%s'\n"),
1022               counter, plugs);
1023
1024   GNUNET_free (plugs);
1025 }
1026
1027 /**
1028  * Main function that will be run by the scheduler.
1029  *
1030  * @param cls closure
1031  * @param args remaining command-line arguments
1032  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1033  * @param cfg configuration
1034  */
1035 static void
1036 run (void *cls, char *const *args, const char *cfgfile,
1037      const struct GNUNET_CONFIGURATION_Handle *cfg)
1038 {
1039   transport_connections = 0;
1040   core_connections = 0;
1041   mycfg = cfg;
1042
1043   init();
1044
1045   stats = GNUNET_STATISTICS_create ("watchdog", cfg);
1046   peers = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
1047
1048   th = GNUNET_TRANSPORT_connect(cfg, NULL, NULL,
1049                                 &transport_notify_receive_cb,
1050                                 &transport_notify_connect_cb,
1051                                 &transport_notify_disconnect_cb);
1052   GNUNET_assert (th != NULL);
1053   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to transport service\n");
1054   ch =  GNUNET_CORE_connect (cfg, NULL,
1055                              &core_init_cb,
1056                              &core_connect_cb,
1057                              &core_disconnect_cb,
1058                              &core_notify_receive_cb, GNUNET_NO,
1059                              NULL, GNUNET_NO,
1060                              NULL);
1061   GNUNET_assert (ch != NULL);
1062
1063   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, NULL);
1064
1065 }
1066
1067
1068 /**
1069  * The main function.
1070  *
1071  * @param argc number of arguments from the command line
1072  * @param argv command line arguments
1073  * @return 0 ok, 1 on error
1074  */
1075 int
1076 main (int argc, char *const *argv)
1077 {
1078   ping = GNUNET_NO;
1079   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1080    {'p', "ping", NULL, gettext_noop ("Send ping messages to test connectivity (default == NO)"),
1081     GNUNET_NO, &GNUNET_GETOPT_set_one, &ping},
1082     GNUNET_GETOPT_OPTION_END
1083   };
1084
1085   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1086     return 2;
1087   ret = (GNUNET_OK ==
1088          GNUNET_PROGRAM_run (argc, argv, "cn",
1089                              gettext_noop ("help text"), options, &run,
1090                              NULL)) ? ret : 1;
1091   GNUNET_free ((void*) argv);
1092   return ret;
1093 }
1094
1095 /* end of connection_watchdog.c */