-docu
[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
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   struct GNUNET_TIME_Relative connect_timeout;
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 == 0) ? 1 : (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, "%s",  ".");
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                                            pg_start_ctx->connect_timeout,
512                                            pg_start_ctx->connect_attempts, NULL,
513                                            NULL);
514
515       pg_start_ctx->connect_meter =
516           create_meter (pg_start_ctx->expected_connections, "Peer connection ",
517                         pg_start_ctx->verbose);
518       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Have %d expected connections\n",
519                   pg_start_ctx->expected_connections);
520     }
521
522     if (pg_start_ctx->expected_connections == 0)
523     {
524       GNUNET_free_non_null (pg_start_ctx->fail_reason);
525       pg_start_ctx->fail_reason =
526           GNUNET_strdup ("from connect topology (bad return)");
527       pg_start_ctx->die_task =
528           GNUNET_SCHEDULER_add_now (&end_badly, pg_start_ctx);
529       return;
530     }
531
532     GNUNET_free_non_null (pg_start_ctx->fail_reason);
533     pg_start_ctx->fail_reason =
534         GNUNET_strdup ("from connect topology (timeout)");
535     pg_start_ctx->die_task =
536         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
537                                       (pg_start_ctx->timeout), &end_badly,
538                                       pg_start_ctx);
539   }
540 }
541
542 /**
543  * Callback indicating that the hostkey was created for a peer.
544  *
545  * @param cls NULL
546  * @param id the peer identity
547  * @param d the daemon handle (pretty useless at this point, remove?)
548  * @param emsg non-null on failure
549  */
550 static void
551 internal_hostkey_callback (void *cls, const struct GNUNET_PeerIdentity *id,
552                            struct GNUNET_TESTING_Daemon *d, const char *emsg)
553 {
554   struct PeerGroupStartupContext *pg_start_ctx = cls;
555   unsigned int create_expected_connections;
556
557   if (emsg != NULL)
558   {
559     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
560                 "Hostkey callback received error: %s\n", emsg);
561   }
562
563 #if VERBOSE > 1
564   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
565               "Hostkey (%d/%d) created for peer `%s'\n",
566               pg_start_ctx->total - pg_start_ctx->peers_left + 1,
567               pg_start_ctx->total, GNUNET_i2s (id));
568 #endif
569
570   pg_start_ctx->peers_left--;
571   if (GNUNET_YES == update_meter (pg_start_ctx->hostkey_meter))
572   {
573     GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
574     GNUNET_free_non_null (pg_start_ctx->fail_reason);
575     /* Set up task in case topology creation doesn't finish
576      * within a reasonable amount of time */
577     pg_start_ctx->fail_reason = GNUNET_strdup ("from create_topology");
578     pg_start_ctx->die_task =
579         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
580                                       (pg_start_ctx->timeout), &end_badly,
581                                       pg_start_ctx);
582     pg_start_ctx->peers_left = pg_start_ctx->total;     /* Reset counter */
583     create_expected_connections =
584         GNUNET_TESTING_create_topology (pg_start_ctx->pg,
585                                         pg_start_ctx->topology,
586                                         pg_start_ctx->restrict_topology,
587                                         pg_start_ctx->restrict_transports);
588     if (create_expected_connections > 0)
589     {
590       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
591                   "Topology set up, have %u expected connections, now starting peers!\n",
592                   create_expected_connections);
593       GNUNET_TESTING_daemons_continue_startup (pg_start_ctx->pg);
594     }
595     else
596     {
597       GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
598       GNUNET_free_non_null (pg_start_ctx->fail_reason);
599       pg_start_ctx->fail_reason =
600           GNUNET_strdup ("from create topology (bad return)");
601       pg_start_ctx->die_task =
602           GNUNET_SCHEDULER_add_now (&end_badly, pg_start_ctx);
603       return;
604     }
605
606     GNUNET_SCHEDULER_cancel (pg_start_ctx->die_task);
607     GNUNET_free_non_null (pg_start_ctx->fail_reason);
608     pg_start_ctx->fail_reason =
609         GNUNET_strdup ("from continue startup (timeout)");
610     pg_start_ctx->die_task =
611         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
612                                       (pg_start_ctx->timeout), &end_badly,
613                                       pg_start_ctx);
614   }
615 }
616
617
618 /**
619  * Prototype of a callback function indicating that two peers
620  * are currently connected.
621  *
622  * @param cls closure
623  * @param first peer id for first daemon
624  * @param second peer id for the second daemon
625  * @param emsg error message (NULL on success)
626  */
627 void
628 write_topology_cb (void *cls, const struct GNUNET_PeerIdentity *first,
629                    const struct GNUNET_PeerIdentity *second, const char *emsg)
630 {
631   struct TopologyOutputContext *topo_ctx;
632   int temp;
633   char *temp_str;
634   char *temp_pid2;
635
636   topo_ctx = (struct TopologyOutputContext *) cls;
637   GNUNET_assert (topo_ctx->file != NULL);
638   if ((emsg == NULL) && (first != NULL) && (second != NULL))
639   {
640     GNUNET_assert (first != NULL);
641     GNUNET_assert (second != NULL);
642     temp_pid2 = GNUNET_strdup (GNUNET_i2s (second));
643     temp =
644         GNUNET_asprintf (&temp_str, "\t\"%s\" -- \"%s\"\n", GNUNET_i2s (first),
645                          temp_pid2);
646     GNUNET_free (temp_pid2);
647     GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp);
648   }
649   else if ((emsg == NULL) && (first == NULL) && (second == NULL))
650   {
651     temp = GNUNET_asprintf (&temp_str, "}\n");
652     GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp);
653     GNUNET_DISK_file_close (topo_ctx->file);
654     topo_ctx->notify_cb (topo_ctx->notify_cb_cls, NULL);
655     GNUNET_free (topo_ctx);
656   }
657   else
658   {
659     temp = GNUNET_asprintf (&temp_str, "}\n");
660     GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp);
661     GNUNET_DISK_file_close (topo_ctx->file);
662     topo_ctx->notify_cb (topo_ctx->notify_cb_cls, emsg);
663     GNUNET_free (topo_ctx);
664   }
665 }
666
667 /**
668  * Print current topology to a graphviz readable file.
669  *
670  * @param pg a currently running peergroup to print to file
671  * @param output_filename the file to write the topology to
672  * @param notify_cb callback to call upon completion or failure
673  * @param notify_cb_cls closure for notify_cb
674  *
675  */
676 void
677 GNUNET_TESTING_peergroup_topology_to_file (struct GNUNET_TESTING_PeerGroup *pg,
678                                            const char *output_filename,
679                                            GNUNET_TESTING_NotifyCompletion
680                                            notify_cb, void *notify_cb_cls)
681 {
682   struct TopologyOutputContext *topo_ctx;
683   int temp;
684   char *temp_str;
685
686   topo_ctx = GNUNET_malloc (sizeof (struct TopologyOutputContext));
687
688   topo_ctx->notify_cb = notify_cb;
689   topo_ctx->notify_cb_cls = notify_cb_cls;
690   topo_ctx->file =
691       GNUNET_DISK_file_open (output_filename,
692                              GNUNET_DISK_OPEN_READWRITE |
693                              GNUNET_DISK_OPEN_CREATE,
694                              GNUNET_DISK_PERM_USER_READ |
695                              GNUNET_DISK_PERM_USER_WRITE);
696   if (topo_ctx->file == NULL)
697   {
698     notify_cb (notify_cb_cls, "Failed to open output file!");
699     GNUNET_free (topo_ctx);
700     return;
701   }
702
703   temp = GNUNET_asprintf (&temp_str, "strict graph G {\n");
704   if (temp > 0)
705     GNUNET_DISK_file_write (topo_ctx->file, temp_str, temp);
706   GNUNET_free_non_null (temp_str);
707   GNUNET_TESTING_get_topology (pg, &write_topology_cb, topo_ctx);
708 }
709
710 /**
711  * Start a peer group with a given number of peers.  Notify
712  * on completion of peer startup and connection based on given
713  * topological constraints.  Optionally notify on each
714  * established connection.
715  *
716  * @param cfg configuration template to use
717  * @param total number of daemons to start
718  * @param timeout total time allowed for peers to start
719  * @param connect_cb function to call each time two daemons are connected
720  * @param peergroup_cb function to call once all peers are up and connected
721  * @param peergroup_cls closure for peergroup callbacks
722  * @param hostnames linked list of host structs to use to start peers on
723  *                  (NULL to run on localhost only)
724  *
725  * @return NULL on error, otherwise handle to control peer group
726  */
727 struct GNUNET_TESTING_PeerGroup *
728 GNUNET_TESTING_peergroup_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
729                                 unsigned int total,
730                                 struct GNUNET_TIME_Relative timeout,
731                                 GNUNET_TESTING_NotifyConnection connect_cb,
732                                 GNUNET_TESTING_NotifyCompletion peergroup_cb,
733                                 void *peergroup_cls,
734                                 const struct GNUNET_TESTING_Host *hostnames)
735 {
736   struct PeerGroupStartupContext *pg_start_ctx;
737   char *temp_str;
738   int temp;
739   struct GNUNET_TIME_Relative rtimeout;
740
741   GNUNET_assert (total > 0);
742   GNUNET_assert (cfg != NULL);
743
744   pg_start_ctx = GNUNET_malloc (sizeof (struct PeerGroupStartupContext));
745
746   if (GNUNET_OK !=
747       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_attempts",
748                                              &pg_start_ctx->connect_attempts))
749   {
750     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
751                 "testing", "connect_attempts");
752     GNUNET_free (pg_start_ctx);
753     return NULL;
754   }
755
756   if (GNUNET_OK !=
757       GNUNET_CONFIGURATION_get_value_time (cfg, "testing", "CONNECT_TIMEOUT",
758                                            &pg_start_ctx->connect_timeout))
759   {
760     pg_start_ctx->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
761   }
762
763   if (GNUNET_OK !=
764       GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
765                                              "max_outstanding_connections",
766                                              &pg_start_ctx->max_concurrent_connections))
767   {
768     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
769                 "testing", "max_outstanding_connections");
770     GNUNET_free (pg_start_ctx);
771     return NULL;
772   }
773
774   if (GNUNET_OK !=
775       GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
776                                              "max_concurrent_ssh",
777                                              &pg_start_ctx->max_concurrent_ssh))
778   {
779     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
780                 "testing", "max_concurrent_ssh");
781     GNUNET_free (pg_start_ctx);
782     return NULL;
783   }
784
785   if (GNUNET_SYSERR ==
786       (pg_start_ctx->verbose =
787        GNUNET_CONFIGURATION_get_value_yesno (cfg, "testing",
788                                              "use_progressbars")))
789   {
790     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
791                 "testing", "use_progressbars");
792     GNUNET_free (pg_start_ctx);
793     return NULL;
794   }
795
796   if (GNUNET_OK !=
797       GNUNET_CONFIGURATION_get_value_time (cfg, "testing", "PEERGROUP_TIMEOUT",
798                                            &rtimeout))
799   {
800     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
801                 "testing", "PEERGROUP_TIMEOUT");
802     GNUNET_free (pg_start_ctx);
803     return NULL;
804   }
805   pg_start_ctx->timeout = GNUNET_TIME_relative_to_absolute (rtimeout);
806
807
808   /* Read topology related options from the configuration file */
809   temp_str = NULL;
810   if ((GNUNET_YES ==
811        GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "topology",
812                                               &temp_str)) &&
813       (GNUNET_NO ==
814        GNUNET_TESTING_topology_get (&pg_start_ctx->topology, temp_str)))
815   {
816     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
817                 "Invalid topology `%s' given for section %s option %s\n",
818                 temp_str, "TESTING", "TOPOLOGY");
819     pg_start_ctx->topology = GNUNET_TESTING_TOPOLOGY_CLIQUE;    /* Defaults to NONE, so set better default here */
820   }
821   GNUNET_free_non_null (temp_str);
822
823   if (GNUNET_YES ==
824       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
825                                              "topology_output_file", &temp_str))
826   {
827     pg_start_ctx->topology_output_file =
828         GNUNET_DISK_file_open (temp_str,
829                                GNUNET_DISK_OPEN_READWRITE |
830                                GNUNET_DISK_OPEN_CREATE,
831                                GNUNET_DISK_PERM_USER_READ |
832                                GNUNET_DISK_PERM_USER_WRITE);
833     if (pg_start_ctx->topology_output_file != NULL)
834     {
835       GNUNET_free (temp_str);
836       temp = GNUNET_asprintf (&temp_str, "strict graph G {\n");
837       if (temp > 0)
838         GNUNET_DISK_file_write (pg_start_ctx->topology_output_file, temp_str,
839                                 temp);
840     }
841     GNUNET_free (temp_str);
842   }
843
844   if (GNUNET_OK !=
845       GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "percentage",
846                                              &temp_str))
847     pg_start_ctx->topology_percentage = 0.5;
848   else
849   {
850     pg_start_ctx->topology_percentage = atof (temp_str);
851     GNUNET_free (temp_str);
852   }
853
854   if (GNUNET_OK !=
855       GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "probability",
856                                              &temp_str))
857     pg_start_ctx->topology_probability = 0.5;
858   else
859   {
860     pg_start_ctx->topology_probability = atof (temp_str);
861     GNUNET_free (temp_str);
862   }
863
864   if ((GNUNET_YES ==
865        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
866                                               "connect_topology", &temp_str)) &&
867       (GNUNET_NO ==
868        GNUNET_TESTING_topology_get (&pg_start_ctx->connect_topology, temp_str)))
869   {
870     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
871                 "Invalid connect topology `%s' given for section %s option %s\n",
872                 temp_str, "TESTING", "CONNECT_TOPOLOGY");
873   }
874   GNUNET_free_non_null (temp_str);
875
876   if ((GNUNET_YES ==
877        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
878                                               "connect_topology_option",
879                                               &temp_str)) &&
880       (GNUNET_NO ==
881        GNUNET_TESTING_topology_option_get
882        (&pg_start_ctx->connect_topology_option, temp_str)))
883   {
884     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
885                 "Invalid connect topology option `%s' given for section %s option %s\n",
886                 temp_str, "TESTING", "CONNECT_TOPOLOGY_OPTION");
887     pg_start_ctx->connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; /* Defaults to NONE, set to ALL */
888   }
889   GNUNET_free_non_null (temp_str);
890
891   if (GNUNET_YES ==
892       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
893                                              "connect_topology_option_modifier",
894                                              &temp_str))
895   {
896     if (sscanf
897         (temp_str, "%lf", &pg_start_ctx->connect_topology_option_modifier) != 1)
898     {
899       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
900                   _
901                   ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
902                   temp_str, "connect_topology_option_modifier", "TESTING");
903       GNUNET_free (temp_str);
904       GNUNET_free (pg_start_ctx);
905       return NULL;
906     }
907     GNUNET_free (temp_str);
908   }
909
910   if (GNUNET_YES !=
911       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
912                                              "blacklist_transports",
913                                              &pg_start_ctx->restrict_transports))
914     pg_start_ctx->restrict_transports = NULL;
915
916   pg_start_ctx->restrict_topology = GNUNET_TESTING_TOPOLOGY_NONE;
917   if ((GNUNET_YES ==
918        GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
919                                               "blacklist_topology", &temp_str))
920       && (GNUNET_NO ==
921           GNUNET_TESTING_topology_get (&pg_start_ctx->restrict_topology,
922                                        temp_str)))
923   {
924     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
925                 "Invalid topology `%s' given for section %s option %s\n",
926                 temp_str, "TESTING", "BLACKLIST_TOPOLOGY");
927   }
928
929   GNUNET_free_non_null (temp_str);
930
931   pg_start_ctx->cfg = cfg;
932   pg_start_ctx->total = total;
933   pg_start_ctx->peers_left = total;
934   pg_start_ctx->connect_cb = connect_cb;
935   pg_start_ctx->peergroup_cb = peergroup_cb;
936   pg_start_ctx->cls = peergroup_cls;
937   pg_start_ctx->hostnames = hostnames;
938   pg_start_ctx->hostkey_meter =
939       create_meter (pg_start_ctx->peers_left, "Hostkeys created ",
940                     pg_start_ctx->verbose);
941   pg_start_ctx->peer_start_meter =
942       create_meter (pg_start_ctx->peers_left, "Peers started ",
943                     pg_start_ctx->verbose);
944   /* Make compilers happy */
945   reset_meter (pg_start_ctx->peer_start_meter);
946   pg_start_ctx->fail_reason =
947       GNUNET_strdup
948       ("didn't generate all hostkeys within allowed startup time!");
949   pg_start_ctx->die_task =
950       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
951                                     (pg_start_ctx->timeout), &end_badly,
952                                     pg_start_ctx);
953
954   pg_start_ctx->pg =
955       GNUNET_TESTING_daemons_start (pg_start_ctx->cfg, pg_start_ctx->peers_left,
956                                     pg_start_ctx->max_concurrent_connections,
957                                     pg_start_ctx->max_concurrent_ssh,
958                                     GNUNET_TIME_absolute_get_remaining
959                                     (pg_start_ctx->timeout),
960                                     &internal_hostkey_callback, pg_start_ctx,
961                                     &internal_peers_started_callback,
962                                     pg_start_ctx, &internal_topology_callback,
963                                     pg_start_ctx, pg_start_ctx->hostnames);
964
965   return pg_start_ctx->pg;
966 }
967
968 /* end of testing_peergroup.c */