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