improving code and build system to be in line with gnunet access control model for...
[oweals/gnunet.git] / src / dht / test_dht_monitor.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 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  * @file dht/test_dht_monitor.c
22  *
23  * @brief Test for the dht service: store, retrieve and monitor in a 2d_torus.
24  * TODO: update this description
25  * Each peer stores it own ID in the DHT and then a different peer tries to
26  * retrieve that key from it. The GET starts after a first round of PUTS has
27  * been made. Periodically, each peer stores its ID into the DHT. If after
28  * a timeout no result has been returned, the test fails.
29  */
30 #include "platform.h"
31 #include "gnunet_testing_lib.h"
32 #include "gnunet_dht_service.h"
33
34 #define VERBOSE 1
35
36 #define REMOVE_DIR GNUNET_YES
37
38
39 /**
40  * How long until we give up on connecting the peers?
41  */
42 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500)
43
44 #define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
45
46 #define PUT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
47
48 static int ok;
49
50 /**
51  * Be verbose
52  */
53 static int verbose;
54
55 /**
56  * Total number of peers in the test.
57  */
58 static unsigned long long num_peers;
59
60 /**
61  * Global configuration file
62  */
63 static struct GNUNET_CONFIGURATION_Handle *testing_cfg;
64
65 /**
66  * Total number of currently running peers.
67  */
68 static unsigned long long peers_running;
69
70 /**
71  * Total number of connections in the whole network.
72  */
73 static unsigned int total_connections;
74
75 /**
76  * The currently running peer group.
77  */
78 static struct GNUNET_TESTING_PeerGroup *pg;
79
80 /**
81  * File to report results to.
82  */
83 static struct GNUNET_DISK_FileHandle *output_file;
84
85 /**
86  * File to log connection info, statistics to.
87  */
88 static struct GNUNET_DISK_FileHandle *data_file;
89
90 /**
91  * Task called to disconnect peers.
92  */
93 static GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
94
95 /**
96  * Task To perform tests
97  */
98 static GNUNET_SCHEDULER_TaskIdentifier test_task;
99
100 /**
101  * Task to do DHT_puts
102  */
103 static GNUNET_SCHEDULER_TaskIdentifier put_task;
104
105 /**
106  * Task called to shutdown test.
107  */
108 static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle;
109
110 static char *topology_file;
111
112 struct GNUNET_TESTING_Daemon *d1;
113
114 struct GNUNET_TESTING_Daemon *d2;
115
116 struct GNUNET_DHT_Handle **hs;
117
118 struct GNUNET_DHT_MonitorHandle **mhs;
119
120 struct GNUNET_DHT_GetHandle *get_h_far;
121
122 const char *id_origin = "FC74";
123 const char *id_far = "KPST";
124
125 unsigned int i_origin;
126 unsigned int i_far;
127
128 struct GNUNET_TESTING_Daemon *d_far;
129 struct GNUNET_TESTING_Daemon *o;
130
131 unsigned int monitor_counter;
132
133 int in_test;
134
135 /**
136  * Check whether peers successfully shut down.
137  */
138 static void
139 shutdown_callback (void *cls, const char *emsg)
140 {
141   if (emsg != NULL)
142   {
143 #if VERBOSE
144     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Shutdown of peers failed!\n");
145 #endif
146     ok++;
147   }
148   else
149   {
150 #if VERBOSE
151     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
152                 "test: All peers successfully shut down!\n");
153 #endif
154   }
155 }
156
157
158 static void
159 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
160 {
161 #if VERBOSE
162   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Ending test.\n");
163 #endif
164
165   if (disconnect_task != GNUNET_SCHEDULER_NO_TASK)
166   {
167     GNUNET_SCHEDULER_cancel (disconnect_task);
168     disconnect_task = GNUNET_SCHEDULER_NO_TASK;
169   }
170
171   if (data_file != NULL)
172     GNUNET_DISK_file_close (data_file);
173   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
174   GNUNET_CONFIGURATION_destroy (testing_cfg);
175 }
176
177
178 static void
179 disconnect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
180 {
181   unsigned int i;
182
183   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: disconnecting peers\n");
184   disconnect_task = GNUNET_SCHEDULER_NO_TASK;
185   GNUNET_SCHEDULER_cancel (put_task);
186   if (NULL != get_h_far)
187     GNUNET_DHT_get_stop (get_h_far);
188   for (i = 0; i < num_peers; i++)
189   {
190     GNUNET_DHT_disconnect (hs[i]);
191   }
192   GNUNET_SCHEDULER_cancel (shutdown_handle);
193   shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
194 }
195
196 static void
197 dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp,
198                     const GNUNET_HashCode * key,
199                     const struct GNUNET_PeerIdentity *get_path,
200                     unsigned int get_path_length,
201                     const struct GNUNET_PeerIdentity *put_path,
202                     unsigned int put_path_length, enum GNUNET_BLOCK_Type type,
203                     size_t size, const void *data)
204 {
205   int i;
206
207   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
208               "test: ************* FOUND!!! ***********\n");
209   if (sizeof (GNUNET_HashCode) == size)
210   {
211     const GNUNET_HashCode *h = data;
212
213     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   Contents: %s\n",
214                 GNUNET_h2s_full (h));
215
216   }
217   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: PATH: (get %u, put %u)\n",
218               get_path_length, put_path_length);
219   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   LOCAL\n");
220   for (i = get_path_length - 1; i >= 0; i--)
221   {
222     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   %s\n",
223                 GNUNET_i2s (&get_path[i]));
224   }
225   for (i = put_path_length - 1; i >= 0; i--)
226   {
227     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:   %s\n",
228                 GNUNET_i2s (&put_path[i]));
229   }
230   if (monitor_counter >= get_path_length + put_path_length)
231   {
232     ok = 0;
233     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "expected at least %u hops, got %u\n",
234                 get_path_length + put_path_length, monitor_counter);
235   }
236   else
237     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "expected at least %u hops, got %u\n",
238                 get_path_length + put_path_length, monitor_counter);
239   GNUNET_SCHEDULER_cancel (disconnect_task);
240   disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL);
241 }
242
243 static void
244 do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
245 {
246   in_test = GNUNET_YES;
247   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: test_task\n");
248   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: looking for %s\n",
249               GNUNET_h2s_full (&d_far->id.hashPubKey));
250   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test:        from %s\n",
251               GNUNET_h2s_full (&o->id.hashPubKey));
252   get_h_far = GNUNET_DHT_get_start (hs[i_origin], GNUNET_TIME_UNIT_FOREVER_REL,        /* timeout */
253                                     GNUNET_BLOCK_TYPE_TEST,     /* type */
254                                     &d_far->id.hashPubKey,      /*key to search */
255                                     4U, /* replication level */
256                                     GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, NULL,    /* xquery */
257                                     0,  /* xquery bits */
258                                     &dht_get_id_handler, NULL);
259   GNUNET_SCHEDULER_cancel (disconnect_task);
260   disconnect_task =
261       GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &disconnect_peers, NULL);
262 }
263
264
265 static void
266 put_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
267 {
268   struct GNUNET_TESTING_Daemon *d;
269
270   d = GNUNET_TESTING_daemon_get (pg, i_far);
271   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: putting into DHT: %s\n",
272               GNUNET_h2s_full (&d->id.hashPubKey));
273   GNUNET_DHT_put (hs[i_far], &d->id.hashPubKey, 10U,
274                   GNUNET_DHT_RO_RECORD_ROUTE |
275                   GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
276                   GNUNET_BLOCK_TYPE_TEST, sizeof (struct GNUNET_PeerIdentity),
277                   (const char *) &d->id, GNUNET_TIME_UNIT_FOREVER_ABS,
278                   GNUNET_TIME_UNIT_FOREVER_REL, NULL, NULL);
279
280   put_task = GNUNET_SCHEDULER_add_delayed (PUT_FREQUENCY, &put_id, NULL);
281 }
282
283 /**
284  * Callback called on each request going through the DHT.
285  *
286  * @param cls Closure.
287  * @param mtype Type of the DHT message monitored.
288  * @param exp When will this value expire.
289  * @param key Key of the result/request.
290  * @param get_path Peers on reply path (or NULL if not recorded).
291  * @param get_path_length number of entries in get_path.
292  * @param put_path peers on the PUT path (or NULL if not recorded).
293  * @param put_path_length number of entries in get_path.
294  * @param desired_replication_level Desired replication level.
295  * @param type Type of the result/request.
296  * @param data Pointer to the result data.
297  * @param size Number of bytes in data.
298  */
299 void
300 monitor_dht_cb (void *cls,
301                 uint16_t mtype,
302                 struct GNUNET_TIME_Absolute exp,
303                 const GNUNET_HashCode * key,
304                 const struct GNUNET_PeerIdentity * get_path,
305                 unsigned int get_path_length,
306                 const struct GNUNET_PeerIdentity * put_path,
307                 unsigned int put_path_length,
308                 uint32_t desired_replication_level,
309                 enum GNUNET_DHT_RouteOption options,
310                 enum GNUNET_BLOCK_Type type,
311                 const void *data,
312                 size_t size)
313 {
314   const char *s_key;
315
316   s_key = GNUNET_h2s(key);
317   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
318               "%u got a message of type %u for key %s\n",
319               cls, mtype, s_key);
320
321   if ((mtype == GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET ||
322        mtype == GNUNET_MESSAGE_TYPE_DHT_MONITOR_PUT) &&
323       strncmp (s_key, id_far, 4) == 0 && in_test == GNUNET_YES)
324     monitor_counter++;
325 }
326
327
328 /**
329  * peergroup_ready: start test when all peers are connected
330  * @param cls closure
331  * @param emsg error message
332  */
333 static void
334 peergroup_ready (void *cls, const char *emsg)
335 {
336   struct GNUNET_TESTING_Daemon *d;
337   char *buf;
338   const char *id_aux;
339   int buf_len;
340   unsigned int i;
341
342   if (emsg != NULL)
343   {
344     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
345                 "test: Peergroup callback called with error, aborting test!\n");
346     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Error from testing: `%s'\n",
347                 emsg);
348     ok++;
349     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
350     return;
351   }
352 #if VERBOSE
353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354               "************************************************************\n");
355   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356               "test: Peer Group started successfully!\n");
357   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Have %u connections\n",
358               total_connections);
359 #endif
360
361   if (data_file != NULL)
362   {
363     buf = NULL;
364     buf_len = GNUNET_asprintf (&buf, "CONNECTIONS_0: %u\n", total_connections);
365     if (buf_len > 0)
366       GNUNET_DISK_file_write (data_file, buf, buf_len);
367     GNUNET_free (buf);
368   }
369   peers_running = GNUNET_TESTING_daemons_running (pg);
370
371   GNUNET_assert (peers_running == num_peers);
372   hs = GNUNET_malloc (num_peers * sizeof (struct GNUNET_DHT_Handle *));
373   mhs = GNUNET_malloc (num_peers * sizeof (struct GNUNET_DHT_MonitorHandle *));
374   d_far = o = NULL;
375   for (i = 0; i < num_peers; i++)
376   {
377     d = GNUNET_TESTING_daemon_get (pg, i);
378     hs[i] = GNUNET_DHT_connect (d->cfg, 32);
379     mhs[i] = GNUNET_DHT_monitor_start(hs[i], GNUNET_BLOCK_TYPE_ANY, NULL,
380                                       &monitor_dht_cb, (void *)(long)i);
381     id_aux = GNUNET_i2s (&d->id);
382     if (strcmp (id_aux, id_origin) == 0)
383     {
384       o = d;
385       i_origin = i;
386     }
387     if (strcmp (id_aux, id_far) == 0)
388     {
389       i_far = i;
390       d_far = d;
391     }
392   }
393
394   if ((NULL == o) || (NULL == d_far))
395   {
396     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
397                 "test: Peers not found (hostkey file changed?)\n");
398     GNUNET_SCHEDULER_cancel (disconnect_task);
399     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL);
400     return;
401   }
402   monitor_counter = 0;
403   put_task = GNUNET_SCHEDULER_add_now (&put_id, NULL);
404   test_task =
405       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
406                                     (GNUNET_TIME_UNIT_SECONDS, 2), &do_test,
407                                     NULL);
408   disconnect_task =
409       GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &disconnect_peers, NULL);
410
411 }
412
413
414 /**
415  * Function that will be called whenever two daemons are connected by
416  * the testing library.
417  *
418  * @param cls closure
419  * @param first peer id for first daemon
420  * @param second peer id for the second daemon
421  * @param distance distance between the connected peers
422  * @param first_cfg config for the first daemon
423  * @param second_cfg config for the second daemon
424  * @param first_daemon handle for the first daemon
425  * @param second_daemon handle for the second daemon
426  * @param emsg error message (NULL on success)
427  */
428 static void
429 connect_cb (void *cls, const struct GNUNET_PeerIdentity *first,
430             const struct GNUNET_PeerIdentity *second, uint32_t distance,
431             const struct GNUNET_CONFIGURATION_Handle *first_cfg,
432             const struct GNUNET_CONFIGURATION_Handle *second_cfg,
433             struct GNUNET_TESTING_Daemon *first_daemon,
434             struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg)
435 {
436
437   if (emsg == NULL)
438   {
439     total_connections++;
440     GNUNET_PEER_intern (first);
441     GNUNET_PEER_intern (second);
442   }
443   else
444   {
445     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446                 "test: Problem with new connection (%s)\n", emsg);
447   }
448
449 }
450
451
452 /**
453  * run: load configuration options and schedule test to run (start peergroup)
454  * @param cls closure
455  * @param args argv
456  * @param cfgfile configuration file name (can be NULL)
457  * @param cfg configuration handle
458  */
459 static void
460 run (void *cls, char *const *args, const char *cfgfile,
461      const struct GNUNET_CONFIGURATION_Handle *cfg)
462 {
463   char *temp_str;
464   struct GNUNET_TESTING_Host *hosts;
465   char *data_filename;
466
467   ok = 1;
468   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
469
470   GNUNET_log_setup ("test_dht_monitor",
471 #if VERBOSE
472                     "DEBUG",
473 #else
474                     "WARNING",
475 #endif
476                     NULL);
477
478 #if VERBOSE
479   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Starting daemons.\n");
480   GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing",
481                                          "use_progressbars", "YES");
482 #endif
483
484   if (GNUNET_OK !=
485       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing",
486                                              "num_peers", &num_peers))
487   {
488     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
489                 "Option TESTING:NUM_PEERS is required!\n");
490     return;
491   }
492
493   if (GNUNET_OK !=
494       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "testing",
495                                              "topology_output_file",
496                                              &topology_file))
497   {
498     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
499                 "Option test_dht_2d:topology_output_file is required!\n");
500     return;
501   }
502
503   if (GNUNET_OK ==
504       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "test_dht_2dtorus",
505                                              "data_output_file",
506                                              &data_filename))
507   {
508     data_file =
509         GNUNET_DISK_file_open (data_filename,
510                                GNUNET_DISK_OPEN_READWRITE |
511                                GNUNET_DISK_OPEN_CREATE,
512                                GNUNET_DISK_PERM_USER_READ |
513                                GNUNET_DISK_PERM_USER_WRITE);
514     if (data_file == NULL)
515     {
516       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
517                   data_filename);
518       GNUNET_free (data_filename);
519     }
520   }
521
522   if (GNUNET_YES ==
523       GNUNET_CONFIGURATION_get_value_string (cfg, "test_dht_2dtorus",
524                                              "output_file", &temp_str))
525   {
526     output_file =
527         GNUNET_DISK_file_open (temp_str,
528                                GNUNET_DISK_OPEN_READWRITE |
529                                GNUNET_DISK_OPEN_CREATE,
530                                GNUNET_DISK_PERM_USER_READ |
531                                GNUNET_DISK_PERM_USER_WRITE);
532     if (output_file == NULL)
533       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
534                   temp_str);
535   }
536   GNUNET_free_non_null (temp_str);
537
538   hosts = GNUNET_TESTING_hosts_load (testing_cfg);
539
540   pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT,
541                                        &connect_cb, &peergroup_ready, NULL,
542                                        hosts);
543   GNUNET_assert (pg != NULL);
544   shutdown_handle =
545       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
546                                     &shutdown_task, NULL);
547 }
548
549
550
551 /**
552  * test_dht_monitor command line options
553  */
554 static struct GNUNET_GETOPT_CommandLineOption options[] = {
555   {'V', "verbose", NULL,
556    gettext_noop ("be verbose (print progress information)"),
557    0, &GNUNET_GETOPT_set_one, &verbose},
558   GNUNET_GETOPT_OPTION_END
559 };
560
561
562 /**
563  * Main: start test
564  */
565 int
566 main (int xargc, char *xargv[])
567 {
568   char *const argv[] = { "test-dht-monitor",
569     "-c",
570     "test_dht_2dtorus.conf", // reuse 2dtorus conf file
571 #if VERBOSE
572     "-L", "DEBUG",
573 #endif
574     NULL
575   };
576
577   in_test = GNUNET_NO;
578   GNUNET_PROGRAM_run (sizeof (argv) / sizeof (char *) - 1, argv,
579                       "test_dht_monitor",
580                       gettext_noop ("Test dht monitoring in a small 2D torus."),
581                       options, &run, NULL);
582 #if REMOVE_DIR
583   GNUNET_DISK_directory_remove ("/tmp/test_dht_monitor");
584 #endif
585   if (0 != ok)
586   {
587     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test: FAILED!\n");
588   }
589   return ok;
590 }
591
592 /* end of test_dht_monitor.c */