- consistency check
[oweals/gnunet.git] / src / integration-tests / connection_watchdog.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file integration-test/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
40 /**
41  * Final status code.
42  */
43 static int ret;
44 static int retry;
45
46 struct GNUNET_TRANSPORT_Handle *th;
47 struct GNUNET_CORE_Handle *ch;
48 struct GNUNET_PeerIdentity my_peer_id;
49
50 static unsigned int transport_connections;
51 static unsigned int core_connections;
52
53 static GNUNET_SCHEDULER_TaskIdentifier check_task;
54
55
56 static struct GNUNET_CONTAINER_MultiHashMap *peers;
57
58 struct PeerContainer
59 {
60   struct GNUNET_PeerIdentity id;
61   int transport_connected;
62   int core_connected;
63 };
64
65
66 int map_check_it (void *cls,
67                   const GNUNET_HashCode * key,
68                   void *value)
69 {
70   int *fail = cls;
71   struct PeerContainer *pc = value;
72   if (pc->core_connected != pc->transport_connected)
73   {
74     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
75      "Inconsistend peer `%s': TRANSPORT %s <-> CORE %s\n",
76      GNUNET_i2s (&pc->id),
77      (GNUNET_YES == pc->transport_connected) ? "YES" : "NO",
78      (GNUNET_YES == pc->core_connected) ? "YES" : "NO");
79     (*fail) ++;
80   }
81
82   return GNUNET_OK;
83 }
84
85
86 int map_cleanup_it (void *cls,
87                   const GNUNET_HashCode * key,
88                   void *value)
89 {
90   struct PeerContainer *pc = value;
91   GNUNET_CONTAINER_multihashmap_remove(peers, key, value);
92   GNUNET_free (pc);
93   return GNUNET_OK;
94 }
95
96 static void
97 map_cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
98 {
99   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_cleanup_it, NULL);
100   GNUNET_CONTAINER_multihashmap_destroy(peers);
101 }
102
103 static void
104 map_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
105 {
106   int fail = 0;
107   check_task = GNUNET_SCHEDULER_NO_TASK;
108   GNUNET_CONTAINER_multihashmap_iterate (peers, &map_check_it, &fail);
109   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
110        "Inconsistent peers after connection consistency check: %u\n", fail);
111
112   if (NULL != cls)
113   {
114     GNUNET_SCHEDULER_add_now (cls, NULL);
115   }
116 }
117
118 static void
119 map_connect (const struct GNUNET_PeerIdentity *peer, void * source)
120 {
121   struct PeerContainer * pc;
122   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
123   {
124     pc = GNUNET_malloc (sizeof (struct PeerContainer));
125     pc->id = *peer;
126     pc->core_connected = GNUNET_NO;
127     pc->transport_connected = GNUNET_NO;
128     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(peers, &peer->hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
129   }
130
131   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
132   if (source == th)
133   {
134     if (GNUNET_NO == pc->transport_connected)
135     {
136       pc->transport_connected = GNUNET_YES;
137     }
138     else
139     {
140       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
141            "%s notified multiple times about for peers `%s' (%s : %s)\n",
142            "TRANSPORT",
143            GNUNET_i2s (&pc->id),
144            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
145       GNUNET_break (0);
146     }
147   }
148   if (source == ch)
149   {
150     if (GNUNET_NO == pc->core_connected)
151     {
152       pc->core_connected = GNUNET_YES;
153     }
154     else
155     {
156       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
157            "%s notified multiple times about for peers `%s' (%s : %s)\n",
158            "CORE",
159            GNUNET_i2s (&pc->id),
160                "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
161       GNUNET_break (0);
162     }
163   }
164   if (GNUNET_SCHEDULER_NO_TASK != check_task)
165     GNUNET_SCHEDULER_cancel(check_task);
166   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
167 }
168
169
170 static void
171 map_disconnect (const struct GNUNET_PeerIdentity * peer, void * source)
172 {
173
174   struct PeerContainer * pc;
175   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(peers, &peer->hashPubKey))
176   {
177     if (source == th)
178     {
179       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
180          "%s disconnect notification for unknown peer `%s'\n",
181          "TRANSPORT", GNUNET_i2s (peer));
182       GNUNET_break (0);
183       return;
184     }
185     if (source == ch)
186     {
187       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
188          "%s disconnect notification for unknown peer `%s'\n",
189          "CORE", GNUNET_i2s (peer));
190       return;
191     }
192   }
193
194   pc = GNUNET_CONTAINER_multihashmap_get(peers, &peer->hashPubKey);
195   if (source == th)
196   {
197     if (GNUNET_YES == pc->transport_connected)
198     {
199       pc->transport_connected = GNUNET_NO;
200     }
201     else
202     {
203       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
204            "%s notified for not connected peer `%s' (%s : %s)\n",
205            "TRANSPORT",
206            GNUNET_i2s (&pc->id),
207            "CORE", (pc->core_connected == GNUNET_YES) ? "yes" : "no");
208       GNUNET_break (0);
209     }
210   }
211   if (source == ch)
212   {
213     if (GNUNET_YES == pc->core_connected)
214     {
215       pc->core_connected = GNUNET_NO;
216     }
217     else
218     {
219       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
220            "%s notified for not connected peer `%s' (%s : %s)\n",
221            "CORE",
222            GNUNET_i2s (&pc->id),
223            "TRANSPORT", (pc->transport_connected == GNUNET_YES) ? "yes" : "no");
224       GNUNET_break (0);
225     }
226   }
227
228   if ((GNUNET_NO == pc->core_connected) && (GNUNET_NO == pc->transport_connected))
229   {
230     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing peer `%s'\n", GNUNET_i2s (&pc->id));
231     GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (peers, &peer->hashPubKey, pc));
232     GNUNET_free (pc);
233   }
234
235   if (GNUNET_SCHEDULER_NO_TASK != check_task)
236     GNUNET_SCHEDULER_cancel(check_task);
237   check_task = GNUNET_SCHEDULER_add_delayed(CHECK_DELAY, &map_check, NULL);
238
239 }
240
241
242 static void
243 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
244 {
245   if (NULL != th)
246   {
247     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from transport service\n");
248     GNUNET_TRANSPORT_disconnect (th);
249     th = NULL;
250   }
251   if (NULL != ch)
252   {
253     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnecting from core service\n");
254     GNUNET_CORE_disconnect (ch);
255     ch = NULL;
256   }
257
258   if (GNUNET_SCHEDULER_NO_TASK != check_task)
259   {
260     GNUNET_SCHEDULER_cancel(check_task);
261     check_task = GNUNET_SCHEDULER_NO_TASK;
262   }
263   check_task = GNUNET_SCHEDULER_add_now (&map_check, &map_cleanup);
264 }
265
266 void
267 transport_notify_connect_cb (void *cls,
268                 const struct GNUNET_PeerIdentity
269                 * peer,
270                 const struct
271                 GNUNET_ATS_Information * ats,
272                 uint32_t ats_count)
273 {
274   transport_connections ++;
275   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT connect notification for peer `%s' (%u total)\n",
276       GNUNET_i2s (peer), transport_connections);
277   map_connect (peer, th);
278 }
279
280 /**
281  * Function called to notify transport users that another
282  * peer disconnected from us.
283  *
284  * @param cls closure
285  * @param peer the peer that disconnected
286  */
287 void
288 transport_notify_disconnect_cb (void *cls,
289                                const struct
290                                GNUNET_PeerIdentity * peer)
291 {
292   GNUNET_assert (transport_connections > 0);
293   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TRANSPORT disconnect notification for peer `%s' (%u total)\n",
294       GNUNET_i2s (peer), transport_connections) ;
295   map_disconnect (peer, th);
296   transport_connections --;
297
298 }
299
300
301 static void
302 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer,
303                       const struct GNUNET_ATS_Information *atsi,
304                       unsigned int atsi_count)
305 {
306   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
307   {
308     core_connections ++;
309     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect notification for peer `%s' (%u total)\n",
310       GNUNET_i2s (peer), core_connections);
311     map_connect (peer, ch);
312   }
313   else
314   {
315     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      connect notification for myself `%s' (%u total)\n",
316       GNUNET_i2s (peer), core_connections);
317   }
318 }
319
320 static void
321 core_disconnect_cb (void *cls,
322                       const struct
323                       GNUNET_PeerIdentity * peer)
324 {
325   if (0 != memcmp (peer, &my_peer_id, sizeof (struct GNUNET_PeerIdentity)))
326   {
327     GNUNET_assert (core_connections >= 0);
328     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect notification for peer `%s' (%u total)\n",
329       GNUNET_i2s (peer), core_connections);
330     map_disconnect (peer, ch);
331     core_connections --;
332   }
333   else
334   {
335     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CORE      disconnect notification for myself `%s' (%u total)\n",
336       GNUNET_i2s (peer), core_connections);
337   }
338
339 }
340
341 static void
342 core_init_cb (void *cls, struct GNUNET_CORE_Handle *server,
343                    const struct GNUNET_PeerIdentity *my_identity)
344 {
345   my_peer_id = *my_identity;
346   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connected to core service\n");
347 }
348
349 /**
350  * Main function that will be run by the scheduler.
351  *
352  * @param cls closure
353  * @param args remaining command-line arguments
354  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
355  * @param cfg configuration
356  */
357 static void
358 run (void *cls, char *const *args, const char *cfgfile,
359      const struct GNUNET_CONFIGURATION_Handle *cfg)
360 {
361   transport_connections = 0;
362   core_connections = 0;
363
364   peers = GNUNET_CONTAINER_multihashmap_create (20);
365
366   th = GNUNET_TRANSPORT_connect(cfg, NULL, NULL, NULL,
367                                 &transport_notify_connect_cb,
368                                 &transport_notify_disconnect_cb);
369   GNUNET_assert (th != NULL);
370   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connected to transport service\n");
371   ch =  GNUNET_CORE_connect (cfg, 1, NULL,
372                              &core_init_cb,
373                              &core_connect_cb,
374                              &core_disconnect_cb,
375                              NULL, GNUNET_NO,
376                              NULL, GNUNET_NO,
377                              NULL);
378   GNUNET_assert (ch != NULL);
379
380   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, NULL);
381 }
382
383
384 /**
385  * The main function.
386  *
387  * @param argc number of arguments from the command line
388  * @param argv command line arguments
389  * @return 0 ok, 1 on error
390  */
391 int
392 main (int argc, char *const *argv)
393 {
394   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
395     /* FIMXE: add options here */
396     GNUNET_GETOPT_OPTION_END
397   };
398   return (GNUNET_OK ==
399           GNUNET_PROGRAM_run (argc, argv, "gnunet-template",
400                               gettext_noop ("help text"), options, &run,
401                               NULL)) ? ret : 1;
402 }
403
404 /* end of connection_watchdog.c */