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