2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file experimentation/gnunet-daemon-experimentation.c
23 * @brief experimentation daemon
24 * @author Christian Grothoff
25 * @author Matthias Wachs
28 #include "gnunet_getopt_lib.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_core_service.h"
31 #include "gnunet_statistics_service.h"
33 #define EXP_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
35 static struct GNUNET_CORE_Handle *ch;
37 static struct GNUNET_PeerIdentity me;
39 static struct GNUNET_STATISTICS_Handle *stats;
42 * A experimentation node
46 struct GNUNET_PeerIdentity id;
48 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
50 struct GNUNET_CORE_TransmitHandle *cth;
54 struct Experimentation_Request
56 struct GNUNET_MessageHeader msg;
59 struct Experimentation_Response
61 struct GNUNET_MessageHeader msg;
66 * Nodes with a pending request
69 struct GNUNET_CONTAINER_MultiHashMap *nodes_requested;
72 * Active experimentation nodes
74 struct GNUNET_CONTAINER_MultiHashMap *nodes_active;
77 * Inactive experimentation nodes
78 * To be excluded from future requests
80 struct GNUNET_CONTAINER_MultiHashMap *nodes_inactive;
83 static void update_stats (struct GNUNET_CONTAINER_MultiHashMap *m)
85 GNUNET_assert (NULL != m);
86 GNUNET_assert (NULL != stats);
88 if (m == nodes_active)
90 GNUNET_STATISTICS_set (stats, "# nodes active",
91 GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
93 else if (m == nodes_inactive)
95 GNUNET_STATISTICS_set (stats, "# nodes inactive",
96 GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
98 else if (m == nodes_requested)
100 GNUNET_STATISTICS_set (stats, "# nodes requested",
101 GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
109 cleanup_nodes (void *cls,
110 const struct GNUNET_HashCode * key,
114 struct GNUNET_CONTAINER_MultiHashMap *cur = cls;
117 if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
119 GNUNET_SCHEDULER_cancel (n->timeout_task);
120 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
124 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
129 GNUNET_CONTAINER_multihashmap_remove (cur, key, value);
135 * Task run during shutdown.
141 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
143 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Experimentation daemon shutting down ...\n"));
146 GNUNET_CORE_disconnect (ch);
150 if (NULL != nodes_requested)
152 GNUNET_CONTAINER_multihashmap_iterate (nodes_requested,
155 update_stats (nodes_requested);
156 GNUNET_CONTAINER_multihashmap_destroy (nodes_requested);
157 nodes_requested = NULL;
160 if (NULL != nodes_active)
162 GNUNET_CONTAINER_multihashmap_iterate (nodes_active,
165 update_stats (nodes_active);
166 GNUNET_CONTAINER_multihashmap_destroy (nodes_active);
170 if (NULL != nodes_inactive)
172 GNUNET_CONTAINER_multihashmap_iterate (nodes_inactive,
175 update_stats (nodes_inactive);
176 GNUNET_CONTAINER_multihashmap_destroy (nodes_inactive);
177 nodes_inactive = NULL;
182 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
187 static int is_me (const struct GNUNET_PeerIdentity *id)
189 if (0 == memcmp (&me, id, sizeof (me)))
196 core_startup_handler (void *cls,
197 struct GNUNET_CORE_Handle * server,
198 const struct GNUNET_PeerIdentity *my_identity)
204 remove_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
206 struct Node *n = cls;
208 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Removing request for peer %s due to timeout\n"),
209 GNUNET_i2s (&n->id));
211 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (nodes_requested, &n->id.hashPubKey))
215 GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &n->id.hashPubKey, n);
216 update_stats (nodes_requested);
217 GNUNET_CONTAINER_multihashmap_put (nodes_inactive, &n->id.hashPubKey, n,
218 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
219 update_stats (nodes_inactive);
220 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
223 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
229 size_t send_request_cb (void *cls, size_t bufsize, void *buf)
231 struct Node *n = cls;
232 struct Experimentation_Request msg;
233 size_t size = sizeof (msg);
238 /* client disconnected */
239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected\n");
240 if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
241 GNUNET_SCHEDULER_cancel (n->timeout_task);
242 GNUNET_SCHEDULER_add_now (&remove_request, n);
245 GNUNET_assert (bufsize >= size);
247 msg.msg.size = htons (size);
248 msg.msg.type = htons (GNUNET_MESSAGE_TYPE_EXPERIMENTATION_REQUEST);
249 memcpy (buf, &msg, size);
251 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending request to peer %s\n"),
252 GNUNET_i2s (&n->id));
256 static void send_request (const struct GNUNET_PeerIdentity *peer)
261 size = sizeof (struct Experimentation_Request);
262 n = GNUNET_malloc (sizeof (struct Node));
264 n->timeout_task = GNUNET_SCHEDULER_add_delayed (EXP_RESPONSE_TIMEOUT, &remove_request, n);
265 n->cth = GNUNET_CORE_notify_transmit_ready(ch, GNUNET_NO, 0,
266 GNUNET_TIME_relative_get_forever_(),
267 peer, size, send_request_cb, n);
269 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (nodes_requested,
270 &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
272 update_stats (nodes_requested);
275 size_t send_response_cb (void *cls, size_t bufsize, void *buf)
277 struct Node *n = cls;
278 struct Experimentation_Response msg;
279 size_t size = sizeof (msg);
284 /* client disconnected */
285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected\n");
288 GNUNET_assert (bufsize >= size);
290 msg.msg.size = htons (size);
291 msg.msg.type = htons (GNUNET_MESSAGE_TYPE_EXPERIMENTATION_RESPONSE);
292 memcpy (buf, &msg, size);
294 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending response to peer %s\n"),
295 GNUNET_i2s (&n->id));
300 static void handle_request (const struct GNUNET_PeerIdentity *peer)
304 if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_active, &peer->hashPubKey)))
306 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
307 "REQUEST", "active", GNUNET_i2s (peer));
309 else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_requested, &peer->hashPubKey)))
311 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
312 "REQUEST", "requested", GNUNET_i2s (peer));
313 GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &peer->hashPubKey, n);
314 if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
316 GNUNET_SCHEDULER_cancel (n->timeout_task);
317 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
321 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
324 update_stats (nodes_requested);
325 GNUNET_CONTAINER_multihashmap_put (nodes_active,
326 &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
327 update_stats (nodes_active);
328 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
331 else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_inactive, &peer->hashPubKey)))
333 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
334 "REQUEST", "inactive", GNUNET_i2s (peer));
335 GNUNET_CONTAINER_multihashmap_remove (nodes_inactive, &peer->hashPubKey, n);
336 update_stats (nodes_inactive);
337 GNUNET_CONTAINER_multihashmap_put (nodes_active,
338 &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
339 update_stats (nodes_active);
340 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
345 /* Create new node */
346 n = GNUNET_malloc (sizeof (struct Node));
348 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
349 "REQUEST", "new", GNUNET_i2s (peer));
350 GNUNET_CONTAINER_multihashmap_put (nodes_active,
351 &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
352 update_stats (nodes_active);
353 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
357 n->cth = GNUNET_CORE_notify_transmit_ready(ch, GNUNET_NO, 0,
358 GNUNET_TIME_relative_get_forever_(),
359 peer, sizeof (struct Experimentation_Response),
360 send_response_cb, n);
363 static void handle_response (const struct GNUNET_PeerIdentity *peer)
367 if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_active, &peer->hashPubKey)))
369 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
370 "RESPONSE", "active", GNUNET_i2s (peer));
372 else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_requested, &peer->hashPubKey)))
374 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
375 "RESPONSE", "requested", GNUNET_i2s (peer));
376 GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &peer->hashPubKey, n);
377 if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
379 GNUNET_SCHEDULER_cancel (n->timeout_task);
380 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
384 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
387 update_stats (nodes_requested);
388 GNUNET_CONTAINER_multihashmap_put (nodes_active,
389 &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
390 update_stats (nodes_active);
391 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
394 else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_inactive, &peer->hashPubKey)))
396 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from peer `%s'\n"),
397 "RESPONSE", "inactive", GNUNET_i2s (peer));
398 GNUNET_CONTAINER_multihashmap_remove (nodes_inactive, &peer->hashPubKey, n);
399 update_stats (nodes_inactive);
400 GNUNET_CONTAINER_multihashmap_put (nodes_active,
401 &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
402 update_stats (nodes_active);
403 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' active node \n"),
408 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
409 "RESPONSE", "unknown", GNUNET_i2s (peer));
414 * Method called whenever a given peer connects.
417 * @param peer peer identity this notification is about
419 void core_connect_handler (void *cls,
420 const struct GNUNET_PeerIdentity *peer)
422 if (GNUNET_YES == is_me(peer))
425 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connected to peer %s\n"),
428 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_requested, &peer->hashPubKey))
429 return; /* We already sent a request */
431 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_active, &peer->hashPubKey))
432 return; /* This peer is known as active */
434 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_inactive, &peer->hashPubKey))
435 return; /* This peer is known as inactive */
443 * Method called whenever a given peer disconnects.
446 * @param peer peer identity this notification is about
448 void core_disconnect_handler (void *cls,
449 const struct GNUNET_PeerIdentity * peer)
451 if (GNUNET_YES == is_me(peer))
454 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Disconnected from peer %s\n"),
461 core_receive_handler (void *cls,
462 const struct GNUNET_PeerIdentity *other,
463 const struct GNUNET_MessageHeader *message)
465 if (ntohs (message->size) < sizeof (struct GNUNET_MessageHeader))
468 return GNUNET_SYSERR;
471 switch (ntohs (message->type)) {
472 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_REQUEST:
473 handle_request (other);
475 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_RESPONSE:
476 handle_response (other);
488 * The main function for the experimentation daemon.
490 * @param argc number of arguments from the command line
491 * @param argv command line arguments
494 run (void *cls, char *const *args, const char *cfgfile,
495 const struct GNUNET_CONFIGURATION_Handle *cfg)
497 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Experimentation daemon starting ...\n"));
499 stats = GNUNET_STATISTICS_create ("experimentation", cfg);
502 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to create statistics!\n"));
506 /* Connecting to core service to find partners */
507 ch = GNUNET_CORE_connect (cfg, NULL,
508 &core_startup_handler,
509 &core_connect_handler,
510 &core_disconnect_handler,
511 &core_receive_handler,
512 GNUNET_NO, NULL, GNUNET_NO, NULL);
515 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Failed to connect to CORE service!\n"));
519 nodes_requested = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
520 nodes_active = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
521 nodes_inactive = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
523 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
530 * The main function for the experimentation daemon.
532 * @param argc number of arguments from the command line
533 * @param argv command line arguments
534 * @return 0 ok, 1 on error
537 main (int argc, char *const *argv)
539 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
540 GNUNET_GETOPT_OPTION_END
544 GNUNET_PROGRAM_run (argc, argv, "experimentation",
545 _("GNUnet hostlist server and client"), options,
546 &run, NULL)) ? 0 : 1;
549 /* end of gnunet-daemon-experimentation.c */