indenting
[oweals/gnunet.git] / src / testing / testing_group.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 2009 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 2, 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_group.c
23  * @brief convenience API for writing testcases for GNUnet
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_testing_lib.h"
29
30 #define VERBOSE_TESTING GNUNET_NO
31
32 #define VERBOSE_TOPOLOGY GNUNET_NO
33
34 #define DEBUG_CHURN GNUNET_NO
35
36 /**
37  * Lowest port used for GNUnet testing.  Should be high enough to not
38  * conflict with other applications running on the hosts but be low
39  * enough to not conflict with client-ports (typically starting around
40  * 32k).
41  */
42 #define LOW_PORT 10000
43
44 /**
45  * Highest port used for GNUnet testing.  Should be low enough to not
46  * conflict with the port range for "local" ports (client apps; see
47  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
48  */
49 #define HIGH_PORT 32000
50
51 #define MAX_OUTSTANDING_CONNECTIONS 50
52
53 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
54
55 #define CONNECT_ATTEMPTS 8
56
57 /**
58  * Prototype of a function called whenever two peers would be connected
59  * in a certain topology.
60  */
61 typedef int (*GNUNET_TESTING_ConnectionProcessor)
62 (struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second);
63
64 /**
65  * Strings representing topologies in enum
66  */
67 static char * GNUNET_TESTING_TopologyStrings[] =
68 {
69   /**
70    * A clique (everyone connected to everyone else).
71    */
72   "CLIQUE",
73
74   /**
75    * Small-world network (2d torus plus random links).
76    */
77   "SMALL_WORLD",
78
79   /**
80    * Small-world network (ring plus random links).
81    */
82   "SMALL_WORLD_RING",
83
84   /**
85    * Ring topology.
86    */
87   "RING",
88
89   /**
90    * 2-d torus.
91    */
92   "2D_TORUS",
93
94   /**
95    * Random graph.
96    */
97   "ERDOS_RENYI",
98
99   /**
100    * Certain percentage of peers are unable to communicate directly
101    * replicating NAT conditions
102    */
103   "INTERNAT",
104
105   /**
106    * Scale free topology.
107    */
108   "SCALE_FREE",
109
110   /**
111    * All peers are disconnected.
112    */
113   "NONE"
114 };
115
116 /**
117  * Options for connecting a topology as strings.
118  */
119 static char * GNUNET_TESTING_TopologyOptionStrings[] =
120 {
121   /**
122    * Try to connect all peers specified in the topology.
123    */
124   "CONNECT_ALL",
125
126   /**
127    * Choose a random subset of connections to create.
128    */
129   "CONNECT_RANDOM_SUBSET",
130
131   /**
132    * Create at least X connections for each peer.
133    */
134   "CONNECT_MINIMUM",
135
136   /**
137    * Using a depth first search, create one connection
138    * per peer.  If any are missed (graph disconnected)
139    * start over at those peers until all have at least one
140    * connection.
141    */
142   "CONNECT_DFS",
143
144   /**
145    * No options specified.
146    */
147   "CONNECT_NONE"
148 };
149
150 /**
151  * Context for handling churning a peer group
152  */
153 struct ChurnContext
154 {
155   /**
156    * Callback used to notify of churning finished
157    */
158   GNUNET_TESTING_NotifyCompletion cb;
159
160   /**
161    * Closure for callback
162    */
163   void *cb_cls;
164
165   /**
166    * Number of peers that still need to be started
167    */
168   unsigned int num_to_start;
169
170   /**
171    * Number of peers that still need to be stopped
172    */
173   unsigned int num_to_stop;
174  
175   /**
176    * Number of peers that failed to start
177    */
178   unsigned int num_failed_start;
179
180   /**
181    * Number of peers that failed to stop
182    */
183   unsigned int num_failed_stop;
184 };
185
186 struct RestartContext
187 {
188   /**
189    * The group of peers being restarted
190    */
191   struct GNUNET_TESTING_PeerGroup *peer_group;
192
193   /**
194    * How many peers have been restarted thus far
195    */
196   unsigned int peers_restarted;
197
198   /**
199    * How many peers got an error when restarting
200    */
201   unsigned int peers_restart_failed;
202
203   /**
204    * The function to call once all peers have been restarted
205    */
206   GNUNET_TESTING_NotifyCompletion callback;
207
208   /**
209    * Closure for callback function
210    */
211   void *callback_cls;
212
213 };
214
215 struct CreateTopologyContext
216 {
217
218   /**
219    * Function to call with number of connections
220    */
221   GNUNET_TESTING_NotifyConnections cont;
222
223   /**
224    * Closure for connection notification
225    */
226   void *cls;
227 };
228
229 #if OLD
230 struct PeerConnection
231 {
232   /*
233    * Linked list
234    */
235   struct PeerConnection *next;
236
237   /*
238    * Pointer to daemon handle
239    */
240   struct GNUNET_TESTING_Daemon *daemon;
241
242 };
243 #endif
244
245 /**
246  * Data we keep per peer.
247  */
248 struct PeerData
249 {
250   /**
251    * (Initial) configuration of the host.
252    * (initial because clients could change
253    *  it and we would not know about those
254    *  updates).
255    */
256   struct GNUNET_CONFIGURATION_Handle *cfg;
257
258   /**
259    * Handle for controlling the daemon.
260    */
261   struct GNUNET_TESTING_Daemon *daemon;
262
263   /**
264    * The peergroup this peer belongs to.
265    */
266   struct GNUNET_TESTING_PeerGroup *pg;
267
268   /**
269    * Linked list of peer connections (pointers)
270    */
271   //struct PeerConnection *connected_peers;
272   /**
273    * Hash map of allowed peer connections (F2F created topology)
274    */
275   struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
276
277   /**
278    * Hash map of blacklisted peers
279    */
280   struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
281
282   /**
283    * Hash map of peer connections
284    */
285   struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
286
287   /**
288    * Temporary hash map of peer connections
289    */
290   struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
291
292   /**
293    * Temporary variable for topology creation, should be reset before
294    * creating any topology so the count is valid once finished.
295    */
296   int num_connections;
297 };
298
299
300 /**
301  * Data we keep per host.
302  */
303 struct HostData
304 {
305   /**
306    * Name of the host.
307    */
308   char *hostname;
309
310   /**
311    * Lowest port that we have not yet used
312    * for GNUnet.
313    */
314   uint16_t minport;
315 };
316
317
318 /**
319  * Handle to a group of GNUnet peers.
320  */
321 struct GNUNET_TESTING_PeerGroup
322 {
323   /**
324    * Our scheduler.
325    */
326   struct GNUNET_SCHEDULER_Handle *sched;
327
328   /**
329    * Configuration template.
330    */
331   const struct GNUNET_CONFIGURATION_Handle *cfg;
332
333   /**
334    * Function to call on each started daemon.
335    */
336   GNUNET_TESTING_NotifyDaemonRunning cb;
337
338   /**
339    * Closure for cb.
340    */
341   void *cb_cls;
342
343   /*
344    * Function to call on each topology connection created
345    */
346   GNUNET_TESTING_NotifyConnection notify_connection;
347
348   /*
349    * Callback for notify_connection
350    */
351   void *notify_connection_cls;
352
353   /**
354    * NULL-terminated array of information about
355    * hosts.
356    */
357   struct HostData *hosts;
358
359   /**
360    * Array of "total" peers.
361    */
362   struct PeerData *peers;
363
364   /**
365    * Number of peers in this group.
366    */
367   unsigned int total;
368
369   /**
370    * At what time should we fail the peer startup process?
371    */
372   struct GNUNET_TIME_Absolute max_timeout;
373 };
374
375 /**
376  * Convert unique ID to hash code.
377  *
378  * @param uid unique ID to convert
379  * @param hash set to uid (extended with zeros)
380  */
381 static void
382 hash_from_uid (uint32_t uid,
383                GNUNET_HashCode *hash)
384 {
385   memset (hash, 0, sizeof(GNUNET_HashCode));
386   *((uint32_t*)hash) = uid;
387 }
388
389 /**
390  * Convert hash code to unique ID.
391  *
392  * @param uid unique ID to convert
393  * @param hash set to uid (extended with zeros)
394  */
395 static void
396 uid_from_hash (const GNUNET_HashCode *hash, uint32_t *uid)
397 {
398   memcpy (uid, hash, sizeof(uint32_t));
399 }
400
401 struct UpdateContext
402 {
403   struct GNUNET_CONFIGURATION_Handle *ret;
404   const char *hostname;
405   unsigned int nport;
406   unsigned int upnum;
407 };
408
409
410 struct ConnectContext
411 {
412   struct GNUNET_TESTING_Daemon *first;
413
414   struct GNUNET_TESTING_Daemon *second;
415
416   struct GNUNET_TESTING_PeerGroup *pg;
417 };
418
419 /**
420  * Number of connects we are waiting on, allows us to rate limit
421  * connect attempts.
422  */
423 static int outstanding_connects;
424
425 /**
426  * Get a topology from a string input.
427  *
428  * @param topology where to write the retrieved topology
429  * @param topology_string The string to attempt to
430  *        get a configuration value from
431  * @return GNUNET_YES if topology string matched a
432  *         known topology, GNUNET_NO if not
433  */
434 int
435 GNUNET_TESTING_topology_get(enum GNUNET_TESTING_Topology *topology, char * topology_string)
436 {
437   int found = 0;
438   int curr = 0;
439
440   if (topology_string == NULL)
441     return GNUNET_NO;
442
443   do
444   {
445     if (strcmp(GNUNET_TESTING_TopologyStrings[curr], topology_string) == 0)
446     {
447       found = GNUNET_YES;
448       break;
449     }
450     curr++;
451   } while (strcmp(GNUNET_TESTING_TopologyStrings[curr], "NONE") != 0);
452   *topology = curr;
453   if (found)
454     return GNUNET_YES;
455   else
456     return GNUNET_NO;
457 }
458
459 /**
460  * Get connect topology option from string input.
461  *
462  * @param topology where to write the retrieved topology
463  * @param topology_string The string to attempt to
464  *        get a configuration value from
465  * @return GNUNET_YES if string matched a known
466  *         topology option, GNUNET_NO if not
467  */
468 int
469 GNUNET_TESTING_topology_option_get(enum GNUNET_TESTING_TopologyOption *topology, char * topology_string)
470 {
471   int found = 0;
472   int curr = 0;
473
474   if (topology_string == NULL)
475     return GNUNET_NO;
476
477   do
478   {
479     if (strcmp(GNUNET_TESTING_TopologyOptionStrings[curr], topology_string) == 0)
480     {
481       found = GNUNET_YES;
482       break;
483     }
484     curr++;
485   } while (strcmp(GNUNET_TESTING_TopologyOptionStrings[curr], "CONNECT_NONE") != 0);
486   *topology = curr;
487   if (found)
488     return GNUNET_YES;
489   else
490     return GNUNET_NO;
491 }
492
493 /**
494  * Function to iterate over options.  Copies
495  * the options to the target configuration,
496  * updating PORT values as needed.
497  *
498  * @param cls closure
499  * @param section name of the section
500  * @param option name of the option
501  * @param value value of the option
502  */
503 static void
504 update_config (void *cls,
505                const char *section, const char *option, const char *value)
506 {
507   struct UpdateContext *ctx = cls;
508   unsigned int ival;
509   char cval[12];
510   char uval[128];
511
512   if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
513     {
514       if (ival != 0)
515         {
516           GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
517           value = cval;
518         }
519     }
520
521   if (0 == strcmp (option, "UNIXPATH"))
522     {
523       GNUNET_snprintf (uval, 
524                        sizeof (uval),
525                        "/tmp/test-service-%s-%u", 
526                        section,
527                        ctx->upnum++);
528       value = uval;
529     }
530
531   if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
532     {
533       value = ctx->hostname;
534     }
535
536   GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
537 }
538
539
540 /**
541  * Create a new configuration using the given configuration
542  * as a template; however, each PORT in the existing cfg
543  * must be renumbered by incrementing "*port".  If we run
544  * out of "*port" numbers, return NULL.
545  *
546  * @param cfg template configuration
547  * @param port port numbers to use, update to reflect
548  *             port numbers that were used
549  * @param upnum number to make unix domain socket names unique
550  * @param hostname hostname of the controlling host, to allow control connections from
551  *
552  * @return new configuration, NULL on error
553  */
554 static struct GNUNET_CONFIGURATION_Handle *
555 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, 
556              uint16_t * port,
557              uint32_t * upnum,
558              const char *hostname)
559 {
560   struct UpdateContext uc;
561   uint16_t orig;
562   char *control_host;
563   char *allowed_hosts;
564
565   orig = *port;
566   uc.nport = *port;
567   uc.upnum = *upnum;
568   uc.ret = GNUNET_CONFIGURATION_create ();
569   uc.hostname = hostname;
570
571   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
572   if (uc.nport >= HIGH_PORT)
573     {
574       *port = orig;
575       GNUNET_CONFIGURATION_destroy (uc.ret);
576       return NULL;
577     }
578
579   if (GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "control_host", &control_host) == GNUNET_OK)
580     {
581       GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", control_host);
582       GNUNET_CONFIGURATION_set_value_string(uc.ret, "core", "ACCEPT_FROM", allowed_hosts);
583       GNUNET_free_non_null(control_host);
584       GNUNET_free(allowed_hosts);
585     }
586
587
588   /* arm needs to know to allow connections from the host on which it is running,
589    * otherwise gnunet-arm is unable to connect to it in some instances */
590   if (hostname != NULL)
591     {
592       GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", hostname);
593       GNUNET_CONFIGURATION_set_value_string(uc.ret, "arm", "ACCEPT_FROM", allowed_hosts);
594       GNUNET_free(allowed_hosts);
595     }
596
597   *port = (uint16_t) uc.nport;
598   *upnum = uc.upnum;
599   return uc.ret;
600 }
601
602
603 /*
604  * Add entries to the peers connect list
605  *
606  * @param pg the peer group we are working with
607  * @param first index of the first peer
608  * @param second index of the second peer
609  *
610  * @return the number of connections added (can be 0, 1 or 2)
611  *         technically should only be 0 or 2, but the small price
612  *         of iterating over the lists (hashmaps in the future)
613  *         for being sure doesn't bother me!
614  *
615  */
616 static int
617 add_actual_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
618 {
619   int added;
620   int add_first;
621   int add_second;
622
623   GNUNET_HashCode hash_first;
624   GNUNET_HashCode hash_second;
625
626   hash_from_uid(first, &hash_first);
627   hash_from_uid(second, &hash_second);
628
629   add_first = GNUNET_NO;
630   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].connect_peers, &hash_second))
631     {
632       add_first = GNUNET_YES;
633     }
634
635   add_second = GNUNET_NO;
636   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].connect_peers, &hash_first))
637     {
638       add_second = GNUNET_YES;
639     }
640
641   added = 0;
642   if (add_first)
643     {
644       GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].connect_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
645       pg->peers[first].num_connections++;
646       added++;
647     }
648
649   if (add_second)
650     {
651       GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].connect_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
652       pg->peers[second].num_connections++;
653       added++;
654     }
655
656   return added;
657 }
658
659
660 /*
661  * Add entries to the peers allowed connections list
662  *
663  * @param pg the peer group we are working with
664  * @param first index of the first peer
665  * @param second index of the second peer
666  *
667  * @return the number of connections added (can be 0, 1 or 2)
668  *         technically should only be 0 or 2, but the small price
669  *         of iterating over the lists (hashmaps in the future)
670  *         for being sure doesn't bother me!
671  *
672  */
673 static int
674 add_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
675 {
676   int added;
677 #if OLD
678   struct PeerConnection *first_iter;
679   struct PeerConnection *second_iter;
680   struct PeerConnection *new_first;
681   struct PeerConnection *new_second;
682 #endif
683   int add_first;
684   int add_second;
685
686   GNUNET_HashCode hash_first;
687   GNUNET_HashCode hash_second;
688
689   hash_from_uid(first, &hash_first);
690   hash_from_uid(second, &hash_second);
691
692   add_first = GNUNET_NO;
693   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].allowed_peers, &hash_second))
694     {
695       add_first = GNUNET_YES;
696     }
697
698   add_second = GNUNET_NO;
699   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].allowed_peers, &hash_first))
700     {
701       add_second = GNUNET_YES;
702     }
703 #if OLD
704   first_iter = pg->peers[first].connected_peers;
705   while (first_iter != NULL)
706     {
707       if (first_iter->daemon == pg->peers[second].daemon)
708         add_first = GNUNET_NO;
709       first_iter = first_iter->next;
710     }
711
712   second_iter = pg->peers[second].connected_peers;
713   add_second = GNUNET_YES;
714   while (second_iter != NULL)
715     {
716       if (second_iter->daemon == pg->peers[first].daemon)
717         add_second = GNUNET_NO;
718       second_iter = second_iter->next;
719     }
720 #endif
721
722   added = 0;
723   if (add_first)
724     {
725       GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].allowed_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
726 #if OLD
727       new_first = GNUNET_malloc(sizeof(struct PeerConnection));
728       new_first->daemon = pg->peers[second].daemon;
729       new_first->next = pg->peers[first].connected_peers;
730       pg->peers[first].connected_peers = new_first;
731 #endif
732       pg->peers[first].num_connections++;
733       added++;
734     }
735
736   if (add_second)
737     {
738       GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].allowed_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
739 #if OLD
740       new_second = GNUNET_malloc(sizeof(struct PeerConnection));
741       new_second->daemon = pg->peers[first].daemon;
742       new_second->next = pg->peers[second].connected_peers;
743       pg->peers[second].connected_peers = new_second;
744       pg->peers[first].num_connections++;
745 #endif
746       pg->peers[second].num_connections++;
747       added++;
748     }
749
750   return added;
751 }
752
753 /*
754  * Add entries to the peers blacklisted list
755  *
756  * @param pg the peer group we are working with
757  * @param first index of the first peer
758  * @param second index of the second peer
759  *
760  * @return the number of connections added (can be 0, 1 or 2)
761  *
762  */
763 static int
764 blacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
765 {
766   int added;
767   int add_first;
768   int add_second;
769   GNUNET_HashCode hash_first;
770   GNUNET_HashCode hash_second;
771
772   hash_from_uid(first, &hash_first);
773   hash_from_uid(second, &hash_second);
774
775   add_first = GNUNET_NO;
776   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second))
777     {
778       add_first = GNUNET_YES;
779     }
780
781   add_second = GNUNET_NO;
782   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first))
783     {
784       add_second = GNUNET_YES;
785     }
786
787   added = 0;
788   if (add_first)
789     {
790       GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
791       pg->peers[first].num_connections++;
792       added++;
793     }
794
795   if (add_second)
796     {
797       GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
798       pg->peers[second].num_connections++;
799       added++;
800     }
801
802   return added;
803 }
804
805 /*
806  * Remove entries from the peers blacklisted list
807  *
808  * @param pg the peer group we are working with
809  * @param first index of the first peer
810  * @param second index of the second peer
811  *
812  * @return the number of connections removed (can be 0, 1 or 2)
813  *
814  */
815 static int
816 unblacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
817 {
818   int removed;
819   int remove_first;
820   int remove_second;
821   GNUNET_HashCode hash_first;
822   GNUNET_HashCode hash_second;
823
824   hash_from_uid(first, &hash_first);
825   hash_from_uid(second, &hash_second);
826
827   remove_first = GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second);
828   remove_second = GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first);
829
830   removed = 0;
831   if (remove_first)
832     {
833       GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon));
834       removed++;
835     }
836
837   if (remove_second)
838     {
839       GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon));
840       removed++;
841     }
842
843   return removed;
844 }
845
846 /**
847  * Scale free network construction as described in:
848  *
849  * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
850  *
851  * Start with a network of "one" peer, then progressively add
852  * peers up to the total number.  At each step, iterate over
853  * all possible peers and connect new peer based on number of
854  * existing connections of the target peer.
855  *
856  * @param pg the peer group we are dealing with
857  * @param proc the connection processor to use
858  *
859  * @return the number of connections created
860  */
861 static int
862 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
863 {
864
865   unsigned int total_connections;
866   unsigned int outer_count;
867   unsigned int i;
868   unsigned int previous_total_connections;
869   double random;
870   double probability;
871
872   GNUNET_assert(pg->total > 1);
873
874   /* Add a connection between the first two nodes */
875   total_connections = proc(pg, 0, 1);
876
877   for (outer_count = 1; outer_count < pg->total; outer_count++)
878     {
879       previous_total_connections = total_connections;
880       for (i = 0; i < outer_count; i++)
881         {
882           probability = pg->peers[i].num_connections / (double)previous_total_connections;
883           random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
884                                                       UINT64_MAX)) / ( (double) UINT64_MAX);
885 #if VERBOSE_TESTING
886           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
887                       "Considering connecting peer %d to peer %d\n",
888                       outer_count, i);
889 #endif
890           if (random < probability)
891             {
892 #if VERBOSE_TESTING
893               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
894                           "Connecting peer %d to peer %d\n",
895                           outer_count, i);
896 #endif
897               total_connections += proc(pg, outer_count, i);
898             }
899         }
900     }
901
902   return total_connections;
903 }
904
905 int
906 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
907 {
908   unsigned int i, j;
909   int nodeToConnect;
910   unsigned int natLog;
911   unsigned int randomPeer;
912   double random, logNModifier, percentage;
913   unsigned int smallWorldConnections;
914   int connsPerPeer;
915   char *p_string;
916   int max;
917   int min;
918   unsigned int useAnd;
919   int connect_attempts;
920
921   logNModifier = 0.5; /* FIXME: default value? */
922   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
923                                                          "TESTING",
924                                                          "LOGNMODIFIER",
925                                                          &p_string))
926     {
927       if (sscanf(p_string, "%lf", &logNModifier) != 1)
928         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
929                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
930                     p_string,
931                     "LOGNMODIFIER",
932                     "TESTING");
933       GNUNET_free (p_string);
934     }
935   percentage = 0.5; /* FIXME: default percentage? */
936   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
937                                                          "TESTING",
938                                                          "PERCENTAGE",
939                                                          &p_string))
940     {
941       if (sscanf(p_string, "%lf", &percentage) != 1)
942         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
943                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
944                     p_string,
945                     "PERCENTAGE",
946                     "TESTING");
947       GNUNET_free (p_string);
948     }
949   natLog = log (pg->total);
950   connsPerPeer = ceil (natLog * logNModifier);
951
952   if (connsPerPeer % 2 == 1)
953     connsPerPeer += 1;
954
955   smallWorldConnections = 0;
956   connect_attempts = 0;
957   for (i = 0; i < pg->total; i++)
958     {
959       useAnd = 0;
960       max = i + connsPerPeer / 2;
961       min = i - connsPerPeer / 2;
962
963       if (max > pg->total - 1)
964         {
965           max = max - pg->total;
966           useAnd = 1;
967         }
968
969       if (min < 0)
970         {
971           min = pg->total - 1 + min;
972           useAnd = 1;
973         }
974
975       for (j = 0; j < connsPerPeer / 2; j++)
976         {
977           random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
978                                                       UINT64_MAX) / ( (double) UINT64_MAX));
979           if (random < percentage)
980             {
981               /* Connect to uniformly selected random peer */
982               randomPeer =
983                 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
984                                    pg->total);
985               while ((((randomPeer < max) && (randomPeer > min))
986                       && (useAnd == 0)) || (((randomPeer > min)
987                                              || (randomPeer < max))
988                                             && (useAnd == 1)))
989                 {
990                   randomPeer =
991                       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
992                                                          pg->total);
993                 }
994               smallWorldConnections +=
995                 proc (pg, i, randomPeer);
996             }
997           else
998             {
999               nodeToConnect = i + j + 1;
1000               if (nodeToConnect > pg->total - 1)
1001                 {
1002                   nodeToConnect = nodeToConnect - pg->total;
1003                 }
1004               connect_attempts +=
1005                 proc (pg, i, nodeToConnect);
1006             }
1007         }
1008
1009     }
1010
1011   connect_attempts += smallWorldConnections;
1012
1013   return connect_attempts;
1014 }
1015
1016
1017 static int
1018 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1019 {
1020   unsigned int outer_count, inner_count;
1021   unsigned int cutoff;
1022   int connect_attempts;
1023   double nat_percentage;
1024   char *p_string;
1025
1026   nat_percentage = 0.6; /* FIXME: default percentage? */
1027   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1028                                                          "TESTING",
1029                                                          "NATPERCENTAGE",
1030                                                          &p_string))
1031     {
1032       if (sscanf(p_string, "%lf", &nat_percentage) != 1)
1033         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1034                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1035                     p_string,
1036                     "NATPERCENTAGE",
1037                     "TESTING");
1038       GNUNET_free (p_string);
1039     }
1040
1041
1042
1043   cutoff = (unsigned int) (nat_percentage * pg->total);
1044
1045   connect_attempts = 0;
1046
1047   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1048     {
1049       for (inner_count = outer_count + 1; inner_count < pg->total;
1050            inner_count++)
1051         {
1052           if ((outer_count > cutoff) || (inner_count > cutoff))
1053             {
1054 #if VERBOSE_TESTING
1055               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1056                           "Connecting peer %d to peer %d\n",
1057                           outer_count, inner_count);
1058 #endif
1059               connect_attempts += proc(pg, outer_count, inner_count);
1060             }
1061         }
1062     }
1063
1064   return connect_attempts;
1065
1066 }
1067
1068
1069
1070 static int
1071 create_small_world (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1072 {
1073   unsigned int i, j, k;
1074   unsigned int square;
1075   unsigned int rows;
1076   unsigned int cols;
1077   unsigned int toggle = 1;
1078   unsigned int nodeToConnect;
1079   unsigned int natLog;
1080   unsigned int node1Row;
1081   unsigned int node1Col;
1082   unsigned int node2Row;
1083   unsigned int node2Col;
1084   unsigned int distance;
1085   double probability, random, percentage;
1086   unsigned int smallWorldConnections;
1087   char *p_string;
1088   int connect_attempts;
1089   square = floor (sqrt (pg->total));
1090   rows = square;
1091   cols = square;
1092
1093   percentage = 0.5; /* FIXME: default percentage? */
1094   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1095                                                          "TESTING",
1096                                                          "PERCENTAGE",
1097                                                          &p_string))
1098     {
1099       if (sscanf(p_string, "%lf", &percentage) != 1)
1100         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1101                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1102                     p_string,
1103                     "PERCENTAGE",
1104                     "TESTING");
1105       GNUNET_free (p_string);
1106     }
1107   probability = 0.5; /* FIXME: default percentage? */
1108   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1109                                                          "TESTING",
1110                                                          "PROBABILITY",
1111                                                          &p_string))
1112     {
1113       if (sscanf(p_string, "%lf", &probability) != 1)
1114         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1115                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1116                     p_string,
1117                     "PROBABILITY",
1118                     "TESTING");
1119       GNUNET_free (p_string);
1120     }
1121   if (square * square != pg->total)
1122     {
1123       while (rows * cols < pg->total)
1124         {
1125           if (toggle % 2 == 0)
1126             rows++;
1127           else
1128             cols++;
1129
1130           toggle++;
1131         }
1132     }
1133 #if VERBOSE_TESTING
1134       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1135                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
1136                   rows, cols);
1137 #endif
1138
1139   connect_attempts = 0;
1140   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
1141    * to the node to its right and above.  Once this is over, we'll have our torus!
1142    * Special case for the last node (if the rows and columns are not equal), connect
1143    * to the first in the row to maintain topology.
1144    */
1145   for (i = 0; i < pg->total; i++)
1146     {
1147       /* First connect to the node to the right */
1148       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
1149         nodeToConnect = i + 1;
1150       else if (i + 1 == pg->total)
1151         nodeToConnect = rows * cols - cols;
1152       else
1153         nodeToConnect = i - cols + 1;
1154
1155       connect_attempts += proc (pg, i, nodeToConnect);
1156
1157       if (i < cols)
1158         nodeToConnect = (rows * cols) - cols + i;
1159       else
1160         nodeToConnect = i - cols;
1161
1162       if (nodeToConnect < pg->total)
1163         connect_attempts += proc (pg, i, nodeToConnect);
1164     }
1165   natLog = log (pg->total);
1166 #if VERBOSE_TESTING > 2
1167   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1168               _("natural log of %d is %d, will run %d iterations\n"),
1169              pg->total, natLog, (int) (natLog * percentage));
1170   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Total connections added thus far: %u!\n"), connect_attempts);
1171 #endif
1172   smallWorldConnections = 0;
1173   for (i = 0; i < (int) (natLog * percentage); i++)
1174     {
1175       for (j = 0; j < pg->total; j++)
1176         {
1177           /* Determine the row and column of node at position j on the 2d torus */
1178           node1Row = j / cols;
1179           node1Col = j - (node1Row * cols);
1180           for (k = 0; k < pg->total; k++)
1181             {
1182               /* Determine the row and column of node at position k on the 2d torus */
1183               node2Row = k / cols;
1184               node2Col = k - (node2Row * cols);
1185               /* Simple Cartesian distance */
1186               distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
1187               if (distance > 1)
1188                 {
1189                   /* Calculate probability as 1 over the square of the distance */
1190                   probability = 1.0 / (distance * distance);
1191                   /* Choose a random value between 0 and 1 */
1192                   random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1193                                                               UINT64_MAX)) / ( (double) UINT64_MAX);
1194                   /* If random < probability, then connect the two nodes */
1195                   if (random < probability)
1196                     smallWorldConnections += proc (pg, j, k);
1197
1198                 }
1199             }
1200         }
1201     }
1202   connect_attempts += smallWorldConnections;
1203 #if VERBOSE_TESTING > 2
1204           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1205                       _("Total connections added for small world: %d!\n"),
1206                       smallWorldConnections);
1207 #endif
1208   return connect_attempts;
1209 }
1210
1211
1212
1213 static int
1214 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1215 {
1216   double temp_rand;
1217   unsigned int outer_count;
1218   unsigned int inner_count;
1219   int connect_attempts;
1220   double probability;
1221   char *p_string;
1222
1223   probability = 0.5; /* FIXME: default percentage? */
1224   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1225                                                          "TESTING",
1226                                                          "PROBABILITY",
1227                                                          &p_string))
1228     {
1229       if (sscanf(p_string, "%lf", &probability) != 1)
1230         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1231                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1232                     p_string,
1233                     "PROBABILITY",
1234                     "TESTING");
1235       GNUNET_free (p_string);
1236     }
1237   connect_attempts = 0;
1238   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1239     {
1240       for (inner_count = outer_count + 1; inner_count < pg->total;
1241            inner_count++)
1242         {
1243           temp_rand = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1244                                                          UINT64_MAX)) / ( (double) UINT64_MAX);
1245 #if VERBOSE_TESTING
1246           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1247                       _("rand is %f probability is %f\n"), temp_rand,
1248                       probability);
1249 #endif
1250           if (temp_rand < probability)
1251             {
1252               connect_attempts += proc (pg, outer_count, inner_count);
1253             }
1254         }
1255     }
1256
1257   return connect_attempts;
1258 }
1259
1260 static int
1261 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1262 {
1263   unsigned int i;
1264   unsigned int square;
1265   unsigned int rows;
1266   unsigned int cols;
1267   unsigned int toggle = 1;
1268   unsigned int nodeToConnect;
1269   int connect_attempts;
1270
1271   connect_attempts = 0;
1272
1273   square = floor (sqrt (pg->total));
1274   rows = square;
1275   cols = square;
1276
1277   if (square * square != pg->total)
1278     {
1279       while (rows * cols < pg->total)
1280         {
1281           if (toggle % 2 == 0)
1282             rows++;
1283           else
1284             cols++;
1285
1286           toggle++;
1287         }
1288     }
1289 #if VERBOSE_TESTING
1290       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1291                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
1292                   rows, cols);
1293 #endif
1294   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
1295    * to the node to its right and above.  Once this is over, we'll have our torus!
1296    * Special case for the last node (if the rows and columns are not equal), connect
1297    * to the first in the row to maintain topology.
1298    */
1299   for (i = 0; i < pg->total; i++)
1300     {
1301       /* First connect to the node to the right */
1302       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
1303         nodeToConnect = i + 1;
1304       else if (i + 1 == pg->total)
1305         nodeToConnect = rows * cols - cols;
1306       else
1307         nodeToConnect = i - cols + 1;
1308 #if VERBOSE_TESTING
1309           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1310                       "Connecting peer %d to peer %d\n",
1311                       i, nodeToConnect);
1312 #endif
1313       connect_attempts += proc(pg, i, nodeToConnect);
1314
1315       /* Second connect to the node immediately above */
1316       if (i < cols)
1317         nodeToConnect = (rows * cols) - cols + i;
1318       else
1319         nodeToConnect = i - cols;
1320
1321       if (nodeToConnect < pg->total)
1322         {
1323 #if VERBOSE_TESTING
1324           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1325                       "Connecting peer %d to peer %d\n",
1326                       i, nodeToConnect);
1327 #endif
1328           connect_attempts += proc(pg, i, nodeToConnect);
1329         }
1330
1331     }
1332
1333   return connect_attempts;
1334 }
1335
1336
1337
1338 static int
1339 create_clique (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1340 {
1341   unsigned int outer_count;
1342   unsigned int inner_count;
1343   int connect_attempts;
1344
1345   connect_attempts = 0;
1346
1347   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1348     {
1349       for (inner_count = outer_count + 1; inner_count < pg->total;
1350            inner_count++)
1351         {
1352 #if VERBOSE_TESTING
1353           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1354                       "Connecting peer %d to peer %d\n",
1355                       outer_count, inner_count);
1356 #endif
1357           connect_attempts += proc(pg, outer_count, inner_count);
1358         }
1359     }
1360
1361   return connect_attempts;
1362 }
1363
1364
1365 static int
1366 create_ring (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1367 {
1368   unsigned int count;
1369   int connect_attempts;
1370
1371   connect_attempts = 0;
1372
1373   /* Connect each peer to the next highest numbered peer */
1374   for (count = 0; count < pg->total - 1; count++)
1375     {
1376 #if VERBOSE_TESTING
1377           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1378                       "Connecting peer %d to peer %d\n",
1379                       count, count + 1);
1380 #endif
1381       connect_attempts += proc(pg, count, count + 1);
1382     }
1383
1384   /* Connect the last peer to the first peer */
1385   connect_attempts += proc(pg, pg->total - 1, 0);
1386
1387   return connect_attempts;
1388 }
1389
1390
1391 /**
1392  * Iterator for writing friends of a peer to a file.
1393  *
1394  * @param cls closure, an open writable file handle
1395  * @param key the key the daemon was stored under
1396  * @param value the GNUNET_TESTING_Daemon that needs to be written.
1397  *
1398  * @return GNUNET_YES to continue iteration
1399  *
1400  * TODO: Could replace friend_file_iterator and blacklist_file_iterator
1401  *       with a single file_iterator that takes a closure which contains
1402  *       the prefix to write before the peer.  Then this could be used
1403  *       for blacklisting multiple transports and writing the friend
1404  *       file.  I'm sure *someone* will complain loudly about other
1405  *       things that negate these functions even existing so no point in
1406  *       "fixing" now.
1407  */
1408 static int
1409 friend_file_iterator (void *cls,
1410                   const GNUNET_HashCode * key,
1411                   void *value)
1412 {
1413   FILE *temp_friend_handle = cls;
1414   struct GNUNET_TESTING_Daemon *peer = value;
1415   struct GNUNET_PeerIdentity *temppeer;
1416   struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
1417
1418   temppeer = &peer->id;
1419   GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
1420   fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
1421
1422   return GNUNET_YES;
1423 }
1424
1425 struct BlacklistContext
1426 {
1427   /*
1428    * The (open) file handle to write to
1429    */
1430   FILE *temp_file_handle;
1431
1432   /*
1433    * The transport that this peer will be blacklisted on.
1434    */
1435   char *transport;
1436 };
1437
1438 /**
1439  * Iterator for writing blacklist data to appropriate files.
1440  *
1441  * @param cls closure, an open writable file handle
1442  * @param key the key the daemon was stored under
1443  * @param value the GNUNET_TESTING_Daemon that needs to be written.
1444  *
1445  * @return GNUNET_YES to continue iteration
1446  */
1447 static int
1448 blacklist_file_iterator (void *cls,
1449                          const GNUNET_HashCode * key,
1450                          void *value)
1451 {
1452   struct BlacklistContext *blacklist_ctx = cls;
1453   //FILE *temp_blacklist_handle = cls;
1454   struct GNUNET_TESTING_Daemon *peer = value;
1455   struct GNUNET_PeerIdentity *temppeer;
1456   struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
1457
1458   temppeer = &peer->id;
1459   GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
1460   fprintf(blacklist_ctx->temp_file_handle, "%s:%s\n", blacklist_ctx->transport, (char *)&peer_enc);
1461
1462   return GNUNET_YES;
1463 }
1464
1465 /*
1466  * Create the friend files based on the PeerConnection's
1467  * of each peer in the peer group, and copy the files
1468  * to the appropriate place
1469  *
1470  * @param pg the peer group we are dealing with
1471  */
1472 static int
1473 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
1474 {
1475   FILE *temp_friend_handle;
1476   unsigned int pg_iter;
1477   char *temp_service_path;
1478   pid_t *pidarr;
1479   char *arg;
1480   char * mytemp;
1481   enum GNUNET_OS_ProcessStatusType type;
1482   unsigned long return_code;
1483   int count;
1484   int ret;
1485   int max_wait = 10;
1486
1487   pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
1488   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1489     {
1490       mytemp = GNUNET_DISK_mktemp("friends");
1491       GNUNET_assert(mytemp != NULL);
1492       temp_friend_handle = fopen (mytemp, "wt");
1493       GNUNET_assert(temp_friend_handle != NULL);
1494       GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, &friend_file_iterator, temp_friend_handle);
1495       fclose(temp_friend_handle);
1496
1497       if (GNUNET_OK !=
1498           GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
1499         {
1500           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1501                       _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
1502                       "SERVICEHOME",
1503                       "PATHS");
1504           if (UNLINK (mytemp) != 0)
1505             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
1506           GNUNET_free (mytemp);
1507           break;
1508         }
1509
1510       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
1511         {
1512           GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
1513           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
1514                                          "mv", mytemp, arg, NULL);
1515 #if VERBOSE_TESTING
1516           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1517                       _("Copying file with command cp %s %s\n"), mytemp, arg);
1518 #endif
1519
1520           GNUNET_free(arg);
1521         }
1522       else /* Remote, scp the file to the correct place */
1523         {
1524           if (NULL != pg->peers[pg_iter].daemon->username)
1525             GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
1526           else
1527             GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
1528           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
1529                                          "scp", mytemp, arg, NULL);
1530
1531 #if VERBOSE_TESTING
1532           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1533                       _("Copying file with command scp %s %s\n"), mytemp, arg);
1534 #endif
1535           GNUNET_free(arg);
1536         }
1537       GNUNET_free (temp_service_path);
1538       GNUNET_free (mytemp);
1539     }
1540
1541   count = 0;
1542   ret = GNUNET_SYSERR;
1543   while ((count < max_wait) && (ret != GNUNET_OK))
1544     {
1545       ret = GNUNET_OK;
1546       for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1547         {
1548 #if VERBOSE_TESTING
1549           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1550                       _("Checking copy status of file %d\n"), pg_iter);
1551 #endif
1552           if (pidarr[pg_iter] != 0) /* Check for already completed! */
1553             {
1554               if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
1555                 {
1556                   ret = GNUNET_SYSERR;
1557                 }
1558               else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
1559                 {
1560                   ret = GNUNET_SYSERR;
1561                 }
1562               else
1563                 {
1564                   pidarr[pg_iter] = 0;
1565 #if VERBOSE_TESTING
1566             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1567                       _("File %d copied\n"), pg_iter);
1568 #endif
1569                 }
1570             }
1571         }
1572       count++;
1573       if (ret == GNUNET_SYSERR)
1574         {
1575           /* FIXME: why sleep here? -CG */
1576           sleep(1);
1577         }
1578     }
1579
1580 #if VERBOSE_TESTING
1581     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1582                 _("Finished copying all friend files!\n"));
1583 #endif
1584   GNUNET_free(pidarr);
1585   return ret;
1586 }
1587
1588
1589 /*
1590  * Create the blacklist files based on the PeerConnection's
1591  * of each peer in the peer group, and copy the files
1592  * to the appropriate place.
1593  *
1594  * @param pg the peer group we are dealing with
1595  * @param transports space delimited list of transports to blacklist
1596  */
1597 static int
1598 create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *transports)
1599 {
1600   FILE *temp_file_handle;
1601   static struct BlacklistContext blacklist_ctx;
1602   unsigned int pg_iter;
1603   char *temp_service_path;
1604   pid_t *pidarr;
1605   char *arg;
1606   char *mytemp;
1607   enum GNUNET_OS_ProcessStatusType type;
1608   unsigned long return_code;
1609   int count;
1610   int ret;
1611   int max_wait = 10;
1612   int transport_len;
1613   unsigned int i;
1614   char *pos;
1615   char *temp_transports;
1616
1617   pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
1618   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1619     {
1620       mytemp = GNUNET_DISK_mktemp("blacklist");
1621       GNUNET_assert(mytemp != NULL);
1622       temp_file_handle = fopen (mytemp, "wt");
1623       GNUNET_assert(temp_file_handle != NULL);
1624       temp_transports = GNUNET_strdup(transports);
1625       blacklist_ctx.temp_file_handle = temp_file_handle;
1626       transport_len = strlen(temp_transports) + 1;
1627       pos = NULL;
1628
1629       for (i = 0; i < transport_len; i++)
1630       {
1631         if ((temp_transports[i] == ' ') && (pos == NULL))
1632           continue; /* At start of string (whitespace) */
1633         else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */
1634         {
1635           temp_transports[i] = '\0';
1636           blacklist_ctx.transport = pos;
1637           GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].blacklisted_peers, &blacklist_file_iterator, &blacklist_ctx);
1638           pos = NULL;
1639         } /* At beginning of actual string */
1640         else if (pos == NULL)
1641         {
1642           pos = &temp_transports[i];
1643         }
1644       }
1645
1646       GNUNET_free (temp_transports);
1647       fclose(temp_file_handle);
1648
1649       if (GNUNET_OK !=
1650           GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
1651         {
1652           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1653                       _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
1654                       "SERVICEHOME",
1655                       "PATHS");
1656           if (UNLINK (mytemp) != 0)
1657             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
1658           GNUNET_free (mytemp);
1659           break;
1660         }
1661
1662       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
1663         {
1664           GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
1665           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
1666                                          "mv", mytemp, arg, NULL);
1667 #if VERBOSE_TESTING
1668           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1669                       _("Copying file with command cp %s %s\n"), mytemp, arg);
1670 #endif
1671
1672           GNUNET_free(arg);
1673         }
1674       else /* Remote, scp the file to the correct place */
1675         {
1676           if (NULL != pg->peers[pg_iter].daemon->username)
1677             GNUNET_asprintf (&arg, "%s@%s:%s/blacklist", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
1678           else
1679             GNUNET_asprintf (&arg, "%s:%s/blacklist", pg->peers[pg_iter].daemon->hostname, temp_service_path);
1680           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
1681                                          "scp", mytemp, arg, NULL);
1682
1683 #if VERBOSE_TESTING
1684           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1685                       _("Copying file with command scp %s %s\n"), mytemp, arg);
1686 #endif
1687           GNUNET_free(arg);
1688         }
1689       GNUNET_free (temp_service_path);
1690       GNUNET_free (mytemp);
1691     }
1692
1693   count = 0;
1694   ret = GNUNET_SYSERR;
1695   while ((count < max_wait) && (ret != GNUNET_OK))
1696     {
1697       ret = GNUNET_OK;
1698       for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1699         {
1700 #if VERBOSE_TESTING
1701           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1702                       _("Checking copy status of file %d\n"), pg_iter);
1703 #endif
1704           if (pidarr[pg_iter] != 0) /* Check for already completed! */
1705             {
1706               if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
1707                 {
1708                   ret = GNUNET_SYSERR;
1709                 }
1710               else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
1711                 {
1712                   ret = GNUNET_SYSERR;
1713                 }
1714               else
1715                 {
1716                   pidarr[pg_iter] = 0;
1717 #if VERBOSE_TESTING
1718             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1719                       _("File %d copied\n"), pg_iter);
1720 #endif
1721                 }
1722             }
1723         }
1724       count++;
1725       if (ret == GNUNET_SYSERR)
1726         {
1727           /* FIXME: why sleep here? -CG */
1728           sleep(1);
1729         }
1730     }
1731
1732 #if VERBOSE_TESTING
1733     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1734                 _("Finished copying all blacklist files!\n"));
1735 #endif
1736   GNUNET_free(pidarr);
1737   return ret;
1738 }
1739
1740
1741 /**
1742  * Internal notification of a connection, kept so that we can ensure some connections
1743  * happen instead of flooding all testing daemons with requests to connect.
1744  */
1745 static void internal_connect_notify (void *cls,
1746                                      const struct GNUNET_PeerIdentity *first,
1747                                      const struct GNUNET_PeerIdentity *second,
1748                                      const struct GNUNET_CONFIGURATION_Handle *first_cfg,
1749                                      const struct GNUNET_CONFIGURATION_Handle *second_cfg,
1750                                      struct GNUNET_TESTING_Daemon *first_daemon,
1751                                      struct GNUNET_TESTING_Daemon *second_daemon,
1752                                      const char *emsg)
1753 {
1754   struct GNUNET_TESTING_PeerGroup *pg = cls;
1755   outstanding_connects--;
1756
1757   pg->notify_connection(pg->notify_connection_cls, 
1758                         first,
1759                         second, 
1760                         first_cfg, second_cfg, 
1761                         first_daemon, second_daemon, 
1762                         emsg);
1763
1764 }
1765
1766
1767 static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1768 {
1769   struct ConnectContext *connect_context = cls;
1770
1771   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1772     return;
1773
1774   if (outstanding_connects > MAX_OUTSTANDING_CONNECTIONS)
1775     {
1776 #if VERBOSE_TESTING > 2
1777           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1778                       _("Delaying connect, we have too many outstanding connections!\n"));
1779 #endif
1780       GNUNET_SCHEDULER_add_delayed(connect_context->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 3), &schedule_connect, connect_context);
1781     }
1782   else
1783     {
1784 #if VERBOSE_TESTING > 2
1785           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1786                       _("Creating connection, outstanding_connections is %d\n"), outstanding_connects);
1787 #endif
1788       outstanding_connects++;
1789       GNUNET_TESTING_daemons_connect (connect_context->first,
1790                                       connect_context->second,
1791                                       CONNECT_TIMEOUT,
1792                                       CONNECT_ATTEMPTS,
1793                                       &internal_connect_notify,
1794                                       connect_context->pg);
1795       GNUNET_free(connect_context);
1796     }
1797 }
1798
1799
1800 /**
1801  * Iterator for actually scheduling connections to be created
1802  * between two peers.
1803  *
1804  * @param cls closure, a GNUNET_TESTING_Daemon
1805  * @param key the key the second Daemon was stored under
1806  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
1807  *
1808  * @return GNUNET_YES to continue iteration
1809  */
1810 static int
1811 connect_iterator (void *cls,
1812                   const GNUNET_HashCode * key,
1813                   void *value)
1814 {
1815   struct PeerData *first = cls;
1816   struct GNUNET_TESTING_Daemon *second = value;
1817   struct ConnectContext *connect_context;
1818
1819   connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1820   connect_context->pg = first->pg;
1821   connect_context->first = first->daemon;
1822   connect_context->second = second;
1823   GNUNET_SCHEDULER_add_now(first->pg->sched, &schedule_connect, connect_context);
1824
1825   return GNUNET_YES;
1826 }
1827
1828
1829 /**
1830  * Iterator for copying all entries in the allowed hashmap to the
1831  * connect hashmap.
1832  *
1833  * @param cls closure, a GNUNET_TESTING_Daemon
1834  * @param key the key the second Daemon was stored under
1835  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
1836  *
1837  * @return GNUNET_YES to continue iteration
1838  */
1839 static int
1840 copy_topology_iterator (void *cls,
1841                   const GNUNET_HashCode * key,
1842                   void *value)
1843 {
1844   struct PeerData *first = cls;
1845
1846   GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(first->connect_peers, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1847
1848   return GNUNET_YES;
1849 }
1850
1851 /**
1852  * Make the peers to connect the same as those that are allowed to be
1853  * connected.
1854  *
1855  * @param pg the peer group
1856  */
1857 static int
1858 copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
1859 {
1860   unsigned int pg_iter;
1861   int ret;
1862   int total;
1863
1864   total = 0;
1865   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1866     {
1867       ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, &copy_topology_iterator, &pg->peers[pg_iter]);
1868       if (GNUNET_SYSERR == ret)
1869         return GNUNET_SYSERR;
1870
1871       total = total + ret;
1872     }
1873
1874   return total;
1875 }
1876
1877
1878 /**
1879  * Connect the topology as specified by the PeerConnection's
1880  * of each peer in the peer group
1881  *
1882  * @param pg the peer group we are dealing with
1883  * @return the number of connections that will be attempted
1884  */
1885 static int
1886 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
1887 {
1888   unsigned int pg_iter;
1889   int ret;
1890   int total;
1891 #if OLD
1892   struct PeerConnection *connection_iter;
1893   struct ConnectContext *connect_context;
1894 #endif
1895
1896   total = 0;
1897   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1898     {
1899       ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &connect_iterator, &pg->peers[pg_iter]);
1900       if (GNUNET_SYSERR == ret)
1901         return GNUNET_SYSERR;
1902
1903       total = total + ret;
1904
1905 #if OLD
1906       connection_iter = ;
1907       while (connection_iter != NULL)
1908         {
1909           connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1910           connect_context->pg = pg;
1911           connect_context->first = ;
1912           connect_context->second = connection_iter->daemon;
1913           GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
1914           connection_iter = connection_iter->next;
1915         }
1916 #endif
1917     }
1918   return total;
1919 }
1920
1921
1922 /**
1923  * Takes a peer group and creates a topology based on the
1924  * one specified.  Creates a topology means generates friend
1925  * files for the peers so they can only connect to those allowed
1926  * by the topology.  This will only have an effect once peers
1927  * are started if the FRIENDS_ONLY option is set in the base
1928  * config.  Also takes an optional restrict topology which
1929  * disallows direct TCP connections UNLESS they are specified in
1930  * the restricted topology.
1931  *
1932  * @param pg the peer group struct representing the running peers
1933  * @param topology which topology to connect the peers in
1934  * @param restrict_topology allow only direct TCP connections in this topology
1935  *                          use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
1936  * @param restrict_transports space delimited list of transports to blacklist
1937  *                            to create restricted topology
1938  *
1939  * @return the maximum number of connections were all allowed peers
1940  *         connected to each other
1941  */
1942 int
1943 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
1944                                 enum GNUNET_TESTING_Topology topology,
1945                                 enum GNUNET_TESTING_Topology restrict_topology,
1946                                 char *restrict_transports)
1947 {
1948   int ret;
1949   int num_connections;
1950   int unblacklisted_connections;
1951
1952   GNUNET_assert (pg->notify_connection != NULL);
1953   ret = GNUNET_OK;
1954
1955   switch (topology)
1956     {
1957     case GNUNET_TESTING_TOPOLOGY_CLIQUE:
1958 #if VERBOSE_TOPOLOGY
1959       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1960                   _("Creating clique topology\n"));
1961 #endif
1962       num_connections = create_clique (pg, &add_allowed_connections);
1963       break;
1964     case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
1965 #if VERBOSE_TOPOLOGY
1966       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1967                   _("Creating small world (ring) topology\n"));
1968 #endif
1969       num_connections = create_small_world_ring (pg, &add_allowed_connections);
1970       break;
1971     case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
1972 #if VERBOSE_TOPOLOGY
1973       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1974                   _("Creating small world (2d-torus) topology\n"));
1975 #endif
1976       num_connections = create_small_world (pg, &add_allowed_connections);
1977       break;
1978     case GNUNET_TESTING_TOPOLOGY_RING:
1979 #if VERBOSE_TOPOLOGY
1980       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1981                   _("Creating ring topology\n"));
1982 #endif
1983       num_connections = create_ring (pg, &add_allowed_connections);
1984       break;
1985     case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
1986 #if VERBOSE_TOPOLOGY
1987       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1988                   _("Creating 2d torus topology\n"));
1989 #endif
1990       num_connections = create_2d_torus (pg, &add_allowed_connections);
1991       break;
1992     case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
1993 #if VERBOSE_TOPOLOGY
1994       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1995                   _("Creating Erdos-Renyi topology\n"));
1996 #endif
1997       num_connections = create_erdos_renyi (pg, &add_allowed_connections);
1998       break;
1999     case GNUNET_TESTING_TOPOLOGY_INTERNAT:
2000 #if VERBOSE_TOPOLOGY
2001       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2002                   _("Creating InterNAT topology\n"));
2003 #endif
2004       num_connections = create_nated_internet (pg, &add_allowed_connections);
2005       break;
2006     case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
2007 #if VERBOSE_TESTING
2008       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2009                   _("Creating Scale Free topology\n"));
2010 #endif
2011       num_connections = create_scale_free (pg, &add_allowed_connections);
2012       break;
2013     case GNUNET_TESTING_TOPOLOGY_NONE:
2014       num_connections = 0;
2015       break;
2016     default:
2017       num_connections = 0;
2018       break;
2019     }
2020   if (num_connections < 1)
2021     return GNUNET_SYSERR;
2022
2023   if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
2024     {
2025       ret = create_and_copy_friend_files(pg);
2026     }
2027
2028   if (ret != GNUNET_OK)
2029     {
2030 #if VERBOSE_TESTING
2031       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2032                   _("Failed during friend file copying!\n"));
2033 #endif
2034       return GNUNET_SYSERR;
2035     }
2036   else
2037     {
2038 #if VERBOSE_TESTING
2039           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2040                       _("Friend files created/copied successfully!\n"));
2041 #endif
2042     }
2043
2044   /* Use the create clique method to initially set all connections as blacklisted. */
2045   create_clique (pg, &blacklist_connections);
2046   unblacklisted_connections = 0;
2047   /* Un-blacklist connections as per the topology specified */
2048   switch (restrict_topology)
2049     {
2050     case GNUNET_TESTING_TOPOLOGY_CLIQUE:
2051 #if VERBOSE_TESTING
2052       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2053                   _("Blacklisting all but clique topology\n"));
2054 #endif
2055       unblacklisted_connections = create_clique (pg, &unblacklist_connections);
2056       break;
2057     case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
2058 #if VERBOSE_TESTING
2059       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2060                   _("Blacklisting all but small world (ring) topology\n"));
2061 #endif
2062       unblacklisted_connections = create_small_world_ring (pg, &unblacklist_connections);
2063       break;
2064     case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
2065 #if VERBOSE_TESTING
2066       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2067                   _("Blacklisting all but small world (2d-torus) topology\n"));
2068 #endif
2069       unblacklisted_connections = create_small_world (pg, &unblacklist_connections);
2070       break;
2071     case GNUNET_TESTING_TOPOLOGY_RING:
2072 #if VERBOSE_TESTING
2073       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2074                   _("Blacklisting all but ring topology\n"));
2075 #endif
2076       unblacklisted_connections = create_ring (pg, &unblacklist_connections);
2077       break;
2078     case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
2079 #if VERBOSE_TESTING
2080       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2081                   _("Blacklisting all but 2d torus topology\n"));
2082 #endif
2083       unblacklisted_connections = create_2d_torus (pg, &unblacklist_connections);
2084       break;
2085     case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
2086 #if VERBOSE_TESTING
2087       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2088                   _("Blacklisting all but Erdos-Renyi topology\n"));
2089 #endif
2090       unblacklisted_connections = create_erdos_renyi (pg, &unblacklist_connections);
2091       break;
2092     case GNUNET_TESTING_TOPOLOGY_INTERNAT:
2093 #if VERBOSE_TESTING
2094       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2095                   _("Blacklisting all but InterNAT topology\n"));
2096 #endif
2097       unblacklisted_connections = create_nated_internet (pg, &unblacklist_connections);
2098       break;
2099     case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
2100 #if VERBOSE_TESTING
2101       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2102                   _("Blacklisting all but Scale Free topology\n"));
2103 #endif
2104       unblacklisted_connections = create_scale_free (pg, &unblacklist_connections);
2105       break;
2106     case GNUNET_TESTING_TOPOLOGY_NONE:
2107       /* Fall through */
2108     default:
2109       break;
2110     }
2111
2112   if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
2113   {
2114     ret = create_and_copy_blacklist_files(pg, restrict_transports);
2115     if (ret != GNUNET_OK)
2116       {
2117 #if VERBOSE_TESTING
2118         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2119                     _("Failed during blacklist file copying!\n"));
2120 #endif
2121         return GNUNET_SYSERR;
2122       }
2123     else
2124       {
2125 #if VERBOSE_TESTING
2126         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2127                     _("Blacklist files created/copied successfully!\n"));
2128 #endif
2129       }
2130   }
2131   return num_connections;
2132 }
2133
2134 struct RandomContext
2135 {
2136   /**
2137    * The peergroup
2138    */
2139   struct GNUNET_TESTING_PeerGroup *pg;
2140
2141   /**
2142    * uid of the first peer
2143    */
2144   uint32_t first_uid;
2145
2146   /**
2147    * Peer data for first peer.
2148    */
2149   struct PeerData *first;
2150
2151   /**
2152    * Random percentage to use
2153    */
2154   double percentage;
2155 };
2156
2157 struct MinimumContext
2158 {
2159   /**
2160    * The peergroup
2161    */
2162   struct GNUNET_TESTING_PeerGroup *pg;
2163
2164   /**
2165    * uid of the first peer
2166    */
2167   uint32_t first_uid;
2168
2169   /**
2170    * Peer data for first peer.
2171    */
2172   struct PeerData *first;
2173
2174   /**
2175    * Number of conns per peer
2176    */
2177   unsigned int num_to_add;
2178
2179   /**
2180    * Permuted array of all possible connections.  Only add the Nth
2181    * peer if it's in the Nth position.
2182    */
2183   unsigned int *pg_array;
2184
2185   /**
2186    * What number is the current element we are iterating over?
2187    */
2188   unsigned int current;
2189 };
2190
2191 struct DFSContext
2192 {
2193   /**
2194    * The peergroup
2195    */
2196   struct GNUNET_TESTING_PeerGroup *pg;
2197
2198   /**
2199    * uid of the first peer
2200    */
2201   uint32_t first_uid;
2202
2203   /**
2204    * uid of the second peer
2205    */
2206   uint32_t second_uid;
2207
2208   /**
2209    * Peer data for first peer.
2210    */
2211   struct PeerData *first;
2212
2213   /**
2214    * Which peer has been chosen as the one to add?
2215    */
2216   unsigned int chosen;
2217
2218   /**
2219    * What number is the current element we are iterating over?
2220    */
2221   unsigned int current;
2222 };
2223
2224 /**
2225  * Iterator for choosing random peers to connect.
2226  *
2227  * @param cls closure, a RandomContext
2228  * @param key the key the second Daemon was stored under
2229  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2230  *
2231  * @return GNUNET_YES to continue iteration
2232  */
2233 static int
2234 random_connect_iterator (void *cls,
2235                   const GNUNET_HashCode * key,
2236                   void *value)
2237 {
2238   struct RandomContext *random_ctx = cls;
2239   double random_number;
2240   uint32_t second_pos;
2241   GNUNET_HashCode first_hash;
2242   random_number = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
2243                                                      UINT64_MAX)) / ( (double) UINT64_MAX);
2244   if (random_number < random_ctx->percentage)
2245   {
2246     GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(random_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2247   }
2248   /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
2249   uid_from_hash(key, &second_pos);
2250   hash_from_uid(random_ctx->first_uid, &first_hash);
2251   GNUNET_assert(random_ctx->pg->total > second_pos);
2252   GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(random_ctx->pg->peers[second_pos].connect_peers, &first_hash, random_ctx->first->daemon));
2253
2254   return GNUNET_YES;
2255 }
2256
2257 /**
2258  * Iterator for adding at least X peers to a peers connection set.
2259  *
2260  * @param cls closure, MinimumContext
2261  * @param key the key the second Daemon was stored under
2262  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2263  *
2264  * @return GNUNET_YES to continue iteration
2265  */
2266 static int
2267 minimum_connect_iterator (void *cls,
2268                   const GNUNET_HashCode * key,
2269                   void *value)
2270 {
2271   struct MinimumContext *min_ctx = cls;
2272   uint32_t second_pos;
2273   GNUNET_HashCode first_hash;
2274   unsigned int i;
2275
2276   if (GNUNET_CONTAINER_multihashmap_size(min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
2277   {
2278     for (i = 0; i < min_ctx->num_to_add; i++)
2279     {
2280       if (min_ctx->pg_array[i] == min_ctx->current)
2281       {
2282         GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2283         uid_from_hash(key, &second_pos);
2284         hash_from_uid(min_ctx->first_uid, &first_hash);
2285         GNUNET_assert(min_ctx->pg->total > second_pos);
2286         GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->pg->peers[second_pos].connect_peers_working_set, &first_hash, min_ctx->first->daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2287         /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
2288         GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(min_ctx->pg->peers[second_pos].connect_peers, &first_hash, min_ctx->first->daemon));
2289       }
2290     }
2291     min_ctx->current++;
2292     return GNUNET_YES;
2293   }
2294   else
2295     return GNUNET_NO; /* We can stop iterating, we have enough peers! */
2296
2297 }
2298
2299
2300 /**
2301  * Iterator for adding peers to a connection set based on a depth first search.
2302  *
2303  * @param cls closure, MinimumContext
2304  * @param key the key the second daemon was stored under
2305  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2306  *
2307  * @return GNUNET_YES to continue iteration
2308  */
2309 static int
2310 dfs_connect_iterator (void *cls,
2311                   const GNUNET_HashCode * key,
2312                   void *value)
2313 {
2314   struct DFSContext *dfs_ctx = cls;
2315   GNUNET_HashCode first_hash;
2316
2317   if (dfs_ctx->current == dfs_ctx->chosen)
2318     {
2319       GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(dfs_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2320       uid_from_hash(key, &dfs_ctx->second_uid);
2321       hash_from_uid(dfs_ctx->first_uid, &first_hash);
2322       GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(dfs_ctx->pg->peers[dfs_ctx->second_uid].connect_peers_working_set, &first_hash, dfs_ctx->first->daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2323       GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(dfs_ctx->pg->peers[dfs_ctx->second_uid].connect_peers, &first_hash, dfs_ctx->first->daemon));
2324       /* Can't remove second from first yet because we are currently iterating, hence the return value in the DFSContext! */
2325       return GNUNET_NO; /* We have found our peer, don't iterate more */
2326     }
2327
2328   dfs_ctx->current++;
2329   return GNUNET_YES;
2330 }
2331
2332
2333 /**
2334  * From the set of connections possible, choose percentage percent of connections
2335  * to actually connect.
2336  *
2337  * @param pg the peergroup we are dealing with
2338  * @param percentage what percent of total connections to make
2339  */
2340 void
2341 choose_random_connections(struct GNUNET_TESTING_PeerGroup *pg, double percentage)
2342 {
2343   struct RandomContext random_ctx;
2344   uint32_t pg_iter;
2345
2346   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2347     {
2348       random_ctx.first_uid = pg_iter;
2349       random_ctx.first = &pg->peers[pg_iter];
2350       random_ctx.percentage = percentage;
2351       random_ctx.pg = pg;
2352       pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total);
2353       GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &random_connect_iterator, &random_ctx);
2354       /* Now remove the old connections */
2355       GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2356       /* And replace with the random set */
2357       pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2358     }
2359 }
2360
2361 /**
2362  * From the set of connections possible, choose at least num connections per
2363  * peer.
2364  *
2365  * @param pg the peergroup we are dealing with
2366  * @param num how many connections at least should each peer have (if possible)?
2367  */
2368 static void
2369 choose_minimum(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
2370 {
2371   struct MinimumContext minimum_ctx;
2372   uint32_t pg_iter;
2373
2374   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2375    {
2376      pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(num);
2377    }
2378
2379   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2380     {
2381       minimum_ctx.first_uid = pg_iter;
2382       minimum_ctx.pg_array = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK, 
2383                                                           GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers));
2384       minimum_ctx.first = &pg->peers[pg_iter];
2385       minimum_ctx.pg = pg;
2386       minimum_ctx.num_to_add = num;
2387       minimum_ctx.current = 0;
2388       GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers,
2389                                             &minimum_connect_iterator, 
2390                                             &minimum_ctx);
2391     }
2392
2393   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2394     {
2395       /* Remove the "old" connections */
2396       GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2397       /* And replace with the working set */
2398       pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2399     }
2400
2401 }
2402
2403
2404 static unsigned int
2405 count_workingset_connections(struct GNUNET_TESTING_PeerGroup *pg)
2406 {
2407   unsigned int count;
2408   unsigned int pg_iter;
2409
2410   count = 0;
2411
2412   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2413     {
2414       count += GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers_working_set);
2415     }
2416
2417   return count;
2418 }
2419
2420
2421 static unsigned int count_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg)
2422 {
2423   unsigned int count;
2424   unsigned int pg_iter;
2425
2426   count = 0;
2427
2428   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2429     {
2430       count += GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers);
2431     }
2432
2433   return count;
2434 }
2435
2436 /**
2437  * From the set of connections possible, choose at least num connections per
2438  * peer based on depth first traversal of peer connections.  If DFS leaves
2439  * peers unconnected, ensure those peers get connections.
2440  *
2441  * @param pg the peergroup we are dealing with
2442  * @param num how many connections at least should each peer have (if possible)?
2443  */
2444 void
2445 perform_dfs (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
2446 {
2447   struct DFSContext dfs_ctx;
2448   uint32_t pg_iter;
2449   uint32_t dfs_count;
2450   uint32_t starting_peer;
2451   uint32_t least_connections;
2452   GNUNET_HashCode second_hash;
2453
2454   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2455     {
2456       pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(num);
2457     }
2458
2459   starting_peer = 0;
2460   dfs_count = 0;
2461   while ((count_workingset_connections(pg) < num * pg->total) && (count_allowed_connections(pg) > 0))
2462     {
2463       if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
2464         {
2465           least_connections = -1; /* Set to very high number */
2466           for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2467             {
2468               if (GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers_working_set) < least_connections)
2469                 {
2470                   starting_peer = pg_iter;
2471                   least_connections = GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers_working_set);
2472                 }
2473             }
2474         }
2475
2476       if (GNUNET_CONTAINER_multihashmap_size(pg->peers[starting_peer].connect_peers) == 0)  /* Ensure there is at least one peer left to connect! */
2477         {
2478           dfs_count = 0;
2479           continue;
2480         }
2481
2482       /* Choose a random peer from the chosen peers set of connections to add */
2483       dfs_ctx.chosen = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CONTAINER_multihashmap_size(pg->peers[starting_peer].connect_peers));
2484       dfs_ctx.first_uid = starting_peer;
2485       dfs_ctx.first = &pg->peers[starting_peer];
2486       dfs_ctx.pg = pg;
2487       dfs_ctx.current = 0;
2488
2489       GNUNET_CONTAINER_multihashmap_iterate(pg->peers[starting_peer].connect_peers, &dfs_connect_iterator, &dfs_ctx);
2490       /* Remove the second from the first, since we will be continuing the search and may encounter the first peer again! */
2491       hash_from_uid(dfs_ctx.second_uid, &second_hash);
2492       GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[starting_peer].connect_peers, &second_hash, pg->peers[dfs_ctx.second_uid].daemon));
2493       starting_peer = dfs_ctx.second_uid;
2494     }
2495
2496   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2497     {
2498       /* Remove the "old" connections */
2499       GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2500       /* And replace with the working set */
2501       pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2502     }
2503 }
2504
2505 /**
2506  * There are many ways to connect peers that are supported by this function.
2507  * To connect peers in the same topology that was created via the
2508  * GNUNET_TESTING_create_topology, the topology variable must be set to
2509  * GNUNET_TESTING_TOPOLOGY_NONE.  If the topology variable is specified,
2510  * a new instance of that topology will be generated and attempted to be
2511  * connected.  This could result in some connections being impossible,
2512  * because some topologies are non-deterministic.
2513  *
2514  * @param pg the peer group struct representing the running peers
2515  * @param topology which topology to connect the peers in
2516  * @param options options for connecting the topology
2517  * @param option_modifier modifier for options that take a parameter
2518  * @return the number of connections that will be attempted, GNUNET_SYSERR on error
2519  */
2520 int
2521 GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
2522                                  enum GNUNET_TESTING_Topology topology,
2523                                  enum GNUNET_TESTING_TopologyOption options,
2524                                  double option_modifier)
2525 {
2526   switch (topology)
2527       {
2528       case GNUNET_TESTING_TOPOLOGY_CLIQUE:
2529 #if VERBOSE_TOPOLOGY
2530       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2531                   _("Creating clique CONNECT topology\n"));
2532 #endif
2533         create_clique (pg, &add_actual_connections);
2534         break;
2535       case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
2536 #if VERBOSE_TOPOLOGY
2537       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2538                   _("Creating small world (ring) CONNECT topology\n"));
2539 #endif
2540         create_small_world_ring (pg, &add_actual_connections);
2541         break;
2542       case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
2543 #if VERBOSE_TOPOLOGY
2544       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2545                   _("Creating small world (2d-torus) CONNECT topology\n"));
2546 #endif
2547         create_small_world (pg, &add_actual_connections);
2548         break;
2549       case GNUNET_TESTING_TOPOLOGY_RING:
2550 #if VERBOSE_TOPOLOGY
2551       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2552                   _("Creating ring CONNECT topology\n"));
2553 #endif
2554         create_ring (pg, &add_actual_connections);
2555         break;
2556       case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
2557 #if VERBOSE_TOPOLOGY
2558       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2559                   _("Creating 2d torus CONNECT topology\n"));
2560 #endif
2561         create_2d_torus (pg, &add_actual_connections);
2562         break;
2563       case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
2564 #if VERBOSE_TOPOLOGY
2565       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2566                   _("Creating Erdos-Renyi CONNECT topology\n"));
2567 #endif
2568         create_erdos_renyi (pg, &add_actual_connections);
2569         break;
2570       case GNUNET_TESTING_TOPOLOGY_INTERNAT:
2571 #if VERBOSE_TOPOLOGY
2572       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2573                   _("Creating InterNAT CONNECT topology\n"));
2574 #endif
2575         create_nated_internet (pg, &add_actual_connections);
2576         break;
2577       case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
2578 #if VERBOSE_TOPOLOGY
2579       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2580                   _("Creating Scale Free CONNECT topology\n"));
2581 #endif
2582         create_scale_free (pg, &add_actual_connections);
2583         break;
2584       case GNUNET_TESTING_TOPOLOGY_NONE:
2585 #if VERBOSE_TOPOLOGY
2586         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2587                   _("Creating no CONNECT topology\n"));
2588 #endif
2589         copy_allowed_topology(pg);
2590         break;
2591       default:
2592         GNUNET_log(GNUNET_ERROR_TYPE_WARNING, 
2593                    _("Unknown topology specification, can't connect peers!\n"));
2594         return GNUNET_SYSERR;
2595       }
2596
2597   switch (options)
2598     {
2599     case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM:
2600 #if VERBOSE_TOPOLOGY
2601       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2602                   _("Connecting random subset (%'.2f percent) of possible peers\n"), 100 * option_modifier);
2603 #endif
2604       choose_random_connections(pg, option_modifier);
2605       break;
2606     case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM:
2607 #if VERBOSE_TOPOLOGY
2608       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2609                   _("Connecting a minimum of %u peers each (if possible)\n"), (unsigned int)option_modifier);
2610 #endif
2611       choose_minimum(pg, (unsigned int)option_modifier);
2612       break;
2613     case GNUNET_TESTING_TOPOLOGY_OPTION_DFS:
2614 #if VERBOSE_TOPOLOGY
2615       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2616                   _("Using DFS to connect a minimum of %u peers each (if possible)\n"), (unsigned int)option_modifier);
2617 #endif
2618       perform_dfs(pg, (int)option_modifier);
2619       break;
2620     case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
2621       break;
2622     case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
2623       break;
2624     default:
2625       break;
2626     }
2627
2628   return connect_topology(pg);
2629 }
2630
2631 /**
2632  * Function which continues a peer group starting up
2633  * after successfully generating hostkeys for each peer.
2634  *
2635  * @param pg the peer group to continue starting
2636  *
2637  */
2638 void
2639 GNUNET_TESTING_daemons_continue_startup(struct GNUNET_TESTING_PeerGroup *pg)
2640 {
2641   unsigned int i;
2642
2643   for (i = 0; i < pg->total; i++)
2644     {
2645       GNUNET_TESTING_daemon_continue_startup(pg->peers[i].daemon);
2646     }
2647 }
2648
2649 /**
2650  * Start count gnunetd processes with the same set of transports and
2651  * applications.  The port numbers (any option called "PORT") will be
2652  * adjusted to ensure that no two peers running on the same system
2653  * have the same port(s) in their respective configurations.
2654  *
2655  * @param sched scheduler to use
2656  * @param cfg configuration template to use
2657  * @param total number of daemons to start
2658  * @param timeout total time allowed for peers to start
2659  * @param hostkey_callback function to call on each peers hostkey generation
2660  *        if NULL, peers will be started by this call, if non-null,
2661  *        GNUNET_TESTING_daemons_continue_startup must be called after
2662  *        successful hostkey generation
2663  * @param hostkey_cls closure for hostkey callback
2664  * @param cb function to call on each daemon that was started
2665  * @param cb_cls closure for cb
2666  * @param connect_callback function to call each time two hosts are connected
2667  * @param connect_callback_cls closure for connect_callback
2668  * @param hostnames space-separated list of hostnames to use; can be NULL (to run
2669  *        everything on localhost).
2670  * @return NULL on error, otherwise handle to control peer group
2671  */
2672 struct GNUNET_TESTING_PeerGroup *
2673 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
2674                               const struct GNUNET_CONFIGURATION_Handle *cfg,
2675                               unsigned int total,
2676                               struct GNUNET_TIME_Relative timeout,
2677                               GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback,
2678                               void *hostkey_cls,
2679                               GNUNET_TESTING_NotifyDaemonRunning cb,
2680                               void *cb_cls,
2681                               GNUNET_TESTING_NotifyConnection
2682                               connect_callback, void *connect_callback_cls,
2683                               const char *hostnames)
2684 {
2685   struct GNUNET_TESTING_PeerGroup *pg;
2686   const char *rpos;
2687   char *pos;
2688   char *start;
2689   const char *hostname;
2690   char *baseservicehome;
2691   char *newservicehome;
2692   char *tmpdir;
2693   struct GNUNET_CONFIGURATION_Handle *pcfg;
2694   unsigned int off;
2695   unsigned int hostcnt;
2696   uint16_t minport;
2697   uint32_t upnum;
2698
2699   if (0 == total)
2700     {
2701       GNUNET_break (0);
2702       return NULL;
2703     }
2704   upnum = 0;
2705   pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
2706   pg->sched = sched;
2707   pg->cfg = cfg;
2708   pg->cb = cb;
2709   pg->cb_cls = cb_cls;
2710   pg->notify_connection = connect_callback;
2711   pg->notify_connection_cls = connect_callback_cls;
2712   pg->total = total;
2713   pg->max_timeout = GNUNET_TIME_relative_to_absolute(timeout);
2714   pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
2715   if (NULL != hostnames)
2716     {
2717       off = 2;
2718       /* skip leading spaces */
2719       while ((0 != *hostnames) && (isspace ( (unsigned char) *hostnames)))
2720         hostnames++;
2721       rpos = hostnames;
2722       while ('\0' != *rpos)
2723         {
2724           if (isspace ( (unsigned char) *rpos))
2725             off++;
2726           rpos++;
2727         }
2728       pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
2729       off = 0;
2730       start = GNUNET_strdup (hostnames);
2731       pos = start;
2732       while ('\0' != *pos)
2733         {
2734           if (isspace ( (unsigned char) *pos))
2735             {
2736               *pos = '\0';
2737               if (strlen (start) > 0)
2738                 {
2739                   pg->hosts[off].minport = LOW_PORT;
2740                   pg->hosts[off++].hostname = start;
2741                 }
2742               start = pos + 1;
2743             }
2744           pos++;
2745         }
2746       if (strlen (start) > 0)
2747         {
2748           pg->hosts[off].minport = LOW_PORT;
2749           pg->hosts[off++].hostname = start;
2750         }
2751       if (off == 0)
2752         {
2753           GNUNET_free (start);
2754           GNUNET_free (pg->hosts);
2755           pg->hosts = NULL;
2756         }
2757       hostcnt = off;
2758       minport = 0;              /* make gcc happy */
2759     }
2760   else
2761     {
2762       hostcnt = 0;
2763       minport = LOW_PORT;
2764     }
2765   for (off = 0; off < total; off++)
2766     {
2767       if (hostcnt > 0)
2768         {
2769           hostname = pg->hosts[off % hostcnt].hostname;
2770           pcfg = make_config (cfg, 
2771                               &pg->hosts[off % hostcnt].minport,
2772                               &upnum,
2773                               hostname);
2774         }
2775       else
2776         {
2777           hostname = NULL;
2778           pcfg = make_config (cfg,
2779                               &minport,
2780                               &upnum,
2781                               hostname);
2782         }
2783
2784       if (NULL == pcfg)
2785         {
2786           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2787                       _
2788                       ("Could not create configuration for peer number %u on `%s'!\n"),
2789                       off, hostname == NULL ? "localhost" : hostname);
2790           continue;
2791         }
2792
2793       if (GNUNET_YES ==
2794           GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
2795                                                  &baseservicehome))
2796         {
2797           GNUNET_asprintf (&newservicehome,
2798                            "%s/%d/", baseservicehome, off);
2799           GNUNET_free (baseservicehome);
2800         }
2801       else
2802         {
2803           tmpdir = getenv ("TMPDIR");
2804           tmpdir = tmpdir ? tmpdir : "/tmp";
2805           GNUNET_asprintf (&newservicehome,
2806                            "%s/%s/%d/",
2807                            tmpdir,
2808                            "gnunet-testing-test-test", off);
2809         }
2810       GNUNET_CONFIGURATION_set_value_string (pcfg,
2811                                              "PATHS",
2812                                              "SERVICEHOME", newservicehome);
2813       GNUNET_free (newservicehome);
2814       pg->peers[off].cfg = pcfg;
2815       pg->peers[off].allowed_peers = GNUNET_CONTAINER_multihashmap_create(total);
2816       pg->peers[off].connect_peers = GNUNET_CONTAINER_multihashmap_create(total);
2817       pg->peers[off].blacklisted_peers = GNUNET_CONTAINER_multihashmap_create(total);
2818       pg->peers[off].pg = pg;
2819       pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
2820                                                            pcfg,
2821                                                            timeout,
2822                                                            hostname,
2823                                                            hostkey_callback,
2824                                                            hostkey_cls,
2825                                                            cb, cb_cls);
2826       if (NULL == pg->peers[off].daemon)
2827         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2828                     _("Could not start peer number %u!\n"), off);
2829
2830     }
2831   return pg;
2832 }
2833
2834 /*
2835  * Get a daemon by number, so callers don't have to do nasty
2836  * offsetting operation.
2837  */
2838 struct GNUNET_TESTING_Daemon *
2839 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int position)
2840 {
2841   if (position < pg->total)
2842     return pg->peers[position].daemon;
2843   else
2844     return NULL;
2845 }
2846
2847 /**
2848  * Prototype of a function that will be called when a
2849  * particular operation was completed the testing library.
2850  *
2851  * @param cls closure (a struct RestartContext)
2852  * @param id id of the peer that was restarted
2853  * @param cfg handle to the configuration of the peer
2854  * @param d handle to the daemon that was restarted
2855  * @param emsg NULL on success
2856  */
2857 void restart_callback (void *cls,
2858                        const struct GNUNET_PeerIdentity *id,
2859                        const struct GNUNET_CONFIGURATION_Handle *cfg,
2860                        struct GNUNET_TESTING_Daemon *d,
2861                        const char *emsg)
2862 {
2863   struct RestartContext *restart_context = cls;
2864
2865   if (emsg == NULL)
2866     {
2867       restart_context->peers_restarted++;
2868     }
2869   else
2870     {
2871       restart_context->peers_restart_failed++;
2872     }
2873
2874   if (restart_context->peers_restarted == restart_context->peer_group->total)
2875     {
2876       restart_context->callback(restart_context->callback_cls, NULL);
2877       GNUNET_free(restart_context);
2878     }
2879   else if (restart_context->peers_restart_failed + restart_context->peers_restarted == restart_context->peer_group->total)
2880     {
2881       restart_context->callback(restart_context->callback_cls, "Failed to restart peers!");
2882       GNUNET_free(restart_context);
2883     }
2884
2885 }
2886
2887 /**
2888  * Callback for informing us about a successful
2889  * or unsuccessful churn stop call.
2890  *
2891  * @param cls a ChurnContext
2892  * @param emsg NULL on success, non-NULL on failure
2893  *
2894  */
2895 void
2896 churn_stop_callback (void *cls, const char *emsg)
2897 {
2898   struct ChurnContext *churn_ctx = cls;
2899   unsigned int total_left;
2900   char *error_message;
2901
2902   error_message = NULL;
2903   if (emsg != NULL)
2904     {
2905       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, 
2906                  "Churn stop callback failed with error `%s'\n", emsg);
2907       churn_ctx->num_failed_stop++;
2908     }
2909   else
2910     {
2911       churn_ctx->num_to_stop--;
2912     }
2913
2914 #if DEBUG_CHURN
2915   GNUNET_log(GNUNET_ERROR_TYPE_WARNING, 
2916              "Stopped peer, %d left.\n", 
2917              churn_ctx->num_to_stop);
2918 #endif
2919   total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
2920
2921   if (total_left == 0)
2922   {
2923     if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
2924       {
2925         GNUNET_asprintf(&error_message, 
2926                         "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!", 
2927                         churn_ctx->num_failed_start, 
2928                         churn_ctx->num_failed_stop);
2929       }
2930     churn_ctx->cb(churn_ctx->cb_cls, error_message);
2931     GNUNET_free_non_null(error_message);
2932     GNUNET_free(churn_ctx);
2933   }
2934 }
2935
2936 /**
2937  * Callback for informing us about a successful
2938  * or unsuccessful churn start call.
2939  *
2940  * @param cls a ChurnContext
2941  * @param id the peer identity of the started peer
2942  * @param cfg the handle to the configuration of the peer
2943  * @param d handle to the daemon for the peer
2944  * @param emsg NULL on success, non-NULL on failure
2945  *
2946  */
2947 void
2948 churn_start_callback (void *cls,
2949                       const struct GNUNET_PeerIdentity *id,
2950                       const struct GNUNET_CONFIGURATION_Handle *cfg,
2951                       struct GNUNET_TESTING_Daemon *d,
2952                       const char *emsg)
2953 {
2954   struct ChurnContext *churn_ctx = cls;
2955   unsigned int total_left;
2956   char *error_message;
2957
2958   error_message = NULL;
2959   if (emsg != NULL)
2960     {
2961       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
2962                   "Churn stop callback failed with error `%s'\n",
2963                   emsg);
2964       churn_ctx->num_failed_start++;
2965     }
2966   else
2967     {
2968       churn_ctx->num_to_start--;
2969     }
2970   
2971 #if DEBUG_CHURN
2972   GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
2973              "Started peer, %d left.\n", 
2974              churn_ctx->num_to_start);
2975 #endif
2976
2977   total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
2978
2979   if (total_left == 0)
2980   {
2981     if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
2982       GNUNET_asprintf(&error_message, 
2983                       "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!", 
2984                       churn_ctx->num_failed_start,
2985                       churn_ctx->num_failed_stop);
2986     churn_ctx->cb(churn_ctx->cb_cls, error_message);
2987     GNUNET_free_non_null(error_message);
2988     GNUNET_free(churn_ctx);
2989   }
2990 }
2991
2992
2993 /**
2994  * Simulate churn by stopping some peers (and possibly
2995  * re-starting others if churn is called multiple times).  This
2996  * function can only be used to create leave-join churn (peers "never"
2997  * leave for good).  First "voff" random peers that are currently
2998  * online will be taken offline; then "von" random peers that are then
2999  * offline will be put back online.  No notifications will be
3000  * generated for any of these operations except for the callback upon
3001  * completion.
3002  *
3003  * @param pg handle for the peer group
3004  * @param voff number of peers that should go offline
3005  * @param von number of peers that should come back online;
3006  *            must be zero on first call (since "testbed_start"
3007  *            always starts all of the peers)
3008  * @param timeout how long to wait for operations to finish before
3009  *        giving up
3010  * @param cb function to call at the end
3011  * @param cb_cls closure for cb
3012  */
3013 void
3014 GNUNET_TESTING_daemons_churn (struct GNUNET_TESTING_PeerGroup *pg,
3015                               unsigned int voff,
3016                               unsigned int von,
3017                               struct GNUNET_TIME_Relative timeout,
3018                               GNUNET_TESTING_NotifyCompletion cb,
3019                               void *cb_cls)
3020 {
3021   struct ChurnContext *churn_ctx;
3022   unsigned int running;
3023   unsigned int stopped;
3024   unsigned int i;
3025   unsigned int *running_arr;
3026   unsigned int *stopped_arr;
3027   unsigned int *running_permute;
3028   unsigned int *stopped_permute;
3029
3030   running = 0;
3031   stopped = 0;
3032
3033   if ((von == 0) && (voff == 0)) /* No peers at all? */
3034     {
3035       cb(cb_cls, NULL);
3036       return;
3037     }
3038
3039   for (i = 0; i < pg->total; i++)
3040   {
3041     if (pg->peers[i].daemon->running == GNUNET_YES)
3042     {
3043       GNUNET_assert(running != -1);
3044       running++;
3045     }
3046     else
3047     {
3048       GNUNET_assert(stopped != -1);
3049       stopped++;
3050     }
3051   }
3052
3053   if (voff > running)
3054   {
3055     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Trying to stop more peers than are currently running!\n");
3056     cb(cb_cls, "Trying to stop more peers than are currently running!");
3057     return;
3058   }
3059
3060   if (von > stopped)
3061   {
3062     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Trying to start more peers than are currently stopped!\n");
3063     cb(cb_cls, "Trying to start more peers than are currently stopped!");
3064     return;
3065   }
3066
3067   churn_ctx = GNUNET_malloc(sizeof(struct ChurnContext));
3068   running_arr = GNUNET_malloc(running * sizeof(unsigned int));
3069   stopped_arr = GNUNET_malloc(stopped * sizeof(unsigned int));
3070
3071   running_permute = NULL;
3072   stopped_permute = NULL;
3073
3074   if (running > 0)
3075     running_permute = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK, running);
3076   if (stopped > 0)
3077     stopped_permute = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK, stopped);
3078
3079   running = 0;
3080   stopped = 0;
3081
3082   churn_ctx->num_to_start = von;
3083   churn_ctx->num_to_stop = voff;
3084   churn_ctx->cb = cb;
3085   churn_ctx->cb_cls = cb_cls;  
3086
3087   for (i = 0; i < pg->total; i++)
3088   {
3089     if (pg->peers[i].daemon->running == GNUNET_YES)
3090     {
3091       running_arr[running] = i;
3092       running++;
3093     }
3094     else
3095     {
3096       stopped_arr[stopped] = i;
3097       stopped++;
3098     }
3099   }
3100
3101   for (i = 0; i < voff; i++)
3102   {
3103 #if DEBUG_CHURN
3104     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Stopping peer %d!\n", running_permute[i]);
3105 #endif
3106     GNUNET_TESTING_daemon_stop (pg->peers[running_arr[running_permute[i]]].daemon,
3107                                 timeout, 
3108                                 &churn_stop_callback, churn_ctx, 
3109                                 GNUNET_NO, GNUNET_YES);
3110   }
3111
3112   for (i = 0; i < von; i++)
3113     {
3114 #if DEBUG_CHURN
3115       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Starting up peer %d!\n", stopped_permute[i]);
3116 #endif
3117       GNUNET_TESTING_daemon_start_stopped(pg->peers[stopped_arr[stopped_permute[i]]].daemon, 
3118                                           timeout, &churn_start_callback, churn_ctx);
3119   }
3120
3121   GNUNET_free(running_arr);
3122   GNUNET_free(stopped_arr);
3123   GNUNET_free_non_null(running_permute);
3124   GNUNET_free_non_null(stopped_permute);
3125 }
3126
3127
3128 /**
3129  * Restart all peers in the given group.
3130  *
3131  * @param pg the handle to the peer group
3132  * @param callback function to call on completion (or failure)
3133  * @param callback_cls closure for the callback function
3134  */
3135 void
3136 GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_NotifyCompletion callback, void *callback_cls)
3137 {
3138   struct RestartContext *restart_context;
3139   unsigned int off;
3140
3141   if (pg->total > 0)
3142     {
3143       restart_context = GNUNET_malloc(sizeof(struct RestartContext));
3144       restart_context->peer_group = pg;
3145       restart_context->peers_restarted = 0;
3146       restart_context->callback = callback;
3147       restart_context->callback_cls = callback_cls;
3148
3149       for (off = 0; off < pg->total; off++)
3150         {
3151           GNUNET_TESTING_daemon_restart (pg->peers[off].daemon, &restart_callback, restart_context);
3152         }
3153     }
3154 }
3155
3156 /**
3157  * Start or stop an individual peer from the given group.
3158  *
3159  * @param pg handle to the peer group
3160  * @param offset which peer to start or stop
3161  * @param desired_status GNUNET_YES to have it running, GNUNET_NO to stop it
3162  * @param timeout how long to wait for shutdown
3163  * @param cb function to call at the end
3164  * @param cb_cls closure for cb
3165  */
3166 void
3167 GNUNET_TESTING_daemons_vary (struct GNUNET_TESTING_PeerGroup *pg, 
3168                              unsigned int offset,
3169                              int desired_status,
3170                              struct GNUNET_TIME_Relative timeout,
3171                              GNUNET_TESTING_NotifyCompletion cb,
3172                              void *cb_cls)
3173 {
3174   struct ChurnContext *churn_ctx;
3175
3176   if (GNUNET_NO == desired_status)
3177     {
3178       if (NULL != pg->peers[offset].daemon)
3179         {
3180           churn_ctx = GNUNET_malloc(sizeof(struct ChurnContext));
3181           churn_ctx->num_to_start = 0;
3182           churn_ctx->num_to_stop = 1;
3183           churn_ctx->cb = cb;
3184           churn_ctx->cb_cls = cb_cls;  
3185           GNUNET_TESTING_daemon_stop(pg->peers[offset].daemon, 
3186                                      timeout, &churn_stop_callback, churn_ctx, 
3187                                      GNUNET_NO, GNUNET_YES);     
3188         }
3189     }
3190   else if (GNUNET_YES == desired_status)
3191     {
3192       if (NULL == pg->peers[offset].daemon)
3193         {
3194           churn_ctx = GNUNET_malloc(sizeof(struct ChurnContext));
3195           churn_ctx->num_to_start = 1;
3196           churn_ctx->num_to_stop = 0;
3197           churn_ctx->cb = cb;
3198           churn_ctx->cb_cls = cb_cls;  
3199           GNUNET_TESTING_daemon_start_stopped(pg->peers[offset].daemon, 
3200                                               timeout, &churn_start_callback, churn_ctx);
3201         }
3202     }
3203   else
3204     GNUNET_break (0);
3205 }
3206
3207
3208 /**
3209  * Shutdown all peers started in the given group.
3210  *
3211  * @param pg handle to the peer group
3212  * @param timeout how long to wait for shutdown
3213  */
3214 void
3215 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg, 
3216                              struct GNUNET_TIME_Relative timeout)
3217 {
3218   unsigned int off;
3219
3220   for (off = 0; off < pg->total; off++)
3221     {
3222       /* FIXME: should we wait for our continuations to be called
3223          here? This would require us to take a continuation as
3224          well... */
3225
3226       if (NULL != pg->peers[off].daemon)
3227         GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, timeout, NULL, NULL, GNUNET_YES, GNUNET_NO);
3228       if (NULL != pg->peers[off].cfg)
3229         GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
3230       if (pg->peers[off].allowed_peers != NULL)
3231         GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].allowed_peers);
3232       if (pg->peers[off].connect_peers != NULL)
3233         GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].connect_peers);
3234       if (pg->peers[off].blacklisted_peers != NULL)
3235         GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].blacklisted_peers);
3236     }
3237   GNUNET_free (pg->peers);
3238   if (NULL != pg->hosts)
3239     {
3240       GNUNET_free (pg->hosts[0].hostname);
3241       GNUNET_free (pg->hosts);
3242     }
3243   GNUNET_free (pg);
3244 }
3245
3246
3247 /* end of testing_group.c */