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