adding capabilities
[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         memcpy (buf, &msg, size);
170
171         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending request to peer %s\n"),
172                         GNUNET_i2s (&n->id));
173         return size;
174 }
175
176 static void send_request (const struct GNUNET_PeerIdentity *peer)
177 {
178         struct Node *n;
179         size_t size;
180
181         size = sizeof (struct Experimentation_Request);
182         n = GNUNET_malloc (sizeof (struct Node));
183         n->id = *peer;
184         n->timeout_task = GNUNET_SCHEDULER_add_delayed (EXP_RESPONSE_TIMEOUT, &remove_request, n);
185         n->cth = GNUNET_CORE_notify_transmit_ready(ch, GNUNET_NO, 0,
186                                                                 GNUNET_TIME_relative_get_forever_(),
187                                                                 peer, size, send_request_cb, n);
188
189         GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (nodes_requested,
190                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
191
192         update_stats (nodes_requested);
193 }
194
195 size_t send_response_cb (void *cls, size_t bufsize, void *buf)
196 {
197         struct Node *n = cls;
198         struct Experimentation_Response msg;
199         size_t size = sizeof (msg);
200
201         n->cth = NULL;
202   if (buf == NULL)
203   {
204     /* client disconnected */
205     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected\n");
206     return 0;
207   }
208   GNUNET_assert (bufsize >= size);
209
210         msg.msg.size = htons (size);
211         msg.msg.type = htons (GNUNET_MESSAGE_TYPE_EXPERIMENTATION_RESPONSE);
212         memcpy (buf, &msg, size);
213
214         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending response to peer %s\n"),
215                         GNUNET_i2s (&n->id));
216         return size;
217 }
218
219
220 static void handle_request (const struct GNUNET_PeerIdentity *peer)
221 {
222         struct Node *n;
223
224         if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_active, &peer->hashPubKey)))
225         {
226                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
227                                         "REQUEST", "active", GNUNET_i2s (peer));
228         }
229         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_requested, &peer->hashPubKey)))
230         {
231                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
232                                         "REQUEST", "requested", GNUNET_i2s (peer));
233                         GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &peer->hashPubKey, n);
234                         if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
235                         {
236                                 GNUNET_SCHEDULER_cancel (n->timeout_task);
237                                 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
238                         }
239                         if (NULL != n->cth)
240                         {
241                                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
242                                 n->cth = NULL;
243                         }
244                         update_stats (nodes_requested);
245                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
246                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
247                         update_stats (nodes_active);
248                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
249                                         GNUNET_i2s (peer));
250         }
251         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_inactive, &peer->hashPubKey)))
252         {
253                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
254                                         "REQUEST", "inactive", GNUNET_i2s (peer));
255                         GNUNET_CONTAINER_multihashmap_remove (nodes_inactive, &peer->hashPubKey, n);
256                         update_stats (nodes_inactive);
257                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
258                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
259                         update_stats (nodes_active);
260                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
261                                         GNUNET_i2s (peer));
262         }
263         else
264         {
265                         /* Create new node */
266                         n = GNUNET_malloc (sizeof (struct Node));
267                         n->id = *peer;
268                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
269                                         "REQUEST", "new", GNUNET_i2s (peer));
270                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
271                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
272                         update_stats (nodes_active);
273                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
274                                         GNUNET_i2s (peer));
275         }
276
277         n->cth = GNUNET_CORE_notify_transmit_ready (ch, GNUNET_NO, 0,
278                                                                 GNUNET_TIME_relative_get_forever_(),
279                                                                 peer, sizeof (struct Experimentation_Response),
280                                                                 send_response_cb, n);
281 }
282
283 static void handle_response (const struct GNUNET_PeerIdentity *peer)
284 {
285         struct Node *n;
286
287         if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_active, &peer->hashPubKey)))
288         {
289                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
290                                         "RESPONSE", "active", GNUNET_i2s (peer));
291         }
292         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_requested, &peer->hashPubKey)))
293         {
294                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
295                                         "RESPONSE", "requested", GNUNET_i2s (peer));
296                         GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &peer->hashPubKey, n);
297                         if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
298                         {
299                                 GNUNET_SCHEDULER_cancel (n->timeout_task);
300                                 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
301                         }
302                         if (NULL != n->cth)
303                         {
304                                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
305                                 n->cth = NULL;
306                         }
307                         update_stats (nodes_requested);
308                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
309                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
310                         update_stats (nodes_active);
311                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
312                                         GNUNET_i2s (peer));
313         }
314         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_inactive, &peer->hashPubKey)))
315         {
316                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from peer `%s'\n"),
317                                         "RESPONSE", "inactive", GNUNET_i2s (peer));
318                         GNUNET_CONTAINER_multihashmap_remove (nodes_inactive, &peer->hashPubKey, n);
319                         update_stats (nodes_inactive);
320                   GNUNET_CONTAINER_multihashmap_put (nodes_active,
321                                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
322                         update_stats (nodes_active);
323                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
324                                         GNUNET_i2s (peer));
325         }
326         else
327         {
328                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
329                                         "RESPONSE", "unknown", GNUNET_i2s (peer));
330         }
331 }
332
333 /**
334  * Method called whenever a given peer connects.
335  *
336  * @param cls closure
337  * @param peer peer identity this notification is about
338  */
339 void core_connect_handler (void *cls,
340                            const struct GNUNET_PeerIdentity *peer)
341 {
342         if (GNUNET_YES == is_me(peer))
343                 return;
344
345         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connected to peer %s\n"),
346                         GNUNET_i2s (peer));
347
348         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_requested, &peer->hashPubKey))
349                 return; /* We already sent a request */
350
351         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_active, &peer->hashPubKey))
352                 return; /* This peer is known as active  */
353
354         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_inactive, &peer->hashPubKey))
355                 return; /* This peer is known as inactive  */
356
357         send_request (peer);
358
359 }
360
361
362 /**
363  * Method called whenever a given peer disconnects.
364  *
365  * @param cls closure
366  * @param peer peer identity this notification is about
367  */
368 void core_disconnect_handler (void *cls,
369                            const struct GNUNET_PeerIdentity * peer)
370 {
371         if (GNUNET_YES == is_me(peer))
372                 return;
373
374         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Disconnected from peer %s\n"),
375                         GNUNET_i2s (peer));
376
377 }
378
379
380 static int
381 core_receive_handler (void *cls,
382                                                                                         const struct GNUNET_PeerIdentity *other,
383                                                                                         const struct GNUNET_MessageHeader *message)
384 {
385         if (ntohs (message->size) < sizeof (struct GNUNET_MessageHeader))
386         {
387                         GNUNET_break (0);
388                         return GNUNET_SYSERR;
389         }
390
391         switch (ntohs (message->type)) {
392                 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_REQUEST:
393                         handle_request (other);
394                         break;
395                 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_RESPONSE:
396                         handle_response (other);
397                         break;
398                 default:
399                         break;
400         }
401
402         return GNUNET_OK;
403 }
404
405
406
407 /**
408  * Start the nodes management
409  *
410  * @param cfg configuration handle
411  */
412 void
413 GNUNET_EXPERIMENTATION_nodes_start ()
414 {
415         /* Connecting to core service to find partners */
416         ch = GNUNET_CORE_connect (GSE_cfg, NULL,
417                                                                                                                 &core_startup_handler,
418                                                                                                                 &core_connect_handler,
419                                                                                                                 &core_disconnect_handler,
420                                                                                                                 &core_receive_handler,
421                                                                                                                 GNUNET_NO, NULL, GNUNET_NO, NULL);
422         if (NULL == ch)
423         {
424                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Failed to connect to CORE service!\n"));
425                         return;
426         }
427
428         nodes_requested = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
429         nodes_active = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
430         nodes_inactive = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
431 }
432
433 /**
434  * Stop the nodes management
435  */
436 void
437 GNUNET_EXPERIMENTATION_nodes_stop ()
438 {
439   if (NULL != ch)
440   {
441                 GNUNET_CORE_disconnect (ch);
442                 ch = NULL;
443   }
444
445   if (NULL != nodes_requested)
446   {
447                 GNUNET_CONTAINER_multihashmap_iterate (nodes_requested,
448                                                                                                                                                                          &cleanup_nodes,
449                                                                                                                                                                          nodes_requested);
450                 update_stats (nodes_requested);
451                 GNUNET_CONTAINER_multihashmap_destroy (nodes_requested);
452                 nodes_requested = NULL;
453   }
454
455   if (NULL != nodes_active)
456   {
457                 GNUNET_CONTAINER_multihashmap_iterate (nodes_active,
458                                                                                                                                                                          &cleanup_nodes,
459                                                                                                                                                                          nodes_active);
460                 update_stats (nodes_active);
461                 GNUNET_CONTAINER_multihashmap_destroy (nodes_active);
462                 nodes_active = NULL;
463   }
464
465   if (NULL != nodes_inactive)
466   {
467                 GNUNET_CONTAINER_multihashmap_iterate (nodes_inactive,
468                                                                                                                                                                          &cleanup_nodes,
469                                                                                                                                                                          nodes_inactive);
470                 update_stats (nodes_inactive);
471                 GNUNET_CONTAINER_multihashmap_destroy (nodes_inactive);
472                 nodes_inactive = NULL;
473   }
474 }
475
476 /* end of gnunet-daemon-experimentation_nodes.c */