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