- cmd line arg
[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, 5)
39 #define STATS_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
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 int map_check_it (void *cls,
123                   const 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 int map_cleanup_it (void *cls,
143                   const GNUNET_HashCode * key,
144                   void *value)
145 {
146   struct PeerContainer *pc = value;
147   GNUNET_CONTAINER_multihashmap_remove(peers, key, value);
148   if (NULL != pc->th_ping)
149   {
150     GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_ping);
151     pc->th_ping = NULL;
152   }
153   if (NULL != pc->th_pong)
154   {
155     GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_pong);
156     pc->th_pong = NULL;
157   }
158   if (NULL != pc->ch_ping)
159   {
160     GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_ping);
161     pc->ch_ping = NULL;
162   }
163   if (NULL != pc->ch_pong)
164   {
165     GNUNET_CORE_notify_transmit_ready_cancel(pc->ch_pong);
166     pc->ch_pong = NULL;
167   }
168   GNUNET_free (pc);
169   return GNUNET_OK;
170 }
171
172 static void
173 map_cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
174 {
175   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_cleanup_it, NULL);
176   GNUNET_CONTAINER_multihashmap_destroy(peers);
177 }
178
179 static void
180 map_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
181 {
182   int fail = 0;
183   check_task = GNUNET_SCHEDULER_NO_TASK;
184   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_check_it, &fail);
185   if (0 > fail)
186     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
187        "Inconsistent peers after connection consistency check: %u\n", fail);
188   else
189     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
190        "Inconsistent peers after connection consistency check: %u\n", fail);
191
192
193   if (NULL != cls)
194   {
195     GNUNET_SCHEDULER_add_now (cls, NULL);
196   }
197 }
198
199
200 static void
201 stats_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
202
203 static int
204 check_lowlevel_connections (int port, int protocol)
205 {
206   FILE *f;
207   char * cmdline;
208   char * proto;
209   char line[1024];
210   int count = -1;
211 #ifdef MINGW
212   /* not supported */
213   return count;
214 #else
215
216   switch (protocol) {
217     case tcp:
218       proto = "-t";
219       break;
220     case udp:
221       proto = "-u";
222       break;
223     case unixdomain:
224       proto = "-x";
225       break;
226     default:
227       proto = "";
228       break;
229   }
230
231
232   GNUNET_asprintf(&cmdline, "netstat -n %s | grep %u", proto, port);
233
234   if (system ("netstat -n > /dev/null 2> /dev/null"))
235     if (system ("netstat -n > /dev/null 2> /dev/null") == 0)
236       f = popen (cmdline, "r");
237     else
238       f = NULL;
239   else
240     f = popen (cmdline, "r");
241   if (!f)
242   {
243     GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "ss");
244     GNUNET_free (cmdline);
245     return -1;
246   }
247
248   count = 0;
249   while (NULL != fgets (line, sizeof (line), f))
250   {
251     /* read */
252     //printf ("%s", line);
253     count ++;
254   }
255
256   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%i TCP connections established with port %u\n",
257        count, port);
258
259   pclose (f);
260   GNUNET_free (cmdline);
261   return count;
262 #endif
263 }
264
265
266 static struct TransportPlugin *
267 find_plugin (char * name)
268 {
269   struct TransportPlugin *cur = NULL;
270
271   for (cur = phead; cur != NULL; cur = phead)
272   {
273     if (0 == strcmp(name, cur->short_name))
274       return cur;
275   }
276   return cur;
277 }
278
279 int stats_check_cb (void *cls, const char *subsystem,
280                    const char *name, uint64_t value,
281                    int is_persistent)
282 {
283   static int counter;
284
285   uint64_t *val = cls;
286
287   if (NULL != val)
288     (*val) = value;
289
290   counter ++;
291   if ((STATS_VALUES == counter) || ((GNUNET_NO == have_tcp) && (STATS_VALUES - 1 == counter)))
292   {
293     int fail = GNUNET_NO;
294
295
296
297     int low_level_connections_udp = check_lowlevel_connections (2086, udp);
298
299     if (transport_connections != core_connections)
300     {
301       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
302            "%u transport notifications <-> %u core notifications\n",
303            transport_connections, core_connections);
304       fail = GNUNET_YES;
305     }
306
307     if (transport_connections != statistics_transport_connections)
308     {
309       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
310            "%u transport notifications <-> %u in statistics (peers connected)\n",
311            transport_connections, statistics_transport_connections);
312       fail = GNUNET_YES;
313     }
314
315     if (core_connections != statistics_core_entries_session_map)
316     {
317       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
318            "%u core notifications <-> %u in statistics (entries session map)\n",
319            core_connections, statistics_core_entries_session_map);
320       fail = GNUNET_YES;
321     }
322
323     if (core_connections != statistics_core_neighbour_entries)
324     {
325       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
326            "%u core notifications <-> %u in statistics (neighbour entries allocated)\n",
327            core_connections, statistics_core_neighbour_entries);
328       fail = GNUNET_YES;
329     }
330
331     if (GNUNET_NO == fail)
332       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
333          "Check successful : (%u transport / %u core) connections established\n", transport_connections, core_connections);
334
335     /* TCP plugin specific checks */
336     if (GNUNET_YES == have_tcp)
337     {
338       struct TransportPlugin * p = find_plugin ("tcp");
339       int low_level_connections_tcp = check_lowlevel_connections (p->port, p->protocol);
340
341       if (low_level_connections_tcp != -1)
342       {
343         if (statistics_transport_tcp_connections > low_level_connections_tcp)
344         {
345           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
346                "%u transport tcp sessions <-> %i established tcp connections\n",
347                statistics_transport_tcp_connections, low_level_connections_tcp);
348           fail = GNUNET_YES;
349         }
350         else if (low_level_connections_tcp != -1)
351         {
352           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
353                "%u TCP connections, %u UDP connections \n",
354                low_level_connections_tcp, low_level_connections_udp);
355         }
356       }
357       if (transport_connections > statistics_transport_tcp_connections)
358       {
359         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
360              "%u transport notifications <-> %u in statistics (statistics_transport_tcp_connections)\n",
361              transport_connections, statistics_transport_tcp_connections);
362         fail = GNUNET_YES;
363       }
364       else
365       {
366         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
367              " %u transport notifications <-> %u in statistics (statistics_transport_tcp_connections)\n",
368              transport_connections, statistics_transport_tcp_connections);
369       }
370     }
371
372     if (GNUNET_SCHEDULER_NO_TASK == statistics_task)
373       statistics_task = GNUNET_SCHEDULER_add_delayed(REPEATED_STATS_DELAY, &stats_check, NULL);
374
375     stat_check_running = GNUNET_NO;
376     counter = 0;
377   }
378
379   return GNUNET_OK;
380 }
381
382 GNUNET_NETWORK_STRUCT_BEGIN
383
384 struct PING
385 {
386   struct GNUNET_MessageHeader header;
387
388   uint16_t src;
389 };
390
391 struct PONG
392 {
393   struct GNUNET_MessageHeader header;
394
395   uint16_t src;
396 };
397 GNUNET_NETWORK_STRUCT_END
398
399
400 size_t send_transport_ping_cb (void *cls, size_t size, void *buf)
401 {
402  struct PeerContainer * pc = cls;
403  struct PING ping;
404  size_t mlen = sizeof (struct PING);
405
406  if (size < mlen)
407  {
408    GNUNET_break (0);
409    return 0;
410  }
411
412  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413       "Sending transport ping to `%s'\n", GNUNET_i2s  (&pc->id));
414  ping.header.size = htons (mlen);
415  ping.header.type = htons (1234);
416  ping.src = htons (0);
417
418  pc->th_ping = NULL;
419
420  memcpy (buf, &ping, mlen);
421  return mlen;
422 }
423
424 size_t send_core_ping_cb (void *cls, size_t size, void *buf)
425 {
426 struct PeerContainer * pc = cls;
427 struct PING ping;
428 size_t mlen = sizeof (struct PING);
429
430 if (size < mlen)
431 {
432   GNUNET_break (0);
433   return 0;
434 }
435
436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
437      "Sending core ping to `%s'\n", GNUNET_i2s  (&pc->id));
438 ping.header.size = htons (mlen);
439 ping.header.type = htons (1234);
440 ping.src = htons (1);
441
442 pc->ch_ping = NULL;
443
444 memcpy (buf, &ping, mlen);
445 return mlen;
446 }
447
448
449 int map_ping_it (void *cls,
450                   const GNUNET_HashCode * key,
451                   void *value)
452 {
453   struct PeerContainer *pc = value;
454
455   if ((GNUNET_YES == pc->transport_connected) && (NULL == pc->th_ping))
456     pc->th_ping = GNUNET_TRANSPORT_notify_transmit_ready(th, &pc->id,
457         sizeof (struct PING), UINT_MAX,
458         GNUNET_TIME_relative_get_forever(), &send_transport_ping_cb, pc);
459   else
460     GNUNET_break(0);
461
462   if ((GNUNET_YES == pc->core_connected) && (NULL == pc->ch_ping))
463     pc->ch_ping = GNUNET_CORE_notify_transmit_ready(ch,
464                                              GNUNET_NO, UINT_MAX,
465                                              GNUNET_TIME_relative_get_forever(),
466                                              &pc->id,
467                                              sizeof (struct PING),
468                                              send_core_ping_cb, pc);
469   else
470     GNUNET_break (0);
471
472   return GNUNET_OK;
473 }
474
475
476 static void
477 stats_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
478 {
479   statistics_task = GNUNET_SCHEDULER_NO_TASK;
480
481   if (GNUNET_YES == stat_check_running)
482   {
483     statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
484   }
485
486   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_ping_it, NULL);
487
488   stat_check_running = GNUNET_YES;
489
490   statistics_transport_connections = 0 ;
491   statistics_core_entries_session_map = 0;
492   statistics_core_neighbour_entries = 0;
493
494   GNUNET_STATISTICS_get (stats, "transport", "# peers connected", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_transport_connections);
495   GNUNET_STATISTICS_get (stats, "core", "# neighbour entries allocated", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_core_neighbour_entries);
496   GNUNET_STATISTICS_get (stats, "core", "# entries in session map", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_core_entries_session_map);
497
498   /* TCP plugin specific checks */
499   if (GNUNET_YES == have_tcp)
500     GNUNET_STATISTICS_get (stats, "transport", "# TCP sessions active", GNUNET_TIME_UNIT_MINUTES, NULL, &stats_check_cb, &statistics_transport_tcp_connections);
501 }
502
503
504
505 size_t send_transport_pong_cb (void *cls, size_t size, void *buf)
506 {
507  struct PeerContainer * pc = cls;
508  struct PING ping;
509  size_t mlen = sizeof (struct PING);
510
511  if (size < mlen)
512  {
513    GNUNET_break (0);
514    return 0;
515  }
516
517  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518       "Sending transport pong to `%s'\n", GNUNET_i2s  (&pc->id));
519  ping.header.size = htons (mlen);
520  ping.header.type = htons (4321);
521  ping.src = htons (0);
522
523  pc->th_pong = NULL;
524
525  memcpy (buf, &ping, mlen);
526  return mlen;
527 }
528
529 size_t send_core_pong_cb (void *cls, size_t size, void *buf)
530 {
531 struct PeerContainer * pc = cls;
532 struct PING ping;
533 size_t mlen = sizeof (struct PING);
534
535 if (size < mlen)
536 {
537   GNUNET_break (0);
538   return 0;
539 }
540
541 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
542      "Sending core pong to `%s'\n", GNUNET_i2s  (&pc->id));
543 ping.header.size = htons (mlen);
544 ping.header.type = htons (4321);
545 ping.src = htons (1);
546
547 pc->ch_pong = NULL;
548
549 memcpy (buf, &ping, mlen);
550 return mlen;
551 }
552
553
554 static void
555 map_connect (const struct GNUNET_PeerIdentity *peer, void * source)
556 {
557   struct PeerContainer * pc;
558   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
559   {
560     pc = GNUNET_malloc (sizeof (struct PeerContainer));
561     pc->id = *peer;
562     pc->core_connected = GNUNET_NO;
563     pc->transport_connected = GNUNET_NO;
564     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(peers, &peer->hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
565   }
566
567   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
568   if (source == th)
569   {
570     if (GNUNET_NO == pc->transport_connected)
571     {
572       pc->transport_connected = GNUNET_YES;
573       if ((GNUNET_YES == ping) && (NULL == pc->th_ping))
574         pc->th_ping = GNUNET_TRANSPORT_notify_transmit_ready(th, peer, sizeof (struct PING), UINT_MAX, GNUNET_TIME_relative_get_forever(), &send_transport_ping_cb, pc);
575       else
576         GNUNET_break(0);
577     }
578     else
579     {
580       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
581            "%s notified multiple times about for peers `%s' (%s : %s)\n",
582            "TRANSPORT",
583            GNUNET_i2s (&pc->id),
584            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
585       GNUNET_break (0);
586     }
587   }
588   if (source == ch)
589   {
590     if (GNUNET_NO == pc->core_connected)
591     {
592       pc->core_connected = GNUNET_YES;
593       if ((GNUNET_YES == ping) && (NULL == pc->ch_ping))
594         pc->ch_ping = GNUNET_CORE_notify_transmit_ready(ch,
595                                                  GNUNET_NO, UINT_MAX,
596                                                  GNUNET_TIME_relative_get_forever(),
597                                                  peer,
598                                                  sizeof (struct PING),
599                                                  send_core_ping_cb, pc);
600       else
601         GNUNET_break (0);
602     }
603     else
604     {
605       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
606            "%s notified multiple times about for peers `%s' (%s : %s)\n",
607            "CORE",
608            GNUNET_i2s (&pc->id),
609                "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
610       GNUNET_break (0);
611     }
612   }
613   if (GNUNET_SCHEDULER_NO_TASK != check_task)
614     GNUNET_SCHEDULER_cancel(check_task);
615   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
616
617   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
618     GNUNET_SCHEDULER_cancel(statistics_task);
619   statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
620 }
621
622
623 static void
624 map_disconnect (const struct GNUNET_PeerIdentity * peer, void * source)
625 {
626
627   struct PeerContainer * pc;
628   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
629   {
630     if (source == th)
631     {
632       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
633          "%s disconnect notification for unknown peer `%s'\n",
634          "TRANSPORT", GNUNET_i2s (peer));
635       GNUNET_break (0);
636       return;
637     }
638     if (source == ch)
639     {
640       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
641          "%s disconnect notification for unknown peer `%s'\n",
642          "CORE", GNUNET_i2s (peer));
643       return;
644     }
645   }
646
647   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
648   if (source == th)
649   {
650     if (NULL != pc->th_ping)
651     {
652       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_ping);
653       pc->th_ping = NULL;
654     }
655     if (NULL != pc->th_pong)
656     {
657       GNUNET_TRANSPORT_notify_transmit_ready_cancel(pc->th_pong);
658       pc->th_pong = NULL;
659     }
660
661     if (GNUNET_YES == pc->transport_connected)
662     {
663       pc->transport_connected = GNUNET_NO;
664     }
665     else
666     {
667       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
668            "%s notified for not connected peer `%s' (%s : %s)\n",
669            "TRANSPORT",
670            GNUNET_i2s (&pc->id),
671            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
672       GNUNET_break (0);
673     }
674   }
675   if (source == ch)
676   {
677     if (NULL != pc->ch_ping)
678     {
679       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_ping);
680       pc->ch_ping = NULL;
681     }
682     if (NULL != pc->ch_pong)
683     {
684       GNUNET_CORE_notify_transmit_ready_cancel (pc->ch_pong);
685       pc->ch_pong = NULL;
686     }
687
688     if (GNUNET_YES == pc->core_connected)
689     {
690       pc->core_connected = GNUNET_NO;
691     }
692     else
693     {
694       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
695            "%s notified for not connected peer `%s' (%s : %s)\n",
696            "CORE",
697            GNUNET_i2s (&pc->id),
698            "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
699       GNUNET_break (0);
700     }
701   }
702
703   if ((GNUNET_NO == pc->core_connected) && (GNUNET_NO == pc->transport_connected))
704   {
705     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing peer `%s'\n", GNUNET_i2s (&pc->id));
706     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey, pc));
707
708
709     GNUNET_free (pc);
710   }
711
712   if (GNUNET_SCHEDULER_NO_TASK != check_task)
713     GNUNET_SCHEDULER_cancel(check_task);
714   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
715
716   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
717     GNUNET_SCHEDULER_cancel(statistics_task);
718   statistics_task = GNUNET_SCHEDULER_add_delayed(STATS_DELAY, &stats_check, NULL);
719 }
720
721
722 static void
723 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
724 {
725   struct TransportPlugin * cur = phead;
726
727   if (NULL != th)
728   {
729     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from transport service\n");
730     GNUNET_TRANSPORT_disconnect (th);
731     th = NULL;
732   }
733
734
735   if (NULL != ch)
736   {
737     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from core service\n");
738     GNUNET_CORE_disconnect (ch);
739     ch = NULL;
740   }
741
742   if (GNUNET_SCHEDULER_NO_TASK != statistics_task)
743   {
744     GNUNET_SCHEDULER_cancel(statistics_task);
745     statistics_task = GNUNET_SCHEDULER_NO_TASK;
746   }
747
748   if (GNUNET_SCHEDULER_NO_TASK != check_task)
749   {
750     GNUNET_SCHEDULER_cancel(check_task);
751     check_task = GNUNET_SCHEDULER_NO_TASK;
752   }
753
754   for (cur = phead; cur != NULL; cur = phead)
755   {
756     GNUNET_CONTAINER_DLL_remove(phead, ptail, cur);
757     GNUNET_free (cur->short_name);
758     GNUNET_free (cur);
759   }
760
761   check_task = GNUNET_SCHEDULER_add_now (&map_check, &map_cleanup);
762 }
763
764 void
765 transport_notify_connect_cb (void *cls,
766                 const struct GNUNET_PeerIdentity
767                 * peer,
768                 const struct
769                 GNUNET_ATS_Information * ats,
770                 uint32_t ats_count)
771 {
772   transport_connections ++;
773   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT connect for peer `%s' (%u total)\n",
774       GNUNET_i2s (peer), transport_connections);
775   map_connect (peer, th);
776 }
777
778 /**
779  * Function called to notify transport users that another
780  * peer disconnected from us.
781  *
782  * @param cls closure
783  * @param peer the peer that disconnected
784  */
785 void
786 transport_notify_disconnect_cb (void *cls,
787                                const struct
788                                GNUNET_PeerIdentity * peer)
789 {
790   GNUNET_assert (transport_connections > 0);
791   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT disconnect for peer `%s' (%u total)\n",
792       GNUNET_i2s (peer), transport_connections) ;
793   map_disconnect (peer, th);
794   transport_connections --;
795
796 }
797
798 static void
799 transport_notify_receive_cb (void *cls,
800                             const struct
801                             GNUNET_PeerIdentity * peer,
802                             const struct
803                             GNUNET_MessageHeader *
804                             message,
805                             const struct
806                             GNUNET_ATS_Information * ats,
807                             uint32_t ats_count)
808 {
809
810
811   struct PeerContainer *pc = NULL;
812
813   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
814
815   if (NULL == pc)
816   {
817     GNUNET_break (0);
818     return;
819   }
820
821   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
822   {
823     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
824         "TRANSPORT",
825         "PING",
826         GNUNET_i2s (peer)) ;
827     if ((GNUNET_YES == ping) && (NULL == pc->th_pong))
828       pc->th_pong = GNUNET_TRANSPORT_notify_transmit_ready(th,
829           peer, sizeof (struct PONG),
830           UINT_MAX, GNUNET_TIME_relative_get_forever(),
831           &send_transport_pong_cb, pc);
832     else
833       GNUNET_break (0);
834
835   }
836   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
837   {
838     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
839         "TRANSPORT",
840         "PONG",
841         GNUNET_i2s (peer));
842   }
843 }
844
845 int core_notify_receive_cb (void *cls,
846                                 const struct GNUNET_PeerIdentity * peer,
847                                 const struct GNUNET_MessageHeader * message,
848                                 const struct GNUNET_ATS_Information* atsi,
849                                 unsigned int atsi_count)
850 {
851   struct PeerContainer *pc = NULL;
852
853   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
854
855   if (NULL == pc)
856   {
857     GNUNET_break (0);
858     return GNUNET_OK;
859   }
860
861   if ((message->size == ntohs (sizeof (struct PING))) && (message->type == ntohs (1234)))
862   {
863     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received %s %s from peer `%s'\n",
864         "CORE",
865         "PING",
866         GNUNET_i2s (peer));
867     if ((GNUNET_YES == ping) && (NULL == pc->ch_pong))
868       pc->ch_pong = GNUNET_CORE_notify_transmit_ready(ch,
869                                                GNUNET_NO, UINT_MAX,
870                                                GNUNET_TIME_relative_get_forever(),
871                                                peer,
872                                                sizeof (struct PONG),
873                                                send_core_pong_cb, pc);
874     else
875       GNUNET_break (0);
876   }
877
878   if ((message->size == ntohs (sizeof (struct PONG))) && (message->type == ntohs (4321)))
879   {
880     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %s %s from peer `%s'\n",
881         "CORE",
882         "PONG",
883         GNUNET_i2s (peer));
884
885   }
886
887   return GNUNET_OK;
888 }
889
890 static void
891 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
892                       const struct GNUNET_ATS_Information *atsi,
893                       unsigned int atsi_count)
894 {
895   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
896   {
897     core_connections ++;
898     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for peer `%s' (%u total)\n",
899       GNUNET_i2s (peer), core_connections);
900     map_connect (peer, ch);
901   }
902   else
903   {
904     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect for myself `%s' (%u total)\n",
905       GNUNET_i2s (peer), core_connections);
906   }
907 }
908
909 static void
910 core_disconnect_cb (void *cls,
911                       const struct
912                       GNUNET_PeerIdentity * peer)
913 {
914   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
915   {
916     GNUNET_assert (core_connections >= 0);
917     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for peer `%s' (%u total)\n",
918       GNUNET_i2s (peer), core_connections);
919     map_disconnect (peer, ch);
920     core_connections --;
921   }
922   else
923   {
924     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect for myself `%s' (%u total)\n",
925       GNUNET_i2s (peer), core_connections);
926   }
927
928 }
929
930 static void
931 core_init_cb (void *cls, struct GNUNET_CORE_Handle *server,
932                    const struct GNUNET_PeerIdentity *my_identity)
933 {
934   my_peer_id = *my_identity;
935   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to core service\n");
936 }
937
938
939 static void
940 init ()
941 {
942   struct TransportPlugin * cur;
943   char *plugs;
944   char *pos;
945   char *secname;
946   int counter;
947   long long unsigned int port;
948
949   have_tcp = GNUNET_NO;
950   have_udp = GNUNET_NO;
951   have_http = GNUNET_NO;
952   have_https = GNUNET_NO;
953   have_unix = GNUNET_NO;
954
955   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (mycfg, "TRANSPORT", "PLUGINS", &plugs))
956     return;
957   counter = 0;
958   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
959   {
960     counter++;
961
962     GNUNET_asprintf(&secname, "transport-%s", pos);
963
964     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (mycfg, secname, "PORT", &port))
965     {
966       GNUNET_free (secname);
967       continue;
968     }
969
970     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport plugin: `%s' port %llu\n"), pos, port);
971     cur = GNUNET_malloc(sizeof (struct TransportPlugin));
972     cur->short_name = strdup (pos);
973     cur->port = port;
974     if (0 == strcmp("tcp", pos))
975     {
976       have_tcp = GNUNET_YES;
977       cur->protocol = tcp;
978     }
979     if (0 == strcmp("udp", pos))
980     {
981       have_udp = GNUNET_YES;
982       cur->protocol = udp;
983     }
984     if (0 == strcmp("http", pos))
985     {
986       have_http = GNUNET_YES;
987       cur->protocol = tcp;
988     }
989     if (0 == strcmp("https", pos))
990     {
991       have_https = GNUNET_YES;
992       cur->protocol = tcp;
993     }
994     if (0 == strcmp("unix", pos))
995     {
996       have_unix = GNUNET_YES;
997       cur->protocol = unixdomain;
998     }
999
1000     GNUNET_CONTAINER_DLL_insert(phead, ptail, cur);
1001     GNUNET_free (secname);
1002   }
1003   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Found %u transport plugins: `%s'\n"),
1004               counter, plugs);
1005
1006   GNUNET_free (plugs);
1007 }
1008
1009 /**
1010  * Main function that will be run by the scheduler.
1011  *
1012  * @param cls closure
1013  * @param args remaining command-line arguments
1014  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1015  * @param cfg configuration
1016  */
1017 static void
1018 run (void *cls, char *const *args, const char *cfgfile,
1019      const struct GNUNET_CONFIGURATION_Handle *cfg)
1020 {
1021   transport_connections = 0;
1022   core_connections = 0;
1023   mycfg = cfg;
1024
1025   init();
1026
1027   stats = GNUNET_STATISTICS_create ("watchdog", cfg);
1028   peers = GNUNET_CONTAINER_multihashmap_create (20);
1029
1030   th = GNUNET_TRANSPORT_connect(cfg, NULL, NULL,
1031                                 &transport_notify_receive_cb,
1032                                 &transport_notify_connect_cb,
1033                                 &transport_notify_disconnect_cb);
1034   GNUNET_assert (th != NULL);
1035   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to transport service\n");
1036   ch =  GNUNET_CORE_connect (cfg, 1, NULL,
1037                              &core_init_cb,
1038                              &core_connect_cb,
1039                              &core_disconnect_cb,
1040                              &core_notify_receive_cb, GNUNET_NO,
1041                              NULL, GNUNET_NO,
1042                              NULL);
1043   GNUNET_assert (ch != NULL);
1044
1045   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, NULL);
1046
1047 }
1048
1049
1050 /**
1051  * The main function.
1052  *
1053  * @param argc number of arguments from the command line
1054  * @param argv command line arguments
1055  * @return 0 ok, 1 on error
1056  */
1057 int
1058 main (int argc, char *const *argv)
1059 {
1060   ping = GNUNET_NO;
1061   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1062    {'p', "ping", NULL, gettext_noop ("Send ping messages to test connectivity (default == NO)"),
1063     GNUNET_NO, &GNUNET_GETOPT_set_one, &ping},
1064     GNUNET_GETOPT_OPTION_END
1065   };
1066   return (GNUNET_OK ==
1067           GNUNET_PROGRAM_run (argc, argv, "cn",
1068                               gettext_noop ("help text"), options, &run,
1069                               NULL)) ? ret : 1;
1070 }
1071
1072 /* end of connection_watchdog.c */