- changes
[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
784                 * peer,
785                 const struct
786                 GNUNET_ATS_Information * ats,
787                 uint32_t ats_count)
788 {
789   transport_connections ++;
790   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT connect for peer `%s' (%u total)\n",
791       GNUNET_i2s (peer), transport_connections);
792   map_connect (peer, th);
793 }
794
795 /**
796  * Function called to notify transport users that another
797  * peer disconnected from us.
798  *
799  * @param cls closure
800  * @param peer the peer that disconnected
801  */
802 static void
803 transport_notify_disconnect_cb (void *cls,
804                                const struct
805                                GNUNET_PeerIdentity * peer)
806 {
807   GNUNET_assert (transport_connections > 0);
808   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT disconnect for peer `%s' (%u total)\n",
809       GNUNET_i2s (peer), transport_connections) ;
810   map_disconnect (peer, th);
811   transport_connections --;
812
813 }
814
815 static void
816 transport_notify_receive_cb (void *cls,
817                             const struct
818                             GNUNET_PeerIdentity * peer,
819                             const struct
820                             GNUNET_MessageHeader *
821                             message,
822                             const struct
823                             GNUNET_ATS_Information * ats,
824                             uint32_t ats_count)
825 {
826
827
828   struct PeerContainer *pc = NULL;
829
830   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
831
832   if (NULL == pc)
833   {
834     GNUNET_break (0);
835     return;
836   }
837
838   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
839   {
840     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
841         "TRANSPORT",
842         "PING",
843         GNUNET_i2s (peer)) ;
844     if (GNUNET_YES == ping)
845     {
846       if (NULL == pc->th_pong)
847         pc->th_pong = GNUNET_TRANSPORT_notify_transmit_ready(th,
848           peer, sizeof (struct PONG),
849                                                              UINT_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
850           &send_transport_pong_cb, pc);
851       else
852         GNUNET_break (0);
853     }
854
855   }
856   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
857   {
858     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
859         "TRANSPORT",
860         "PONG",
861         GNUNET_i2s (peer));
862   }
863 }
864
865 static int 
866 core_notify_receive_cb (void *cls,
867                         const struct GNUNET_PeerIdentity * peer,
868                         const struct GNUNET_MessageHeader * message,
869                         const struct GNUNET_ATS_Information* atsi,
870                         unsigned int atsi_count)
871 {
872   struct PeerContainer *pc = NULL;
873
874   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
875
876   if (NULL == pc)
877   {
878     if (0 == memcmp (peer, &my_peer_id, sizeof (my_peer_id)))
879         return GNUNET_OK;
880
881     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received unexpected message type %u from unknown peer `%s'\n",
882         ntohs (message->type),
883         GNUNET_i2s (peer));
884
885     GNUNET_break (0);
886     return GNUNET_OK;
887   }
888
889   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
890   {
891     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
892         "CORE",
893         "PING",
894         GNUNET_i2s (peer));
895     if (GNUNET_YES == ping)
896     {
897       if (NULL == pc->ch_pong)
898         pc->ch_pong = GNUNET_CORE_notify_transmit_ready(ch,
899                                                GNUNET_NO, UINT_MAX,
900                                                         GNUNET_TIME_UNIT_FOREVER_REL,
901                                                peer,
902                                                sizeof (struct PONG),
903                                                send_core_pong_cb, pc);
904       else
905         GNUNET_break (0);
906     }
907   }
908
909   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
910   {
911     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
912         "CORE",
913         "PONG",
914         GNUNET_i2s (peer));
915
916   }
917
918   return GNUNET_OK;
919 }
920
921 static void
922 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
923                       const struct GNUNET_ATS_Information *atsi,
924                       unsigned int atsi_count)
925 {
926   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
927   {
928     core_connections ++;
929     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for peer `%s' (%u total)\n",
930       GNUNET_i2s (peer), core_connections);
931     map_connect (peer, ch);
932   }
933   else
934   {
935     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for myself `%s' (%u total)\n",
936       GNUNET_i2s (peer), core_connections);
937   }
938 }
939
940 static void
941 core_disconnect_cb (void *cls,
942                       const struct
943                       GNUNET_PeerIdentity * peer)
944 {
945   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
946   {
947     GNUNET_assert (core_connections > 0);
948     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for peer `%s' (%u total)\n",
949       GNUNET_i2s (peer), core_connections);
950     map_disconnect (peer, ch);
951     core_connections --;
952   }
953   else
954   {
955     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for myself `%s' (%u total)\n",
956       GNUNET_i2s (peer), core_connections);
957   }
958
959 }
960
961 static void
962 core_init_cb (void *cls, struct GNUNET_CORE_Handle *server,
963                    const struct GNUNET_PeerIdentity *my_identity)
964 {
965   my_peer_id = *my_identity;
966   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to core service\n");
967 }
968
969
970 static void
971 init ()
972 {
973   struct TransportPlugin * cur;
974   char *plugs;
975   char *pos;
976   char *secname;
977   int counter;
978   unsigned long long port;
979
980   have_tcp = GNUNET_NO;
981   have_udp = GNUNET_NO;
982   have_http = GNUNET_NO;
983   have_https = GNUNET_NO;
984   have_unix = GNUNET_NO;
985
986   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (mycfg, "TRANSPORT", "PLUGINS", &plugs))
987     return;
988   counter = 0;
989   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
990   {
991     counter++;
992
993     GNUNET_asprintf(&secname, "transport-%s", pos);
994
995     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (mycfg, secname, "PORT", &port))
996     {
997       GNUNET_free (secname);
998       continue;
999     }
1000
1001     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport plugin: `%s' port %llu\n"), pos, port);
1002     cur = GNUNET_malloc(sizeof (struct TransportPlugin));
1003     cur->short_name = GNUNET_strdup (pos);
1004     cur->port = port;
1005     if (0 == strcmp("tcp", pos))
1006     {
1007       have_tcp = GNUNET_YES;
1008       cur->protocol = tcp;
1009     }
1010     if (0 == strcmp("udp", pos))
1011     {
1012       have_udp = GNUNET_YES;
1013       cur->protocol = udp;
1014     }
1015     if (0 == strcmp("http", pos))
1016     {
1017       have_http = GNUNET_YES;
1018       cur->protocol = tcp;
1019     }
1020     if (0 == strcmp("https", pos))
1021     {
1022       have_https = GNUNET_YES;
1023       cur->protocol = tcp;
1024     }
1025     if (0 == strcmp("unix", pos))
1026     {
1027       have_unix = GNUNET_YES;
1028       cur->protocol = unixdomain;
1029     }
1030
1031     GNUNET_CONTAINER_DLL_insert(phead, ptail, cur);
1032     GNUNET_free (secname);
1033   }
1034   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Found %u transport plugins: `%s'\n"),
1035               counter, plugs);
1036
1037   GNUNET_free (plugs);
1038 }
1039
1040 /**
1041  * Main function that will be run by the scheduler.
1042  *
1043  * @param cls closure
1044  * @param args remaining command-line arguments
1045  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1046  * @param cfg configuration
1047  */
1048 static void
1049 run (void *cls, char *const *args, const char *cfgfile,
1050      const struct GNUNET_CONFIGURATION_Handle *cfg)
1051 {
1052   transport_connections = 0;
1053   core_connections = 0;
1054   mycfg = cfg;
1055
1056   init();
1057
1058   stats = GNUNET_STATISTICS_create ("watchdog", cfg);
1059   peers = GNUNET_CONTAINER_multihashmap_create (20);
1060
1061   th = GNUNET_TRANSPORT_connect(cfg, NULL, NULL,
1062                                 &transport_notify_receive_cb,
1063                                 &transport_notify_connect_cb,
1064                                 &transport_notify_disconnect_cb);
1065   GNUNET_assert (th != NULL);
1066   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to transport service\n");
1067   ch =  GNUNET_CORE_connect (cfg, NULL,
1068                              &core_init_cb,
1069                              &core_connect_cb,
1070                              &core_disconnect_cb,
1071                              &core_notify_receive_cb, GNUNET_NO,
1072                              NULL, GNUNET_NO,
1073                              NULL);
1074   GNUNET_assert (ch != NULL);
1075
1076   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, NULL);
1077
1078 }
1079
1080
1081 /**
1082  * The main function.
1083  *
1084  * @param argc number of arguments from the command line
1085  * @param argv command line arguments
1086  * @return 0 ok, 1 on error
1087  */
1088 int
1089 main (int argc, char *const *argv)
1090 {
1091   ping = GNUNET_NO;
1092   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1093    {'p', "ping", NULL, gettext_noop ("Send ping messages to test connectivity (default == NO)"),
1094     GNUNET_NO, &GNUNET_GETOPT_set_one, &ping},
1095     GNUNET_GETOPT_OPTION_END
1096   };
1097
1098   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1099     return 2;
1100
1101   return (GNUNET_OK ==
1102           GNUNET_PROGRAM_run (argc, argv, "cn",
1103                               gettext_noop ("help text"), options, &run,
1104                               NULL)) ? ret : 1;
1105 }
1106
1107 /* end of connection_watchdog.c */