use ring 10, test all-to-all GET/PUT
[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, 5)
35
36 /* Timeout for waiting for replies to get requests */
37 #define GET_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
38
39 /* Timeout for waiting for gets to complete */
40 #define GET_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1)
41
42 /* Timeout for waiting for puts to complete */
43 #define PUT_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 1)
44
45 #define SECONDS_PER_PEER_START 45
46
47 /* If number of peers not in config file, use this number */
48 #define DEFAULT_NUM_PEERS 10
49
50 #define TEST_DATA_SIZE 8
51
52 #define MAX_OUTSTANDING_PUTS 10
53
54 #define MAX_OUTSTANDING_GETS 10
55
56 #define PATH_TRACKING GNUNET_YES
57
58 /* Structs */
59
60 struct TestPutContext
61 {
62   /**
63    * This is a linked list
64    */
65   struct TestPutContext *next;
66
67   /**
68    * Handle to the first peers DHT service (via the API)
69    */
70   struct GNUNET_DHT_Handle *dht_handle;
71
72   /**
73    *  Handle to the PUT peer daemon
74    */
75   struct GNUNET_TESTING_Daemon *daemon;
76
77   /**
78    *  Identifier for this PUT
79    */
80   uint32_t uid;
81
82   /**
83    * Task for disconnecting DHT handles
84    */
85   GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
86 };
87
88 struct TestGetContext
89 {
90   /* This is a linked list */
91   struct TestGetContext *next;
92
93   /**
94    * Handle to the first peers DHT service (via the API)
95    */
96   struct GNUNET_DHT_Handle *dht_handle;
97
98   /**
99    * Handle for the DHT get request
100    */
101   struct GNUNET_DHT_GetHandle *get_handle;
102
103   /**
104    *  Handle to the GET peer daemon
105    */
106   struct GNUNET_TESTING_Daemon *daemon;
107
108   /**
109    *  Identifier for this GET
110    */
111   uint32_t uid;
112
113   /**
114    * Task for disconnecting DHT handles (and stopping GET)
115    */
116   GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
117
118   /**
119    * Whether or not this request has been fulfilled already.
120    */
121   int succeeded;
122 };
123
124 /* Globals */
125
126 /**
127  * List of GETS to perform
128  */
129 struct TestGetContext *all_gets;
130
131 /**
132  * List of PUTS to perform
133  */
134 struct TestPutContext *all_puts;
135
136 /**
137  * Directory to store temp data in, defined in config file
138  */
139 static char *test_directory;
140
141 /**
142  * Variable used to store the number of connections we should wait for.
143  */
144 static unsigned int expected_connections;
145
146 /**
147  * Variable used to keep track of how many peers aren't yet started.
148  */
149 static unsigned long long peers_left;
150
151 /**
152  * Handle to the set of all peers run for this test.
153  */
154 static struct GNUNET_TESTING_PeerGroup *pg;
155
156
157 /**
158  * Total number of peers to run, set based on config file.
159  */
160 static unsigned long long num_peers;
161
162 /**
163  * How many puts do we currently have in flight?
164  */
165 static unsigned long long outstanding_puts;
166
167 /**
168  * How many puts are done?
169  */
170 static unsigned long long puts_completed;
171
172 /**
173  * How many puts do we currently have in flight?
174  */
175 static unsigned long long outstanding_gets;
176
177 /**
178  * How many gets are done?
179  */
180 static unsigned long long gets_completed;
181
182 /**
183  * How many gets failed?
184  */
185 static unsigned long long gets_failed;
186
187 /**
188  * Global used to count how many connections we have currently
189  * been notified about (how many times has topology_callback been called
190  * with success?)
191  */
192 static unsigned int total_connections;
193
194 /**
195  * Global used to count how many failed connections we have
196  * been notified about (how many times has topology_callback
197  * been called with failure?)
198  */
199 static unsigned int failed_connections;
200
201 static enum GNUNET_DHT_RouteOption route_option;
202
203 /* Task handle to use to schedule test failure */
204 static GNUNET_SCHEDULER_TaskIdentifier die_task;
205
206 static char *blacklist_transports;
207
208 static enum GNUNET_TESTING_Topology topology;
209
210 static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_NONE;  /* Don't do any blacklisting */
211
212 static enum GNUNET_TESTING_Topology connection_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */
213
214 static enum GNUNET_TESTING_TopologyOption connect_topology_option =
215     GNUNET_TESTING_TOPOLOGY_OPTION_ALL;
216
217 static double connect_topology_option_modifier = 0.0;
218
219 /* Global return value (0 for success, anything else for failure) */
220 static int ok;
221
222 /**
223  * Check whether peers successfully shut down.
224  */
225 static void
226 shutdown_callback (void *cls, const char *emsg)
227 {
228   if (emsg != NULL)
229   {
230     if (ok == 0)
231       ok = 2;
232   }
233 }
234
235 /**
236  * Task to release DHT handles for PUT
237  */
238 static void
239 put_disconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
240 {
241   struct TestPutContext *test_put = cls;
242
243   test_put->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
244   GNUNET_DHT_disconnect (test_put->dht_handle);
245   test_put->dht_handle = NULL;
246 }
247
248 /**
249  * Function scheduled to be run on the successful completion of this
250  * testcase.
251  */
252 static void
253 finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
254 {
255   GNUNET_assert (pg != NULL);
256   struct TestPutContext *test_put = all_puts;
257   struct TestGetContext *test_get = all_gets;
258
259   while (test_put != NULL)
260   {
261     if (test_put->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
262       GNUNET_SCHEDULER_cancel (test_put->disconnect_task);
263     if (test_put->dht_handle != NULL)
264       GNUNET_DHT_disconnect (test_put->dht_handle);
265     test_put = test_put->next;
266   }
267
268   while (test_get != NULL)
269   {
270     if (test_get->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
271       GNUNET_SCHEDULER_cancel (test_get->disconnect_task);
272     if (test_get->get_handle != NULL)
273       GNUNET_DHT_get_stop (test_get->get_handle);
274     if (test_get->dht_handle != NULL)
275       GNUNET_DHT_disconnect (test_get->dht_handle);
276     test_get = test_get->next;
277   }
278
279   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
280   ok = 0;
281 }
282
283
284 /**
285  * Check if the get_handle is being used, if so stop the request.  Either
286  * way, schedule the end_badly_cont function which actually shuts down the
287  * test.
288  */
289 static void
290 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
291 {
292   const char *emsg = cls;
293
294   fprintf (stderr, 
295            "Failing test with error: `%s'!\n",
296            emsg);
297
298   struct TestPutContext *test_put = all_puts;
299   struct TestGetContext *test_get = all_gets;
300
301   while (test_put != NULL)
302   {
303     if (test_put->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
304       GNUNET_SCHEDULER_cancel (test_put->disconnect_task);
305     if (test_put->dht_handle != NULL)
306       GNUNET_DHT_disconnect (test_put->dht_handle);
307     test_put = test_put->next;
308   }
309
310   while (test_get != NULL)
311   {
312     if (test_get->disconnect_task != GNUNET_SCHEDULER_NO_TASK)
313       GNUNET_SCHEDULER_cancel (test_get->disconnect_task);
314     if (test_get->get_handle != NULL)
315       GNUNET_DHT_get_stop (test_get->get_handle);
316     if (test_get->dht_handle != NULL)
317       GNUNET_DHT_disconnect (test_get->dht_handle);
318     test_get = test_get->next;
319   }
320
321   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
322   ok = 1;
323 }
324
325
326 /**
327  * Task to release get handle.
328  */
329 static void
330 get_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
331 {
332   struct TestGetContext *test_get = cls;
333   GNUNET_HashCode search_key;   /* Key stored under */
334   char original_data[TEST_DATA_SIZE];   /* Made up data to store */
335
336   test_get->disconnect_task = GNUNET_SCHEDULER_NO_TASK;
337   memset (original_data, test_get->uid, sizeof (original_data));
338   GNUNET_CRYPTO_hash (original_data, TEST_DATA_SIZE, &search_key);
339
340   if (test_get->succeeded != GNUNET_YES)
341   {
342     gets_failed++;
343     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344                 "Get from peer %s for key %s failed!\n",
345                 test_get->daemon->shortname, GNUNET_h2s (&search_key));
346   }
347   GNUNET_assert (test_get->get_handle != NULL);
348   GNUNET_DHT_get_stop (test_get->get_handle);
349   test_get->get_handle = NULL;
350
351   outstanding_gets--;           /* GET is really finished */
352   GNUNET_DHT_disconnect (test_get->dht_handle);
353   test_get->dht_handle = NULL;
354
355   fprintf (stderr,
356            "%llu gets succeeded, %llu gets failed!\n",
357            gets_completed, gets_failed);
358   if ((gets_failed > 0) && (outstanding_gets == 0))       /* Had some failures */
359   {
360       GNUNET_SCHEDULER_cancel (die_task);
361       die_task =
362         GNUNET_SCHEDULER_add_now (&end_badly, "not all gets succeeded");
363       return;
364   }
365
366   if ( (gets_completed == num_peers * num_peers) && 
367        (outstanding_gets == 0) )  /* All gets successful */
368   {
369     GNUNET_SCHEDULER_cancel (die_task);
370     //GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5), &get_topology, NULL);
371     die_task = GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
372   }
373 }
374
375 /**
376  * Iterator called if the GET request initiated returns a response.
377  *
378  * @param cls closure
379  * @param exp when will this value expire
380  * @param key key of the result
381  * @param type type of the result
382  * @param size number of bytes in data
383  * @param data pointer to the result data
384  */
385 static void
386 get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp,
387                      const GNUNET_HashCode * key,
388                      const struct GNUNET_PeerIdentity *get_path,
389                      unsigned int get_path_length,
390                      const struct GNUNET_PeerIdentity *put_path,
391                      unsigned int put_path_length,
392                      enum GNUNET_BLOCK_Type type, size_t size, const void *data)
393 {
394   struct TestGetContext *test_get = cls;
395   GNUNET_HashCode search_key;   /* Key stored under */
396   char original_data[TEST_DATA_SIZE];   /* Made up data to store */
397   unsigned int i;
398
399   memset (original_data, test_get->uid, sizeof (original_data));
400   GNUNET_CRYPTO_hash (original_data, TEST_DATA_SIZE, &search_key);
401
402   if (test_get->succeeded == GNUNET_YES)
403     return;                     /* Get has already been successful, probably ending now */
404
405 #if PATH_TRACKING
406   if (put_path != NULL)
407   {
408     fprintf (stderr, "PUT Path: ");
409     for (i = 0; i<put_path_length; i++)
410       fprintf (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&put_path[i]));
411     fprintf (stderr, "\n");
412   }
413   if (get_path != NULL)
414   {
415     fprintf (stderr, "GET Path: ");
416     for (i = 0; i < get_path_length; i++)
417       fprintf (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&get_path[i]));
418     fprintf (stderr, "\n");
419   }
420 #endif
421
422   if ((0 != memcmp (&search_key, key, sizeof (GNUNET_HashCode))) ||
423       (0 != memcmp (original_data, data, sizeof (original_data))))
424   {
425     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
426                 "Key or data is not the same as was inserted!\n");
427   }
428   else
429   {
430     fprintf (stderr, "GET successful!\n");
431     gets_completed++;
432     test_get->succeeded = GNUNET_YES;
433   }
434
435   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received correct GET response!\n");
436   GNUNET_SCHEDULER_cancel (test_get->disconnect_task);
437   test_get->disconnect_task = GNUNET_SCHEDULER_add_now (&get_stop_task, test_get);
438 }
439
440
441 /**
442  * Set up some data, and call API PUT function
443  */
444 static void
445 do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
446 {
447   struct TestGetContext *test_get = cls;
448   GNUNET_HashCode key;          /* Made up key to store data under */
449   char data[TEST_DATA_SIZE];    /* Made up data to store */
450
451   if (test_get == NULL)
452     return;                     /* End of the list */
453   memset (data, test_get->uid, sizeof (data));
454   GNUNET_CRYPTO_hash (data, TEST_DATA_SIZE, &key);
455
456   if (outstanding_gets > MAX_OUTSTANDING_GETS)
457   {
458     GNUNET_SCHEDULER_add_delayed (GET_DELAY, &do_get, test_get);
459     return;
460   }
461
462   test_get->dht_handle = GNUNET_DHT_connect (test_get->daemon->cfg, 10);
463   /* Insert the data at the first peer */
464   GNUNET_assert (test_get->dht_handle != NULL);
465   outstanding_gets++;
466   test_get->get_handle =
467       GNUNET_DHT_get_start (test_get->dht_handle, GNUNET_TIME_UNIT_FOREVER_REL,
468                             GNUNET_BLOCK_TYPE_TEST, &key,
469                             1, route_option, NULL, 0,
470                             &get_result_iterator, test_get);
471 #if VERBOSE
472   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting get for uid %u from peer %s\n",
473               test_get->uid, test_get->daemon->shortname);
474 #endif
475   test_get->disconnect_task =
476       GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &get_stop_task, test_get);
477   GNUNET_SCHEDULER_add_now (&do_get, test_get->next);
478 }
479
480 /**
481  * Called when the PUT request has been transmitted to the DHT service.
482  * Schedule the GET request for some time in the future.
483  */
484 static void
485 put_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
486 {
487   struct TestPutContext *test_put = cls;
488
489   outstanding_puts--;
490   puts_completed++;
491
492   GNUNET_SCHEDULER_cancel (test_put->disconnect_task);
493   test_put->disconnect_task =
494       GNUNET_SCHEDULER_add_now (&put_disconnect_task, test_put);
495   if (puts_completed == num_peers)
496   {
497     GNUNET_assert (outstanding_puts == 0);
498     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
499                                   (GNUNET_TIME_UNIT_SECONDS, 10), &do_get,
500                                   all_gets);
501     return;
502   }
503 }
504
505 /**
506  * Set up some data, and call API PUT function
507  */
508 static void
509 do_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
510 {
511   struct TestPutContext *test_put = cls;
512   GNUNET_HashCode key;          /* Made up key to store data under */
513   char data[TEST_DATA_SIZE];    /* Made up data to store */
514
515   if (test_put == NULL)
516     return;                     /* End of list */
517
518   memset (data, test_put->uid, sizeof (data));
519   GNUNET_CRYPTO_hash (data, TEST_DATA_SIZE, &key);
520
521   if (outstanding_puts > MAX_OUTSTANDING_PUTS)
522   {
523     GNUNET_SCHEDULER_add_delayed (PUT_DELAY, &do_put, test_put);
524     return;
525   }
526
527 #if VERBOSE
528   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting put for uid %u from peer %s\n",
529               test_put->uid, test_put->daemon->shortname);
530 #endif
531   test_put->dht_handle = GNUNET_DHT_connect (test_put->daemon->cfg, 10);
532
533   GNUNET_assert (test_put->dht_handle != NULL);
534   outstanding_puts++;
535   GNUNET_DHT_put (test_put->dht_handle, &key, 1,
536                   route_option, GNUNET_BLOCK_TYPE_TEST, sizeof (data), data,
537                   GNUNET_TIME_UNIT_FOREVER_ABS, GNUNET_TIME_UNIT_FOREVER_REL,
538                   &put_finished, test_put);
539   test_put->disconnect_task =
540     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
541                                   &put_disconnect_task, test_put);
542   GNUNET_SCHEDULER_add_now (&do_put, test_put->next);
543 }
544
545
546
547 /**
548  * This function is called whenever a connection attempt is finished between two of
549  * the started peers (started with GNUNET_TESTING_daemons_start).  The total
550  * number of times this function is called should equal the number returned
551  * from the GNUNET_TESTING_connect_topology call.
552  *
553  * The emsg variable is NULL on success (peers connected), and non-NULL on
554  * failure (peers failed to connect).
555  */
556 static void
557 topology_callback (void *cls, const struct GNUNET_PeerIdentity *first,
558                    const struct GNUNET_PeerIdentity *second, uint32_t distance,
559                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
560                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
561                    struct GNUNET_TESTING_Daemon *first_daemon,
562                    struct GNUNET_TESTING_Daemon *second_daemon,
563                    const char *emsg)
564 {
565   unsigned long long i;
566   unsigned long long j;
567   uint32_t temp_daemon;
568   struct TestPutContext *test_put;
569   struct TestGetContext *test_get;
570
571   if (emsg == NULL)
572   {
573     total_connections++;
574 #if VERBOSE > 1
575     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
576                 "connected peer %s to peer %s, distance %u\n",
577                 first_daemon->shortname, second_daemon->shortname, distance);
578 #endif
579   }
580   else
581   {
582     failed_connections++;
583     fprintf (stderr,
584              "Failed to connect peer %s to peer %s with error :\n%s\n",
585              first_daemon->shortname, second_daemon->shortname, emsg);
586     GNUNET_SCHEDULER_cancel (die_task);
587     die_task =
588       GNUNET_SCHEDULER_add_now (&end_badly,
589                                 "from topology_callback (connections failed)");
590     return;
591   }
592   if (total_connections < expected_connections)
593     return;
594   fprintf (stderr,
595            "Created topology with %u connections\n",
596            total_connections);
597   GNUNET_SCHEDULER_cancel (die_task);
598   die_task =
599     GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
600                                   "from setup puts/gets");
601   fprintf (stderr, 
602            "Issuing %llu PUTs (one per peer)\n", 
603            num_peers);
604   for (i = 0; i < num_peers; i++)
605   {
606     test_put = GNUNET_malloc (sizeof (struct TestPutContext));
607     test_put->uid = i;
608     test_put->daemon = GNUNET_TESTING_daemon_get (pg, i);    
609     test_put->next = all_puts;
610     all_puts = test_put;
611   }
612   GNUNET_SCHEDULER_add_now (&do_put, all_puts);
613
614   fprintf (stderr, 
615            "Issuing %llu GETs\n",
616            num_peers * num_peers);
617   for (i = 0; i < num_peers; i++)
618     for (j = 0; j < num_peers; j++)
619       {
620         test_get = GNUNET_malloc (sizeof (struct TestGetContext));
621         test_get->uid = i;
622         temp_daemon = j;
623         test_get->daemon = GNUNET_TESTING_daemon_get (pg, temp_daemon);
624         test_get->next = all_gets;
625         all_gets = test_get;
626       }
627 }
628
629
630 static void
631 peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id,
632                         const struct GNUNET_CONFIGURATION_Handle *cfg,
633                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
634 {
635   if (emsg != NULL)
636   {
637     fprintf (stderr,
638              "Failed to start daemon with error: `%s'\n", emsg);
639     return;
640   }
641   GNUNET_assert (id != NULL);
642
643 #if VERBOSE
644   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
645               (num_peers - peers_left) + 1, num_peers);
646 #endif
647
648   peers_left--;
649   if (peers_left > 0)
650     return;
651
652 #if VERBOSE
653   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
654               "All %d daemons started, now connecting peers!\n", num_peers);
655 #endif
656   
657   expected_connections = -1;
658   if ((pg != NULL) && (peers_left == 0))
659     {
660       expected_connections =
661         GNUNET_TESTING_connect_topology (pg, connection_topology,
662                                          connect_topology_option,
663                                          connect_topology_option_modifier,
664                                          TIMEOUT, num_peers, NULL, NULL);
665 #if VERBOSE
666       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Have %d expected connections\n",
667                   expected_connections);
668 #endif
669     }
670   
671   GNUNET_SCHEDULER_cancel (die_task);
672   if (expected_connections == GNUNET_SYSERR)
673     {
674       die_task =
675         GNUNET_SCHEDULER_add_now (&end_badly,
676                                   "from connect topology (bad return)");
677     }
678   else
679     {
680       die_task =
681         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
682                                       "from connect topology (timeout)");
683     }
684   ok = 0;
685 }
686
687
688 /**
689  * Callback indicating that the hostkey was created for a peer.
690  *
691  * @param cls NULL
692  * @param id the peer identity
693  * @param d the daemon handle (pretty useless at this point, remove?)
694  * @param emsg non-null on failure
695  */
696 static void
697 hostkey_callback (void *cls, const struct GNUNET_PeerIdentity *id,
698                   struct GNUNET_TESTING_Daemon *d, const char *emsg)
699 {
700   if (emsg != NULL)
701   {
702     fprintf (stderr,
703              "Hostkey callback received error: %s\n", emsg);
704     return;
705   }
706
707 #if VERBOSE > 1
708   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
709               "Hostkey (%d/%d) created for peer `%s'\n", num_peers - peers_left,
710               num_peers, GNUNET_i2s (id));
711 #endif
712
713
714   peers_left--;
715   if (peers_left == 0)
716   {
717 #if VERBOSE
718     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
719                 "All %d hostkeys created, now creating topology!\n", num_peers);
720 #endif
721     GNUNET_SCHEDULER_cancel (die_task);
722     /* Set up task in case topology creation doesn't finish
723      * within a reasonable amount of time */
724     ok = 0;
725     die_task =
726         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
727                                       "from create_topology");
728     peers_left = num_peers;       /* Reset counter */
729     GNUNET_SCHEDULER_cancel (die_task);
730     if (GNUNET_TESTING_create_topology
731         (pg, topology, blacklist_topology, blacklist_transports) != GNUNET_SYSERR)
732       {
733 #if VERBOSE
734         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
735                     "Topology set up, now starting peers!\n");
736 #endif
737         GNUNET_TESTING_daemons_continue_startup (pg);
738         die_task =
739           GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
740                                         "from continue startup (timeout)");
741       }
742     else
743       {
744         die_task =
745           GNUNET_SCHEDULER_add_now (&end_badly,
746                                     "from create topology (bad return)");
747       }
748   }
749 }
750
751
752 static void
753 run (void *cls, char *const *args, const char *cfgfile,
754      const struct GNUNET_CONFIGURATION_Handle *cfg)
755 {
756   char *topology_str;
757   char *connect_topology_str;
758   char *blacklist_topology_str;
759   char *connect_topology_option_str;
760   char *connect_topology_option_modifier_string;
761
762 #if PATH_TRACKING
763   route_option = GNUNET_DHT_RO_RECORD_ROUTE;
764 #else
765   route_option = GNUNET_DHT_RO_NONE;
766 #endif
767
768   /* Get path from configuration file */
769   if (GNUNET_YES !=
770       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
771                                              &test_directory))
772   {
773     GNUNET_break (0);
774     ok = 404;
775     return;
776   }
777
778   if ((GNUNET_YES ==
779        GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "topology",
780                                               &topology_str)) &&
781       (GNUNET_NO == GNUNET_TESTING_topology_get (&topology, topology_str)))
782   {
783     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
784                 "Invalid topology `%s' given for section %s option %s\n",
785                 topology_str, "TESTING", "TOPOLOGY");
786     topology = GNUNET_TESTING_TOPOLOGY_CLIQUE;  /* Defaults to NONE, so set better default here */
787   }
788
789   if ((GNUNET_YES ==
790        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
791                                               "connect_topology",
792                                               &connect_topology_str)) &&
793       (GNUNET_NO ==
794        GNUNET_TESTING_topology_get (&connection_topology,
795                                     connect_topology_str)))
796   {
797     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
798                 "Invalid connect topology `%s' given for section %s option %s\n",
799                 connect_topology_str, "TESTING", "CONNECT_TOPOLOGY");
800   }
801   GNUNET_free_non_null (connect_topology_str);
802   if ((GNUNET_YES ==
803        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
804                                               "connect_topology_option",
805                                               &connect_topology_option_str)) &&
806       (GNUNET_NO ==
807        GNUNET_TESTING_topology_option_get (&connect_topology_option,
808                                            connect_topology_option_str)))
809   {
810     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
811                 "Invalid connect topology option `%s' given for section %s option %s\n",
812                 connect_topology_option_str, "TESTING",
813                 "CONNECT_TOPOLOGY_OPTION");
814     connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL;       /* Defaults to NONE, set to ALL */
815   }
816   GNUNET_free_non_null (connect_topology_option_str);
817   if (GNUNET_YES ==
818       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
819                                              "connect_topology_option_modifier",
820                                              &connect_topology_option_modifier_string))
821   {
822     if (sscanf
823         (connect_topology_option_modifier_string, "%lf",
824          &connect_topology_option_modifier) != 1)
825     {
826       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
827                   _
828                   ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
829                   connect_topology_option_modifier_string,
830                   "connect_topology_option_modifier", "TESTING");
831     }
832     GNUNET_free (connect_topology_option_modifier_string);
833   }
834
835   if (GNUNET_YES !=
836       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
837                                              "blacklist_transports",
838                                              &blacklist_transports))
839     blacklist_transports = NULL;
840
841   if ((GNUNET_YES ==
842        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
843                                               "blacklist_topology",
844                                               &blacklist_topology_str)) &&
845       (GNUNET_NO ==
846        GNUNET_TESTING_topology_get (&blacklist_topology,
847                                     blacklist_topology_str)))
848   {
849     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
850                 "Invalid topology `%s' given for section %s option %s\n",
851                 topology_str, "TESTING", "BLACKLIST_TOPOLOGY");
852   }
853   GNUNET_free_non_null (topology_str);
854   GNUNET_free_non_null (blacklist_topology_str);
855
856   /* Get number of peers to start from configuration */
857   if (GNUNET_SYSERR ==
858       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
859                                              &num_peers))
860     num_peers = DEFAULT_NUM_PEERS;
861
862   /* Set peers_left so we know when all peers started */
863   peers_left = num_peers;
864
865   /* Set up a task to end testing if peer start fails */
866   die_task =
867       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
868                                     (GNUNET_TIME_UNIT_SECONDS,
869                                      SECONDS_PER_PEER_START * num_peers),
870                                     &end_badly,
871                                     "didn't generate all hostkeys within a reasonable amount of time!!!");
872   fprintf (stderr, 
873            "Starting P2P network with %llu peers\n",
874            peers_left);
875   pg = GNUNET_TESTING_daemons_start (cfg, peers_left,   /* Total number of peers */
876                                      peers_left,        /* Number of outstanding connections */
877                                      peers_left,        /* Number of parallel ssh connections, or peers being started at once */
878                                      GNUNET_TIME_relative_multiply
879                                      (GNUNET_TIME_UNIT_SECONDS,
880                                       SECONDS_PER_PEER_START * num_peers),
881                                      &hostkey_callback, NULL,
882                                      &peers_started_callback, NULL,
883                                      &topology_callback, NULL, NULL);
884
885 }
886
887
888 static int
889 check ()
890 {
891   int ret;
892
893   /* Arguments for GNUNET_PROGRAM_run */
894   char *const argv[] = { "test-dht-multipeer",  /* Name to give running binary */
895     "-c",
896     "test_dht_multipeer_data.conf",     /* Config file to use */
897 #if VERBOSE
898     "-L", "DEBUG",
899 #endif
900     NULL
901   };
902   struct GNUNET_GETOPT_CommandLineOption options[] = {
903     GNUNET_GETOPT_OPTION_END
904   };
905   /* Run the run function as a new program */
906   ret =
907       GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
908                           "test-dht-multipeer", "nohelp", options, &run, &ok);
909   if (ret != GNUNET_OK)
910   {
911     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
912                 "`test-dht-multipeer': Failed with error code %d\n", ret);
913   }
914   return ok;
915 }
916
917 int
918 main (int argc, char *argv[])
919 {
920   int ret;
921
922   GNUNET_log_setup ("test-dht-multipeer",
923 #if VERBOSE
924                     "DEBUG",
925 #else
926                     "WARNING",
927 #endif
928                     NULL);
929   ret = check ();
930   /**
931    * Need to remove base directory, subdirectories taken care
932    * of by the testing framework.
933    */
934   if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
935   {
936     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
937                 "Failed to remove testing directory %s\n", test_directory);
938   }
939   return ret;
940 }
941
942 /* end of test_dht_multipeer.c */