documentation
[oweals/gnunet.git] / src / experimentation / gnunet-daemon-experimentation_nodes.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 /**
22  * @file experimentation/gnunet-daemon-experimentation_nodes.c
23  * @brief experimentation daemon: node management
24  * @author Christian Grothoff
25  * @author Matthias Wachs
26  */
27 #include "platform.h"
28 #include "gnunet_getopt_lib.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_core_service.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet-daemon-experimentation.h"
33
34 static struct GNUNET_CORE_Handle *ch;
35
36 static struct GNUNET_PeerIdentity me;
37
38 static struct GNUNET_STATISTICS_Handle *stats;
39
40 /**
41  * Nodes with a pending request
42  */
43
44 struct GNUNET_CONTAINER_MultiHashMap *nodes_requested;
45
46 /**
47  * Active experimentation nodes
48  */
49 struct GNUNET_CONTAINER_MultiHashMap *nodes_active;
50
51 /**
52  * Inactive experimentation nodes
53  * To be excluded from future requests
54  */
55 struct GNUNET_CONTAINER_MultiHashMap *nodes_inactive;
56
57
58 static void update_stats (struct GNUNET_CONTAINER_MultiHashMap *m)
59 {
60         GNUNET_assert (NULL != m);
61         GNUNET_assert (NULL != stats);
62
63         if (m == nodes_active)
64         {
65                         GNUNET_STATISTICS_set (stats, "# nodes active",
66                                         GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
67         }
68         else if (m == nodes_inactive)
69         {
70                         GNUNET_STATISTICS_set (stats, "# nodes inactive",
71                                         GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
72         }
73         else if (m == nodes_requested)
74         {
75                         GNUNET_STATISTICS_set (stats, "# nodes requested",
76                                         GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
77         }
78         else
79                 GNUNET_break (0);
80
81 }
82
83 static int
84 cleanup_nodes (void *cls,
85                                                          const struct GNUNET_HashCode * key,
86                                                          void *value)
87 {
88         struct Node *n;
89         struct GNUNET_CONTAINER_MultiHashMap *cur = cls;
90
91         n = value;
92         if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
93         {
94                 GNUNET_SCHEDULER_cancel (n->timeout_task);
95                 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
96         }
97         if (NULL != n->cth)
98         {
99                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
100                 n->cth = NULL;
101         }
102
103
104         GNUNET_CONTAINER_multihashmap_remove (cur, key, value);
105         GNUNET_free (value);
106         return GNUNET_OK;
107 }
108
109
110 static int is_me (const struct GNUNET_PeerIdentity *id)
111 {
112         if (0 == memcmp (&me, id, sizeof (me)))
113                 return GNUNET_YES;
114         else
115                 return GNUNET_NO;
116 }
117
118 static void
119 core_startup_handler (void *cls,
120                                                                                         struct GNUNET_CORE_Handle * server,
121                       const struct GNUNET_PeerIdentity *my_identity)
122 {
123         me = *my_identity;
124 }
125
126 static void
127 remove_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
128 {
129         struct Node *n = cls;
130
131         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Removing request for peer %s due to timeout\n"),
132                         GNUNET_i2s (&n->id));
133
134         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_requested, &n->id.hashPubKey))
135         {
136                         GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &n->id.hashPubKey, n);
137                         update_stats (nodes_requested);
138                         GNUNET_CONTAINER_multihashmap_put (nodes_inactive, &n->id.hashPubKey, n,
139                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
140                         update_stats (nodes_inactive);
141         }
142
143         n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
144         if (NULL != n->cth)
145         {
146                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
147                 n->cth = NULL;
148         }
149 }
150
151 size_t send_request_cb (void *cls, size_t bufsize, void *buf)
152 {
153         struct Node *n = cls;
154         struct Experimentation_Request msg;
155         size_t size = sizeof (msg);
156
157         n->cth = NULL;
158   if (buf == NULL)
159   {
160     /* client disconnected */
161     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected\n");
162     if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
163                 GNUNET_SCHEDULER_cancel (n->timeout_task);
164     GNUNET_SCHEDULER_add_now (&remove_request, n);
165     return 0;
166   }
167   GNUNET_assert (bufsize >= size);
168
169         msg.msg.size = htons (size);
170         msg.msg.type = htons (GNUNET_MESSAGE_TYPE_EXPERIMENTATION_REQUEST);
171         memcpy (buf, &msg, size);
172
173         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending request to peer %s\n"),
174                         GNUNET_i2s (&n->id));
175         return size;
176 }
177
178 static void send_request (const struct GNUNET_PeerIdentity *peer)
179 {
180         struct Node *n;
181         size_t size;
182
183         size = sizeof (struct Experimentation_Request);
184         n = GNUNET_malloc (sizeof (struct Node));
185         n->id = *peer;
186         n->timeout_task = GNUNET_SCHEDULER_add_delayed (EXP_RESPONSE_TIMEOUT, &remove_request, n);
187         n->cth = GNUNET_CORE_notify_transmit_ready(ch, GNUNET_NO, 0,
188                                                                 GNUNET_TIME_relative_get_forever_(),
189                                                                 peer, size, send_request_cb, n);
190
191         GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (nodes_requested,
192                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
193
194         update_stats (nodes_requested);
195 }
196
197 size_t send_response_cb (void *cls, size_t bufsize, void *buf)
198 {
199         struct Node *n = cls;
200         struct Experimentation_Response msg;
201         size_t size = sizeof (msg);
202
203         n->cth = NULL;
204   if (buf == NULL)
205   {
206     /* client disconnected */
207     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected\n");
208     return 0;
209   }
210   GNUNET_assert (bufsize >= size);
211
212         msg.msg.size = htons (size);
213         msg.msg.type = htons (GNUNET_MESSAGE_TYPE_EXPERIMENTATION_RESPONSE);
214         memcpy (buf, &msg, size);
215
216         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending response to peer %s\n"),
217                         GNUNET_i2s (&n->id));
218         return size;
219 }
220
221
222 static void handle_request (const struct GNUNET_PeerIdentity *peer)
223 {
224         struct Node *n;
225
226         if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_active, &peer->hashPubKey)))
227         {
228                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
229                                         "REQUEST", "active", GNUNET_i2s (peer));
230         }
231         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_requested, &peer->hashPubKey)))
232         {
233                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
234                                         "REQUEST", "requested", GNUNET_i2s (peer));
235                         GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &peer->hashPubKey, n);
236                         if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
237                         {
238                                 GNUNET_SCHEDULER_cancel (n->timeout_task);
239                                 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
240                         }
241                         if (NULL != n->cth)
242                         {
243                                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
244                                 n->cth = NULL;
245                         }
246                         update_stats (nodes_requested);
247                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
248                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
249                         update_stats (nodes_active);
250                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
251                                         GNUNET_i2s (peer));
252         }
253         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_inactive, &peer->hashPubKey)))
254         {
255                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
256                                         "REQUEST", "inactive", GNUNET_i2s (peer));
257                         GNUNET_CONTAINER_multihashmap_remove (nodes_inactive, &peer->hashPubKey, n);
258                         update_stats (nodes_inactive);
259                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
260                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
261                         update_stats (nodes_active);
262                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
263                                         GNUNET_i2s (peer));
264         }
265         else
266         {
267                         /* Create new node */
268                         n = GNUNET_malloc (sizeof (struct Node));
269                         n->id = *peer;
270                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
271                                         "REQUEST", "new", GNUNET_i2s (peer));
272                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
273                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
274                         update_stats (nodes_active);
275                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
276                                         GNUNET_i2s (peer));
277         }
278
279         n->cth = GNUNET_CORE_notify_transmit_ready (ch, GNUNET_NO, 0,
280                                                                 GNUNET_TIME_relative_get_forever_(),
281                                                                 peer, sizeof (struct Experimentation_Response),
282                                                                 send_response_cb, n);
283 }
284
285 static void handle_response (const struct GNUNET_PeerIdentity *peer)
286 {
287         struct Node *n;
288
289         if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_active, &peer->hashPubKey)))
290         {
291                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
292                                         "RESPONSE", "active", GNUNET_i2s (peer));
293         }
294         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_requested, &peer->hashPubKey)))
295         {
296                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
297                                         "RESPONSE", "requested", GNUNET_i2s (peer));
298                         GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &peer->hashPubKey, n);
299                         if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
300                         {
301                                 GNUNET_SCHEDULER_cancel (n->timeout_task);
302                                 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
303                         }
304                         if (NULL != n->cth)
305                         {
306                                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
307                                 n->cth = NULL;
308                         }
309                         update_stats (nodes_requested);
310                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
311                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
312                         update_stats (nodes_active);
313                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
314                                         GNUNET_i2s (peer));
315         }
316         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_inactive, &peer->hashPubKey)))
317         {
318                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from peer `%s'\n"),
319                                         "RESPONSE", "inactive", GNUNET_i2s (peer));
320                         GNUNET_CONTAINER_multihashmap_remove (nodes_inactive, &peer->hashPubKey, n);
321                         update_stats (nodes_inactive);
322                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
323                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
324                         update_stats (nodes_active);
325                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
326                                         GNUNET_i2s (peer));
327         }
328         else
329         {
330                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
331                                         "RESPONSE", "unknown", GNUNET_i2s (peer));
332         }
333 }
334
335 /**
336  * Method called whenever a given peer connects.
337  *
338  * @param cls closure
339  * @param peer peer identity this notification is about
340  */
341 void core_connect_handler (void *cls,
342                            const struct GNUNET_PeerIdentity *peer)
343 {
344         if (GNUNET_YES == is_me(peer))
345                 return;
346
347         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connected to peer %s\n"),
348                         GNUNET_i2s (peer));
349
350         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_requested, &peer->hashPubKey))
351                 return; /* We already sent a request */
352
353         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_active, &peer->hashPubKey))
354                 return; /* This peer is known as active  */
355
356         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_inactive, &peer->hashPubKey))
357                 return; /* This peer is known as inactive  */
358
359         send_request (peer);
360
361 }
362
363
364 /**
365  * Method called whenever a given peer disconnects.
366  *
367  * @param cls closure
368  * @param peer peer identity this notification is about
369  */
370 void core_disconnect_handler (void *cls,
371                            const struct GNUNET_PeerIdentity * peer)
372 {
373         if (GNUNET_YES == is_me(peer))
374                 return;
375
376         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Disconnected from peer %s\n"),
377                         GNUNET_i2s (peer));
378
379 }
380
381
382 static int
383 core_receive_handler (void *cls,
384                                                                                         const struct GNUNET_PeerIdentity *other,
385                                                                                         const struct GNUNET_MessageHeader *message)
386 {
387         if (ntohs (message->size) < sizeof (struct GNUNET_MessageHeader))
388         {
389                         GNUNET_break (0);
390                         return GNUNET_SYSERR;
391         }
392
393         switch (ntohs (message->type)) {
394                 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_REQUEST:
395                         handle_request (other);
396                         break;
397                 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_RESPONSE:
398                         handle_response (other);
399                         break;
400                 default:
401                         break;
402         }
403
404         return GNUNET_OK;
405 }
406
407
408
409 /**
410  * Start the nodes management
411  *
412  * @param cfg configuration handle
413  */
414 void
415 GNUNET_EXPERIMENTATION_nodes_start (const struct GNUNET_CONFIGURATION_Handle *cfg)
416 {
417         stats = GNUNET_STATISTICS_create ("experimentation", cfg);
418         if (NULL == stats)
419         {
420                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to create statistics!\n"));
421                 return;
422         }
423
424         /* Connecting to core service to find partners */
425         ch = GNUNET_CORE_connect (cfg, NULL,
426                                                                                                                 &core_startup_handler,
427                                                                                                                 &core_connect_handler,
428                                                                                                                 &core_disconnect_handler,
429                                                                                                                 &core_receive_handler,
430                                                                                                                 GNUNET_NO, NULL, GNUNET_NO, NULL);
431         if (NULL == ch)
432         {
433                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Failed to connect to CORE service!\n"));
434                         return;
435         }
436
437         nodes_requested = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
438         nodes_active = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
439         nodes_inactive = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
440 }
441
442 /**
443  * Stop the nodes management
444  */
445 void
446 GNUNET_EXPERIMENTATION_nodes_stop ()
447 {
448   if (NULL != ch)
449   {
450                 GNUNET_CORE_disconnect (ch);
451                 ch = NULL;
452   }
453
454   if (NULL != nodes_requested)
455   {
456                 GNUNET_CONTAINER_multihashmap_iterate (nodes_requested,
457                                                                                                                                                                          &cleanup_nodes,
458                                                                                                                                                                          nodes_requested);
459                 update_stats (nodes_requested);
460                 GNUNET_CONTAINER_multihashmap_destroy (nodes_requested);
461                 nodes_requested = NULL;
462   }
463
464   if (NULL != nodes_active)
465   {
466                 GNUNET_CONTAINER_multihashmap_iterate (nodes_active,
467                                                                                                                                                                          &cleanup_nodes,
468                                                                                                                                                                          nodes_active);
469                 update_stats (nodes_active);
470                 GNUNET_CONTAINER_multihashmap_destroy (nodes_active);
471                 nodes_active = NULL;
472   }
473
474   if (NULL != nodes_inactive)
475   {
476                 GNUNET_CONTAINER_multihashmap_iterate (nodes_inactive,
477                                                                                                                                                                          &cleanup_nodes,
478                                                                                                                                                                          nodes_inactive);
479                 update_stats (nodes_inactive);
480                 GNUNET_CONTAINER_multihashmap_destroy (nodes_inactive);
481                 nodes_inactive = NULL;
482   }
483
484   if (NULL != stats)
485   {
486                 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
487                 stats = NULL;
488   }
489 }
490
491 /* end of gnunet-daemon-experimentation_nodes.c */