renaming ats files
[oweals/gnunet.git] / src / testing / testing_peergroup.c
1 /*
2  This file is part of GNUnet
3  (C) 2008-2011 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 /**
22  * @file testing/testing_peergroup.c
23  * @brief API implementation for easy peer group creation
24  * @author Nathan Evans
25  * @author Christian Grothoff
26  *
27  */
28 #include "platform.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_arm_service.h"
31 #include "gnunet_testing_lib.h"
32 #include "gnunet_core_service.h"
33 #include "gnunet_disk_lib.h"
34
35 /** Globals **/
36 #define DEFAULT_CONNECT_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30)
37
38 #define DEFAULT_CONNECT_ATTEMPTS 2
39
40 /** Struct definitions **/
41
42 struct PeerGroupStartupContext
43 {
44   struct GNUNET_TESTING_PeerGroup *pg;
45   const struct GNUNET_CONFIGURATION_Handle *cfg;
46   unsigned int total;
47   unsigned int peers_left;
48   unsigned long long max_concurrent_connections;
49   unsigned long long connect_attempts;
50   unsigned long long max_concurrent_ssh;
51   struct GNUNET_TIME_Absolute timeout;
52   GNUNET_TESTING_NotifyConnection connect_cb;
53   GNUNET_TESTING_NotifyCompletion peergroup_cb;
54
55   /**
56    * Closure for all peergroup callbacks.
57    */
58   void *cls;
59
60   const struct GNUNET_TESTING_Host *hostnames;
61   enum GNUNET_TESTING_Topology topology;
62
63   float topology_percentage;
64
65   float topology_probability;
66
67   enum GNUNET_TESTING_Topology restrict_topology;
68   char *restrict_transports;
69   enum GNUNET_TESTING_Topology connect_topology;
70   enum GNUNET_TESTING_TopologyOption connect_topology_option;
71   double connect_topology_option_modifier;
72   int verbose;
73
74   struct ProgressMeter *hostkey_meter;
75   struct ProgressMeter *peer_start_meter;
76   struct ProgressMeter *connect_meter;
77
78   /**
79    * Task used to kill the peergroup.
80    */
81   GNUNET_SCHEDULER_TaskIdentifier die_task;
82
83   char *fail_reason;
84
85   /**
86    * Variable used to store the number of connections we should wait for.
87    */
88   unsigned int expected_connections;
89
90   /**
91    * Time when the connecting peers was started.
92    */
93   struct GNUNET_TIME_Absolute connect_start_time;
94
95   /**
96    * The total number of connections that have been created so far.
97    */
98   unsigned int total_connections;
99
100   /**
101    * The total number of connections that have failed so far.
102    */
103   unsigned int failed_connections;
104
105   /**
106    * File handle to write out topology in dot format.
107    */
108   struct GNUNET_DISK_FileHandle *topology_output_file;
109 };
110
111 /**
112  * Simple struct to keep track of progress, and print a
113  * percentage meter for long running tasks.
114  */
115 struct ProgressMeter
116 {
117   /**
118    * Total number of tasks to complete.
119    */
120   unsigned int total;
121
122   /**
123    * Print percentage done after modnum tasks.
124    */
125   unsigned int modnum;
126
127   /**
128    * Print a . each dotnum tasks.
129    */
130   unsigned int dotnum;
131
132   /**
133    * Total number completed thus far.
134    */
135   unsigned int completed;
136
137   /**
138    * Whether or not to print.
139    */
140   int print;
141
142   /**
143    * Startup string for progress meter.
144    */
145   char *startup_string;
146 };
147
148
149 /** Utility functions **/
150
151 /**
152  * Create a meter to keep track of the progress of some task.
153  *
154  * @param total the total number of items to complete
155  * @param start_string a string to prefix the meter with (if printing)
156  * @param print GNUNET_YES to print the meter, GNUNET_NO to count
157  *              internally only
158  *
159  * @return the progress meter
160  */
161 static struct ProgressMeter *
162 create_meter(unsigned int total, char * start_string, int print)
163 {
164   struct ProgressMeter *ret;
165   ret = GNUNET_malloc(sizeof(struct ProgressMeter));
166   ret->print = print;
167   ret->total = total;
168   ret->modnum = total / 4;
169   ret->dotnum = (total / 50) + 1;
170   if (start_string != NULL)
171     ret->startup_string = GNUNET_strdup(start_string);
172   else
173     ret->startup_string = GNUNET_strdup("");
174
175   return ret;
176 }
177
178 /**
179  * Update progress meter (increment by one).
180  *
181  * @param meter the meter to update and print info for
182  *
183  * @return GNUNET_YES if called the total requested,
184  *         GNUNET_NO if more items expected
185  */
186 static int
187 update_meter(struct ProgressMeter *meter)
188 {
189   if (meter->print == GNUNET_YES)
190     {
191       if (meter->completed % meter->modnum == 0)
192         {
193           if (meter->completed == 0)
194             {
195               fprintf (stdout, "%sProgress: [0%%", meter->startup_string);
196             }
197           else
198             fprintf (stdout, "%d%%", (int) (((float) meter->completed
199                 / meter->total) * 100));
200         }
201       else if (meter->completed % meter->dotnum == 0)
202         fprintf (stdout, ".");
203
204       if (meter->completed + 1 == meter->total)
205         fprintf (stdout, "%d%%]\n", 100);
206       fflush (stdout);
207     }
208   meter->completed++;
209
210   if (meter->completed == meter->total)
211     return GNUNET_YES;
212   return GNUNET_NO;
213 }
214
215 /**
216  * Reset progress meter.
217  *
218  * @param meter the meter to reset
219  *
220  * @return GNUNET_YES if meter reset,
221  *         GNUNET_SYSERR on error
222  */
223 static int
224 reset_meter(struct ProgressMeter *meter)
225 {
226   if (meter == NULL)
227     return GNUNET_SYSERR;
228
229   meter->completed = 0;
230   return GNUNET_YES;
231 }
232
233 /**
234  * Release resources for meter
235  *
236  * @param meter the meter to free
237  */
238 static void
239 free_meter(struct ProgressMeter *meter)
240 {
241   GNUNET_free_non_null (meter->startup_string);
242   GNUNET_free (meter);
243 }
244
245
246 /** Functions for creating, starting and connecting the peergroup **/
247
248 /**
249  * Check whether peers successfully shut down.
250  */
251 static void
252 internal_shutdown_callback(void *cls, const char *emsg)
253 {
254   struct PeerGroupStartupContext *pg_start_ctx = cls;
255   if (emsg != NULL)
256     pg_start_ctx->peergroup_cb(pg_start_ctx->cls, emsg);
257   else
258     pg_start_ctx->peergroup_cb(pg_start_ctx->cls, pg_start_ctx->fail_reason);
259 }
260
261 /**
262  * Check if the get_handle is being used, if so stop the request.  Either
263  * way, schedule the end_badly_cont function which actually shuts down the
264  * test.
265  */
266 static void
267 end_badly(void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
268 {
269   struct PeerGroupStartupContext *pg_start_ctx = cls;
270   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failing peer group startup with error: `%s'!\n",
271               pg_start_ctx->fail_reason);
272
273   GNUNET_TESTING_daemons_stop (pg_start_ctx->pg, GNUNET_TIME_absolute_get_remaining(pg_start_ctx->timeout), &internal_shutdown_callback, pg_start_ctx);
274
275   if (pg_start_ctx->hostkey_meter != NULL)
276     free_meter (pg_start_ctx->hostkey_meter);
277   if (pg_start_ctx->peer_start_meter != NULL)
278     free_meter (pg_start_ctx->peer_start_meter);
279   if (pg_start_ctx->connect_meter != NULL)
280     free_meter (pg_start_ctx->connect_meter);
281 }
282
283 /**
284  * This function is called whenever a connection attempt is finished between two of
285  * the started peers (started with GNUNET_TESTING_daemons_start).  The total
286  * number of times this function is called should equal the number returned
287  * from the GNUNET_TESTING_connect_topology call.
288  *
289  * The emsg variable is NULL on success (peers connected), and non-NULL on
290  * failure (peers failed to connect).
291  */
292 static void
293 internal_topology_callback(
294                            void *cls,
295                            const struct GNUNET_PeerIdentity *first,
296                            const struct GNUNET_PeerIdentity *second,
297                            uint32_t distance,
298                            const struct GNUNET_CONFIGURATION_Handle *first_cfg,
299                            const struct GNUNET_CONFIGURATION_Handle *second_cfg,
300                            struct GNUNET_TESTING_Daemon *first_daemon,
301                            struct GNUNET_TESTING_Daemon *second_daemon,
302                            const char *emsg)
303 {
304   struct PeerGroupStartupContext *pg_start_ctx = cls;
305   char *temp_str;
306   char *second_str;
307   int temp;
308 #if TIMING
309   unsigned long long duration;
310   unsigned long long total_duration;
311   unsigned int new_connections;
312   unsigned int new_failed_connections;
313   double conns_per_sec_recent;
314   double conns_per_sec_total;
315   double failed_conns_per_sec_recent;
316   double failed_conns_per_sec_total;
317 #endif
318
319 #if TIMING
320   if (GNUNET_TIME_absolute_get_difference (connect_last_time,
321                                            GNUNET_TIME_absolute_get ()).rel_value
322       > GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
323                                        CONN_UPDATE_DURATION).rel_value)
324     {
325       /* Get number of new connections */
326       new_connections = total_connections - previous_connections;
327
328       /* Get number of new FAILED connections */
329       new_failed_connections = failed_connections - previous_failed_connections;
330
331       /* Get duration in seconds */
332       duration
333           = GNUNET_TIME_absolute_get_difference (connect_last_time,
334                                                  GNUNET_TIME_absolute_get ()).rel_value
335               / 1000;
336       total_duration
337           = GNUNET_TIME_absolute_get_difference (connect_start_time,
338                                                  GNUNET_TIME_absolute_get ()).rel_value
339               / 1000;
340
341       failed_conns_per_sec_recent = (double) new_failed_connections / duration;
342       failed_conns_per_sec_total = (double) failed_connections / total_duration;
343       conns_per_sec_recent = (double) new_connections / duration;
344       conns_per_sec_total = (double) total_connections / total_duration;
345       GNUNET_log (
346                   GNUNET_ERROR_TYPE_WARNING,
347                   "Recent: %.2f/s, Total: %.2f/s, Recent failed: %.2f/s, total failed %.2f/s\n",
348                   conns_per_sec_recent, CONN_UPDATE_DURATION,
349                   conns_per_sec_total, failed_conns_per_sec_recent,
350                   failed_conns_per_sec_total);
351       connect_last_time = GNUNET_TIME_absolute_get ();
352       previous_connections = total_connections;
353       previous_failed_connections = failed_connections;
354       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
355                   "have %u total_connections, %u failed\n", total_connections,
356                   failed_connections);
357     }
358 #endif
359
360
361   if (emsg == NULL)
362     {
363       pg_start_ctx->total_connections++;
364 #if VERBOSE > 1
365       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s, distance %u\n",
366           first_daemon->shortname,
367           second_daemon->shortname,
368           distance);
369 #endif
370       if (pg_start_ctx->topology_output_file != NULL)
371         {
372           second_str = GNUNET_strdup(GNUNET_i2s(second));
373           temp = GNUNET_asprintf(&temp_str, "\t\"%s\" -> \"%s\"\n", GNUNET_i2s(first), second_str);
374           GNUNET_free(second_str);
375           if (temp > 0)
376             GNUNET_DISK_file_write(pg_start_ctx->topology_output_file, temp_str, temp);
377           GNUNET_free(temp_str);
378         }
379     }
380   else
381     {
382       pg_start_ctx->failed_connections++;
383 #if VERBOSE
384       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to connect peer %s to peer %s with error :\n%s\n",
385           first_daemon->shortname,
386           second_daemon->shortname, emsg);
387
388       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect peer %s to peer %s with error :\n%s\n",
389           first_daemon->shortname,
390           second_daemon->shortname, emsg);
391 #endif
392     }
393
394   GNUNET_assert(pg_start_ctx->connect_meter != NULL);
395   if (pg_start_ctx->connect_cb != NULL)
396     pg_start_ctx->connect_cb(pg_start_ctx->cls, first,
397                              second,
398                              distance,
399                              first_cfg,
400                              second_cfg,
401                              first_daemon,
402                              second_daemon,
403                              emsg);
404   if (GNUNET_YES == update_meter (pg_start_ctx->connect_meter))
405     {
406 #if VERBOSE
407       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
408           "Created %d total connections, which is our target number!  Starting next phase of testing.\n",
409           total_connections);
410 #endif
411
412 #if TIMING
413       total_duration
414           = GNUNET_TIME_absolute_get_difference (connect_start_time,
415                                                  GNUNET_TIME_absolute_get ()).rel_value
416               / 1000;
417       failed_conns_per_sec_total = (double) failed_connections / total_duration;
418       conns_per_sec_total = (double) total_connections / total_duration;
419       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
420                   "Overall connection info --- Total: %u, Total Failed %u/s\n",
421                   total_connections, failed_connections);
422       GNUNET_log (
423                   GNUNET_ERROR_TYPE_WARNING,
424                   "Overall connection info --- Total: %.2f/s, Total Failed %.2f/s\n",
425                   conns_per_sec_total, failed_conns_per_sec_total);
426 #endif
427
428       GNUNET_assert(pg_start_ctx->die_task != GNUNET_SCHEDULER_NO_TASK);
429       GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
430
431       /* Call final callback, signifying that the peer group has been started and connected */
432       if (pg_start_ctx->peergroup_cb != NULL)
433         pg_start_ctx->peergroup_cb(pg_start_ctx->cls, NULL);
434
435       if (pg_start_ctx->topology_output_file != NULL)
436         {
437           temp = GNUNET_asprintf(&temp_str, "}\n");
438           if (temp > 0)
439             GNUNET_DISK_file_write(pg_start_ctx->topology_output_file, temp_str, temp);
440           GNUNET_free(temp_str);
441           GNUNET_DISK_file_close(pg_start_ctx->topology_output_file);
442         }
443     }
444 }
445
446 static void
447 internal_peers_started_callback(void *cls, const struct GNUNET_PeerIdentity *id,
448     const struct GNUNET_CONFIGURATION_Handle *cfg,
449     struct GNUNET_TESTING_Daemon *d, const char *emsg)
450 {
451   struct PeerGroupStartupContext *pg_start_ctx = cls;
452   if (emsg != NULL)
453     {
454       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
455                   "Failed to start daemon with error: `%s'\n", emsg);
456       return;
457     }
458   GNUNET_assert (id != NULL);
459
460 #if VERBOSE > 1
461   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
462       (num_peers - peers_left) + 1, num_peers);
463 #endif
464
465   pg_start_ctx->peers_left--;
466
467   if (GNUNET_YES == update_meter (pg_start_ctx->peer_start_meter))
468     {
469 #if VERBOSE
470       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471           "All %d daemons started, now connecting peers!\n",
472           num_peers);
473 #endif
474       GNUNET_assert(pg_start_ctx->die_task != GNUNET_SCHEDULER_NO_TASK);
475       GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
476
477       pg_start_ctx->expected_connections = UINT_MAX;
478       if ((pg_start_ctx->pg != NULL) && (pg_start_ctx->peers_left == 0))
479         {
480           pg_start_ctx->connect_start_time = GNUNET_TIME_absolute_get ();
481           pg_start_ctx->expected_connections
482               = GNUNET_TESTING_connect_topology (
483                                                  pg_start_ctx->pg,
484                                                  pg_start_ctx->connect_topology,
485                                                  pg_start_ctx->connect_topology_option,
486                                                  pg_start_ctx->connect_topology_option_modifier,
487                                                  DEFAULT_CONNECT_TIMEOUT,
488                                                  pg_start_ctx->connect_attempts,
489                                                  NULL, NULL);
490
491           pg_start_ctx->connect_meter
492               = create_meter (pg_start_ctx->expected_connections,
493                               "Peer connection ", pg_start_ctx->verbose);
494           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495                       "Have %d expected connections\n",
496                       pg_start_ctx->expected_connections);
497         }
498
499       if (pg_start_ctx->expected_connections == 0)
500         {
501           GNUNET_free_non_null(pg_start_ctx->fail_reason);
502           pg_start_ctx->fail_reason = GNUNET_strdup("from connect topology (bad return)");
503           pg_start_ctx->die_task
504               = GNUNET_SCHEDULER_add_now (&end_badly,
505                                           pg_start_ctx);
506         }
507
508       GNUNET_free_non_null(pg_start_ctx->fail_reason);
509       pg_start_ctx->fail_reason = GNUNET_strdup("from connect topology (timeout)");
510       pg_start_ctx->die_task
511           = GNUNET_SCHEDULER_add_delayed (
512                                           GNUNET_TIME_absolute_get_remaining (pg_start_ctx->timeout),
513                                           &end_badly,
514                                           pg_start_ctx);
515     }
516 }
517
518 /**
519  * Callback indicating that the hostkey was created for a peer.
520  *
521  * @param cls NULL
522  * @param id the peer identity
523  * @param d the daemon handle (pretty useless at this point, remove?)
524  * @param emsg non-null on failure
525  */
526 static void
527 internal_hostkey_callback(void *cls, const struct GNUNET_PeerIdentity *id,
528                           struct GNUNET_TESTING_Daemon *d, const char *emsg)
529 {
530   struct PeerGroupStartupContext *pg_start_ctx = cls;
531   unsigned int create_expected_connections;
532
533   if (emsg != NULL)
534     {
535       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
536                   "Hostkey callback received error: %s\n", emsg);
537     }
538
539 #if VERBOSE > 1
540   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
541       "Hostkey (%d/%d) created for peer `%s'\n",
542       num_peers - peers_left, num_peers, GNUNET_i2s(id));
543 #endif
544
545   pg_start_ctx->peers_left--;
546   if (GNUNET_YES == update_meter (pg_start_ctx->hostkey_meter))
547     {
548       GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
549       /* Set up task in case topology creation doesn't finish
550        * within a reasonable amount of time */
551       pg_start_ctx->die_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining(pg_start_ctx->timeout),
552                                                              &end_badly,
553                                                              "from create_topology");
554       pg_start_ctx->peers_left = pg_start_ctx->total; /* Reset counter */
555       create_expected_connections = GNUNET_TESTING_create_topology (pg_start_ctx->pg, pg_start_ctx->topology, pg_start_ctx->restrict_topology,
556                                                                     pg_start_ctx->restrict_transports);
557       if (create_expected_connections > 0)
558         {
559           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
560                       "Topology set up, have %u expected connections, now starting peers!\n", create_expected_connections);
561           GNUNET_TESTING_daemons_continue_startup (pg_start_ctx->pg);
562         }
563       else
564         {
565           GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
566           pg_start_ctx->die_task = GNUNET_SCHEDULER_add_now (&end_badly,
567                                                "from create topology (bad return)");
568         }
569
570       GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
571       pg_start_ctx->die_task
572           = GNUNET_SCHEDULER_add_delayed (
573                                           GNUNET_TIME_absolute_get_remaining(pg_start_ctx->timeout),
574                                           &end_badly,
575                                           "from continue startup (timeout)");
576     }
577 }
578
579
580 /**
581  * Start a peer group with a given number of peers.  Notify
582  * on completion of peer startup and connection based on given
583  * topological constraints.  Optionally notify on each
584  * established connection.
585  *
586  * @param cfg configuration template to use
587  * @param total number of daemons to start
588  * @param timeout total time allowed for peers to start
589  * @param connect_cb function to call each time two daemons are connected
590  * @param peergroup_cb function to call once all peers are up and connected
591  * @param peergroup_cls closure for peergroup callbacks
592  * @param hostnames linked list of host structs to use to start peers on
593  *                  (NULL to run on localhost only)
594  *
595  * @return NULL on error, otherwise handle to control peer group
596  */
597 struct GNUNET_TESTING_PeerGroup *
598 GNUNET_TESTING_peergroup_start(
599                                const struct GNUNET_CONFIGURATION_Handle *cfg,
600                                unsigned int total,
601                                struct GNUNET_TIME_Relative timeout,
602                                GNUNET_TESTING_NotifyConnection connect_cb,
603                                GNUNET_TESTING_NotifyCompletion peergroup_cb,
604                                void *peergroup_cls,
605                                const struct GNUNET_TESTING_Host *hostnames)
606 {
607   struct PeerGroupStartupContext *pg_start_ctx;
608   unsigned long long temp_config_number;
609   char *temp_str;
610   int temp;
611   GNUNET_assert(total > 0);
612   GNUNET_assert(cfg != NULL);
613
614   pg_start_ctx = GNUNET_malloc(sizeof(struct PeerGroupStartupContext));
615
616   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
617                                                           "connect_attempts",
618                                                           &pg_start_ctx->connect_attempts))
619     {
620       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
621                   "testing", "connect_attempts");
622       GNUNET_free(pg_start_ctx);
623       return NULL;
624     }
625
626   if (GNUNET_OK
627       != GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
628                                                 "max_outstanding_connections",
629                                                 &pg_start_ctx->max_concurrent_connections))
630     {
631       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
632                   "testing", "max_outstanding_connections");
633       GNUNET_free(pg_start_ctx);
634       return NULL;
635     }
636
637   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
638                                                           "max_concurrent_ssh",
639                                                           &pg_start_ctx->max_concurrent_ssh))
640     {
641       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
642                   "testing", "max_concurrent_ssh");
643       GNUNET_free(pg_start_ctx);
644       return NULL;
645     }
646
647   if (GNUNET_SYSERR == (pg_start_ctx->verbose = GNUNET_CONFIGURATION_get_value_yesno (cfg, "testing",
648                                                           "use_progressbars")))
649     {
650       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
651                   "testing", "use_progressbars");
652       GNUNET_free(pg_start_ctx);
653       return NULL;
654     }
655
656   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
657                                                           "peergroup_timeout",
658                                                           &temp_config_number))
659     pg_start_ctx->timeout = GNUNET_TIME_relative_to_absolute(GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
660                                                      temp_config_number));
661   else
662     {
663       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
664                   "testing", "peergroup_timeout");
665       GNUNET_free(pg_start_ctx);
666       return NULL;
667     }
668
669
670   /* Read topology related options from the configuration file */
671   temp_str = NULL;
672   if ((GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
673                                                             "topology",
674                                                             &temp_str))
675       && (GNUNET_NO == GNUNET_TESTING_topology_get (&pg_start_ctx->topology, temp_str)))
676     {
677       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
678                   "Invalid topology `%s' given for section %s option %s\n",
679                   temp_str, "TESTING", "TOPOLOGY");
680       pg_start_ctx->topology = GNUNET_TESTING_TOPOLOGY_CLIQUE; /* Defaults to NONE, so set better default here */
681     }
682   GNUNET_free_non_null(temp_str);
683
684   if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "topology_output_file", &temp_str))
685     {
686       pg_start_ctx->topology_output_file = GNUNET_DISK_file_open (temp_str, GNUNET_DISK_OPEN_READWRITE
687                                                                   | GNUNET_DISK_OPEN_CREATE,
688                                                                   GNUNET_DISK_PERM_USER_READ |
689                                                                   GNUNET_DISK_PERM_USER_WRITE);
690       if (pg_start_ctx->topology_output_file != NULL)
691         {
692           GNUNET_free(temp_str);
693           temp = GNUNET_asprintf(&temp_str, "digraph G {\n");
694           if (temp > 0)
695             GNUNET_DISK_file_write(pg_start_ctx->topology_output_file, temp_str, temp);
696         }
697       GNUNET_free(temp_str);
698     }
699
700   if (GNUNET_OK
701       != GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "percentage",
702                                                 &temp_str))
703     pg_start_ctx->topology_percentage = 0.5;
704   else
705     {
706       pg_start_ctx->topology_percentage = atof (temp_str);
707       GNUNET_free(temp_str);
708     }
709
710   if (GNUNET_OK
711       != GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "probability",
712                                                 &temp_str))
713     pg_start_ctx->topology_probability = 0.5;
714   else
715     {
716       pg_start_ctx->topology_probability = atof (temp_str);
717       GNUNET_free(temp_str);
718     }
719
720   if ((GNUNET_YES
721       == GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
722                                                 "connect_topology",
723                                                 &temp_str))
724       && (GNUNET_NO == GNUNET_TESTING_topology_get (&pg_start_ctx->connect_topology,
725                                                     temp_str)))
726     {
727       GNUNET_log (
728                   GNUNET_ERROR_TYPE_WARNING,
729                   "Invalid connect topology `%s' given for section %s option %s\n",
730                   temp_str, "TESTING", "CONNECT_TOPOLOGY");
731     }
732   GNUNET_free_non_null(temp_str);
733
734   if ((GNUNET_YES
735       == GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
736                                                 "connect_topology_option",
737                                                 &temp_str))
738       && (GNUNET_NO
739           == GNUNET_TESTING_topology_option_get (&pg_start_ctx->connect_topology_option,
740                                                  temp_str)))
741     {
742       GNUNET_log (
743                   GNUNET_ERROR_TYPE_WARNING,
744                   "Invalid connect topology option `%s' given for section %s option %s\n",
745                   temp_str, "TESTING",
746                   "CONNECT_TOPOLOGY_OPTION");
747       pg_start_ctx->connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; /* Defaults to NONE, set to ALL */
748     }
749   GNUNET_free_non_null(temp_str);
750
751   if (GNUNET_YES
752       == GNUNET_CONFIGURATION_get_value_string (
753                                                 cfg,
754                                                 "testing",
755                                                 "connect_topology_option_modifier",
756                                                 &temp_str))
757     {
758       if (sscanf (temp_str, "%lf",
759                   &pg_start_ctx->connect_topology_option_modifier) != 1)
760         {
761           GNUNET_log (
762                       GNUNET_ERROR_TYPE_WARNING,
763                       _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
764                           temp_str,
765                       "connect_topology_option_modifier", "TESTING");
766           GNUNET_free (temp_str);
767           GNUNET_free(pg_start_ctx);
768           return NULL;
769         }
770       GNUNET_free (temp_str);
771     }
772
773   if (GNUNET_YES
774       != GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
775                                                 "blacklist_transports",
776                                                 &pg_start_ctx->restrict_transports))
777     pg_start_ctx->restrict_transports = NULL;
778
779   if ((GNUNET_YES
780       == GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
781                                                 "blacklist_topology",
782                                                 &temp_str))
783       && (GNUNET_NO == GNUNET_TESTING_topology_get (&pg_start_ctx->restrict_topology,
784                                                     temp_str)))
785     {
786       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
787                   "Invalid topology `%s' given for section %s option %s\n",
788                   temp_str, "TESTING", "BLACKLIST_TOPOLOGY");
789     }
790   GNUNET_free_non_null(temp_str);
791
792   pg_start_ctx->cfg = cfg;
793   pg_start_ctx->total = total;
794   pg_start_ctx->peers_left = total;
795   pg_start_ctx->connect_cb = connect_cb;
796   pg_start_ctx->peergroup_cb = peergroup_cb;
797   pg_start_ctx->cls = peergroup_cls;
798   pg_start_ctx->hostnames = hostnames;
799   pg_start_ctx->hostkey_meter = create_meter (pg_start_ctx->peers_left, "Hostkeys created ", pg_start_ctx->verbose);
800   pg_start_ctx->peer_start_meter = create_meter (pg_start_ctx->peers_left, "Peers started ", pg_start_ctx->verbose);
801   /* Make compilers happy */
802   reset_meter(pg_start_ctx->peer_start_meter);
803   pg_start_ctx->die_task
804       = GNUNET_SCHEDULER_add_delayed (
805                                       GNUNET_TIME_absolute_get_remaining (
806                                                                           pg_start_ctx->timeout),
807                                       &end_badly,
808                                       "didn't generate all hostkeys within allowed startup time!");
809
810   pg_start_ctx->pg
811       = GNUNET_TESTING_daemons_start (
812                                       pg_start_ctx->cfg,
813                                       pg_start_ctx->peers_left,
814                                       pg_start_ctx->max_concurrent_connections,
815                                       pg_start_ctx->max_concurrent_ssh,
816                                       GNUNET_TIME_absolute_get_remaining(pg_start_ctx->timeout),
817                                       &internal_hostkey_callback, pg_start_ctx,
818                                       &internal_peers_started_callback,
819                                       pg_start_ctx,
820                                       &internal_topology_callback,
821                                       pg_start_ctx, pg_start_ctx->hostnames);
822
823   return pg_start_ctx->pg;
824 }
825
826 /* end of testing_peergroup.c */