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