-fix compile
[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   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   if (source == th)
662   {
663     if (NULL != pc->th_ping)
664     {
665       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_ping);
666       pc->th_ping = NULL;
667     }
668     if (NULL != pc->th_pong)
669     {
670       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_pong);
671       pc->th_pong = NULL;
672     }
673
674     if (GNUNET_YES == pc->transport_connected)
675     {
676       pc->transport_connected = GNUNET_NO;
677     }
678     else
679     {
680       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
681            "%s notified for not connected peer `%s' (%s : %s)\n",
682            "TRANSPORT",
683            GNUNET_i2s (&pc->id),
684            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
685       GNUNET_break (0);
686     }
687   }
688   if (source == ch)
689   {
690     if (NULL != pc->ch_ping)
691     {
692       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_ping);
693       pc->ch_ping = NULL;
694     }
695     if (NULL != pc->ch_pong)
696     {
697       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_pong);
698       pc->ch_pong = NULL;
699     }
700
701     if (GNUNET_YES == pc->core_connected)
702     {
703       pc->core_connected = GNUNET_NO;
704     }
705     else
706     {
707       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
708            "%s notified for not connected peer `%s' (%s : %s)\n",
709            "CORE",
710            GNUNET_i2s (&pc->id),
711            "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
712       GNUNET_break (0);
713     }
714   }
715
716   if ((GNUNET_NO == pc->core_connected) && (GNUNET_NO == pc->transport_connected))
717   {
718     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing peer `%s'\n", GNUNET_i2s (&pc->id));
719     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey, pc));
720
721
722     GNUNET_free (pc);
723   }
724
725   if (GNUNET_SCHEDULER_NO_TASK != check_task)
726     GNUNET_SCHEDULER_cancel(check_task);
727   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
728
729   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
730     GNUNET_SCHEDULER_cancel(statistics_task);
731   statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
732 }
733
734
735 static void
736 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
737 {
738   struct TransportPlugin * cur = phead;
739
740   if (NULL != th)
741   {
742     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from transport service\n");
743     GNUNET_TRANSPORT_disconnect (th);
744     th = NULL;
745   }
746
747
748   if (NULL != ch)
749   {
750     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from core service\n");
751     GNUNET_CORE_disconnect (ch);
752     ch = NULL;
753   }
754
755   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
756   {
757     GNUNET_SCHEDULER_cancel(statistics_task);
758     statistics_task = GNUNET_SCHEDULER_NO_TASK;
759   }
760
761   if (GNUNET_SCHEDULER_NO_TASK != check_task)
762   {
763     GNUNET_SCHEDULER_cancel(check_task);
764     check_task = GNUNET_SCHEDULER_NO_TASK;
765   }
766
767   for (cur = phead; cur != NULL; cur = phead)
768   {
769     GNUNET_CONTAINER_DLL_remove(phead, ptail, cur);
770     GNUNET_free (cur->short_name);
771     GNUNET_free (cur);
772   }
773
774   check_task = GNUNET_SCHEDULER_add_now (&map_check, &map_cleanup);
775 }
776
777 static void
778 transport_notify_connect_cb (void *cls,
779                 const struct GNUNET_PeerIdentity
780                 * peer,
781                 const struct
782                 GNUNET_ATS_Information * ats,
783                 uint32_t ats_count)
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                             const struct
819                             GNUNET_ATS_Information * ats,
820                             uint32_t ats_count)
821 {
822
823
824   struct PeerContainer *pc = NULL;
825
826   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
827
828   if (NULL == pc)
829   {
830     GNUNET_break (0);
831     return;
832   }
833
834   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
835   {
836     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
837         "TRANSPORT",
838         "PING",
839         GNUNET_i2s (peer)) ;
840     if (GNUNET_YES == ping)
841     {
842       if (NULL == pc->th_pong)
843         pc->th_pong = GNUNET_TRANSPORT_notify_transmit_ready(th,
844           peer, sizeof (struct PONG),
845                                                              UINT_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
846           &send_transport_pong_cb, pc);
847       else
848         GNUNET_break (0);
849     }
850
851   }
852   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
853   {
854     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
855         "TRANSPORT",
856         "PONG",
857         GNUNET_i2s (peer));
858   }
859 }
860
861 static int 
862 core_notify_receive_cb (void *cls,
863                         const struct GNUNET_PeerIdentity * peer,
864                         const struct GNUNET_MessageHeader * message,
865                         const struct GNUNET_ATS_Information* atsi,
866                         unsigned int atsi_count)
867 {
868   struct PeerContainer *pc = NULL;
869
870   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
871
872   if (NULL == pc)
873   {
874     if (0 == memcmp (peer, &my_peer_id, sizeof (my_peer_id)))
875         return GNUNET_OK;
876
877     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received unexpected message type %u from unknown peer `%s'\n",
878         ntohs (message->type),
879         GNUNET_i2s (peer));
880
881     GNUNET_break (0);
882     return GNUNET_OK;
883   }
884
885   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
886   {
887     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
888         "CORE",
889         "PING",
890         GNUNET_i2s (peer));
891     if (GNUNET_YES == ping)
892     {
893       if (NULL == pc->ch_pong)
894         pc->ch_pong = GNUNET_CORE_notify_transmit_ready(ch,
895                                                GNUNET_NO, UINT_MAX,
896                                                         GNUNET_TIME_UNIT_FOREVER_REL,
897                                                peer,
898                                                sizeof (struct PONG),
899                                                send_core_pong_cb, pc);
900       else
901         GNUNET_break (0);
902     }
903   }
904
905   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
906   {
907     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
908         "CORE",
909         "PONG",
910         GNUNET_i2s (peer));
911
912   }
913
914   return GNUNET_OK;
915 }
916
917 static void
918 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
919                       const struct GNUNET_ATS_Information *atsi,
920                       unsigned int atsi_count)
921 {
922   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
923   {
924     core_connections ++;
925     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for peer `%s' (%u total)\n",
926       GNUNET_i2s (peer), core_connections);
927     map_connect (peer, ch);
928   }
929   else
930   {
931     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for myself `%s' (%u total)\n",
932       GNUNET_i2s (peer), core_connections);
933   }
934 }
935
936 static void
937 core_disconnect_cb (void *cls,
938                       const struct
939                       GNUNET_PeerIdentity * peer)
940 {
941   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
942   {
943     GNUNET_assert (core_connections > 0);
944     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for peer `%s' (%u total)\n",
945       GNUNET_i2s (peer), core_connections);
946     map_disconnect (peer, ch);
947     core_connections --;
948   }
949   else
950   {
951     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for myself `%s' (%u total)\n",
952       GNUNET_i2s (peer), core_connections);
953   }
954
955 }
956
957 static void
958 core_init_cb (void *cls, struct GNUNET_CORE_Handle *server,
959                    const struct GNUNET_PeerIdentity *my_identity)
960 {
961   my_peer_id = *my_identity;
962   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to core service\n");
963 }
964
965
966 static void
967 init ()
968 {
969   struct TransportPlugin * cur;
970   char *plugs;
971   char *pos;
972   char *secname;
973   int counter;
974   unsigned long long port;
975
976   have_tcp = GNUNET_NO;
977   have_udp = GNUNET_NO;
978   have_http = GNUNET_NO;
979   have_https = GNUNET_NO;
980   have_unix = GNUNET_NO;
981
982   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (mycfg, "TRANSPORT", "PLUGINS", &plugs))
983     return;
984   counter = 0;
985   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
986   {
987     counter++;
988
989     GNUNET_asprintf(&secname, "transport-%s", pos);
990
991     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (mycfg, secname, "PORT", &port))
992     {
993       GNUNET_free (secname);
994       continue;
995     }
996
997     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport plugin: `%s' port %llu\n"), pos, port);
998     cur = GNUNET_malloc(sizeof (struct TransportPlugin));
999     cur->short_name = GNUNET_strdup (pos);
1000     cur->port = port;
1001     if (0 == strcmp("tcp", pos))
1002     {
1003       have_tcp = GNUNET_YES;
1004       cur->protocol = tcp;
1005     }
1006     if (0 == strcmp("udp", pos))
1007     {
1008       have_udp = GNUNET_YES;
1009       cur->protocol = udp;
1010     }
1011     if (0 == strcmp("http", pos))
1012     {
1013       have_http = GNUNET_YES;
1014       cur->protocol = tcp;
1015     }
1016     if (0 == strcmp("https", pos))
1017     {
1018       have_https = GNUNET_YES;
1019       cur->protocol = tcp;
1020     }
1021     if (0 == strcmp("unix", pos))
1022     {
1023       have_unix = GNUNET_YES;
1024       cur->protocol = unixdomain;
1025     }
1026
1027     GNUNET_CONTAINER_DLL_insert(phead, ptail, cur);
1028     GNUNET_free (secname);
1029   }
1030   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Found %u transport plugins: `%s'\n"),
1031               counter, plugs);
1032
1033   GNUNET_free (plugs);
1034 }
1035
1036 /**
1037  * Main function that will be run by the scheduler.
1038  *
1039  * @param cls closure
1040  * @param args remaining command-line arguments
1041  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1042  * @param cfg configuration
1043  */
1044 static void
1045 run (void *cls, char *const *args, const char *cfgfile,
1046      const struct GNUNET_CONFIGURATION_Handle *cfg)
1047 {
1048   transport_connections = 0;
1049   core_connections = 0;
1050   mycfg = cfg;
1051
1052   init();
1053
1054   stats = GNUNET_STATISTICS_create ("watchdog", cfg);
1055   peers = GNUNET_CONTAINER_multihashmap_create (20);
1056
1057   th = GNUNET_TRANSPORT_connect(cfg, NULL, NULL,
1058                                 &transport_notify_receive_cb,
1059                                 &transport_notify_connect_cb,
1060                                 &transport_notify_disconnect_cb);
1061   GNUNET_assert (th != NULL);
1062   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to transport service\n");
1063   ch =  GNUNET_CORE_connect (cfg, NULL,
1064                              &core_init_cb,
1065                              &core_connect_cb,
1066                              &core_disconnect_cb,
1067                              &core_notify_receive_cb, GNUNET_NO,
1068                              NULL, GNUNET_NO,
1069                              NULL);
1070   GNUNET_assert (ch != NULL);
1071
1072   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, NULL);
1073
1074 }
1075
1076
1077 /**
1078  * The main function.
1079  *
1080  * @param argc number of arguments from the command line
1081  * @param argv command line arguments
1082  * @return 0 ok, 1 on error
1083  */
1084 int
1085 main (int argc, char *const *argv)
1086 {
1087   ping = GNUNET_NO;
1088   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1089    {'p', "ping", NULL, gettext_noop ("Send ping messages to test connectivity (default == NO)"),
1090     GNUNET_NO, &GNUNET_GETOPT_set_one, &ping},
1091     GNUNET_GETOPT_OPTION_END
1092   };
1093
1094   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1095     return 2;
1096
1097   return (GNUNET_OK ==
1098           GNUNET_PROGRAM_run (argc, argv, "cn",
1099                               gettext_noop ("help text"), options, &run,
1100                               NULL)) ? ret : 1;
1101 }
1102
1103 /* end of connection_watchdog.c */