eb70424a3fc794a7f7b2ca23b917e31678b34510
[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_NO
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,
224              "Failed to shutdown testing topology: %s\n",
225              emsg);
226     if (ok == 0)
227       ok = 2;
228   }
229 }
230
231 static void
232 do_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
233 {
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   {"transport", "# bytes received via TCP", 0},
263   {"transport", "# bytes transmitted via TCP", 0},
264   {"dht", "# PUT messages queued for transmission"},
265   {"dht", "# P2P PUT requests received"},
266   {"dht", "# GET messages queued for transmission"},
267   {"dht", "# P2P GET requests received"},
268   {"dht", "# RESULT messages queued for transmission"},
269   {"dht", "# P2P RESULTS received"},
270   {NULL, NULL}
271 };
272
273
274 /**
275  * Callback function to process statistic values.
276  *
277  * @param cls closure
278  * @param subsystem name of subsystem that created the statistic
279  * @param name the name of the datum
280  * @param value the current value
281  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
282  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
283  */
284 static int
285 print_stat (void *cls, const char *subsystem, const char *name, uint64_t value,
286             int is_persistent)
287 {
288   struct StatMaster *sm = cls;
289
290   stats[sm->value].total += value;
291   fprintf (stderr, "Peer %2u: %12s/%50s = %12llu\n", sm->daemon, subsystem,
292            name, (unsigned long long) value);
293   return GNUNET_OK;
294 }
295
296
297 /**
298  * Function that gathers stats from all daemons.
299  */
300 static void
301 stat_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
302
303
304 /**
305  * Function called when GET operation on stats is done.
306  */
307 static void
308 get_done (void *cls, int success)
309 {
310   struct StatMaster *sm = cls;
311
312   GNUNET_break (GNUNET_OK == success);
313   sm->value++;
314   GNUNET_SCHEDULER_add_now (&stat_run, sm);
315 }
316
317
318 /**
319  * Function that gathers stats from all daemons.
320  */
321 static void
322 stat_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
323 {
324   struct StatMaster *sm = cls;
325   unsigned int i;
326
327   die_task = GNUNET_SCHEDULER_NO_TASK;
328   if (stats[sm->value].name != NULL)
329   {
330     GNUNET_STATISTICS_get (sm->stat,
331 #if 0
332                            NULL, NULL,
333 #else
334                            stats[sm->value].subsystem, stats[sm->value].name,
335 #endif
336                            GNUNET_TIME_UNIT_FOREVER_REL, &get_done, &print_stat,
337                            sm);
338     return;
339   }
340   GNUNET_STATISTICS_destroy (sm->stat, GNUNET_NO);
341   sm->value = 0;
342   sm->daemon++;
343   if (sm->daemon == num_peers)
344   {
345     GNUNET_free (sm);
346     i = 0;
347     while (stats[i].name != NULL)
348       {
349         fprintf (stderr, "Total  : %12s/%50s = %12llu\n", stats[i].subsystem,
350                  stats[i].name, (unsigned long long) stats[i].total);
351         i++;
352       }
353     die_task = GNUNET_SCHEDULER_add_now (&do_stop, NULL);
354     return;
355   }
356   sm->stat =
357       GNUNET_STATISTICS_create ("<driver>",
358                                 GNUNET_TESTING_daemon_get (pg, 
359                                                            sm->daemon)->cfg);
360   die_task = GNUNET_SCHEDULER_add_now (&stat_run, sm);
361 }
362
363
364 /**
365  * Function scheduled to be run on the successful completion of this
366  * testcase.
367  */
368 static void
369 finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
370 {
371   struct TestPutContext *test_put;
372   struct TestGetContext *test_get;
373   struct StatMaster *sm;
374
375   die_task = GNUNET_SCHEDULER_NO_TASK;
376   while (NULL != (test_put = all_puts_head))
377   {
378     if (test_put->task != GNUNET_SCHEDULER_NO_TASK)
379       GNUNET_SCHEDULER_cancel (test_put->task);
380     if (test_put->dht_handle != NULL)
381       GNUNET_DHT_disconnect (test_put->dht_handle);
382     GNUNET_CONTAINER_DLL_remove (all_puts_head,
383                                  all_puts_tail,
384                                  test_put);
385     GNUNET_free (test_put);
386   }
387
388   while (NULL != (test_get = all_gets_head))
389   {
390     if (test_get->task != GNUNET_SCHEDULER_NO_TASK)
391       GNUNET_SCHEDULER_cancel (test_get->task);
392     if (test_get->get_handle != NULL)
393       GNUNET_DHT_get_stop (test_get->get_handle);
394     if (test_get->dht_handle != NULL)
395       GNUNET_DHT_disconnect (test_get->dht_handle);
396     GNUNET_CONTAINER_DLL_remove (all_gets_head,
397                                  all_gets_tail,
398                                  test_get);
399     GNUNET_free (test_get);
400   }
401   sm = GNUNET_malloc (sizeof (struct StatMaster));
402   sm->stat =
403     GNUNET_STATISTICS_create ("<driver>",
404                               GNUNET_TESTING_daemon_get (pg, 
405                                                          sm->daemon)->cfg);
406   die_task = GNUNET_SCHEDULER_add_now (&stat_run, sm);
407 }
408
409
410 /**
411  * Check if the get_handle is being used, if so stop the request.  Either
412  * way, schedule the end_badly_cont function which actually shuts down the
413  * test.
414  */
415 static void
416 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
417 {
418   const char *emsg = cls;
419   struct TestPutContext *test_put;
420   struct TestGetContext *test_get;
421
422   die_task = GNUNET_SCHEDULER_NO_TASK;
423   fprintf (stderr, 
424            "Failing test with error: `%s'!\n",
425            emsg);
426   while (NULL != (test_put = all_puts_head))
427   {
428     if (test_put->task != GNUNET_SCHEDULER_NO_TASK)
429       GNUNET_SCHEDULER_cancel (test_put->task);
430     if (test_put->dht_handle != NULL)
431       GNUNET_DHT_disconnect (test_put->dht_handle);
432     GNUNET_CONTAINER_DLL_remove (all_puts_head,
433                                  all_puts_tail,
434                                  test_put);
435     GNUNET_free (test_put);
436   }
437
438   while (NULL != (test_get = all_gets_head))
439   {
440     if (test_get->task != GNUNET_SCHEDULER_NO_TASK)
441       GNUNET_SCHEDULER_cancel (test_get->task);
442     if (test_get->get_handle != NULL)
443       GNUNET_DHT_get_stop (test_get->get_handle);
444     if (test_get->dht_handle != NULL)
445       GNUNET_DHT_disconnect (test_get->dht_handle);
446     GNUNET_CONTAINER_DLL_remove (all_gets_head,
447                                  all_gets_tail,
448                                  test_get);
449     GNUNET_free (test_get);
450   }
451   ok = 1;
452   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
453   pg = NULL;
454 }
455
456
457 /**
458  * Task to release get handle.
459  */
460 static void
461 get_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
462 {
463   struct TestGetContext *test_get = cls;
464   GNUNET_HashCode search_key;   /* Key stored under */
465   char original_data[TEST_DATA_SIZE];   /* Made up data to store */
466
467   test_get->task = GNUNET_SCHEDULER_NO_TASK;
468   memset (original_data, test_get->uid, sizeof (original_data));
469   GNUNET_CRYPTO_hash (original_data, TEST_DATA_SIZE, &search_key);
470   if (test_get->succeeded != GNUNET_YES)
471   {
472     gets_failed++;
473     fprintf (stderr,
474              "Get from peer %s for key %s failed!\n",
475              GNUNET_i2s (&test_get->daemon->id), 
476              GNUNET_h2s (&search_key));
477   }
478   GNUNET_assert (test_get->get_handle != NULL);
479   GNUNET_DHT_get_stop (test_get->get_handle);
480   test_get->get_handle = NULL;
481
482   outstanding_gets--;           /* GET is really finished */
483   GNUNET_DHT_disconnect (test_get->dht_handle);
484   test_get->dht_handle = NULL;
485
486   GNUNET_CONTAINER_DLL_remove (all_gets_head,
487                                all_gets_tail,
488                                test_get);
489   GNUNET_free (test_get);
490   if ((gets_failed > 10) && (outstanding_gets == 0))       
491   {
492     /* Had more than 10% failures */
493     fprintf (stderr,
494              "%llu gets succeeded, %llu gets failed!\n",
495              gets_completed, gets_failed);
496     GNUNET_SCHEDULER_cancel (die_task);
497     ok = 1; 
498     die_task = GNUNET_SCHEDULER_add_now (&finish_testing, "not all gets succeeded");
499     return;
500   }
501   if ( (gets_completed + gets_failed == num_peers * num_peers) && 
502        (outstanding_gets == 0) )  /* All gets successful */
503   {
504     fprintf (stderr,
505              "%llu gets succeeded, %llu gets failed!\n",
506              gets_completed, gets_failed);
507     GNUNET_SCHEDULER_cancel (die_task);
508     ok = 0; 
509     die_task = GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
510   }
511 }
512
513
514 /**
515  * Iterator called if the GET request initiated returns a response.
516  *
517  * @param cls closure
518  * @param exp when will this value expire
519  * @param key key of the result
520  * @param type type of the result
521  * @param size number of bytes in data
522  * @param data pointer to the result data
523  */
524 static void
525 get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp,
526                      const GNUNET_HashCode * key,
527                      const struct GNUNET_PeerIdentity *get_path,
528                      unsigned int get_path_length,
529                      const struct GNUNET_PeerIdentity *put_path,
530                      unsigned int put_path_length,
531                      enum GNUNET_BLOCK_Type type, size_t size, const void *data)
532 {
533   struct TestGetContext *test_get = cls;
534   GNUNET_HashCode search_key;   /* Key stored under */
535   char original_data[TEST_DATA_SIZE];   /* Made up data to store */
536
537   memset (original_data, test_get->uid, sizeof (original_data));
538   GNUNET_CRYPTO_hash (original_data, TEST_DATA_SIZE, &search_key);
539   if (test_get->succeeded == GNUNET_YES)
540     return;                     /* Get has already been successful, probably ending now */
541
542 #if PATH_TRACKING
543   if (put_path != NULL)
544   {
545     unsigned int i;
546
547     fprintf (stderr, "PUT (%u) Path: ",
548              test_get->uid);
549     for (i = 0; i<put_path_length; i++)
550       fprintf (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&put_path[i]));
551     fprintf (stderr, "\n");
552   }
553   if (get_path != NULL)
554   {
555     unsigned int i;
556
557     fprintf (stderr, "GET (%u) Path: ",
558              test_get->uid);
559     for (i = 0; i < get_path_length; i++)
560       fprintf (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&get_path[i]));
561     fprintf (stderr, "%s%s\n",
562              get_path_length > 0 ? "->":"",
563              GNUNET_i2s (&test_get->daemon->id));
564   }
565 #endif
566
567   if ((0 != memcmp (&search_key, key, sizeof (GNUNET_HashCode))) ||
568       (0 != memcmp (original_data, data, sizeof (original_data))))
569   {
570     fprintf (stderr,
571              "Key or data is not the same as was inserted!\n");
572     return;
573   }
574   gets_completed++;
575   test_get->succeeded = GNUNET_YES;  
576   GNUNET_SCHEDULER_cancel (test_get->task);
577   test_get->task = GNUNET_SCHEDULER_add_now (&get_stop_task, test_get);
578 }
579
580
581 /**
582  * Set up some data, and call API PUT function
583  */
584 static void
585 do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
586 {
587   struct TestGetContext *test_get = cls;
588   GNUNET_HashCode key;          /* Made up key to store data under */
589   char data[TEST_DATA_SIZE];    /* Made up data to store */
590
591   if (outstanding_gets > MAX_OUTSTANDING_GETS)
592   {
593     test_get->task = GNUNET_SCHEDULER_add_delayed (GET_DELAY, &do_get, test_get);
594     return;
595   }
596   memset (data, test_get->uid, sizeof (data));
597   GNUNET_CRYPTO_hash (data, TEST_DATA_SIZE, &key);
598   test_get->dht_handle = GNUNET_DHT_connect (test_get->daemon->cfg, 10);
599   GNUNET_assert (test_get->dht_handle != NULL);
600   outstanding_gets++;
601   test_get->get_handle =
602     GNUNET_DHT_get_start (test_get->dht_handle, GNUNET_TIME_UNIT_FOREVER_REL,
603                           GNUNET_BLOCK_TYPE_TEST, &key,
604                           1, route_option, NULL, 0,
605                           &get_result_iterator, test_get);
606   test_get->task =
607     GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &get_stop_task, test_get);
608 }
609
610
611 /**
612  * Task to release DHT handles for PUT
613  */
614 static void
615 put_disconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
616 {
617   struct TestPutContext *test_put = cls;
618
619   test_put->task = GNUNET_SCHEDULER_NO_TASK;
620   GNUNET_DHT_disconnect (test_put->dht_handle);
621   test_put->dht_handle = NULL;
622   GNUNET_CONTAINER_DLL_remove (all_puts_head,
623                                all_puts_tail,
624                                test_put);
625   GNUNET_free (test_put);
626 }
627
628
629 /**
630  * Schedule the GET requests
631  */
632 static void
633 start_gets (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
634 {
635   unsigned long long i;
636   unsigned long long j;
637   struct TestGetContext *test_get;
638
639 #if VERBOSE 
640   fprintf (stderr, 
641            "Issuing %llu GETs\n",
642            num_peers * num_peers);
643 #endif
644   for (i = 0; i < num_peers; i++)
645     for (j = 0; j < num_peers; j++)
646       {
647         test_get = GNUNET_malloc (sizeof (struct TestGetContext));
648         test_get->uid = i + j*num_peers;
649         test_get->daemon = GNUNET_TESTING_daemon_get (pg, j);
650         GNUNET_CONTAINER_DLL_insert (all_gets_head,
651                                      all_gets_tail,
652                                      test_get);
653         test_get->task = GNUNET_SCHEDULER_add_now (&do_get,
654                                                    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   GNUNET_SCHEDULER_cancel (test_put->task);
670   test_put->task =
671       GNUNET_SCHEDULER_add_now (&put_disconnect_task, test_put);
672   if (puts_completed != num_peers * num_peers)
673     return;
674   
675   GNUNET_assert (outstanding_puts == 0);
676   GNUNET_SCHEDULER_add_delayed (START_DELAY,
677                                 &start_gets,
678                                 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 = GNUNET_SCHEDULER_add_delayed (PUT_DELAY, &do_put, test_put);
696     return;
697   }
698   memset (data, test_put->uid, sizeof (data));
699   GNUNET_CRYPTO_hash (data, TEST_DATA_SIZE, &key);
700   test_put->dht_handle = GNUNET_DHT_connect (test_put->daemon->cfg, 10);
701   GNUNET_assert (test_put->dht_handle != NULL);
702   outstanding_puts++;
703 #if VERBOSE > 2
704   fprintf (stderr, "PUT %u at `%s'\n",
705            test_put->uid,
706            GNUNET_i2s (&test_put->daemon->id));
707 #endif
708   GNUNET_DHT_put (test_put->dht_handle, &key, 1,
709                   route_option, 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 PATH_TRACKING
725   route_option = GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE;
726 #else
727   route_option = GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE;
728 #endif
729   die_task =
730     GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
731                                   "from setup puts/gets");
732   fprintf (stderr, 
733            "Issuing %llu PUTs (one per peer)\n", 
734            num_peers);
735   for (i = 0; i < num_peers * num_peers; i++)
736   {
737     test_put = GNUNET_malloc (sizeof (struct TestPutContext));
738     test_put->uid = i;
739     test_put->daemon = GNUNET_TESTING_daemon_get (pg, i % num_peers);    
740     test_put->task = GNUNET_SCHEDULER_add_now (&do_put, test_put);
741     GNUNET_CONTAINER_DLL_insert (all_puts_head,
742                                  all_puts_tail,
743                                  test_put);
744   }
745 }
746
747
748 /**
749  * This function is called once testing has finished setting up the topology.
750  *
751  * @param cls unused
752  * @param emsg variable is NULL on success (peers connected), and non-NULL on
753  * failure (peers failed to connect).
754  */
755 static void
756 startup_done (void *cls, const char *emsg)
757 {
758   if (emsg != NULL)
759   {
760     fprintf (stderr,
761              "Failed to setup topology: %s\n",
762              emsg);
763     die_task =
764       GNUNET_SCHEDULER_add_now (&end_badly,
765                                 "topology setup failed");
766     return;
767   }
768   die_task =
769     GNUNET_SCHEDULER_add_delayed (START_DELAY, &run_dht_test,
770                                   "from setup puts/gets");
771 }
772
773
774 static void
775 run (void *cls, char *const *args, const char *cfgfile,
776      const struct GNUNET_CONFIGURATION_Handle *cfg)
777 {
778   /* Get path from configuration file */
779   if (GNUNET_YES !=
780       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
781                                              &test_directory))
782   {
783     GNUNET_break (0);
784     ok = 404;
785     return;
786   }
787   if (GNUNET_SYSERR ==
788       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
789                                              &num_peers))
790     num_peers = DEFAULT_NUM_PEERS;
791   pg = GNUNET_TESTING_peergroup_start (cfg,
792                                        num_peers,
793                                        TIMEOUT,
794                                        NULL,
795                                        &startup_done,
796                                        NULL,
797                                        NULL);
798   GNUNET_assert (NULL != pg);
799 }
800
801
802 static int
803 check ()
804 {
805   int ret;
806
807   /* Arguments for GNUNET_PROGRAM_run */
808   char *const argv[] = { "test-dht-multipeer",  /* Name to give running binary */
809     "-c",
810     "test_dht_multipeer_data.conf",     /* Config file to use */
811 #if VERBOSE
812     "-L", "DEBUG",
813 #endif
814     NULL
815   };
816   struct GNUNET_GETOPT_CommandLineOption options[] = {
817     GNUNET_GETOPT_OPTION_END
818   };
819   /* Run the run function as a new program */
820   ret =
821       GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
822                           "test-dht-multipeer", "nohelp", options, &run, &ok);
823   if (ret != GNUNET_OK)
824   {
825     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
826                 "`test-dht-multipeer': Failed with error code %d\n", ret);
827   }
828   return ok;
829 }
830
831
832 int
833 main (int argc, char *argv[])
834 {
835   int ret;
836
837
838   GNUNET_log_setup ("test-dht-multipeer",
839 #if VERBOSE
840                     "DEBUG",
841 #else
842                     "WARNING",
843 #endif
844                     NULL);
845   ret = check ();
846   /**
847    * Need to remove base directory, subdirectories taken care
848    * of by the testing framework.
849    */
850   if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
851   {
852     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
853                 "Failed to remove testing directory %s\n", test_directory);
854   }
855   return ret;
856 }
857
858 /* end of test_dht_multipeer.c */