- Attemp to avoid testing hang on ctrl-c during bootstrap
[oweals/gnunet.git] / src / dht / test_dht_multipeer.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  * @file dht/test_dht_multipeer.c
22  * @brief testcase for testing DHT service with
23  *        multiple peers.
24  */
25 #include "platform.h"
26 #include "gnunet_testing_lib.h"
27 #include "gnunet_core_service.h"
28 #include "gnunet_dht_service.h"
29
30 /* DEFINES */
31 #define VERBOSE GNUNET_EXTRA_LOGGING
32
33 /* Timeout for entire testcase */
34 #define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 30)
35
36 /* Timeout for waiting for replies to get requests */
37 #define GET_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 300)
38
39 /* */
40 #define START_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30)
41
42 /* Timeout for waiting for gets to complete */
43 #define GET_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 50)
44
45 /* Timeout for waiting for puts to complete */
46 #define PUT_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 50)
47
48 /* If number of peers not in config file, use this number */
49 #define DEFAULT_NUM_PEERS 10
50
51 #define TEST_DATA_SIZE 8
52
53 #define MAX_OUTSTANDING_PUTS 100
54
55 #define MAX_OUTSTANDING_GETS 100
56
57 #define PATH_TRACKING GNUNET_NO
58
59
60
61 struct TestPutContext
62 {
63   /**
64    * This is a linked list
65    */
66   struct TestPutContext *next;
67
68   /**
69    * This is a linked list
70    */
71   struct TestPutContext *prev;
72
73   /**
74    * Handle to the first peers DHT service (via the API)
75    */
76   struct GNUNET_DHT_Handle *dht_handle;
77
78   /**
79    *  Handle to the PUT peer daemon
80    */
81   struct GNUNET_TESTING_Daemon *daemon;
82
83   /**
84    *  Identifier for this PUT
85    */
86   uint32_t uid;
87
88   /**
89    * Task handle for processing of the put.
90    */
91   GNUNET_SCHEDULER_TaskIdentifier task;
92 };
93
94
95 struct TestGetContext
96 {
97   /**
98    * This is a linked list
99    */
100   struct TestGetContext *next;
101
102   /**
103    * This is a linked list
104    */
105   struct TestGetContext *prev;
106
107   /**
108    * Handle to the first peers DHT service (via the API)
109    */
110   struct GNUNET_DHT_Handle *dht_handle;
111
112   /**
113    * Handle for the DHT get request
114    */
115   struct GNUNET_DHT_GetHandle *get_handle;
116
117   /**
118    *  Handle to the GET peer daemon
119    */
120   struct GNUNET_TESTING_Daemon *daemon;
121
122   /**
123    *  Identifier for this GET
124    */
125   uint32_t uid;
126
127   /**
128    * Task for disconnecting DHT handles (and stopping GET)
129    */
130   GNUNET_SCHEDULER_TaskIdentifier task;
131
132   /**
133    * Whether or not this request has been fulfilled already.
134    */
135   int succeeded;
136 };
137
138
139 /**
140  * List of GETS to perform
141  */
142 static struct TestGetContext *all_gets_head;
143
144 /**
145  * List of GETS to perform
146  */
147 static struct TestGetContext *all_gets_tail;
148
149 /**
150  * List of PUTS to perform
151  */
152 static struct TestPutContext *all_puts_head;
153
154 /**
155  * List of PUTS to perform
156  */
157 static struct TestPutContext *all_puts_tail;
158
159 /**
160  * Handle to the set of all peers run for this test.
161  */
162 static struct GNUNET_TESTING_PeerGroup *pg;
163
164 /**
165  * Total number of peers to run, set based on config file.
166  */
167 static unsigned long long num_peers;
168
169 /**
170  * How many puts do we currently have in flight?
171  */
172 static unsigned long long outstanding_puts;
173
174 /**
175  * How many puts are done?
176  */
177 static unsigned long long puts_completed;
178
179 /**
180  * How many puts do we currently have in flight?
181  */
182 static unsigned long long outstanding_gets;
183
184 /**
185  * How many gets are done?
186  */
187 static unsigned long long gets_completed;
188
189 /**
190  * How many gets failed?
191  */
192 static unsigned long long gets_failed;
193
194 /**
195  * Directory to remove on shutdown.
196  */
197 static char *test_directory;
198
199 /**
200  * Option to use when routing.
201  */
202 static enum GNUNET_DHT_RouteOption route_option;
203
204 /**
205  * Task handle to use to schedule test failure / success.
206  */
207 static GNUNET_SCHEDULER_TaskIdentifier die_task;
208
209 /**
210  * Global return value (0 for success, anything else for failure)
211  */
212 static int ok;
213
214
215 /**
216  * Check whether peers successfully shut down.
217  */
218 static void
219 shutdown_callback (void *cls, const char *emsg)
220 {
221   if (emsg != NULL)
222   {
223     FPRINTF (stderr, "Failed to shutdown testing topology: %s\n", emsg);
224     if (ok == 0)
225       ok = 2;
226   }
227   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Shutdown callback completed.\n");
228 }
229
230 static void
231 do_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
232 {
233   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Shutdown requested.\n");
234   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
235   pg = NULL;
236 }
237
238
239 /**
240  * Master context for 'stat_run'.
241  */
242 struct StatMaster
243 {
244   struct GNUNET_STATISTICS_Handle *stat;
245   unsigned int daemon;
246   unsigned int value;
247 };
248
249 struct StatValues
250 {
251   const char *subsystem;
252   const char *name;
253   unsigned long long total;
254 };
255
256 /**
257  * Statistics we print out.
258  */
259 static struct StatValues stats[] = {
260   {"core", "# bytes decrypted", 0},
261   {"core", "# bytes encrypted", 0},
262   {"core", "# type maps received", 0},
263   {"core", "# session keys confirmed via PONG", 0},
264   {"core", "# entries in session map", 0},
265   {"core", "# key exchanges initiated", 0},
266   {"core", "# send requests dropped (disconnected)", 0},
267   {"core", "# transmissions delayed due to corking", 0},
268   {"core", "# messages discarded (expired prior to transmission)", 0},
269   {"core", "# messages discarded (disconnected)", 0},
270   {"core", "# discarded CORE_SEND requests", 0},
271   {"core", "# discarded lower priority CORE_SEND requests", 0},
272   {"transport", "# bytes received via TCP", 0},
273   {"transport", "# bytes transmitted via TCP", 0},
274   {"dht", "# PUT messages queued for transmission", 0},
275   {"dht", "# P2P PUT requests received", 0},
276   {"dht", "# GET messages queued for transmission", 0},
277   {"dht", "# P2P GET requests received", 0},
278   {"dht", "# RESULT messages queued for transmission", 0},
279   {"dht", "# P2P RESULTS received", 0},
280   {"dht", "# Queued messages discarded (peer disconnected)", 0},
281   {"dht", "# Peers excluded from routing due to Bloomfilter", 0},
282   {"dht", "# Peer selection failed", 0},
283   {"dht", "# FIND PEER requests ignored due to Bloomfilter", 0},
284   {"dht", "# FIND PEER requests ignored due to lack of HELLO", 0},
285   {"dht", "# P2P FIND PEER requests processed", 0},
286   {"dht", "# P2P GET requests ONLY routed", 0},
287   {"dht", "# Preference updates given to core", 0},
288   {"dht", "# REPLIES ignored for CLIENTS (no match)", 0},
289   {"dht", "# GET requests from clients injected", 0},
290   {"dht", "# GET requests received from clients", 0},
291   {"dht", "# GET STOP requests received from clients", 0},
292   {"dht", "# ITEMS stored in datacache", 0},
293   {"dht", "# Good RESULTS found in datacache", 0},
294   {"dht", "# GET requests given to datacache", 0},
295   {NULL, NULL, 0}
296 };
297
298
299 /**
300  * Callback function to process statistic values.
301  *
302  * @param cls closure
303  * @param subsystem name of subsystem that created the statistic
304  * @param name the name of the datum
305  * @param value the current value
306  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
307  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
308  */
309 static int
310 print_stat (void *cls, const char *subsystem, const char *name, uint64_t value,
311             int is_persistent)
312 {
313   struct StatMaster *sm = cls;
314
315   stats[sm->value].total += value;
316   FPRINTF (stderr, "Peer %2u: %12s/%50s = %12llu\n", sm->daemon, subsystem,
317            name, (unsigned long long) value);
318   return GNUNET_OK;
319 }
320
321
322 /**
323  * Function that gathers stats from all daemons.
324  */
325 static void
326 stat_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
327
328
329 /**
330  * Function called when GET operation on stats is done.
331  */
332 static void
333 get_done (void *cls, int success)
334 {
335   struct StatMaster *sm = cls;
336
337   GNUNET_break (GNUNET_OK == success);
338   sm->value++;
339   GNUNET_SCHEDULER_add_now (&stat_run, sm);
340 }
341
342
343 /**
344  * Function that gathers stats from all daemons.
345  */
346 static void
347 stat_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
348 {
349   struct StatMaster *sm = cls;
350   unsigned int i;
351
352   die_task = GNUNET_SCHEDULER_NO_TASK;
353   if (stats[sm->value].name != NULL)
354   {
355     GNUNET_STATISTICS_get (sm->stat,
356 #if 0
357                            NULL, NULL,
358 #else
359                            stats[sm->value].subsystem, stats[sm->value].name,
360 #endif
361                            GNUNET_TIME_UNIT_FOREVER_REL, &get_done, &print_stat,
362                            sm);
363     return;
364   }
365   GNUNET_STATISTICS_destroy (sm->stat, GNUNET_NO);
366   sm->value = 0;
367   sm->daemon++;
368   if (sm->daemon == num_peers)
369   {
370     GNUNET_free (sm);
371     i = 0;
372     while (stats[i].name != NULL)
373     {
374       FPRINTF (stderr, "Total  : %12s/%50s = %12llu\n", stats[i].subsystem,
375                stats[i].name, (unsigned long long) stats[i].total);
376       i++;
377     }
378     die_task = GNUNET_SCHEDULER_add_now (&do_stop, NULL);
379     return;
380   }
381   sm->stat =
382       GNUNET_STATISTICS_create ("<driver>",
383                                 GNUNET_TESTING_daemon_get (pg,
384                                                            sm->daemon)->cfg);
385   die_task = GNUNET_SCHEDULER_add_now (&stat_run, sm);
386 }
387
388
389 /**
390  * Function scheduled to be run on the successful completion of this
391  * testcase.
392  */
393 static void
394 finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
395 {
396   struct TestPutContext *test_put;
397   struct TestGetContext *test_get;
398   struct StatMaster *sm;
399
400   die_task = GNUNET_SCHEDULER_NO_TASK;
401   while (NULL != (test_put = all_puts_head))
402   {
403     if (test_put->task != GNUNET_SCHEDULER_NO_TASK)
404       GNUNET_SCHEDULER_cancel (test_put->task);
405     if (test_put->dht_handle != NULL)
406       GNUNET_DHT_disconnect (test_put->dht_handle);
407     GNUNET_CONTAINER_DLL_remove (all_puts_head, all_puts_tail, test_put);
408     GNUNET_free (test_put);
409   }
410
411   while (NULL != (test_get = all_gets_head))
412   {
413     if (test_get->task != GNUNET_SCHEDULER_NO_TASK)
414       GNUNET_SCHEDULER_cancel (test_get->task);
415     if (test_get->get_handle != NULL)
416       GNUNET_DHT_get_stop (test_get->get_handle);
417     if (test_get->dht_handle != NULL)
418       GNUNET_DHT_disconnect (test_get->dht_handle);
419     GNUNET_CONTAINER_DLL_remove (all_gets_head, all_gets_tail, test_get);
420     GNUNET_free (test_get);
421   }
422   sm = GNUNET_malloc (sizeof (struct StatMaster));
423   sm->stat =
424       GNUNET_STATISTICS_create ("<driver>",
425                                 GNUNET_TESTING_daemon_get (pg,
426                                                            sm->daemon)->cfg);
427   die_task = GNUNET_SCHEDULER_add_now (&stat_run, sm);
428 }
429
430
431 /**
432  * Check if the get_handle is being used, if so stop the request.  Either
433  * way, schedule the end_badly_cont function which actually shuts down the
434  * test.
435  */
436 static void
437 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
438 {
439   const char *emsg = cls;
440   struct TestPutContext *test_put;
441   struct TestGetContext *test_get;
442
443   die_task = GNUNET_SCHEDULER_NO_TASK;
444   FPRINTF (stderr, "Failing test with error: `%s'!\n", emsg);
445   while (NULL != (test_put = all_puts_head))
446   {
447     if (test_put->task != GNUNET_SCHEDULER_NO_TASK)
448       GNUNET_SCHEDULER_cancel (test_put->task);
449     if (test_put->dht_handle != NULL)
450       GNUNET_DHT_disconnect (test_put->dht_handle);
451     GNUNET_CONTAINER_DLL_remove (all_puts_head, all_puts_tail, test_put);
452     GNUNET_free (test_put);
453   }
454
455   while (NULL != (test_get = all_gets_head))
456   {
457     if (test_get->task != GNUNET_SCHEDULER_NO_TASK)
458       GNUNET_SCHEDULER_cancel (test_get->task);
459     if (test_get->get_handle != NULL)
460       GNUNET_DHT_get_stop (test_get->get_handle);
461     if (test_get->dht_handle != NULL)
462       GNUNET_DHT_disconnect (test_get->dht_handle);
463     GNUNET_CONTAINER_DLL_remove (all_gets_head, all_gets_tail, test_get);
464     GNUNET_free (test_get);
465   }
466   ok = 1;
467   /* testing_peergroup will do that in its own end_badly() handler */
468   /*GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); */
469   pg = NULL;
470 }
471
472
473 /**
474  * Task to release get handle.
475  */
476 static void
477 get_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
478 {
479   struct TestGetContext *test_get = cls;
480   GNUNET_HashCode search_key;   /* Key stored under */
481   char original_data[TEST_DATA_SIZE];   /* Made up data to store */
482
483   test_get->task = GNUNET_SCHEDULER_NO_TASK;
484   memset (original_data, test_get->uid, sizeof (original_data));
485   GNUNET_CRYPTO_hash (original_data, TEST_DATA_SIZE, &search_key);
486   if (test_get->succeeded != GNUNET_YES)
487   {
488     gets_failed++;
489     FPRINTF (stderr, "Get from peer %s for key %s failed!\n",
490              GNUNET_i2s (&test_get->daemon->id), GNUNET_h2s (&search_key));
491   }
492   GNUNET_assert (test_get->get_handle != NULL);
493   GNUNET_DHT_get_stop (test_get->get_handle);
494   test_get->get_handle = NULL;
495
496   outstanding_gets--;           /* GET is really finished */
497   GNUNET_DHT_disconnect (test_get->dht_handle);
498   test_get->dht_handle = NULL;
499
500   GNUNET_CONTAINER_DLL_remove (all_gets_head, all_gets_tail, test_get);
501   GNUNET_free (test_get);
502   if ((gets_failed > 10) && (outstanding_gets == 0))
503   {
504     /* Had more than 10% failures */
505     FPRINTF (stderr, "%llu gets succeeded, %llu gets failed!\n", gets_completed,
506              gets_failed);
507     GNUNET_SCHEDULER_cancel (die_task);
508     ok = 1;
509     die_task =
510         GNUNET_SCHEDULER_add_now (&finish_testing, "not all gets succeeded");
511     return;
512   }
513   if ((gets_completed + gets_failed == num_peers * num_peers) && (outstanding_gets == 0))       /* All gets successful */
514   {
515     FPRINTF (stderr, "%llu gets succeeded, %llu gets failed!\n", gets_completed,
516              gets_failed);
517     GNUNET_SCHEDULER_cancel (die_task);
518     ok = 0;
519     die_task = GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
520   }
521 }
522
523
524 /**
525  * Iterator called if the GET request initiated returns a response.
526  *
527  * @param cls closure
528  * @param exp when will this value expire
529  * @param key key of the result
530  * @param type type of the result
531  * @param size number of bytes in data
532  * @param data pointer to the result data
533  */
534 static void
535 get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp,
536                      const GNUNET_HashCode * key,
537                      const struct GNUNET_PeerIdentity *get_path,
538                      unsigned int get_path_length,
539                      const struct GNUNET_PeerIdentity *put_path,
540                      unsigned int put_path_length, enum GNUNET_BLOCK_Type type,
541                      size_t size, const void *data)
542 {
543   struct TestGetContext *test_get = cls;
544   GNUNET_HashCode search_key;   /* Key stored under */
545   char original_data[TEST_DATA_SIZE];   /* Made up data to store */
546
547   memset (original_data, test_get->uid, sizeof (original_data));
548   GNUNET_CRYPTO_hash (original_data, TEST_DATA_SIZE, &search_key);
549   if (test_get->succeeded == GNUNET_YES)
550     return;                     /* Get has already been successful, probably ending now */
551
552 #if PATH_TRACKING
553   if (put_path != NULL)
554   {
555     unsigned int i;
556
557     FPRINTF (stderr, "PUT (%u) Path: ", test_get->uid);
558     for (i = 0; i < put_path_length; i++)
559       FPRINTF (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&put_path[i]));
560     FPRINTF (stderr, "%s",  "\n");
561   }
562   if (get_path != NULL)
563   {
564     unsigned int i;
565
566     FPRINTF (stderr, "GET (%u) Path: ", test_get->uid);
567     for (i = 0; i < get_path_length; i++)
568       FPRINTF (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&get_path[i]));
569     FPRINTF (stderr, "%s%s\n", get_path_length > 0 ? "->" : "",
570              GNUNET_i2s (&test_get->daemon->id));
571   }
572 #endif
573
574   if ((0 != memcmp (&search_key, key, sizeof (GNUNET_HashCode))) ||
575       (0 != memcmp (original_data, data, sizeof (original_data))))
576   {
577     FPRINTF (stderr, "%s",  "Key or data is not the same as was inserted!\n");
578     return;
579   }
580   gets_completed++;
581   test_get->succeeded = GNUNET_YES;
582   GNUNET_SCHEDULER_cancel (test_get->task);
583   test_get->task = GNUNET_SCHEDULER_add_now (&get_stop_task, test_get);
584 }
585
586
587 /**
588  * Set up some data, and call API PUT function
589  */
590 static void
591 do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
592 {
593   struct TestGetContext *test_get = cls;
594   GNUNET_HashCode key;          /* Made up key to store data under */
595   char data[TEST_DATA_SIZE];    /* Made up data to store */
596
597   if (outstanding_gets > MAX_OUTSTANDING_GETS)
598   {
599     test_get->task =
600         GNUNET_SCHEDULER_add_delayed (GET_DELAY, &do_get, test_get);
601     return;
602   }
603   memset (data, test_get->uid, sizeof (data));
604   GNUNET_CRYPTO_hash (data, TEST_DATA_SIZE, &key);
605   test_get->dht_handle = GNUNET_DHT_connect (test_get->daemon->cfg, 10);
606   GNUNET_assert (test_get->dht_handle != NULL);
607   outstanding_gets++;
608   test_get->get_handle =
609       GNUNET_DHT_get_start (test_get->dht_handle, GNUNET_TIME_UNIT_FOREVER_REL,
610                             GNUNET_BLOCK_TYPE_TEST, &key, 1, route_option, NULL,
611                             0, &get_result_iterator, test_get);
612   test_get->task =
613       GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &get_stop_task, test_get);
614 }
615
616
617 /**
618  * Task to release DHT handles for PUT
619  */
620 static void
621 put_disconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
622 {
623   struct TestPutContext *test_put = cls;
624
625   test_put->task = GNUNET_SCHEDULER_NO_TASK;
626   GNUNET_DHT_disconnect (test_put->dht_handle);
627   test_put->dht_handle = NULL;
628   GNUNET_CONTAINER_DLL_remove (all_puts_head, all_puts_tail, test_put);
629   GNUNET_free (test_put);
630 }
631
632
633 /**
634  * Schedule the GET requests
635  */
636 static void
637 start_gets (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
638 {
639   unsigned long long i;
640   unsigned long long j;
641   struct TestGetContext *test_get;
642
643 #if VERBOSE
644   FPRINTF (stderr, "Issuing %llu GETs\n",
645            (unsigned long long) (num_peers * num_peers));
646 #endif
647   for (i = 0; i < num_peers; i++)
648     for (j = 0; j < num_peers; j++)
649     {
650       test_get = GNUNET_malloc (sizeof (struct TestGetContext));
651       test_get->uid = i + j * num_peers;
652       test_get->daemon = GNUNET_TESTING_daemon_get (pg, j);
653       GNUNET_CONTAINER_DLL_insert (all_gets_head, all_gets_tail, test_get);
654       test_get->task = GNUNET_SCHEDULER_add_now (&do_get, test_get);
655     }
656 }
657
658
659 /**
660  * Called when the PUT request has been transmitted to the DHT service.
661  */
662 static void
663 put_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
664 {
665   struct TestPutContext *test_put = cls;
666
667   outstanding_puts--;
668   puts_completed++;
669   if (GNUNET_SCHEDULER_NO_TASK != test_put->task)
670   {
671     GNUNET_SCHEDULER_cancel (test_put->task);
672   }
673   test_put->task = GNUNET_SCHEDULER_add_now (&put_disconnect_task, test_put);
674   if (puts_completed != num_peers * num_peers)
675     return;
676
677   GNUNET_assert (outstanding_puts == 0);
678   GNUNET_SCHEDULER_add_delayed (START_DELAY, &start_gets, NULL);
679 }
680
681
682 /**
683  * Set up some data, and call API PUT function
684  */
685 static void
686 do_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
687 {
688   struct TestPutContext *test_put = cls;
689   GNUNET_HashCode key;          /* Made up key to store data under */
690   char data[TEST_DATA_SIZE];    /* Made up data to store */
691
692   test_put->task = GNUNET_SCHEDULER_NO_TASK;
693   if (outstanding_puts > MAX_OUTSTANDING_PUTS)
694   {
695     test_put->task =
696         GNUNET_SCHEDULER_add_delayed (PUT_DELAY, &do_put, test_put);
697     return;
698   }
699   memset (data, test_put->uid, sizeof (data));
700   GNUNET_CRYPTO_hash (data, TEST_DATA_SIZE, &key);
701   test_put->dht_handle = GNUNET_DHT_connect (test_put->daemon->cfg, 10);
702   GNUNET_assert (test_put->dht_handle != NULL);
703   outstanding_puts++;
704 #if VERBOSE > 2
705   FPRINTF (stderr, "PUT %u at `%s'\n", test_put->uid,
706            GNUNET_i2s (&test_put->daemon->id));
707 #endif
708   GNUNET_DHT_put (test_put->dht_handle, &key, 1, route_option,
709                   GNUNET_BLOCK_TYPE_TEST, sizeof (data), data,
710                   GNUNET_TIME_UNIT_FOREVER_ABS, GNUNET_TIME_UNIT_FOREVER_REL,
711                   &put_finished, test_put);
712   test_put->task =
713       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
714                                     &put_disconnect_task, test_put);
715 }
716
717
718 static void
719 run_dht_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
720 {
721   unsigned long long i;
722   struct TestPutContext *test_put;
723
724   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
725   {
726     ok = 1;
727     return;
728   }
729 #if PATH_TRACKING
730   route_option =
731       GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE;
732 #else
733   route_option = GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE;
734 #endif
735   die_task =
736       GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
737                                     "from setup puts/gets");
738   FPRINTF (stderr, "Issuing %llu PUTs (one per peer)\n",
739            (unsigned long long) (num_peers * num_peers));
740   for (i = 0; i < num_peers * num_peers; i++)
741   {
742     test_put = GNUNET_malloc (sizeof (struct TestPutContext));
743     test_put->uid = i;
744     test_put->daemon = GNUNET_TESTING_daemon_get (pg, i % num_peers);
745     test_put->task = GNUNET_SCHEDULER_add_now (&do_put, test_put);
746     GNUNET_CONTAINER_DLL_insert (all_puts_head, all_puts_tail, test_put);
747   }
748 }
749
750
751 /**
752  * This function is called once testing has finished setting up the topology.
753  *
754  * @param cls unused
755  * @param emsg variable is NULL on success (peers connected), and non-NULL on
756  * failure (peers failed to connect).
757  */
758 static void
759 startup_done (void *cls, const char *emsg)
760 {
761   if (emsg != NULL)
762   {
763     FPRINTF (stderr, "Failed to setup topology: %s\n", emsg);
764     die_task = GNUNET_SCHEDULER_add_now (&end_badly, "topology setup failed");
765     return;
766   }
767   die_task =
768       GNUNET_SCHEDULER_add_delayed (START_DELAY, &run_dht_test,
769                                     "from setup puts/gets");
770 }
771
772
773 static void
774 run (void *cls, char *const *args, const char *cfgfile,
775      const struct GNUNET_CONFIGURATION_Handle *cfg)
776 {
777   /* Get path from configuration file */
778   if (GNUNET_YES !=
779       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
780                                              &test_directory))
781   {
782     GNUNET_break (0);
783     ok = 404;
784     return;
785   }
786   if (GNUNET_SYSERR ==
787       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
788                                              &num_peers))
789     num_peers = DEFAULT_NUM_PEERS;
790   pg = GNUNET_TESTING_peergroup_start (cfg, num_peers, TIMEOUT, NULL,
791                                        &startup_done, NULL, NULL);
792   GNUNET_assert (NULL != pg);
793   GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_FOREVER_REL,
794                                &do_stop, NULL);
795 }
796
797
798 static int
799 check ()
800 {
801   int ret;
802
803   /* Arguments for GNUNET_PROGRAM_run */
804   char *const argv[] = { "test-dht-multipeer",  /* Name to give running binary */
805     "-c",
806     "test_dht_multipeer_data.conf",     /* Config file to use */
807 #if VERBOSE
808     "-L", "DEBUG",
809 #endif
810     NULL
811   };
812   struct GNUNET_GETOPT_CommandLineOption options[] = {
813     GNUNET_GETOPT_OPTION_END
814   };
815   /* Run the run function as a new program */
816   ret =
817       GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
818                           "test-dht-multipeer", "nohelp", options, &run, &ok);
819   if (ret != GNUNET_OK)
820   {
821     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
822                 "`test-dht-multipeer': Failed with error code %d\n", ret);
823   }
824   return ok;
825 }
826
827
828 int
829 main (int argc, char *argv[])
830 {
831   int ret;
832
833
834   GNUNET_log_setup ("test-dht-multipeer",
835 #if VERBOSE
836                     "DEBUG",
837 #else
838                     "WARNING",
839 #endif
840                     NULL);
841   ret = check ();
842   /**
843    * Need to remove base directory, subdirectories taken care
844    * of by the testing framework.
845    */
846   if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
847   {
848     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
849                 "Failed to remove testing directory %s\n", test_directory);
850   }
851   return ret;
852 }
853
854 /* end of test_dht_multipeer.c */