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