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