changes
[oweals/gnunet.git] / src / experimentation / gnunet-daemon-experimentation.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.c
23  * @brief experimentation daemon
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
33 #define EXP_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
34
35 static struct GNUNET_CORE_Handle *ch;
36
37 static struct GNUNET_PeerIdentity me;
38
39 static struct GNUNET_STATISTICS_Handle *stats;
40
41 /**
42  * A experimentation node
43  */
44 struct Node
45 {
46         struct GNUNET_PeerIdentity id;
47
48         GNUNET_SCHEDULER_TaskIdentifier timeout_task;
49
50         struct GNUNET_CORE_TransmitHandle *cth;
51 };
52
53
54 struct Experimentation_Request
55 {
56         struct GNUNET_MessageHeader msg;
57 };
58
59 /**
60  * Nodes with a pending request
61  */
62
63 struct GNUNET_CONTAINER_MultiHashMap *nodes_requested;
64
65 /**
66  * Active experimentation nodes
67  */
68 struct GNUNET_CONTAINER_MultiHashMap *nodes_active;
69
70 /**
71  * Inactive experimentation nodes
72  * To be excluded from future requests
73  */
74 struct GNUNET_CONTAINER_MultiHashMap *nodes_inactive;
75
76
77 static void update_stats (struct GNUNET_CONTAINER_MultiHashMap *m)
78 {
79         GNUNET_assert (NULL != m);
80         GNUNET_assert (NULL != stats);
81
82         if (m == nodes_active)
83         {
84                         GNUNET_STATISTICS_set (stats, "# nodes active",
85                                         GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
86         }
87         else if (m == nodes_inactive)
88         {
89                         GNUNET_STATISTICS_set (stats, "# nodes inactive",
90                                         GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
91         }
92         else if (m == nodes_requested)
93         {
94                         GNUNET_STATISTICS_set (stats, "# nodes requested",
95                                         GNUNET_CONTAINER_multihashmap_size(m), GNUNET_NO);
96         }
97         else
98                 GNUNET_break (0);
99
100 }
101
102 static int
103 cleanup_nodes (void *cls,
104                                                          const struct GNUNET_HashCode * key,
105                                                          void *value)
106 {
107         struct Node *n;
108         struct GNUNET_CONTAINER_MultiHashMap *cur = cls;
109
110         n = value;
111         if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
112         {
113                 GNUNET_SCHEDULER_cancel (n->timeout_task);
114                 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
115         }
116         if (NULL != n->cth)
117         {
118                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
119                 n->cth = NULL;
120         }
121
122
123         GNUNET_CONTAINER_multihashmap_remove (cur, key, value);
124         GNUNET_free (value);
125         return GNUNET_OK;
126 }
127
128 /**
129  * Task run during shutdown.
130  *
131  * @param cls unused
132  * @param tc unused
133  */
134 static void
135 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
136 {
137   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Experimentation daemon shutting down ...\n"));
138   if (NULL != ch)
139   {
140                 GNUNET_CORE_disconnect (ch);
141                 ch = NULL;
142   }
143
144   if (NULL != nodes_requested)
145   {
146                 GNUNET_CONTAINER_multihashmap_iterate (nodes_requested,
147                                                                                                                                                                          &cleanup_nodes,
148                                                                                                                                                                          nodes_requested);
149                 update_stats (nodes_requested);
150                 GNUNET_CONTAINER_multihashmap_destroy (nodes_requested);
151                 nodes_requested = NULL;
152   }
153
154   if (NULL != nodes_active)
155   {
156                 GNUNET_CONTAINER_multihashmap_iterate (nodes_active,
157                                                                                                                                                                          &cleanup_nodes,
158                                                                                                                                                                          nodes_active);
159                 update_stats (nodes_active);
160                 GNUNET_CONTAINER_multihashmap_destroy (nodes_active);
161                 nodes_active = NULL;
162   }
163
164   if (NULL != nodes_inactive)
165   {
166                 GNUNET_CONTAINER_multihashmap_iterate (nodes_inactive,
167                                                                                                                                                                          &cleanup_nodes,
168                                                                                                                                                                          nodes_inactive);
169                 update_stats (nodes_inactive);
170                 GNUNET_CONTAINER_multihashmap_destroy (nodes_inactive);
171                 nodes_inactive = NULL;
172   }
173
174   if (NULL != stats)
175   {
176                 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
177                 stats = NULL;
178   }
179 }
180
181 static int is_me (const struct GNUNET_PeerIdentity *id)
182 {
183         if (0 == memcmp (&me, id, sizeof (me)))
184                 return GNUNET_YES;
185         else
186                 return GNUNET_NO;
187 }
188
189 static void
190 core_startup_handler (void *cls,
191                                                                                         struct GNUNET_CORE_Handle * server,
192                       const struct GNUNET_PeerIdentity *my_identity)
193 {
194         me = *my_identity;
195 }
196
197 static void
198 remove_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
199 {
200         struct Node *n = cls;
201
202         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Removing request for peer %s due to timeout\n"),
203                         GNUNET_i2s (&n->id));
204
205         if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (nodes_requested, &n->id.hashPubKey))
206                         GNUNET_break (0);
207         else
208         {
209                         GNUNET_CONTAINER_multihashmap_remove (nodes_requested, &n->id.hashPubKey, n);
210                         update_stats (nodes_requested);
211                         GNUNET_CONTAINER_multihashmap_put (nodes_inactive, &n->id.hashPubKey, n,
212                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
213                         update_stats (nodes_inactive);
214                         n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
215                         if (NULL != n->cth)
216                         {
217                                 GNUNET_CORE_notify_transmit_ready_cancel (n->cth);
218                                 n->cth = NULL;
219                         }
220         }
221 }
222
223
224 size_t send_request_cb (void *cls, size_t bufsize, void *buf)
225 {
226         struct Node *n = cls;
227         struct Experimentation_Request msg;
228         size_t size = sizeof (msg);
229
230         n->cth = NULL;
231   if (buf == NULL)
232   {
233     /* client disconnected */
234     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected\n");
235     if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
236                 GNUNET_SCHEDULER_cancel (n->timeout_task);
237     GNUNET_SCHEDULER_add_now (&remove_request, n);
238     return 0;
239   }
240   GNUNET_assert (bufsize >= size);
241
242         msg.msg.size = htons (size);
243         msg.msg.type = htons (GNUNET_MESSAGE_TYPE_EXPERIMENTATION_REQUEST);
244         memcpy (buf, &msg, size);
245
246         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Sending request to peer %s\n"),
247                         GNUNET_i2s (&n->id));
248         return size;
249 }
250
251 static void send_request (const struct GNUNET_PeerIdentity *peer)
252 {
253         struct Node *n;
254         size_t size;
255
256         size = sizeof (struct Experimentation_Request);
257         n = GNUNET_malloc (sizeof (struct Node));
258         n->id = *peer;
259         n->timeout_task = GNUNET_SCHEDULER_add_delayed (EXP_RESPONSE_TIMEOUT, &remove_request, n);
260         n->cth = GNUNET_CORE_notify_transmit_ready(ch, GNUNET_NO, 0,
261                                                                 GNUNET_TIME_relative_get_forever_(),
262                                                                 peer, size, send_request_cb, n);
263
264         GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (nodes_requested,
265                         &peer->hashPubKey, n, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
266
267         update_stats (nodes_requested);
268 }
269
270 /**
271  * Method called whenever a given peer connects.
272  *
273  * @param cls closure
274  * @param peer peer identity this notification is about
275  */
276 void core_connect_handler (void *cls,
277                            const struct GNUNET_PeerIdentity *peer)
278 {
279         if (GNUNET_YES == is_me(peer))
280                 return;
281
282         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connected to peer %s\n"),
283                         GNUNET_i2s (peer));
284
285         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_requested, &peer->hashPubKey))
286                 return; /* We already sent a request */
287
288         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_active, &peer->hashPubKey))
289                 return; /* This peer is known as active  */
290
291         if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (nodes_inactive, &peer->hashPubKey))
292                 return; /* This peer is known as inactive  */
293
294         send_request (peer);
295
296 }
297
298
299 /**
300  * Method called whenever a given peer disconnects.
301  *
302  * @param cls closure
303  * @param peer peer identity this notification is about
304  */
305 void core_disconnect_handler (void *cls,
306                            const struct GNUNET_PeerIdentity * peer)
307 {
308         if (GNUNET_YES == is_me(peer))
309                 return;
310
311         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Disconnected from peer %s\n"),
312                         GNUNET_i2s (peer));
313
314 }
315
316 /**
317  * The main function for the experimentation daemon.
318  *
319  * @param argc number of arguments from the command line
320  * @param argv command line arguments
321  */
322 static void
323 run (void *cls, char *const *args, const char *cfgfile,
324      const struct GNUNET_CONFIGURATION_Handle *cfg)
325 {
326         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Experimentation daemon starting ...\n"));
327
328         stats = GNUNET_STATISTICS_create ("experimentation", cfg);
329         if (NULL == stats)
330         {
331                 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to create statistics!\n"));
332                 return;
333         }
334
335         /* Connecting to core service to find partners */
336         ch = GNUNET_CORE_connect (cfg, NULL,
337                                                                                                                 &core_startup_handler,
338                                                                                                                 &core_connect_handler,
339                                                                                                                 &core_disconnect_handler,
340                                                                                                                 NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
341         if (NULL == ch)
342         {
343                         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Failed to connect to CORE service!\n"));
344                         return;
345         }
346
347         nodes_requested = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
348         nodes_active = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
349         nodes_inactive = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
350
351   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
352                                 NULL);
353
354 }
355
356
357 /**
358  * The main function for the experimentation daemon.
359  *
360  * @param argc number of arguments from the command line
361  * @param argv command line arguments
362  * @return 0 ok, 1 on error
363  */
364 int
365 main (int argc, char *const *argv)
366 {
367   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
368     GNUNET_GETOPT_OPTION_END
369   };
370
371   return (GNUNET_OK ==
372           GNUNET_PROGRAM_run (argc, argv, "experimentation",
373                                                                                         _("GNUnet hostlist server and client"), options,
374                               &run, NULL)) ? 0 : 1;
375 }
376
377 /* end of gnunet-daemon-experimentation.c */