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