refactoring since names are too long
[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
35 /**
36  * Core handle
37  */
38 static struct GNUNET_CORE_Handle *ch;
39
40
41 /**
42  * Peer's own identity
43  */
44 static struct GNUNET_PeerIdentity me;
45
46
47 /**
48  * Nodes with a pending request
49  */
50 struct GNUNET_CONTAINER_MultiHashMap *nodes_requested;
51
52
53 /**
54  * Active experimentation nodes
55  */
56 struct GNUNET_CONTAINER_MultiHashMap *nodes_active;
57
58
59 /**
60  * Inactive experimentation nodes
61  * To be excluded from future requests
62  */
63 struct GNUNET_CONTAINER_MultiHashMap *nodes_inactive;
64
65
66 /**
67  * Update statistics
68  *
69  * @param m hashmap to update values from
70  */
71 static void update_stats (struct GNUNET_CONTAINER_MultiHashMap *m)
72 {
73         GNUNET_assert (NULL != m);
74         GNUNET_assert (NULL != GSE_stats);
75
76         if (m == nodes_active)
77         {
78                         GNUNET_STATISTICS_set (GSE_stats, "# nodes active",
79                                         GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
80         }
81         else if (m == nodes_inactive)
82         {
83                         GNUNET_STATISTICS_set (GSE_stats, "# nodes inactive",
84                                         GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
85         }
86         else if (m == nodes_requested)
87         {
88                         GNUNET_STATISTICS_set (GSE_stats, "# nodes requested",
89                                         GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
90         }
91         else
92                 GNUNET_break (0);
93
94 }
95
96
97 /**
98  * Clean up nodes
99  *
100  * @param cls the hashmap to clean up
101  * @param key key of the current node
102  * @param value related node object
103  * @return always GNUNET_OK
104  */
105 static int
106 cleanup_nodes (void *cls,
107                                                          const struct GNUNET_HashCode * key,
108                                                          void *value)
109 {
110         struct Node *n;
111         struct GNUNET_CONTAINER_MultiHashMap *cur = cls;
112
113         n = value;
114         if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
115         {
116                 GNUNET_SCHEDULER_cancel (n->timeout_task);
117                 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
118         }
119         if (NULL != n->cth)
120         {
121                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
122                 n->cth = NULL;
123         }
124         GNUNET_free_non_null (n->issuer_id);
125
126         GNUNET_CONTAINER_multihashmap_remove (cur, key, value);
127         GNUNET_free (value);
128         return GNUNET_OK;
129 }
130
131
132 /**
133  * Check if id passed is my id
134  *
135  * @param id the id to check
136  * @return GNUNET_YES or GNUNET_NO
137  */
138 static int is_me (const struct GNUNET_PeerIdentity *id)
139 {
140         if (0 == memcmp (&me, id, sizeof (me)))
141                 return GNUNET_YES;
142         else
143                 return GNUNET_NO;
144 }
145
146 /**
147  * Core startup callback
148  *
149  * @param cls unused
150  * @param server core service's server handle
151  * @param my_identity my id
152  */
153 static void
154 core_startup_handler (void *cls,
155                                                                                         struct GNUNET_CORE_Handle *server,
156                       const struct GNUNET_PeerIdentity *my_identity)
157 {
158         me = *my_identity;
159 }
160
161
162 /**
163  * Remove experimentation request due to timeout
164  *
165  * @param cls the related node
166  * @param tc scheduler's task context
167  */
168 static void
169 remove_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
170 {
171         struct Node *n = cls;
172
173         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Removing request for peer %s due to timeout\n"),
174                         GNUNET_i2s (&n->id));
175
176         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_requested, &n->id.hashPubKey))
177         {
178                         GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &n->id.hashPubKey, n);
179                         update_stats (nodes_requested);
180                         GNUNET_CONTAINER_multihashmap_put (nodes_inactive, &n->id.hashPubKey, n,
181                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
182                         update_stats (nodes_inactive);
183         }
184
185         n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
186         if (NULL != n->cth)
187         {
188                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
189                 n->cth = NULL;
190         }
191 }
192
193
194 /**
195  * Core's transmit notify callback to send request
196  *
197  * @param cls the related node
198  * @param bufsize buffer size
199  * @param buf the buffer to copy to
200  * @return bytes passed
201  */
202 size_t send_experimentation_request_cb (void *cls, size_t bufsize, void *buf)
203 {
204         struct Node *n = cls;
205         struct Experimentation_Request msg;
206         size_t msg_size = sizeof (msg);
207         size_t ri_size = sizeof (struct Experimentation_Issuer) * GSE_my_issuer_count;
208         size_t total_size = msg_size + ri_size;
209
210         memset (buf, '0', bufsize);
211         n->cth = NULL;
212   if (buf == NULL)
213   {
214     /* client disconnected */
215     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected\n");
216     if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
217                 GNUNET_SCHEDULER_cancel (n->timeout_task);
218     GNUNET_SCHEDULER_add_now (&remove_request, n);
219     return 0;
220   }
221   GNUNET_assert (bufsize >= total_size);
222
223         msg.msg.size = htons (total_size);
224         msg.msg.type = htons (GNUNET_MESSAGE_TYPE_EXPERIMENTATION_REQUEST);
225         msg.capabilities = htonl (GSE_node_capabilities);
226         msg.issuer_count = htonl (GSE_my_issuer_count);
227         memcpy (buf, &msg, msg_size);
228         memcpy (&((char *) buf)[msg_size], GSE_my_issuer, ri_size);
229
230         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending request to peer %s\n"),
231                         GNUNET_i2s (&n->id));
232         return total_size;
233 }
234
235
236 /**
237  * Send request to peer to start add him to to the set of experimentation nodes
238  *
239  * @param peer the peer to send to
240  */
241 static void send_experimentation_request (const struct GNUNET_PeerIdentity *peer)
242 {
243         struct Node *n;
244         size_t size;
245         size_t c_issuers;
246
247         c_issuers = GSE_my_issuer_count;
248
249         size = sizeof (struct Experimentation_Request) +
250                                  c_issuers * sizeof (struct Experimentation_Issuer);
251         n = GNUNET_malloc (sizeof (struct Node));
252         n->id = *peer;
253         n->timeout_task = GNUNET_SCHEDULER_add_delayed (EXP_RESPONSE_TIMEOUT, &remove_request, n);
254         n->capabilities = NONE;
255         n->cth = GNUNET_CORE_notify_transmit_ready(ch, GNUNET_NO, 0,
256                                                                 GNUNET_TIME_relative_get_forever_(),
257                                                                 peer, size, send_experimentation_request_cb, n);
258
259         GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (nodes_requested,
260                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
261         update_stats (nodes_requested);
262 }
263
264
265 /**
266  * Core's transmit notify callback to send response
267  *
268  * @param cls the related node
269  * @param bufsize buffer size
270  * @param buf the buffer to copy to
271  * @return bytes passed
272  */
273 size_t send_response_cb (void *cls, size_t bufsize, void *buf)
274 {
275         struct Node *n = cls;
276         struct Experimentation_Response msg;
277         size_t ri_size = GSE_my_issuer_count * sizeof (struct Experimentation_Issuer);
278         size_t msg_size = sizeof (msg);
279         size_t total_size = msg_size + ri_size;
280
281         n->cth = NULL;
282   if (buf == NULL)
283   {
284     /* client disconnected */
285     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected\n");
286     return 0;
287   }
288   GNUNET_assert (bufsize >= total_size);
289
290         msg.msg.size = htons (total_size);
291         msg.msg.type = htons (GNUNET_MESSAGE_TYPE_EXPERIMENTATION_RESPONSE);
292         msg.capabilities = htonl (GSE_node_capabilities);
293         msg.issuer_count = htonl (GSE_my_issuer_count);
294         memcpy (buf, &msg, msg_size);
295         memcpy (&((char *) buf)[msg_size], GSE_my_issuer, ri_size);
296
297         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending response to peer %s\n"),
298                         GNUNET_i2s (&n->id));
299         return total_size;
300 }
301
302
303 static void
304 get_experiments_cb (struct Node *n, struct Experiment *e)
305 {
306         static int counter = 0;
307         if (NULL == e)
308         {
309                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added %u experiments for peer %s\n"),
310                                         counter, GNUNET_i2s (&n->id));
311                         return;
312         }
313
314         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting experiment `%s' with peer %s\n"),
315                         e->name,
316                         GNUNET_i2s (&n->id));
317
318         /* Tell the scheduler to add a node with an experiment */
319         GED_scheduler_add (n, e);
320         counter ++;
321 }
322
323 /**
324  * Set a specific node as active
325  *
326  * @param n the node
327  */
328 static void node_make_active (struct Node *n)
329 {
330         int c1;
331   GNUNET_CONTAINER_multihashmap_put (nodes_active,
332                         &n->id.hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
333         update_stats (nodes_active);
334         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Added peer `%s' as active node\n"),
335                         GNUNET_i2s (&n->id));
336
337         /* Request experiments for this node to start them */
338         for (c1 = 0; c1 < n->issuer_count; c1++)
339         {
340
341                 GED_experiments_get (n, &n->issuer_id[c1], &get_experiments_cb);
342         }
343 }
344
345
346 /**
347  * Handle a request and send a response
348  *
349  * @param peer the source
350  * @param message the message
351  */
352 static void handle_request (const struct GNUNET_PeerIdentity *peer,
353                                                                                                                 const struct GNUNET_MessageHeader *message)
354 {
355         struct Node *n;
356         struct Experimentation_Request *rm = (struct Experimentation_Request *) message;
357         struct Experimentation_Issuer *rmi = (struct Experimentation_Issuer *) &rm[1];
358         int c1;
359         int c2;
360         uint32_t ic;
361         uint32_t ic_accepted;
362         int make_active;
363
364         if (ntohs (message->size) < sizeof (struct Experimentation_Request))
365         {
366                 GNUNET_break (0);
367                 return;
368         }
369         ic = ntohl (rm->issuer_count);
370         if (ntohs (message->size) != sizeof (struct Experimentation_Request) + ic * sizeof (struct Experimentation_Issuer))
371         {
372                 GNUNET_break (0);
373                 return;
374         }
375
376         make_active = GNUNET_NO;
377         if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_active, &peer->hashPubKey)))
378         {
379                         /* Nothing to do */
380         }
381         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_requested, &peer->hashPubKey)))
382         {
383                         GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &peer->hashPubKey, n);
384                         if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
385                         {
386                                 GNUNET_SCHEDULER_cancel (n->timeout_task);
387                                 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
388                         }
389                         if (NULL != n->cth)
390                         {
391                                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
392                                 n->cth = NULL;
393                         }
394                         update_stats (nodes_requested);
395                         make_active = GNUNET_YES;
396         }
397         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_inactive, &peer->hashPubKey)))
398         {
399                         GNUNET_CONTAINER_multihashmap_remove (nodes_inactive, &peer->hashPubKey, n);
400                         update_stats (nodes_inactive);
401                         make_active = GNUNET_YES;
402         }
403         else
404         {
405                         /* Create new node */
406                         n = GNUNET_malloc (sizeof (struct Node));
407                         n->id = *peer;
408                         n->capabilities = NONE;
409                         make_active = GNUNET_YES;
410         }
411
412         /* Update node */
413         n->capabilities = ntohl (rm->capabilities);
414
415         /* Filter accepted issuer */
416         ic_accepted = 0;
417         for (c1 = 0; c1 < ic; c1++)
418         {
419                 if (GNUNET_YES == GED_experiments_issuer_accepted(&rmi[c1].issuer_id))
420                         ic_accepted ++;
421         }
422         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Request from peer `%s' with %u issuers, we accepted %u issuer \n"),
423                         GNUNET_i2s (peer), ic, ic_accepted);
424         GNUNET_free_non_null (n->issuer_id);
425         n->issuer_id = GNUNET_malloc (ic_accepted * sizeof (struct GNUNET_PeerIdentity));
426         c2 = 0;
427         for (c1 = 0; c1 < ic; c1++)
428         {
429                         if (GNUNET_YES == GED_experiments_issuer_accepted(&rmi[c1].issuer_id))
430                         {
431                                 n->issuer_id[c2] = rmi[c1].issuer_id;
432                                 c2 ++;
433                         }
434         }
435         n->issuer_count = ic_accepted;
436
437         if (GNUNET_YES == make_active)
438                 node_make_active (n);
439
440         /* Send response */
441         n->cth = GNUNET_CORE_notify_transmit_ready (ch, GNUNET_NO, 0,
442                                                                 GNUNET_TIME_relative_get_forever_(),
443                                                                 peer,
444                                                                 sizeof (struct Experimentation_Response) +
445                                                                 GSE_my_issuer_count * sizeof (struct Experimentation_Issuer),
446                                                                 send_response_cb, n);
447 }
448
449
450 /**
451  * Handle a response
452  *
453  * @param peer the source
454  * @param message the message
455  */
456 static void handle_response (const struct GNUNET_PeerIdentity *peer,
457                                                                                                                  const struct GNUNET_MessageHeader *message)
458 {
459         struct Node *n;
460         struct Experimentation_Response *rm = (struct Experimentation_Response *) message;
461         struct Experimentation_Issuer *rmi = (struct Experimentation_Issuer *) &rm[1];
462         uint32_t ic;
463         uint32_t ic_accepted;
464         int make_active;
465         unsigned int c1;
466         unsigned int c2;
467
468
469         if (ntohs (message->size) < sizeof (struct Experimentation_Response))
470         {
471                 GNUNET_break (0);
472                 return;
473         }
474         ic = ntohl (rm->issuer_count);
475         if (ntohs (message->size) != sizeof (struct Experimentation_Response) + ic * sizeof (struct Experimentation_Issuer))
476         {
477                 GNUNET_break (0);
478                 return;
479         }
480
481         make_active = GNUNET_NO;
482         if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_active, &peer->hashPubKey)))
483         {
484                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
485                                         "RESPONSE", "active", GNUNET_i2s (peer));
486         }
487         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_requested, &peer->hashPubKey)))
488         {
489                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
490                                         "RESPONSE", "requested", GNUNET_i2s (peer));
491                         GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &peer->hashPubKey, n);
492                         if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
493                         {
494                                 GNUNET_SCHEDULER_cancel (n->timeout_task);
495                                 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
496                         }
497                         if (NULL != n->cth)
498                         {
499                                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
500                                 n->cth = NULL;
501                         }
502                         update_stats (nodes_requested);
503                         make_active = GNUNET_YES;
504         }
505         else if (NULL != (n = GNUNET_CONTAINER_multihashmap_get (nodes_inactive, &peer->hashPubKey)))
506         {
507                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from peer `%s'\n"),
508                                         "RESPONSE", "inactive", GNUNET_i2s (peer));
509                         GNUNET_CONTAINER_multihashmap_remove (nodes_inactive, &peer->hashPubKey, n);
510                         update_stats (nodes_inactive);
511                         make_active = GNUNET_YES;
512         }
513         else
514         {
515                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Received %s from %s peer `%s'\n"),
516                                         "RESPONSE", "unknown", GNUNET_i2s (peer));
517                         return;
518         }
519
520         /* Update */
521         n->capabilities = ntohl (rm->capabilities);
522
523         /* Filter accepted issuer */
524         ic_accepted = 0;
525         for (c1 = 0; c1 < ic; c1++)
526         {
527                 if (GNUNET_YES == GED_experiments_issuer_accepted(&rmi[c1].issuer_id))
528                         ic_accepted ++;
529         }
530         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Response from peer `%s' with %u issuers, we accepted %u issuer \n"),
531                         GNUNET_i2s (peer), ic, ic_accepted);
532         GNUNET_free_non_null (n->issuer_id);
533         n->issuer_id = GNUNET_malloc (ic_accepted * sizeof (struct GNUNET_PeerIdentity));
534         c2 = 0;
535         for (c1 = 0; c1 < ic; c1++)
536         {
537                         if (GNUNET_YES == GED_experiments_issuer_accepted(&rmi[c1].issuer_id))
538                         {
539                                 n->issuer_id[c2] = rmi[c1].issuer_id;
540                                 c2 ++;
541                         }
542         }
543         n->issuer_count = ic_accepted;
544
545         if (GNUNET_YES == make_active)
546                 node_make_active (n);
547 }
548
549 /**
550  * Handle a response
551  *
552  * @param peer the source
553  * @param message the message
554  */
555 static void handle_start (const struct GNUNET_PeerIdentity *peer,
556                                                                                                                  const struct GNUNET_MessageHeader *message)
557 {
558         GED_scheduler_handle_start (NULL, NULL);
559 }
560
561 /**
562  * Handle a response
563  *
564  * @param peer the source
565  * @param message the message
566  */
567 static void handle_start_ack (const struct GNUNET_PeerIdentity *peer,
568                                                                                                                  const struct GNUNET_MessageHeader *message)
569 {
570         GED_scheduler_handle_start_ack (NULL, NULL);
571 }
572
573 /**
574  * Handle a response
575  *
576  * @param peer the source
577  * @param message the message
578  */
579 static void handle_stop (const struct GNUNET_PeerIdentity *peer,
580                                                                                                                  const struct GNUNET_MessageHeader *message)
581 {
582         GED_scheduler_handle_stop (NULL, NULL);
583 }
584
585 /**
586  * Method called whenever a given peer connects.
587  *
588  * @param cls closure
589  * @param peer peer identity this notification is about
590  */
591 void core_connect_handler (void *cls,
592                            const struct GNUNET_PeerIdentity *peer)
593 {
594         if (GNUNET_YES == is_me(peer))
595                 return;
596
597         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connected to peer %s\n"),
598                         GNUNET_i2s (peer));
599
600         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_requested, &peer->hashPubKey))
601                 return; /* We already sent a request */
602
603         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_active, &peer->hashPubKey))
604                 return; /* This peer is known as active  */
605
606         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_inactive, &peer->hashPubKey))
607                 return; /* This peer is known as inactive  */
608
609         send_experimentation_request (peer);
610
611 }
612
613
614 /**
615  * Method called whenever a given peer disconnects.
616  *
617  * @param cls closure
618  * @param peer peer identity this notification is about
619  */
620 void core_disconnect_handler (void *cls,
621                            const struct GNUNET_PeerIdentity * peer)
622 {
623         if (GNUNET_YES == is_me(peer))
624                 return;
625
626         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Disconnected from peer %s\n"),
627                         GNUNET_i2s (peer));
628
629 }
630
631
632 /**
633  * Handle a request and send a response
634  *
635  * @param cls unused
636  * @param other the sender
637  * @param message the message
638  * @return GNUNET_OK to keep connection, GNUNET_SYSERR on error
639  */
640 static int
641 core_receive_handler (void *cls,
642                                                                                         const struct GNUNET_PeerIdentity *other,
643                                                                                         const struct GNUNET_MessageHeader *message)
644 {
645         if (ntohs (message->size) < sizeof (struct GNUNET_MessageHeader))
646         {
647                         GNUNET_break (0);
648                         return GNUNET_SYSERR;
649         }
650
651         switch (ntohs (message->type)) {
652                 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_REQUEST:
653                         handle_request (other, message);
654                         break;
655                 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_RESPONSE:
656                         handle_response (other, message);
657                         break;
658                 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_START:
659                         handle_start (other, message);
660                         break;
661                 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_START_ACK:
662                         handle_start_ack (other, message);
663                         break;
664                 case GNUNET_MESSAGE_TYPE_EXPERIMENTATION_STOP:
665                         handle_stop (other, message);
666                         break;
667                 default:
668                         break;
669         }
670
671         return GNUNET_OK;
672 }
673
674 #define FAST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
675
676 struct GNUNET_EXPERIMENTATION_start_message
677 {
678         struct GNUNET_MessageHeader header;
679 };
680
681 struct ExperimentStartCtx
682 {
683         struct ExperimentStartCtx *prev;
684         struct ExperimentStartCtx *next;
685
686         struct Node *n;
687         struct Experiment *e;
688 };
689
690 size_t node_experiment_start_cb (void *cls, size_t bufsize, void *buf)
691 {
692         struct ExperimentStartCtx *e_ctx = cls;
693         struct GNUNET_EXPERIMENTATION_start_message msg;
694
695         GNUNET_CONTAINER_DLL_remove (e_ctx->n->e_req_head, e_ctx->n->e_req_tail, e_ctx);
696         e_ctx->n->cth = NULL;
697         if (NULL == buf)
698         {
699                 GNUNET_free (e_ctx);
700                 return 0;
701         }
702
703         size_t size = sizeof (struct GNUNET_EXPERIMENTATION_start_message);
704         msg.header.size = htons (size);
705         msg.header.type = htons (GNUNET_MESSAGE_TYPE_EXPERIMENTATION_START);
706
707         memcpy (buf, &msg, size);
708         GNUNET_free (e_ctx);
709         return size;
710 }
711
712 int
713 GED_nodes_rts (struct Node *n)
714 {
715         if (NULL == n->cth)
716                 return GNUNET_YES;
717         else
718                 return GNUNET_NO;
719
720 }
721
722 /**
723  * Request a experiment to start with a node
724  *
725  * @return GNUNET_NO if core was busy with sending, GNUNET_OK otherwise
726  */
727 int
728 GED_nodes_request_start (struct Node *n, struct Experiment *e)
729 {
730         struct ExperimentStartCtx *e_ctx;
731
732         if (NULL != n->cth)
733         {
734                 GNUNET_break (0); /* should call rts before */
735                 return GNUNET_NO;
736         }
737
738         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending experiment start request to peer `%s' for experiment `%s'\n"),
739                         GNUNET_i2s(&n->id), e->name);
740
741         e_ctx = GNUNET_malloc (sizeof (struct ExperimentStartCtx));
742         e_ctx->n = n;
743         e_ctx->e = e;
744         n->cth = GNUNET_CORE_notify_transmit_ready (ch, GNUNET_NO, 0, FAST_TIMEOUT, &n->id,
745                         sizeof (struct GNUNET_EXPERIMENTATION_start_message),
746                         &node_experiment_start_cb, e_ctx);
747         if (NULL == n->cth)
748         {
749                 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Cannot send experiment start request to peer `%s' for experiment `%s'\n"),
750                                 GNUNET_i2s(&n->id), e->name);
751                 GNUNET_free (e_ctx);
752         }
753         GNUNET_CONTAINER_DLL_insert (n->e_req_head, n->e_req_tail, e_ctx);
754
755         return GNUNET_OK;
756 }
757
758
759 /**
760  * Start the nodes management
761  */
762 void
763 GED_nodes_start ()
764 {
765         /* Connecting to core service to find partners */
766         ch = GNUNET_CORE_connect (GSE_cfg, NULL,
767                                                                                                                 &core_startup_handler,
768                                                                                                                 &core_connect_handler,
769                                                                                                                 &core_disconnect_handler,
770                                                                                                                 &core_receive_handler,
771                                                                                                                 GNUNET_NO, NULL, GNUNET_NO, NULL);
772         if (NULL == ch)
773         {
774                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Failed to connect to CORE service!\n"));
775                         return;
776         }
777
778         nodes_requested = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
779         nodes_active = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
780         nodes_inactive = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
781 }
782
783
784 /**
785  * Stop the nodes management
786  */
787 void
788 GED_nodes_stop ()
789 {
790   if (NULL != ch)
791   {
792                 GNUNET_CORE_disconnect (ch);
793                 ch = NULL;
794   }
795
796   if (NULL != nodes_requested)
797   {
798                 GNUNET_CONTAINER_multihashmap_iterate (nodes_requested,
799                                                                                                                                                                          &cleanup_nodes,
800                                                                                                                                                                          nodes_requested);
801                 update_stats (nodes_requested);
802                 GNUNET_CONTAINER_multihashmap_destroy (nodes_requested);
803                 nodes_requested = NULL;
804   }
805
806   if (NULL != nodes_active)
807   {
808                 GNUNET_CONTAINER_multihashmap_iterate (nodes_active,
809                                                                                                                                                                          &cleanup_nodes,
810                                                                                                                                                                          nodes_active);
811                 update_stats (nodes_active);
812                 GNUNET_CONTAINER_multihashmap_destroy (nodes_active);
813                 nodes_active = NULL;
814   }
815
816   if (NULL != nodes_inactive)
817   {
818                 GNUNET_CONTAINER_multihashmap_iterate (nodes_inactive,
819                                                                                                                                                                          &cleanup_nodes,
820                                                                                                                                                                          nodes_inactive);
821                 update_stats (nodes_inactive);
822                 GNUNET_CONTAINER_multihashmap_destroy (nodes_inactive);
823                 nodes_inactive = NULL;
824   }
825 }
826
827 /* end of gnunet-daemon-experimentation_nodes.c */