normal distribution
[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 3, or (at your
8  option) any later version.
9
10  GNUnet is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  General Public License for more details.
14
15  You should have received a copy of the GNU General Public License
16  along with GNUnet; see the file COPYING.  If not, write to the
17  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file testing/testing_group.c
23  * @brief convenience API for writing testcases for GNUnet
24  * @author Nathan Evans
25  * @author Christian Grothoff
26  *
27  */
28 #include "platform.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_arm_service.h"
31 #include "gnunet_testing_lib.h"
32 #include "gnunet_core_service.h"
33
34 #define VERBOSE_TESTING GNUNET_NO
35
36 #define VERBOSE_TOPOLOGY GNUNET_NO
37
38 #define DEBUG_CHURN GNUNET_NO
39
40 #define USE_START_HELPER GNUNET_YES
41
42 #define OLD 1
43
44 /* Before connecting peers, send all of the HELLOs */
45 #define USE_SEND_HELLOS GNUNET_NO
46
47 #define TOPOLOGY_HACK GNUNET_YES
48
49
50 /**
51  * Lowest port used for GNUnet testing.  Should be high enough to not
52  * conflict with other applications running on the hosts but be low
53  * enough to not conflict with client-ports (typically starting around
54  * 32k).
55  */
56 #define LOW_PORT 12000
57
58 /**
59  * Highest port used for GNUnet testing.  Should be low enough to not
60  * conflict with the port range for "local" ports (client apps; see
61  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
62  */
63 #define HIGH_PORT 56000
64
65 /* Maximum time to delay connect attempt */
66 #define MAX_CONNECT_DELAY 300
67
68 /**
69  * Which list of peers do we need to modify?
70  */
71 enum PeerLists
72 {
73   /** Modify allowed peers */
74   ALLOWED,
75
76   /** Modify connect peers */
77   CONNECT,
78
79   /** Modify blacklist peers */
80   BLACKLIST,
81
82   /** Modify workingset peers */
83   WORKING_SET
84 };
85
86 /**
87  * Prototype of a function called whenever two peers would be connected
88  * in a certain topology.
89  */
90 typedef unsigned int
91 (*GNUNET_TESTING_ConnectionProcessor)(struct GNUNET_TESTING_PeerGroup * pg,
92                                       unsigned int first, unsigned int second,
93                                       enum PeerLists list, unsigned int check);
94
95 /**
96  * Context for handling churning a peer group
97  */
98 struct ChurnContext
99 {
100   /**
101    * The peergroup we are dealing with.
102    */
103   struct GNUNET_TESTING_PeerGroup *pg;
104
105   /**
106    * Name of the service to churn on/off, NULL
107    * to churn entire peer.
108    */
109   char *service;
110
111   /**
112    * Callback used to notify of churning finished
113    */
114   GNUNET_TESTING_NotifyCompletion cb;
115
116   /**
117    * Closure for callback
118    */
119   void *cb_cls;
120
121   /**
122    * Number of peers that still need to be started
123    */
124   unsigned int num_to_start;
125
126   /**
127    * Number of peers that still need to be stopped
128    */
129   unsigned int num_to_stop;
130
131   /**
132    * Number of peers that failed to start
133    */
134   unsigned int num_failed_start;
135
136   /**
137    * Number of peers that failed to stop
138    */
139   unsigned int num_failed_stop;
140 };
141
142 struct RestartContext
143 {
144   /**
145    * The group of peers being restarted
146    */
147   struct GNUNET_TESTING_PeerGroup *peer_group;
148
149   /**
150    * How many peers have been restarted thus far
151    */
152   unsigned int peers_restarted;
153
154   /**
155    * How many peers got an error when restarting
156    */
157   unsigned int peers_restart_failed;
158
159   /**
160    * The function to call once all peers have been restarted
161    */
162   GNUNET_TESTING_NotifyCompletion callback;
163
164   /**
165    * Closure for callback function
166    */
167   void *callback_cls;
168
169 };
170
171 struct SendHelloContext
172 {
173   /**
174    * Global handle to the peer group.
175    */
176   struct GNUNET_TESTING_PeerGroup *pg;
177
178   /**
179    * The data about this specific peer.
180    */
181   struct PeerData *peer;
182
183   /**
184    * The next HELLO that needs sent to this peer.
185    */
186   struct PeerConnection *peer_pos;
187
188   /**
189    * Are we connected to CORE yet?
190    */
191   unsigned int core_ready;
192
193   /**
194    * How many attempts should we make for failed connections?
195    */
196   unsigned int connect_attempts;
197
198   /**
199    * Task for scheduling core connect requests to be sent.
200    */
201   GNUNET_SCHEDULER_TaskIdentifier core_connect_task;
202 };
203
204 struct ShutdownContext
205 {
206   struct GNUNET_TESTING_PeerGroup *pg;
207   /**
208    * Total peers to wait for
209    */
210   unsigned int total_peers;
211
212   /**
213    * Number of peers successfully shut down
214    */
215   unsigned int peers_down;
216
217   /**
218    * Number of peers failed to shut down
219    */
220   unsigned int peers_failed;
221
222   /**
223    * Number of peers we have started shutting
224    * down.  If too many, wait on them.
225    */
226   unsigned int outstanding;
227
228   /**
229    * Timeout for shutdown.
230    */
231   struct GNUNET_TIME_Relative timeout;
232
233   /**
234    * Callback to call when all peers either
235    * shutdown or failed to shutdown
236    */
237   GNUNET_TESTING_NotifyCompletion cb;
238
239   /**
240    * Closure for cb
241    */
242   void *cb_cls;
243
244   /**
245    * Should we delete all of the files from the peers?
246    */
247   int delete_files;
248 };
249
250 /**
251  * Individual shutdown context for a particular peer.
252  */
253 struct PeerShutdownContext
254 {
255   /**
256    * Pointer to the high level shutdown context.
257    */
258   struct ShutdownContext *shutdown_ctx;
259
260   /**
261    * The daemon handle for the peer to shut down.
262    */
263   struct GNUNET_TESTING_Daemon *daemon;
264 };
265
266 /**
267  * Individual shutdown context for a particular peer.
268  */
269 struct PeerRestartContext
270 {
271   /**
272    * Pointer to the high level restart context.
273    */
274   struct ChurnRestartContext *churn_restart_ctx;
275
276   /**
277    * The daemon handle for the peer to shut down.
278    */
279   struct GNUNET_TESTING_Daemon *daemon;
280 };
281
282 struct CreateTopologyContext
283 {
284
285   /**
286    * Function to call with number of connections
287    */
288   GNUNET_TESTING_NotifyConnections cont;
289
290   /**
291    * Closure for connection notification
292    */
293   void *cls;
294 };
295
296 enum States
297 {
298   /** Waiting to read number of peers */
299   NUM_PEERS,
300
301   /** Should find next peer index */
302   PEER_INDEX,
303
304   /** Should find colon */
305   COLON,
306
307   /** Should read other peer index, space, or endline */
308   OTHER_PEER_INDEX
309 };
310
311 #if OLD
312 struct PeerConnection
313 {
314   /**
315    * Doubly Linked list
316    */
317   struct PeerConnection *prev;
318
319   /*
320    * Doubly Linked list
321    */
322   struct PeerConnection *next;
323
324   /*
325    * Index of daemon in pg->peers
326    */
327   uint32_t index;
328
329 };
330 #endif
331
332 struct InternalStartContext
333 {
334   /**
335    * Pointer to peerdata
336    */
337   struct PeerData *peer;
338
339   /**
340    * Timeout for peer startup
341    */
342   struct GNUNET_TIME_Relative timeout;
343
344   /**
345    * Client callback for hostkey notification
346    */
347   GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback;
348
349   /**
350    * Closure for hostkey_callback
351    */
352   void *hostkey_cls;
353
354   /**
355    * Client callback for peer start notification
356    */
357   GNUNET_TESTING_NotifyDaemonRunning start_cb;
358
359   /**
360    * Closure for cb
361    */
362   void *start_cb_cls;
363
364   /**
365    * Hostname, where to start the peer
366    */
367   const char *hostname;
368
369   /**
370    * Username to use when connecting to the
371    * host via ssh.
372    */
373   const char *username;
374
375   /**
376    * Pointer to starting memory location of a hostkey
377    */
378   const char *hostkey;
379
380   /**
381    * Port to use for ssh.
382    */
383   uint16_t sshport;
384
385 };
386
387 struct ChurnRestartContext
388 {
389   /**
390    * PeerGroup that we are working with.
391    */
392   struct GNUNET_TESTING_PeerGroup *pg;
393
394   /**
395    * Number of restarts currently in flight.
396    */
397   unsigned int outstanding;
398
399   /**
400    * Handle to the underlying churn context.
401    */
402   struct ChurnContext *churn_ctx;
403
404   /**
405    * How long to allow the operation to take.
406    */
407   struct GNUNET_TIME_Relative timeout;
408 };
409
410 struct OutstandingSSH
411 {
412   struct OutstandingSSH *next;
413
414   struct OutstandingSSH *prev;
415
416   /**
417    * Number of current ssh connections.
418    */
419   uint32_t outstanding;
420
421   /**
422    * The hostname of this peer.
423    */
424   const char *hostname;
425 };
426
427 /**
428  * Data we keep per peer.
429  */
430 struct PeerData
431 {
432   /**
433    * (Initial) configuration of the host.
434    * (initial because clients could change
435    *  it and we would not know about those
436    *  updates).
437    */
438   struct GNUNET_CONFIGURATION_Handle *cfg;
439
440   /**
441    * Handle for controlling the daemon.
442    */
443   struct GNUNET_TESTING_Daemon *daemon;
444
445   /**
446    * The peergroup this peer belongs to.
447    */
448   struct GNUNET_TESTING_PeerGroup *pg;
449
450 #if OLD
451   /**
452    * Linked list of allowed peer connections.
453    */
454   struct PeerConnection *allowed_peers_head;
455
456   /**
457    * Linked list of allowed peer connections.
458    */
459   struct PeerConnection *allowed_peers_tail;
460
461   /**
462    * Linked list of blacklisted peer connections.
463    */
464   struct PeerConnection *blacklisted_peers_head;
465
466   /**
467    * Linked list of blacklisted peer connections.
468    */
469   struct PeerConnection *blacklisted_peers_tail;
470
471   /**
472    * Linked list of connect peer connections.
473    */
474   struct PeerConnection *connect_peers_head;
475
476   /**
477    * Linked list of connect peer connections.
478    */
479   struct PeerConnection *connect_peers_tail;
480
481   /**
482    * Linked list of connect peer connections.
483    */
484   struct PeerConnection *connect_peers_working_set_head;
485
486   /**
487    * Linked list of connect peer connections.
488    */
489   struct PeerConnection *connect_peers_working_set_tail;
490
491 #else
492   /**
493    * Hash map of allowed peer connections (F2F created topology)
494    */
495   struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
496
497   /**
498    * Hash map of blacklisted peers
499    */
500   struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
501
502   /**
503    * Hash map of peer connections
504    */
505   struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
506
507   /**
508    * Temporary hash map of peer connections
509    */
510   struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
511 #endif
512
513   /**
514    * Temporary variable for topology creation, should be reset before
515    * creating any topology so the count is valid once finished.
516    */
517   int num_connections;
518
519   /**
520    * Context to keep track of peers being started, to
521    * stagger hostkey generation and peer startup.
522    */
523   struct InternalStartContext internal_context;
524 };
525
526 /**
527  * Linked list of per-host data.
528  */
529 struct HostData
530 {
531   /**
532    * Name of the host.
533    */
534   char *hostname;
535
536   /**
537    * SSH username to use when connecting to this host.
538    */
539   char *username;
540
541   /**
542    * SSH port to use when connecting to this host.
543    */
544   uint16_t sshport;
545
546   /**
547    * Lowest port that we have not yet used
548    * for GNUnet.
549    */
550   uint16_t minport;
551 };
552
553 struct TopologyIterateContext
554 {
555   /**
556    * The peergroup we are working with.
557    */
558   struct GNUNET_TESTING_PeerGroup *pg;
559
560   /**
561    * Callback for notifying of two connected peers.
562    */
563   GNUNET_TESTING_NotifyTopology topology_cb;
564
565   /**
566    * Closure for topology_cb
567    */
568   void *cls;
569
570   /**
571    * Number of peers currently connected to.
572    */
573   unsigned int connected;
574
575   /**
576    * Number of peers we have finished iterating.
577    */
578   unsigned int completed;
579
580   /**
581    * Number of peers total.
582    */
583   unsigned int total;
584 };
585
586 struct StatsIterateContext
587 {
588   /**
589    * The peergroup that we are dealing with.
590    */
591   struct GNUNET_TESTING_PeerGroup *pg;
592
593   /**
594    * Continuation to call once all stats information has been retrieved.
595    */
596   GNUNET_STATISTICS_Callback cont;
597
598   /**
599    * Proc function to call on each value received.
600    */
601   GNUNET_TESTING_STATISTICS_Iterator proc;
602
603   /**
604    * Closure for topology_cb
605    */
606   void *cls;
607
608   /**
609    * Number of peers currently connected to.
610    */
611   unsigned int connected;
612
613   /**
614    * Number of peers we have finished iterating.
615    */
616   unsigned int completed;
617
618   /**
619    * Number of peers total.
620    */
621   unsigned int total;
622 };
623
624 struct CoreContext
625 {
626   void *iter_context;
627   struct GNUNET_TESTING_Daemon *daemon;
628 };
629
630 struct StatsCoreContext
631 {
632   void *iter_context;
633   struct GNUNET_TESTING_Daemon *daemon;
634   /**
635    * Handle to the statistics service.
636    */
637   struct GNUNET_STATISTICS_Handle *stats_handle;
638
639   /**
640    * Handle for getting statistics.
641    */
642   struct GNUNET_STATISTICS_GetHandle *stats_get_handle;
643 };
644
645 struct ConnectTopologyContext
646 {
647   /**
648    * How many connections are left to create.
649    */
650   unsigned int remaining_connections;
651
652   /**
653    * Handle to group of peers.
654    */
655   struct GNUNET_TESTING_PeerGroup *pg;
656
657   /**
658    * How long to try this connection before timing out.
659    */
660   struct GNUNET_TIME_Relative connect_timeout;
661
662   /**
663    * How many times to retry connecting the two peers.
664    */
665   unsigned int connect_attempts;
666
667   /**
668    * Temp value set for each iteration.
669    */
670   //struct PeerData *first;
671
672   /**
673    * Notification that all peers are connected.
674    */
675   GNUNET_TESTING_NotifyCompletion notify_connections_done;
676
677   /**
678    * Closure for notify.
679    */
680   void *notify_cls;
681 };
682
683 /**
684  * Handle to a group of GNUnet peers.
685  */
686 struct GNUNET_TESTING_PeerGroup
687 {
688   /**
689    * Configuration template.
690    */
691   const struct GNUNET_CONFIGURATION_Handle *cfg;
692
693   /**
694    * Function to call on each started daemon.
695    */
696   //GNUNET_TESTING_NotifyDaemonRunning cb;
697
698   /**
699    * Closure for cb.
700    */
701   //void *cb_cls;
702
703   /*
704    * Function to call on each topology connection created
705    */
706   GNUNET_TESTING_NotifyConnection notify_connection;
707
708   /*
709    * Callback for notify_connection
710    */
711   void *notify_connection_cls;
712
713   /**
714    * Array of information about hosts.
715    */
716   struct HostData *hosts;
717
718   /**
719    * Number of hosts (size of HostData)
720    */
721   unsigned int num_hosts;
722
723   /**
724    * Array of "total" peers.
725    */
726   struct PeerData *peers;
727
728   /**
729    * Number of peers in this group.
730    */
731   unsigned int total;
732
733   /**
734    * At what time should we fail the peer startup process?
735    */
736   struct GNUNET_TIME_Absolute max_timeout;
737
738   /**
739    * How many peers are being started right now?
740    */
741   unsigned int starting;
742
743   /**
744    * How many peers have already been started?
745    */
746   unsigned int started;
747
748   /**
749    * Number of possible connections to peers
750    * at a time.
751    */
752   unsigned int max_outstanding_connections;
753
754   /**
755    * Number of ssh connections to peers (max).
756    */
757   unsigned int max_concurrent_ssh;
758
759   /**
760    * Number of connects we are waiting on, allows us to rate limit
761    * connect attempts.
762    */
763   unsigned int outstanding_connects;
764
765   /**
766    * Number of HELLOs we have yet to send.
767    */
768   unsigned int remaining_hellos;
769
770   /**
771    * How many connects have already been scheduled?
772    */
773   unsigned int total_connects_scheduled;
774
775   /**
776    * Hostkeys loaded from a file.
777    */
778   char *hostkey_data;
779
780   /**
781    * Head of DLL to keep track of the number of outstanding
782    * ssh connections per peer.
783    */
784   struct OutstandingSSH *ssh_head;
785
786   /**
787    * Tail of DLL to keep track of the number of outstanding
788    * ssh connections per peer.
789    */
790   struct OutstandingSSH *ssh_tail;
791
792   /**
793    * Stop scheduling peers connecting.
794    */
795   unsigned int stop_connects;
796
797   /**
798    * Connection context for peer group.
799    */
800   struct ConnectTopologyContext ct_ctx;
801 };
802
803 struct UpdateContext
804 {
805   /**
806    * The altered configuration.
807    */
808   struct GNUNET_CONFIGURATION_Handle *ret;
809
810   /**
811    * The original configuration to alter.
812    */
813   const struct GNUNET_CONFIGURATION_Handle *orig;
814
815   /**
816    * The hostname that this peer will run on.
817    */
818   const char *hostname;
819
820   /**
821    * The next possible port to assign.
822    */
823   unsigned int nport;
824
825   /**
826    * Unique number for unix domain sockets.
827    */
828   unsigned int upnum;
829
830   /**
831    * Unique number for this peer/host to offset
832    * things that are grouped by host.
833    */
834   unsigned int fdnum;
835 };
836
837 struct ConnectContext
838 {
839   /**
840    * Index of peer to connect second to.
841    */
842   uint32_t first_index;
843
844   /**
845    * Index of peer to connect first to.
846    */
847   uint32_t second_index;
848
849   /**
850    * Higher level topology connection context.
851    */
852   struct ConnectTopologyContext *ct_ctx;
853
854   /**
855    * Whether this connection has been accounted for in the schedule_connect call.
856    */
857   int counted;
858 };
859
860 struct UnblacklistContext
861 {
862   /**
863    * The peergroup
864    */
865   struct GNUNET_TESTING_PeerGroup *pg;
866
867   /**
868    * uid of the first peer
869    */
870   uint32_t first_uid;
871 };
872
873 struct RandomContext
874 {
875   /**
876    * The peergroup
877    */
878   struct GNUNET_TESTING_PeerGroup *pg;
879
880   /**
881    * uid of the first peer
882    */
883   uint32_t first_uid;
884
885   /**
886    * Peer data for first peer.
887    */
888   struct PeerData *first;
889
890   /**
891    * Random percentage to use
892    */
893   double percentage;
894 };
895
896 struct MinimumContext
897 {
898   /**
899    * The peergroup
900    */
901   struct GNUNET_TESTING_PeerGroup *pg;
902
903   /**
904    * uid of the first peer
905    */
906   uint32_t first_uid;
907
908   /**
909    * Peer data for first peer.
910    */
911   struct PeerData *first;
912
913   /**
914    * Number of conns per peer
915    */
916   unsigned int num_to_add;
917
918   /**
919    * Permuted array of all possible connections.  Only add the Nth
920    * peer if it's in the Nth position.
921    */
922   unsigned int *pg_array;
923
924   /**
925    * What number is the current element we are iterating over?
926    */
927   unsigned int current;
928 };
929
930 struct DFSContext
931 {
932   /**
933    * The peergroup
934    */
935   struct GNUNET_TESTING_PeerGroup *pg;
936
937   /**
938    * uid of the first peer
939    */
940   uint32_t first_uid;
941
942   /**
943    * uid of the second peer
944    */
945   uint32_t second_uid;
946
947   /**
948    * Peer data for first peer.
949    */
950   struct PeerData *first;
951
952   /**
953    * Which peer has been chosen as the one to add?
954    */
955   unsigned int chosen;
956
957   /**
958    * What number is the current element we are iterating over?
959    */
960   unsigned int current;
961 };
962
963 /**
964  * Simple struct to keep track of progress, and print a
965  * nice little percentage meter for long running tasks.
966  */
967 struct ProgressMeter
968 {
969   unsigned int total;
970
971   unsigned int modnum;
972
973   unsigned int dotnum;
974
975   unsigned int completed;
976
977   int print;
978
979   char *startup_string;
980 };
981
982 #if !OLD
983 /**
984  * Convert unique ID to hash code.
985  *
986  * @param uid unique ID to convert
987  * @param hash set to uid (extended with zeros)
988  */
989 static void
990 hash_from_uid (uint32_t uid, GNUNET_HashCode * hash)
991   {
992     memset (hash, 0, sizeof (GNUNET_HashCode));
993     *((uint32_t *) hash) = uid;
994   }
995
996 /**
997  * Convert hash code to unique ID.
998  *
999  * @param uid unique ID to convert
1000  * @param hash set to uid (extended with zeros)
1001  */
1002 static void
1003 uid_from_hash (const GNUNET_HashCode * hash, uint32_t * uid)
1004   {
1005     memcpy (uid, hash, sizeof (uint32_t));
1006   }
1007 #endif
1008
1009 #if USE_SEND_HELLOS
1010 static struct GNUNET_CORE_MessageHandler no_handlers[] =
1011   {
1012       { NULL, 0, 0}};
1013 #endif
1014
1015 /**
1016  * Create a meter to keep track of the progress of some task.
1017  *
1018  * @param total the total number of items to complete
1019  * @param start_string a string to prefix the meter with (if printing)
1020  * @param print GNUNET_YES to print the meter, GNUNET_NO to count
1021  *              internally only
1022  *
1023  * @return the progress meter
1024  */
1025 static struct ProgressMeter *
1026 create_meter(unsigned int total, char * start_string, int print)
1027 {
1028   struct ProgressMeter *ret;
1029   ret = GNUNET_malloc(sizeof(struct ProgressMeter));
1030   ret->print = print;
1031   ret->total = total;
1032   ret->modnum = total / 4;
1033   if (ret->modnum == 0) /* Divide by zero check */
1034     ret->modnum = 1;
1035   ret->dotnum = (total / 50) + 1;
1036   if (start_string != NULL)
1037     ret->startup_string = GNUNET_strdup(start_string);
1038   else
1039     ret->startup_string = GNUNET_strdup("");
1040
1041   return ret;
1042 }
1043
1044 /**
1045  * Update progress meter (increment by one).
1046  *
1047  * @param meter the meter to update and print info for
1048  *
1049  * @return GNUNET_YES if called the total requested,
1050  *         GNUNET_NO if more items expected
1051  */
1052 static int
1053 update_meter(struct ProgressMeter *meter)
1054 {
1055   if (meter->print == GNUNET_YES)
1056     {
1057       if (meter->completed % meter->modnum == 0)
1058         {
1059           if (meter->completed == 0)
1060             {
1061               fprintf (stdout, "%sProgress: [0%%", meter->startup_string);
1062             }
1063           else
1064             fprintf (stdout, "%d%%", (int) (((float) meter->completed
1065                 / meter->total) * 100));
1066         }
1067       else if (meter->completed % meter->dotnum == 0)
1068         fprintf (stdout, ".");
1069
1070       if (meter->completed + 1 == meter->total)
1071         fprintf (stdout, "%d%%]\n", 100);
1072       fflush (stdout);
1073     }
1074   meter->completed++;
1075
1076   if (meter->completed == meter->total)
1077     return GNUNET_YES;
1078   if (meter->completed > meter->total)
1079     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Progress meter overflow!!\n");
1080   return GNUNET_NO;
1081 }
1082
1083 /**
1084  * Reset progress meter.
1085  *
1086  * @param meter the meter to reset
1087  *
1088  * @return GNUNET_YES if meter reset,
1089  *         GNUNET_SYSERR on error
1090  */
1091 static int
1092 reset_meter(struct ProgressMeter *meter)
1093 {
1094   if (meter == NULL)
1095     return GNUNET_SYSERR;
1096
1097   meter->completed = 0;
1098   return GNUNET_YES;
1099 }
1100
1101 /**
1102  * Release resources for meter
1103  *
1104  * @param meter the meter to free
1105  */
1106 static void
1107 free_meter(struct ProgressMeter *meter)
1108 {
1109   GNUNET_free_non_null (meter->startup_string);
1110   GNUNET_free (meter);
1111 }
1112
1113 /**
1114  * Get a topology from a string input.
1115  *
1116  * @param topology where to write the retrieved topology
1117  * @param topology_string The string to attempt to
1118  *        get a configuration value from
1119  * @return GNUNET_YES if topology string matched a
1120  *         known topology, GNUNET_NO if not
1121  */
1122 int
1123 GNUNET_TESTING_topology_get(enum GNUNET_TESTING_Topology *topology,
1124                             const char *topology_string)
1125 {
1126   /**
1127    * Strings representing topologies in enum
1128    */
1129   static const char *topology_strings[] =
1130     {
1131     /**
1132      * A clique (everyone connected to everyone else).
1133      */
1134     "CLIQUE",
1135
1136     /**
1137      * Small-world network (2d torus plus random links).
1138      */
1139     "SMALL_WORLD",
1140
1141     /**
1142      * Small-world network (ring plus random links).
1143      */
1144     "SMALL_WORLD_RING",
1145
1146     /**
1147      * Ring topology.
1148      */
1149     "RING",
1150
1151     /**
1152      * 2-d torus.
1153      */
1154     "2D_TORUS",
1155
1156     /**
1157      * Random graph.
1158      */
1159     "ERDOS_RENYI",
1160
1161     /**
1162      * Certain percentage of peers are unable to communicate directly
1163      * replicating NAT conditions
1164      */
1165     "INTERNAT",
1166
1167     /**
1168      * Scale free topology.
1169      */
1170     "SCALE_FREE",
1171
1172     /**
1173      * Straight line topology.
1174      */
1175     "LINE",
1176
1177     /**
1178      * All peers are disconnected.
1179      */
1180     "NONE",
1181
1182     /**
1183      * Read the topology from a file.
1184      */
1185     "FROM_FILE",
1186
1187     NULL };
1188
1189   int curr = 0;
1190   if (topology_string == NULL)
1191     return GNUNET_NO;
1192   while (topology_strings[curr] != NULL)
1193     {
1194       if (strcasecmp (topology_strings[curr], topology_string) == 0)
1195         {
1196           *topology = curr;
1197           return GNUNET_YES;
1198         }
1199       curr++;
1200     }
1201   *topology = GNUNET_TESTING_TOPOLOGY_NONE;
1202   return GNUNET_NO;
1203 }
1204
1205 /**
1206  * Get connect topology option from string input.
1207  *
1208  * @param topology_option where to write the retrieved topology
1209  * @param topology_string The string to attempt to
1210  *        get a configuration value from
1211  * @return GNUNET_YES if string matched a known
1212  *         topology option, GNUNET_NO if not
1213  */
1214 int
1215 GNUNET_TESTING_topology_option_get(
1216                                    enum GNUNET_TESTING_TopologyOption *topology_option,
1217                                    const char *topology_string)
1218 {
1219   /**
1220    * Options for connecting a topology as strings.
1221    */
1222   static const char *topology_option_strings[] =
1223     {
1224     /**
1225      * Try to connect all peers specified in the topology.
1226      */
1227     "CONNECT_ALL",
1228
1229     /**
1230      * Choose a random subset of connections to create.
1231      */
1232     "CONNECT_RANDOM_SUBSET",
1233
1234     /**
1235      * Create at least X connections for each peer.
1236      */
1237     "CONNECT_MINIMUM",
1238
1239     /**
1240      * Using a depth first search, create one connection
1241      * per peer.  If any are missed (graph disconnected)
1242      * start over at those peers until all have at least one
1243      * connection.
1244      */
1245     "CONNECT_DFS",
1246
1247     /**
1248      * Find the N closest peers to each allowed peer in the
1249      * topology and make sure a connection to those peers
1250      * exists in the connect topology.
1251      */
1252     "CONNECT_CLOSEST",
1253
1254     /**
1255      * No options specified.
1256      */
1257     "CONNECT_NONE",
1258
1259     NULL };
1260   int curr = 0;
1261
1262   if (topology_string == NULL)
1263     return GNUNET_NO;
1264   while (NULL != topology_option_strings[curr])
1265     {
1266       if (strcasecmp (topology_option_strings[curr], topology_string) == 0)
1267         {
1268           *topology_option = curr;
1269           return GNUNET_YES;
1270         }
1271       curr++;
1272     }
1273   *topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_NONE;
1274   return GNUNET_NO;
1275 }
1276
1277 /**
1278  * Function to iterate over options.  Copies
1279  * the options to the target configuration,
1280  * updating PORT values as needed.
1281  *
1282  * @param cls closure
1283  * @param section name of the section
1284  * @param option name of the option
1285  * @param value value of the option
1286  */
1287 static void
1288 update_config(void *cls, const char *section, const char *option,
1289               const char *value)
1290 {
1291   struct UpdateContext *ctx = cls;
1292   unsigned int ival;
1293   char cval[12];
1294   char uval[128];
1295   char *single_variable;
1296   char *per_host_variable;
1297   unsigned long long num_per_host;
1298
1299   GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
1300   GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
1301
1302   if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
1303     {
1304       if ((ival != 0) && (GNUNET_YES
1305           != GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
1306                                                    single_variable)))
1307         {
1308           GNUNET_snprintf (cval, sizeof(cval), "%u", ctx->nport++);
1309           value = cval;
1310         }
1311       else if ((ival != 0) && (GNUNET_YES
1312           == GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
1313                                                    single_variable))
1314           && GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
1315                                                     per_host_variable,
1316                                                     &num_per_host))
1317         {
1318           GNUNET_snprintf (cval, sizeof(cval), "%u", ival + ctx->fdnum
1319               % num_per_host);
1320           value = cval;
1321         }
1322
1323       /* FIXME: REMOVE FOREVER HACK HACK HACK */
1324       if (0 == strcasecmp (section, "transport-tcp"))
1325         GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, "ADVERTISED_PORT", value);
1326     }
1327
1328   if (0 == strcmp (option, "UNIXPATH"))
1329     {
1330       if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (ctx->orig,
1331                                                               "testing",
1332                                                               single_variable))
1333         {
1334           GNUNET_snprintf (uval, sizeof(uval), "/tmp/test-service-%s-%u",
1335                            section, ctx->upnum++);
1336           value = uval;
1337         }
1338       else if ((GNUNET_YES
1339           == GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
1340                                                     per_host_variable,
1341                                                     &num_per_host))
1342           && (num_per_host > 0))
1343
1344         {
1345           GNUNET_snprintf (uval, sizeof(uval), "/tmp/test-service-%s-%u",
1346                            section, ctx->fdnum % num_per_host);
1347           value = uval;
1348         }
1349     }
1350
1351   if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
1352     {
1353       value = ctx->hostname;
1354     }
1355   GNUNET_free (single_variable);
1356   GNUNET_free (per_host_variable);
1357   GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
1358 }
1359
1360 /**
1361  * Create a new configuration using the given configuration
1362  * as a template; however, each PORT in the existing cfg
1363  * must be renumbered by incrementing "*port".  If we run
1364  * out of "*port" numbers, return NULL.
1365  *
1366  * @param cfg template configuration
1367  * @param off the current peer offset
1368  * @param port port numbers to use, update to reflect
1369  *             port numbers that were used
1370  * @param upnum number to make unix domain socket names unique
1371  * @param hostname hostname of the controlling host, to allow control connections from
1372  * @param fdnum number used to offset the unix domain socket for grouped processes
1373  *              (such as statistics or peerinfo, which can be shared among others)
1374  *
1375  * @return new configuration, NULL on error
1376  */
1377 static struct GNUNET_CONFIGURATION_Handle *
1378 make_config(const struct GNUNET_CONFIGURATION_Handle *cfg, uint32_t off,
1379             uint16_t * port, uint32_t * upnum, const char *hostname,
1380             uint32_t * fdnum)
1381 {
1382   struct UpdateContext uc;
1383   uint16_t orig;
1384   char *control_host;
1385   char *allowed_hosts;
1386   unsigned long long skew_variance;
1387   unsigned long long skew_offset;
1388   long long actual_offset;
1389
1390   orig = *port;
1391   uc.nport = *port;
1392   uc.upnum = *upnum;
1393   uc.fdnum = *fdnum;
1394   uc.ret = GNUNET_CONFIGURATION_create ();
1395   uc.hostname = hostname;
1396   uc.orig = cfg;
1397
1398   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
1399   if (uc.nport >= HIGH_PORT)
1400     {
1401       *port = orig;
1402       GNUNET_CONFIGURATION_destroy (uc.ret);
1403       return NULL;
1404     }
1405
1406   if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
1407                                                            "skew_variance",
1408                                                            &skew_variance))
1409       && (skew_variance > 0))
1410     {
1411       skew_offset = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK, skew_variance + 1);
1412       actual_offset = skew_offset - GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK, skew_variance + 1);
1413       /* Min is -skew_variance, Max is skew_variance */
1414       skew_offset = skew_variance + actual_offset; /* Normal distribution around 0 */
1415       GNUNET_CONFIGURATION_set_value_number(uc.ret, "testing", "skew_offset", skew_offset);
1416     }
1417
1418   if (GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "control_host",
1419                                              &control_host) == GNUNET_OK)
1420     {
1421       if (hostname != NULL)
1422         GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1; %s;", control_host,
1423                          hostname);
1424       else
1425         GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", control_host);
1426
1427       GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "ACCEPT_FROM",
1428                                              allowed_hosts);
1429       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport",
1430                                              "ACCEPT_FROM", allowed_hosts);
1431       GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "ACCEPT_FROM",
1432                                              allowed_hosts);
1433       GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics",
1434                                              "ACCEPT_FROM", allowed_hosts);
1435
1436       GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "UNIXPATH", "");
1437       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport", "UNIXPATH",
1438                                              "");
1439       GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "UNIXPATH", "");
1440       GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics", "UNIXPATH",
1441                                              "");
1442
1443       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp", "USE_LOCALADDR",
1444                                              "YES");
1445       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp", "USE_LOCALADDR",
1446                                              "YES");
1447       GNUNET_free_non_null (control_host);
1448       GNUNET_free (allowed_hosts);
1449     }
1450
1451   /* arm needs to know to allow connections from the host on which it is running,
1452    * otherwise gnunet-arm is unable to connect to it in some instances */
1453   if (hostname != NULL)
1454     {
1455       GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", hostname);
1456       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp", "BINDTO",
1457                                              hostname);
1458       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp", "BINDTO",
1459                                              hostname);
1460       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp", "USE_LOCALADDR",
1461                                              "YES");
1462       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp", "USE_LOCALADDR",
1463                                              "YES");
1464       GNUNET_CONFIGURATION_set_value_string (uc.ret, "arm", "ACCEPT_FROM",
1465                                              allowed_hosts);
1466       GNUNET_free (allowed_hosts);
1467     }
1468   else
1469     {
1470       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp", "BINDTO",
1471                                              "127.0.0.1");
1472       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp", "BINDTO",
1473                                              "127.0.0.1");
1474       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp", "USE_LOCALADDR",
1475                                              "YES");
1476       GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp", "USE_LOCALADDR",
1477                                              "YES");
1478     }
1479
1480   *port = (uint16_t) uc.nport;
1481   *upnum = uc.upnum;
1482   uc.fdnum++;
1483   *fdnum = uc.fdnum;
1484   return uc.ret;
1485 }
1486
1487 /*
1488  * Remove entries from the peer connection list
1489  *
1490  * @param pg the peer group we are working with
1491  * @param first index of the first peer
1492  * @param second index of the second peer
1493  * @param list the peer list to use
1494  * @param check UNUSED
1495  *
1496  * @return the number of connections added (can be 0, 1 or 2)
1497  *
1498  */
1499 static unsigned int
1500 remove_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first,
1501                    unsigned int second, enum PeerLists list, unsigned int check)
1502 {
1503   int removed;
1504 #if OLD
1505   struct PeerConnection **first_list;
1506   struct PeerConnection **second_list;
1507   struct PeerConnection *first_iter;
1508   struct PeerConnection *second_iter;
1509   struct PeerConnection **first_tail;
1510   struct PeerConnection **second_tail;
1511
1512 #else
1513   GNUNET_HashCode hash_first;
1514   GNUNET_HashCode hash_second;
1515
1516   hash_from_uid (first, &hash_first);
1517   hash_from_uid (second, &hash_second);
1518 #endif
1519
1520   removed = 0;
1521 #if OLD
1522   switch (list)
1523     {
1524   case ALLOWED:
1525     first_list = &pg->peers[first].allowed_peers_head;
1526     second_list = &pg->peers[second].allowed_peers_head;
1527     first_tail = &pg->peers[first].allowed_peers_tail;
1528     second_tail = &pg->peers[second].allowed_peers_tail;
1529     break;
1530   case CONNECT:
1531     first_list = &pg->peers[first].connect_peers_head;
1532     second_list = &pg->peers[second].connect_peers_head;
1533     first_tail = &pg->peers[first].connect_peers_tail;
1534     second_tail = &pg->peers[second].connect_peers_tail;
1535     break;
1536   case BLACKLIST:
1537     first_list = &pg->peers[first].blacklisted_peers_head;
1538     second_list = &pg->peers[second].blacklisted_peers_head;
1539     first_tail = &pg->peers[first].blacklisted_peers_tail;
1540     second_tail = &pg->peers[second].blacklisted_peers_tail;
1541     break;
1542   case WORKING_SET:
1543     first_list = &pg->peers[first].connect_peers_working_set_head;
1544     second_list = &pg->peers[second].connect_peers_working_set_head;
1545     first_tail = &pg->peers[first].connect_peers_working_set_tail;
1546     second_tail = &pg->peers[second].connect_peers_working_set_tail;
1547     break;
1548   default:
1549     GNUNET_break(0);
1550     return 0;
1551     }
1552
1553   first_iter = *first_list;
1554   while (first_iter != NULL)
1555     {
1556       if (first_iter->index == second)
1557         {
1558           GNUNET_CONTAINER_DLL_remove(*first_list, *first_tail, first_iter);
1559           GNUNET_free(first_iter);
1560           removed++;
1561           break;
1562         }
1563       first_iter = first_iter->next;
1564     }
1565
1566   second_iter = *second_list;
1567   while (second_iter != NULL)
1568     {
1569       if (second_iter->index == first)
1570         {
1571           GNUNET_CONTAINER_DLL_remove(*second_list, *second_tail, second_iter);
1572           GNUNET_free(second_iter);
1573           removed++;
1574           break;
1575         }
1576       second_iter = second_iter->next;
1577     }
1578 #else
1579   if (GNUNET_YES ==
1580       GNUNET_CONTAINER_multihashmap_contains (pg->peers[first].blacklisted_peers,
1581           &hash_second))
1582     {
1583       GNUNET_CONTAINER_multihashmap_remove_all (pg->peers[first].blacklisted_peers,
1584           &hash_second);
1585     }
1586
1587   if (GNUNET_YES ==
1588       GNUNET_CONTAINER_multihashmap_contains (pg->peers[second].blacklisted_peers,
1589           &hash_first))
1590     {
1591       GNUNET_CONTAINER_multihashmap_remove_all (pg->peers[second].blacklisted_peers,
1592           &hash_first);
1593     }
1594 #endif
1595
1596   return removed;
1597 }
1598
1599 /*
1600  * Add entries to the some list
1601  *
1602  * @param pg the peer group we are working with
1603  * @param first index of the first peer
1604  * @param second index of the second peer
1605  * @param list the list type that we should modify
1606  * @param check GNUNET_YES to check lists before adding
1607  *              GNUNET_NO to force add
1608  *
1609  * @return the number of connections added (can be 0, 1 or 2)
1610  *
1611  */
1612 static unsigned int
1613 add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first,
1614                 unsigned int second, enum PeerLists list, unsigned int check)
1615 {
1616   int added;
1617   int add_first;
1618   int add_second;
1619
1620   struct PeerConnection **first_list;
1621   struct PeerConnection **second_list;
1622   struct PeerConnection *first_iter;
1623   struct PeerConnection *second_iter;
1624   struct PeerConnection *new_first;
1625   struct PeerConnection *new_second;
1626   struct PeerConnection **first_tail;
1627   struct PeerConnection **second_tail;
1628
1629   switch (list)
1630     {
1631   case ALLOWED:
1632     first_list = &pg->peers[first].allowed_peers_head;
1633     second_list = &pg->peers[second].allowed_peers_head;
1634     first_tail = &pg->peers[first].allowed_peers_tail;
1635     second_tail = &pg->peers[second].allowed_peers_tail;
1636     break;
1637   case CONNECT:
1638     first_list = &pg->peers[first].connect_peers_head;
1639     second_list = &pg->peers[second].connect_peers_head;
1640     first_tail = &pg->peers[first].connect_peers_tail;
1641     second_tail = &pg->peers[second].connect_peers_tail;
1642     break;
1643   case BLACKLIST:
1644     first_list = &pg->peers[first].blacklisted_peers_head;
1645     second_list = &pg->peers[second].blacklisted_peers_head;
1646     first_tail = &pg->peers[first].blacklisted_peers_tail;
1647     second_tail = &pg->peers[second].blacklisted_peers_tail;
1648     break;
1649   case WORKING_SET:
1650     first_list = &pg->peers[first].connect_peers_working_set_head;
1651     second_list = &pg->peers[second].connect_peers_working_set_head;
1652     first_tail = &pg->peers[first].connect_peers_working_set_tail;
1653     second_tail = &pg->peers[second].connect_peers_working_set_tail;
1654     break;
1655   default:
1656     GNUNET_break(0);
1657     return 0;
1658     }
1659
1660   add_first = GNUNET_YES;
1661   add_second = GNUNET_YES;
1662
1663   if (check == GNUNET_YES)
1664     {
1665       first_iter = *first_list;
1666       while (first_iter != NULL)
1667         {
1668           if (first_iter->index == second)
1669             {
1670               add_first = GNUNET_NO;
1671               break;
1672             }
1673           first_iter = first_iter->next;
1674         }
1675
1676       second_iter = *second_list;
1677       while (second_iter != NULL)
1678         {
1679           if (second_iter->index == first)
1680             {
1681               add_second = GNUNET_NO;
1682               break;
1683             }
1684           second_iter = second_iter->next;
1685         }
1686     }
1687
1688   added = 0;
1689   if (add_first)
1690     {
1691       new_first = GNUNET_malloc (sizeof (struct PeerConnection));
1692       new_first->index = second;
1693       GNUNET_CONTAINER_DLL_insert(*first_list, *first_tail, new_first);
1694       pg->peers[first].num_connections++;
1695       added++;
1696     }
1697
1698   if (add_second)
1699     {
1700       new_second = GNUNET_malloc (sizeof (struct PeerConnection));
1701       new_second->index = first;
1702       GNUNET_CONTAINER_DLL_insert(*second_list, *second_tail, new_second);
1703       pg->peers[second].num_connections++;
1704       added++;
1705     }
1706
1707   return added;
1708 }
1709
1710 /**
1711  * Scale free network construction as described in:
1712  *
1713  * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
1714  *
1715  * Start with a network of "one" peer, then progressively add
1716  * peers up to the total number.  At each step, iterate over
1717  * all possible peers and connect new peer based on number of
1718  * existing connections of the target peer.
1719  *
1720  * @param pg the peer group we are dealing with
1721  * @param proc the connection processor to use
1722  * @param list the peer list to use
1723  *
1724  * @return the number of connections created
1725  */
1726 static unsigned int
1727 create_scale_free(struct GNUNET_TESTING_PeerGroup *pg,
1728                   GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1729 {
1730
1731   unsigned int total_connections;
1732   unsigned int outer_count;
1733   unsigned int i;
1734   unsigned int previous_total_connections;
1735   double random;
1736   double probability;
1737
1738   GNUNET_assert (pg->total > 1);
1739
1740   /* Add a connection between the first two nodes */
1741   total_connections = proc (pg, 0, 1, list, GNUNET_YES);
1742
1743   for (outer_count = 1; outer_count < pg->total; outer_count++)
1744     {
1745       previous_total_connections = total_connections;
1746       for (i = 0; i < outer_count; i++)
1747         {
1748           probability = pg->peers[i].num_connections
1749               / (double) previous_total_connections;
1750           random
1751               = ((double) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1752                                                     UINT64_MAX))
1753                   / ((double) UINT64_MAX);
1754 #if VERBOSE_TESTING
1755           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1756               "Considering connecting peer %d to peer %d\n",
1757               outer_count, i);
1758 #endif
1759           if (random < probability)
1760             {
1761 #if VERBOSE_TESTING
1762               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1763                   "Connecting peer %d to peer %d\n", outer_count, i);
1764 #endif
1765               total_connections += proc (pg, outer_count, i, list, GNUNET_YES);
1766             }
1767         }
1768     }
1769
1770   return total_connections;
1771 }
1772
1773 /**
1774  * Create a topology given a peer group (set of running peers)
1775  * and a connection processor.  Creates a small world topology
1776  * according to the rewired ring construction.  The basic
1777  * behavior is that a ring topology is created, but with some
1778  * probability instead of connecting a peer to the next
1779  * neighbor in the ring a connection will be created to a peer
1780  * selected uniformly at random.   We use the TESTING
1781  * PERCENTAGE option to specify what number of
1782  * connections each peer should have.  Default is 2,
1783  * which makes the ring, any given number is multiplied by
1784  * the log of the network size; i.e. a PERCENTAGE of 2 makes
1785  * each peer have on average 2logn connections.  The additional
1786  * connections are made at increasing distance around the ring
1787  * from the original peer, or to random peers based on the re-
1788  * wiring probability. The TESTING
1789  * PROBABILITY option is used as the probability that a given
1790  * connection is rewired.
1791  *
1792  * @param pg the peergroup to create the topology on
1793  * @param proc the connection processor to call to actually set
1794  *        up connections between two peers
1795  * @param list the peer list to use
1796  *
1797  * @return the number of connections that were set up
1798  *
1799  */
1800 static unsigned int
1801 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg,
1802                         GNUNET_TESTING_ConnectionProcessor proc,
1803                         enum PeerLists list)
1804 {
1805   unsigned int i, j;
1806   int nodeToConnect;
1807   unsigned int natLog;
1808   unsigned int randomPeer;
1809   double random, logNModifier, probability;
1810   unsigned int smallWorldConnections;
1811   int connsPerPeer;
1812   char *p_string;
1813   int max;
1814   int min;
1815   unsigned int useAnd;
1816   int connect_attempts;
1817
1818   logNModifier = 0.5; /* FIXME: default value? */
1819   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
1820                                                           "PERCENTAGE",
1821                                                           &p_string))
1822     {
1823       if (sscanf (p_string, "%lf", &logNModifier) != 1)
1824         GNUNET_log (
1825                     GNUNET_ERROR_TYPE_WARNING,
1826                     _
1827                     ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1828                     p_string, "LOGNMODIFIER", "TESTING");
1829       GNUNET_free (p_string);
1830     }
1831   probability = 0.5; /* FIXME: default percentage? */
1832   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
1833                                                           "PROBABILITY",
1834                                                           &p_string))
1835     {
1836       if (sscanf (p_string, "%lf", &probability) != 1)
1837         GNUNET_log (
1838                     GNUNET_ERROR_TYPE_WARNING,
1839                     _
1840                     ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1841                     p_string, "PERCENTAGE", "TESTING");
1842       GNUNET_free (p_string);
1843     }
1844   natLog = log (pg->total);
1845   connsPerPeer = ceil (natLog * logNModifier);
1846
1847   if (connsPerPeer % 2 == 1)
1848     connsPerPeer += 1;
1849
1850   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Target is %d connections per peer."),
1851               connsPerPeer);
1852
1853   smallWorldConnections = 0;
1854   connect_attempts = 0;
1855   for (i = 0; i < pg->total; i++)
1856     {
1857       useAnd = 0;
1858       max = i + connsPerPeer / 2;
1859       min = i - connsPerPeer / 2;
1860
1861       if (max > pg->total - 1)
1862         {
1863           max = max - pg->total;
1864           useAnd = 1;
1865         }
1866
1867       if (min < 0)
1868         {
1869           min = pg->total - 1 + min;
1870           useAnd = 1;
1871         }
1872
1873       for (j = 0; j < connsPerPeer / 2; j++)
1874         {
1875           random
1876               = ((double) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1877                                                     UINT64_MAX)
1878                   / ((double) UINT64_MAX));
1879           if (random < probability)
1880             {
1881               /* Connect to uniformly selected random peer */
1882               randomPeer
1883                   = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1884                                               pg->total);
1885               while ((((randomPeer < max) && (randomPeer > min)) && (useAnd
1886                   == 0)) || (((randomPeer > min) || (randomPeer < max))
1887                   && (useAnd == 1)))
1888                 {
1889                   randomPeer
1890                       = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1891                                                   pg->total);
1892                 }
1893               smallWorldConnections += proc (pg, i, randomPeer, list,
1894                                              GNUNET_YES);
1895             }
1896           else
1897             {
1898               nodeToConnect = i + j + 1;
1899               if (nodeToConnect > pg->total - 1)
1900                 {
1901                   nodeToConnect = nodeToConnect - pg->total;
1902                 }
1903               connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
1904             }
1905         }
1906
1907     }
1908
1909   connect_attempts += smallWorldConnections;
1910
1911   return connect_attempts;
1912 }
1913
1914 /**
1915  * Create a topology given a peer group (set of running peers)
1916  * and a connection processor.
1917  *
1918  * @param pg the peergroup to create the topology on
1919  * @param proc the connection processor to call to actually set
1920  *        up connections between two peers
1921  * @param list the peer list to use
1922  *
1923  * @return the number of connections that were set up
1924  *
1925  */
1926 static unsigned int
1927 create_nated_internet(struct GNUNET_TESTING_PeerGroup *pg,
1928                       GNUNET_TESTING_ConnectionProcessor proc,
1929                       enum PeerLists list)
1930 {
1931   unsigned int outer_count, inner_count;
1932   unsigned int cutoff;
1933   int connect_attempts;
1934   double nat_percentage;
1935   char *p_string;
1936
1937   nat_percentage = 0.6; /* FIXME: default percentage? */
1938   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
1939                                                           "PERCENTAGE",
1940                                                           &p_string))
1941     {
1942       if (sscanf (p_string, "%lf", &nat_percentage) != 1)
1943         GNUNET_log (
1944                     GNUNET_ERROR_TYPE_WARNING,
1945                     _
1946                     ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1947                     p_string, "PERCENTAGE", "TESTING");
1948       GNUNET_free (p_string);
1949     }
1950
1951   cutoff = (unsigned int) (nat_percentage * pg->total);
1952   connect_attempts = 0;
1953   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1954     {
1955       for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
1956         {
1957           if ((outer_count > cutoff) || (inner_count > cutoff))
1958             {
1959 #if VERBOSE_TESTING
1960               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1961                   "Connecting peer %d to peer %d\n",
1962                   outer_count, inner_count);
1963 #endif
1964               connect_attempts += proc (pg, outer_count, inner_count, list,
1965                                         GNUNET_YES);
1966             }
1967         }
1968     }
1969   return connect_attempts;
1970 }
1971
1972 #if TOPOLOGY_HACK
1973 /**
1974  * Create a topology given a peer group (set of running peers)
1975  * and a connection processor.
1976  *
1977  * @param pg the peergroup to create the topology on
1978  * @param proc the connection processor to call to actually set
1979  *        up connections between two peers
1980  * @param list the peer list to use
1981  *
1982  * @return the number of connections that were set up
1983  *
1984  */
1985 static unsigned int
1986 create_nated_internet_copy(struct GNUNET_TESTING_PeerGroup *pg,
1987                            GNUNET_TESTING_ConnectionProcessor proc,
1988                            enum PeerLists list)
1989 {
1990   unsigned int outer_count, inner_count;
1991   unsigned int cutoff;
1992   int connect_attempts;
1993   double nat_percentage;
1994   char *p_string;
1995   unsigned int count;
1996   struct ProgressMeter *conn_meter;
1997
1998   nat_percentage = 0.6; /* FIXME: default percentage? */
1999   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
2000                                                           "PERCENTAGE",
2001                                                           &p_string))
2002     {
2003       if (sscanf (p_string, "%lf", &nat_percentage) != 1)
2004         GNUNET_log (
2005                     GNUNET_ERROR_TYPE_WARNING,
2006                     _
2007                     ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
2008                     p_string, "PERCENTAGE", "TESTING");
2009       GNUNET_free (p_string);
2010     }
2011
2012   cutoff = (unsigned int) (nat_percentage * pg->total);
2013   count = 0;
2014   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2015     {
2016       for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
2017         {
2018           if ((outer_count > cutoff) || (inner_count > cutoff))
2019             {
2020               count++;
2021             }
2022         }
2023     }
2024   conn_meter = create_meter (count, "NAT COPY", GNUNET_YES);
2025   connect_attempts = 0;
2026   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2027     {
2028       for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
2029         {
2030           if ((outer_count > cutoff) || (inner_count > cutoff))
2031             {
2032 #if VERBOSE_TESTING
2033               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2034                   "Connecting peer %d to peer %d\n",
2035                   outer_count, inner_count);
2036 #endif
2037               connect_attempts += proc (pg, outer_count, inner_count, list,
2038                                         GNUNET_YES);
2039               add_connections (pg, outer_count, inner_count, ALLOWED, GNUNET_NO);
2040               update_meter (conn_meter);
2041             }
2042         }
2043     }
2044   free_meter (conn_meter);
2045
2046   return connect_attempts;
2047 }
2048 #endif
2049
2050 /**
2051  * Create a topology given a peer group (set of running peers)
2052  * and a connection processor.
2053  *
2054  * @param pg the peergroup to create the topology on
2055  * @param proc the connection processor to call to actually set
2056  *        up connections between two peers
2057  * @param list the peer list to use
2058  *
2059  * @return the number of connections that were set up
2060  *
2061  */
2062 static unsigned int
2063 create_small_world(struct GNUNET_TESTING_PeerGroup *pg,
2064                    GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2065 {
2066   unsigned int i, j, k;
2067   unsigned int square;
2068   unsigned int rows;
2069   unsigned int cols;
2070   unsigned int toggle = 1;
2071   unsigned int nodeToConnect;
2072   unsigned int natLog;
2073   unsigned int node1Row;
2074   unsigned int node1Col;
2075   unsigned int node2Row;
2076   unsigned int node2Col;
2077   unsigned int distance;
2078   double probability, random, percentage;
2079   unsigned int smallWorldConnections;
2080   unsigned int small_world_it;
2081   char *p_string;
2082   int connect_attempts;
2083   square = floor (sqrt (pg->total));
2084   rows = square;
2085   cols = square;
2086
2087   percentage = 0.5; /* FIXME: default percentage? */
2088   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
2089                                                           "PERCENTAGE",
2090                                                           &p_string))
2091     {
2092       if (sscanf (p_string, "%lf", &percentage) != 1)
2093         GNUNET_log (
2094                     GNUNET_ERROR_TYPE_WARNING,
2095                     _
2096                     ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
2097                     p_string, "PERCENTAGE", "TESTING");
2098       GNUNET_free (p_string);
2099     }
2100   if (percentage < 0.0)
2101     {
2102       GNUNET_log (
2103                   GNUNET_ERROR_TYPE_WARNING,
2104                   _
2105                   ("Invalid value `%s' for option `%s' in section `%s': got %f, needed value greater than 0\n"),
2106                   "PERCENTAGE", "TESTING", percentage);
2107       percentage = 0.5;
2108     }
2109   probability = 0.5; /* FIXME: default percentage? */
2110   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
2111                                                           "PROBABILITY",
2112                                                           &p_string))
2113     {
2114       if (sscanf (p_string, "%lf", &probability) != 1)
2115         GNUNET_log (
2116                     GNUNET_ERROR_TYPE_WARNING,
2117                     _
2118                     ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
2119                     p_string, "PROBABILITY", "TESTING");
2120       GNUNET_free (p_string);
2121     }
2122   if (square * square != pg->total)
2123     {
2124       while (rows * cols < pg->total)
2125         {
2126           if (toggle % 2 == 0)
2127             rows++;
2128           else
2129             cols++;
2130
2131           toggle++;
2132         }
2133     }
2134 #if VERBOSE_TESTING
2135   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2136       _
2137       ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
2138       rows, cols);
2139 #endif
2140
2141   connect_attempts = 0;
2142   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
2143    * to the node to its right and above.  Once this is over, we'll have our torus!
2144    * Special case for the last node (if the rows and columns are not equal), connect
2145    * to the first in the row to maintain topology.
2146    */
2147   for (i = 0; i < pg->total; i++)
2148     {
2149       /* First connect to the node to the right */
2150       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
2151         nodeToConnect = i + 1;
2152       else if (i + 1 == pg->total)
2153         nodeToConnect = rows * cols - cols;
2154       else
2155         nodeToConnect = i - cols + 1;
2156
2157       connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
2158
2159       if (i < cols)
2160         {
2161           nodeToConnect = (rows * cols) - cols + i;
2162           if (nodeToConnect >= pg->total)
2163             nodeToConnect -= cols;
2164         }
2165       else
2166         nodeToConnect = i - cols;
2167
2168       if (nodeToConnect < pg->total)
2169         connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
2170     }
2171   natLog = log (pg->total);
2172 #if VERBOSE_TESTING > 2
2173   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2174       _("natural log of %d is %d, will run %d iterations\n"),
2175       pg->total, natLog, (int) (natLog * percentage));
2176   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2177       _("Total connections added thus far: %u!\n"), connect_attempts);
2178 #endif
2179   smallWorldConnections = 0;
2180   small_world_it = (unsigned int) (natLog * percentage);
2181   if (small_world_it < 1)
2182     small_world_it = 1;
2183   GNUNET_assert (small_world_it > 0 && small_world_it < (unsigned int) -1);
2184   for (i = 0; i < small_world_it; i++)
2185     {
2186       for (j = 0; j < pg->total; j++)
2187         {
2188           /* Determine the row and column of node at position j on the 2d torus */
2189           node1Row = j / cols;
2190           node1Col = j - (node1Row * cols);
2191           for (k = 0; k < pg->total; k++)
2192             {
2193               /* Determine the row and column of node at position k on the 2d torus */
2194               node2Row = k / cols;
2195               node2Col = k - (node2Row * cols);
2196               /* Simple Cartesian distance */
2197               distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
2198               if (distance > 1)
2199                 {
2200                   /* Calculate probability as 1 over the square of the distance */
2201                   probability = 1.0 / (distance * distance);
2202                   /* Choose a random value between 0 and 1 */
2203                   random
2204                       = ((double) GNUNET_CRYPTO_random_u64 (
2205                                                             GNUNET_CRYPTO_QUALITY_WEAK,
2206                                                             UINT64_MAX))
2207                           / ((double) UINT64_MAX);
2208                   /* If random < probability, then connect the two nodes */
2209                   if (random < probability)
2210                     smallWorldConnections += proc (pg, j, k, list, GNUNET_YES);
2211
2212                 }
2213             }
2214         }
2215     }
2216   connect_attempts += smallWorldConnections;
2217 #if VERBOSE_TESTING > 2
2218   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2219       _("Total connections added for small world: %d!\n"),
2220       smallWorldConnections);
2221 #endif
2222   return connect_attempts;
2223 }
2224
2225 /**
2226  * Create a topology given a peer group (set of running peers)
2227  * and a connection processor.
2228  *
2229  * @param pg the peergroup to create the topology on
2230  * @param proc the connection processor to call to actually set
2231  *        up connections between two peers
2232  * @param list the peer list to use
2233  *
2234  * @return the number of connections that were set up
2235  *
2236  */
2237 static unsigned int
2238 create_erdos_renyi(struct GNUNET_TESTING_PeerGroup *pg,
2239                    GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2240 {
2241   double temp_rand;
2242   unsigned int outer_count;
2243   unsigned int inner_count;
2244   int connect_attempts;
2245   double probability;
2246   char *p_string;
2247
2248   probability = 0.5; /* FIXME: default percentage? */
2249   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
2250                                                           "PROBABILITY",
2251                                                           &p_string))
2252     {
2253       if (sscanf (p_string, "%lf", &probability) != 1)
2254         GNUNET_log (
2255                     GNUNET_ERROR_TYPE_WARNING,
2256                     _
2257                     ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
2258                     p_string, "PROBABILITY", "TESTING");
2259       GNUNET_free (p_string);
2260     }
2261   connect_attempts = 0;
2262   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2263     {
2264       for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
2265         {
2266           temp_rand
2267               = ((double) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2268                                                     UINT64_MAX))
2269                   / ((double) UINT64_MAX);
2270 #if VERBOSE_TESTING
2271           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2272               _("rand is %f probability is %f\n"), temp_rand,
2273               probability);
2274 #endif
2275           if (temp_rand < probability)
2276             {
2277               connect_attempts += proc (pg, outer_count, inner_count, list,
2278                                         GNUNET_YES);
2279             }
2280         }
2281     }
2282
2283   return connect_attempts;
2284 }
2285
2286 /**
2287  * Create a topology given a peer group (set of running peers)
2288  * and a connection processor.  This particular function creates
2289  * the connections for a 2d-torus, plus additional "closest"
2290  * connections per peer.
2291  *
2292  * @param pg the peergroup to create the topology on
2293  * @param proc the connection processor to call to actually set
2294  *        up connections between two peers
2295  * @param list the peer list to use
2296  *
2297  * @return the number of connections that were set up
2298  *
2299  */
2300 static unsigned int
2301 create_2d_torus(struct GNUNET_TESTING_PeerGroup *pg,
2302                 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2303 {
2304   unsigned int i;
2305   unsigned int square;
2306   unsigned int rows;
2307   unsigned int cols;
2308   unsigned int toggle = 1;
2309   unsigned int nodeToConnect;
2310   int connect_attempts;
2311
2312   connect_attempts = 0;
2313
2314   square = floor (sqrt (pg->total));
2315   rows = square;
2316   cols = square;
2317
2318   if (square * square != pg->total)
2319     {
2320       while (rows * cols < pg->total)
2321         {
2322           if (toggle % 2 == 0)
2323             rows++;
2324           else
2325             cols++;
2326
2327           toggle++;
2328         }
2329     }
2330 #if VERBOSE_TESTING
2331   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2332       _
2333       ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
2334       rows, cols);
2335 #endif
2336   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
2337    * to the node to its right and above.  Once this is over, we'll have our torus!
2338    * Special case for the last node (if the rows and columns are not equal), connect
2339    * to the first in the row to maintain topology.
2340    */
2341   for (i = 0; i < pg->total; i++)
2342     {
2343       /* First connect to the node to the right */
2344       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
2345         nodeToConnect = i + 1;
2346       else if (i + 1 == pg->total)
2347         nodeToConnect = rows * cols - cols;
2348       else
2349         nodeToConnect = i - cols + 1;
2350 #if VERBOSE_TESTING
2351       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2352           "Connecting peer %d to peer %d\n", i, nodeToConnect);
2353 #endif
2354       connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
2355
2356       /* Second connect to the node immediately above */
2357       if (i < cols)
2358         {
2359           nodeToConnect = (rows * cols) - cols + i;
2360           if (nodeToConnect >= pg->total)
2361             nodeToConnect -= cols;
2362         }
2363       else
2364         nodeToConnect = i - cols;
2365
2366       if (nodeToConnect < pg->total)
2367         {
2368 #if VERBOSE_TESTING
2369           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2370               "Connecting peer %d to peer %d\n", i, nodeToConnect);
2371 #endif
2372           connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
2373         }
2374
2375     }
2376
2377   return connect_attempts;
2378 }
2379
2380 /**
2381  * Create a topology given a peer group (set of running peers)
2382  * and a connection processor.
2383  *
2384  * @param pg the peergroup to create the topology on
2385  * @param proc the connection processor to call to actually set
2386  *        up connections between two peers
2387  * @param list the peer list to use
2388  * @param check does the connection processor need to check before
2389  *              performing an action on the list?
2390  *
2391  * @return the number of connections that were set up
2392  *
2393  */
2394 static unsigned int
2395 create_clique(struct GNUNET_TESTING_PeerGroup *pg,
2396               GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list,
2397               unsigned int check)
2398 {
2399   unsigned int outer_count;
2400   unsigned int inner_count;
2401   int connect_attempts;
2402   struct ProgressMeter *conn_meter;
2403   connect_attempts = 0;
2404
2405   conn_meter = create_meter ((((pg->total * pg->total) + pg->total) / 2)
2406       - pg->total, "Create Clique ", GNUNET_NO);
2407   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2408     {
2409       for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
2410         {
2411 #if VERBOSE_TESTING
2412           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2413               "Connecting peer %d to peer %d\n",
2414               outer_count, inner_count);
2415 #endif
2416           connect_attempts += proc (pg, outer_count, inner_count, list, check);
2417           update_meter (conn_meter);
2418         }
2419     }
2420   reset_meter (conn_meter);
2421   free_meter (conn_meter);
2422   return connect_attempts;
2423 }
2424
2425 #if !OLD
2426 /**
2427  * Iterator over hash map entries.
2428  *
2429  * @param cls closure the peer group
2430  * @param key the key stored in the hashmap is the
2431  *            index of the peer to connect to
2432  * @param value value in the hash map, handle to the peer daemon
2433  * @return GNUNET_YES if we should continue to
2434  *         iterate,
2435  *         GNUNET_NO if not.
2436  */
2437 static int
2438 unblacklist_iterator (void *cls,
2439     const GNUNET_HashCode * key,
2440     void *value)
2441   {
2442     struct UnblacklistContext *un_ctx = cls;
2443     uint32_t second_pos;
2444
2445     uid_from_hash (key, &second_pos);
2446
2447     unblacklist_connections(un_ctx->pg, un_ctx->first_uid, second_pos);
2448
2449     return GNUNET_YES;
2450   }
2451 #endif
2452
2453 #if !OLD
2454 /**
2455  * Create a blacklist topology based on the allowed topology
2456  * which disallows any connections not in the allowed topology
2457  * at the transport level.
2458  *
2459  * @param pg the peergroup to create the topology on
2460  * @param proc the connection processor to call to allow
2461  *        up connections between two peers
2462  *
2463  * @return the number of connections that were set up
2464  *
2465  */
2466 static unsigned int
2467 copy_allowed (struct GNUNET_TESTING_PeerGroup *pg,
2468               GNUNET_TESTING_ConnectionProcessor proc)
2469 {
2470   unsigned int count;
2471   unsigned int total;
2472   struct PeerConnection *iter;
2473 #if !OLD
2474   struct UnblacklistContext un_ctx;
2475
2476   un_ctx.pg = pg;
2477 #endif
2478   total = 0;
2479   for (count = 0; count < pg->total - 1; count++)
2480     {
2481 #if OLD
2482       iter = pg->peers[count].allowed_peers_head;
2483       while (iter != NULL)
2484         {
2485           remove_connections (pg, count, iter->index, BLACKLIST, GNUNET_YES);
2486           //unblacklist_connections(pg, count, iter->index);
2487           iter = iter->next;
2488         }
2489 #else
2490       un_ctx.first_uid = count;
2491       total += GNUNET_CONTAINER_multihashmap_iterate(pg->peers[count].allowed_peers, 
2492                                                      &unblacklist_iterator,
2493                                                      &un_ctx);
2494 #endif
2495     }
2496   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
2497               "Unblacklisted %u peers\n", 
2498               total);
2499   return total;
2500 }
2501 #endif
2502
2503 /**
2504  * Create a topology given a peer group (set of running peers)
2505  * and a connection processor.
2506  *
2507  * @param pg the peergroup to create the topology on
2508  * @param proc the connection processor to call to actually set
2509  *        up connections between two peers
2510  * @param list which list should be modified
2511  *
2512  * @return the number of connections that were set up
2513  *
2514  */
2515 static unsigned int
2516 create_line(struct GNUNET_TESTING_PeerGroup *pg,
2517             GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2518 {
2519   unsigned int count;
2520   unsigned int connect_attempts;
2521
2522   connect_attempts = 0;
2523   /* Connect each peer to the next highest numbered peer */
2524   for (count = 0; count < pg->total - 1; count++)
2525     {
2526 #if VERBOSE_TESTING
2527       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2528                   "Connecting peer %d to peer %d\n", 
2529                   count, count + 1);
2530 #endif
2531       connect_attempts += proc (pg, count, count + 1, list, GNUNET_YES);
2532     }
2533
2534   return connect_attempts;
2535 }
2536
2537 /**
2538  * Create a topology given a peer group (set of running peers)
2539  * and a connection processor.
2540  *
2541  * @param pg the peergroup to create the topology on
2542  * @param filename the file to read topology information from
2543  * @param proc the connection processor to call to actually set
2544  *        up connections between two peers
2545  * @param list the peer list to use
2546  *
2547  * @return the number of connections that were set up
2548  *
2549  */
2550 static unsigned int
2551 create_from_file(struct GNUNET_TESTING_PeerGroup *pg, char *filename,
2552                  GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2553 {
2554   int connect_attempts;
2555   unsigned int first_peer_index;
2556   unsigned int second_peer_index;
2557   struct stat frstat;
2558   int count;
2559   char *data;
2560   const char *buf;
2561   unsigned int total_peers;
2562   enum States curr_state;
2563
2564   connect_attempts = 0;
2565   if (GNUNET_OK != GNUNET_DISK_file_test (filename))
2566     GNUNET_DISK_fn_write (filename, NULL, 0, GNUNET_DISK_PERM_USER_READ);
2567
2568   if ((0 != STAT (filename, &frstat)) || (frstat.st_size == 0))
2569     {
2570       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2571                   "Could not open file `%s' specified for topology!", filename);
2572       return connect_attempts;
2573     }
2574
2575   data = GNUNET_malloc_large (frstat.st_size);
2576   GNUNET_assert(data != NULL);
2577   if (frstat.st_size != GNUNET_DISK_fn_read (filename, data, frstat.st_size))
2578     {
2579       GNUNET_log (
2580                   GNUNET_ERROR_TYPE_ERROR,
2581                   "Could not read file %s specified for host list, ending test!",
2582                   filename);
2583       GNUNET_free (data);
2584       return connect_attempts;
2585     }
2586
2587   buf = data;
2588   count = 0;
2589   first_peer_index = 0;
2590   /* First line should contain a single integer, specifying the number of peers */
2591   /* Each subsequent line should contain this format PEER_INDEX:OTHER_PEER_INDEX[,...] */
2592   curr_state = NUM_PEERS;
2593   while (count < frstat.st_size - 1)
2594     {
2595       if ((buf[count] == '\n') || (buf[count] == ' '))
2596         {
2597           count++;
2598           continue;
2599         }
2600
2601       switch (curr_state)
2602         {
2603         case NUM_PEERS:
2604           errno = 0;
2605           total_peers = strtoul(&buf[count], NULL, 10);
2606           if (errno != 0)
2607             {
2608               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2609                           "Failed to read number of peers from topology file!\n");
2610               GNUNET_free (data);
2611               return connect_attempts;
2612             }
2613           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2614                       "Read %u total peers in topology\n", total_peers);
2615           GNUNET_assert(total_peers == pg->total);
2616           curr_state = PEER_INDEX;
2617           while ((buf[count] != '\n') && (count < frstat.st_size - 1))
2618             count++;
2619           count++;
2620           break;
2621         case PEER_INDEX:
2622           errno = 0;
2623           first_peer_index = strtoul(&buf[count], NULL, 10);
2624           if (errno != 0)
2625             {
2626               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2627                           "Failed to read peer index from topology file!\n");
2628               GNUNET_free (data);
2629               return connect_attempts;
2630             }
2631           while ((buf[count] != ':') && (count < frstat.st_size - 1))
2632             count++;
2633           count++;
2634           curr_state = OTHER_PEER_INDEX;
2635           break;
2636         case COLON:
2637           if (1 == sscanf (&buf[count], ":"))
2638             curr_state = OTHER_PEER_INDEX;
2639           count++;
2640           break;
2641         case OTHER_PEER_INDEX:
2642           errno = 0;
2643           second_peer_index = strtoul(&buf[count], NULL, 10);
2644           if (errno != 0)
2645             {
2646               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2647                           "Failed to peer index from topology file!\n");
2648               GNUNET_free (data);
2649               return connect_attempts;
2650             }
2651           /* Assume file is written with first peer 1, but array index is 0 */
2652           connect_attempts += proc (pg, first_peer_index - 1, second_peer_index
2653                                     - 1, list, GNUNET_YES);
2654           while ((buf[count] != '\n') && (buf[count] != ',') && (count
2655                                                                  < frstat.st_size - 1))
2656             count++;
2657           if (buf[count] == '\n')
2658             {
2659               curr_state = PEER_INDEX;
2660             }
2661           else if (buf[count] != ',')
2662             {
2663               curr_state = OTHER_PEER_INDEX;
2664             }
2665           count++;
2666           break;
2667         default:
2668           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2669                       "Found bad data in topology file while in state %d!\n",
2670                       curr_state);
2671           GNUNET_break(0);
2672           GNUNET_free (data);
2673           return connect_attempts;
2674         }
2675     }
2676   GNUNET_free (data);
2677   return connect_attempts;
2678 }
2679
2680 /**
2681  * Create a topology given a peer group (set of running peers)
2682  * and a connection processor.
2683  *
2684  * @param pg the peergroup to create the topology on
2685  * @param proc the connection processor to call to actually set
2686  *        up connections between two peers
2687  * @param list the peer list to use
2688  *
2689  * @return the number of connections that were set up
2690  *
2691  */
2692 static unsigned int
2693 create_ring(struct GNUNET_TESTING_PeerGroup *pg,
2694             GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2695 {
2696   unsigned int count;
2697   int connect_attempts;
2698
2699   connect_attempts = 0;
2700
2701   /* Connect each peer to the next highest numbered peer */
2702   for (count = 0; count < pg->total - 1; count++)
2703     {
2704 #if VERBOSE_TESTING
2705       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2706           "Connecting peer %d to peer %d\n", count, count + 1);
2707 #endif
2708       connect_attempts += proc (pg, count, count + 1, list, GNUNET_YES);
2709     }
2710
2711   /* Connect the last peer to the first peer */
2712   connect_attempts += proc (pg, pg->total - 1, 0, list, GNUNET_YES);
2713
2714   return connect_attempts;
2715 }
2716
2717 #if !OLD
2718 /**
2719  * Iterator for writing friends of a peer to a file.
2720  *
2721  * @param cls closure, an open writable file handle
2722  * @param key the key the daemon was stored under
2723  * @param value the GNUNET_TESTING_Daemon that needs to be written.
2724  *
2725  * @return GNUNET_YES to continue iteration
2726  *
2727  * TODO: Could replace friend_file_iterator and blacklist_file_iterator
2728  *       with a single file_iterator that takes a closure which contains
2729  *       the prefix to write before the peer.  Then this could be used
2730  *       for blacklisting multiple transports and writing the friend
2731  *       file.  I'm sure *someone* will complain loudly about other
2732  *       things that negate these functions even existing so no point in
2733  *       "fixing" now.
2734  */
2735 static int
2736 friend_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2737   {
2738     FILE *temp_friend_handle = cls;
2739     struct GNUNET_TESTING_Daemon *peer = value;
2740     struct GNUNET_PeerIdentity *temppeer;
2741     struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2742
2743     temppeer = &peer->id;
2744     GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2745     fprintf (temp_friend_handle, "%s\n", (char *) &peer_enc);
2746
2747     return GNUNET_YES;
2748   }
2749
2750 struct BlacklistContext
2751   {
2752     /*
2753      * The (open) file handle to write to
2754      */
2755     FILE *temp_file_handle;
2756
2757     /*
2758      * The transport that this peer will be blacklisted on.
2759      */
2760     char *transport;
2761   };
2762
2763 /**
2764  * Iterator for writing blacklist data to appropriate files.
2765  *
2766  * @param cls closure, an open writable file handle
2767  * @param key the key the daemon was stored under
2768  * @param value the GNUNET_TESTING_Daemon that needs to be written.
2769  *
2770  * @return GNUNET_YES to continue iteration
2771  */
2772 static int
2773 blacklist_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2774   {
2775     struct BlacklistContext *blacklist_ctx = cls;
2776     struct GNUNET_TESTING_Daemon *peer = value;
2777     struct GNUNET_PeerIdentity *temppeer;
2778     struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2779
2780     temppeer = &peer->id;
2781     GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2782     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Writing entry %s:%s to file\n", blacklist_ctx->transport, (char *) &peer_enc);
2783     fprintf (blacklist_ctx->temp_file_handle, "%s:%s\n",
2784         blacklist_ctx->transport, (char *) &peer_enc);
2785
2786     return GNUNET_YES;
2787   }
2788 #endif
2789
2790 /*
2791  * Create the friend files based on the PeerConnection's
2792  * of each peer in the peer group, and copy the files
2793  * to the appropriate place
2794  *
2795  * @param pg the peer group we are dealing with
2796  */
2797 static int
2798 create_and_copy_friend_files(struct GNUNET_TESTING_PeerGroup *pg)
2799 {
2800   FILE *temp_friend_handle;
2801   unsigned int pg_iter;
2802   char *temp_service_path;
2803   struct GNUNET_OS_Process **procarr;
2804   char *arg;
2805   char *mytemp;
2806 #if NOT_STUPID
2807   enum GNUNET_OS_ProcessStatusType type;
2808   unsigned long return_code;
2809   int count;
2810   int max_wait = 10;
2811 #endif
2812   int ret;
2813
2814   ret = GNUNET_OK;
2815 #if OLD
2816   struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2817   struct PeerConnection *conn_iter;
2818 #endif
2819   procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
2820   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2821     {
2822       mytemp = GNUNET_DISK_mktemp ("friends");
2823       GNUNET_assert (mytemp != NULL);
2824       temp_friend_handle = fopen (mytemp, "wt");
2825       GNUNET_assert (temp_friend_handle != NULL);
2826 #if OLD
2827       conn_iter = pg->peers[pg_iter].allowed_peers_head;
2828       while (conn_iter != NULL)
2829         {
2830           GNUNET_CRYPTO_hash_to_enc (
2831                                      &pg->peers[conn_iter->index].daemon->id.hashPubKey,
2832                                      &peer_enc);
2833           fprintf (temp_friend_handle, "%s\n", (char *) &peer_enc);
2834           conn_iter = conn_iter->next;
2835         }
2836 #else
2837       GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].allowed_peers,
2838           &friend_file_iterator,
2839           temp_friend_handle);
2840 #endif
2841       fclose (temp_friend_handle);
2842
2843       if (GNUNET_OK
2844           != GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter]. 
2845                                                     daemon->cfg,
2846                                                     "PATHS", "SERVICEHOME",
2847                                                     &temp_service_path))
2848         {
2849           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2850                       _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
2851                       "SERVICEHOME", "PATHS");
2852           if (UNLINK (mytemp) != 0)
2853             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2854                                       "unlink",
2855                                       mytemp);
2856           GNUNET_free (mytemp);
2857           break;
2858         }
2859
2860       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
2861         {
2862           GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
2863           procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv", "mv",
2864                                                       mytemp, arg, NULL);
2865           GNUNET_assert(procarr[pg_iter] != NULL);
2866 #if VERBOSE_TESTING
2867           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2868                       "Copying file with command cp %s %s\n", 
2869                       mytemp, 
2870                       arg);
2871 #endif
2872           ret = GNUNET_OS_process_wait (procarr[pg_iter]); /* FIXME: schedule this, throttle! */
2873           GNUNET_OS_process_close (procarr[pg_iter]);
2874           GNUNET_free (arg);
2875         }
2876       else /* Remote, scp the file to the correct place */
2877         {
2878           if (NULL != pg->peers[pg_iter].daemon->username)
2879             GNUNET_asprintf (&arg, "%s@%s:%s/friends",
2880                              pg->peers[pg_iter].daemon->username,
2881                              pg->peers[pg_iter].daemon->hostname,
2882                              temp_service_path);
2883           else
2884             GNUNET_asprintf (&arg, "%s:%s/friends",
2885                              pg->peers[pg_iter].daemon->hostname,
2886                              temp_service_path);
2887           procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp", "scp",
2888                                                       mytemp, arg, NULL);
2889           GNUNET_assert(procarr[pg_iter] != NULL);
2890           ret = GNUNET_OS_process_wait (procarr[pg_iter]); /* FIXME: schedule this, throttle! */
2891           GNUNET_OS_process_close (procarr[pg_iter]);
2892           if (ret != GNUNET_OK)
2893             {
2894               /* FIXME: free contents of 'procarr' array */
2895               GNUNET_free (procarr);
2896               GNUNET_free (temp_service_path);
2897               GNUNET_free (mytemp);
2898               GNUNET_free (arg);
2899               return ret;
2900             }
2901           procarr[pg_iter] = NULL;
2902 #if VERBOSE_TESTING
2903           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2904                       "Copying file with command scp %s %s\n",
2905                       mytemp,
2906                       arg);
2907 #endif
2908           GNUNET_free (arg);
2909         }
2910       GNUNET_free (temp_service_path);
2911       GNUNET_free (mytemp);
2912     }
2913
2914 #if NOT_STUPID
2915   count = 0;
2916   ret = GNUNET_SYSERR;
2917   while ((count < max_wait) && (ret != GNUNET_OK))
2918     {
2919       ret = GNUNET_OK;
2920       for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2921         {
2922 #if VERBOSE_TESTING
2923           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2924                       "Checking copy status of file %d\n",
2925                       pg_iter);
2926 #endif
2927           if (procarr[pg_iter] != NULL) /* Check for already completed! */
2928             {
2929               if (GNUNET_OS_process_status
2930                   (procarr[pg_iter], &type, &return_code) != GNUNET_OK)
2931                 {
2932                   ret = GNUNET_SYSERR;
2933                 }
2934               else if ((type != GNUNET_OS_PROCESS_EXITED)
2935                   || (return_code != 0))
2936                 {
2937                   ret = GNUNET_SYSERR;
2938                 }
2939               else
2940                 {
2941                   GNUNET_OS_process_close (procarr[pg_iter]);
2942                   procarr[pg_iter] = NULL;
2943 #if VERBOSE_TESTING
2944                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2945                               "File %d copied\n",
2946                               pg_iter);
2947 #endif
2948                 }
2949             }
2950         }
2951       count++;
2952       if (ret == GNUNET_SYSERR)
2953         {
2954           /* FIXME: why sleep here? -CG */
2955           sleep (1);
2956         }
2957     }
2958
2959 #if VERBOSE_TESTING
2960   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2961       _("Finished copying all friend files!\n"));
2962 #endif
2963 #endif
2964   GNUNET_free (procarr);
2965   return ret;
2966 }
2967
2968 /*
2969  * Create the blacklist files based on the PeerConnection's
2970  * of each peer in the peer group, and copy the files
2971  * to the appropriate place.
2972  *
2973  * @param pg the peer group we are dealing with
2974  * @param transports space delimited list of transports to blacklist
2975  */
2976 static int
2977 create_and_copy_blacklist_files(struct GNUNET_TESTING_PeerGroup *pg,
2978                                 const char *transports)
2979 {
2980   FILE *temp_file_handle;
2981   unsigned int pg_iter;
2982   char *temp_service_path;
2983   struct GNUNET_OS_Process **procarr;
2984   char *arg;
2985   char *mytemp;
2986   enum GNUNET_OS_ProcessStatusType type;
2987   unsigned long return_code;
2988   int count;
2989   int ret;
2990   int max_wait = 10;
2991   int transport_len;
2992   unsigned int i;
2993   char *pos;
2994   char *temp_transports;
2995 #if OLD
2996   struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2997   struct PeerConnection *conn_iter;
2998 #else
2999   static struct BlacklistContext blacklist_ctx;
3000 #endif
3001
3002   procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
3003   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3004     {
3005       mytemp = GNUNET_DISK_mktemp ("blacklist");
3006       GNUNET_assert (mytemp != NULL);
3007       temp_file_handle = fopen (mytemp, "wt");
3008       GNUNET_assert (temp_file_handle != NULL);
3009       temp_transports = GNUNET_strdup (transports);
3010 #if !OLD
3011       blacklist_ctx.temp_file_handle = temp_file_handle;
3012 #endif
3013       transport_len = strlen (temp_transports) + 1;
3014       pos = NULL;
3015
3016       for (i = 0; i < transport_len; i++)
3017         {
3018           if ((temp_transports[i] == ' ') && (pos == NULL))
3019             continue; /* At start of string (whitespace) */
3020           else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */
3021             {
3022               temp_transports[i] = '\0';
3023 #if OLD
3024               conn_iter = pg->peers[pg_iter].blacklisted_peers_head;
3025               while (conn_iter != NULL)
3026                 {
3027                   GNUNET_CRYPTO_hash_to_enc (
3028                                              &pg->peers[conn_iter->index].daemon->id.hashPubKey,
3029                                              &peer_enc);
3030                   fprintf (temp_file_handle, "%s:%s\n", pos, (char *) &peer_enc);
3031                   conn_iter = conn_iter->next;
3032                 }
3033 #else
3034               blacklist_ctx.transport = pos;
3035               (void) GNUNET_CONTAINER_multihashmap_iterate (pg->
3036                                                             peers
3037                                                             [pg_iter].blacklisted_peers,
3038                                                             &blacklist_file_iterator,
3039                                                             &blacklist_ctx);
3040 #endif
3041               pos = NULL;
3042             } /* At beginning of actual string */
3043           else if (pos == NULL)
3044             {
3045               pos = &temp_transports[i];
3046             }
3047         }
3048
3049       GNUNET_free (temp_transports);
3050       fclose (temp_file_handle);
3051
3052       if (GNUNET_OK
3053           != GNUNET_CONFIGURATION_get_value_string (
3054                                                     pg->peers[pg_iter]. daemon->cfg,
3055                                                     "PATHS", "SERVICEHOME",
3056                                                     &temp_service_path))
3057         {
3058           GNUNET_log (
3059                       GNUNET_ERROR_TYPE_WARNING,
3060                       _
3061                       ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
3062                       "SERVICEHOME", "PATHS");
3063           if (UNLINK (mytemp) != 0)
3064             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
3065                 mytemp);
3066           GNUNET_free (mytemp);
3067           break;
3068         }
3069
3070       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
3071         {
3072           GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
3073           procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv", "mv",
3074                                                       mytemp, arg, NULL);
3075 #if VERBOSE_TESTING
3076           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3077               _("Copying file with command cp %s %s\n"), mytemp, arg);
3078 #endif
3079
3080           GNUNET_free (arg);
3081         }
3082       else /* Remote, scp the file to the correct place */
3083         {
3084           if (NULL != pg->peers[pg_iter].daemon->username)
3085             GNUNET_asprintf (&arg, "%s@%s:%s/blacklist",
3086                              pg->peers[pg_iter].daemon->username,
3087                              pg->peers[pg_iter].daemon->hostname,
3088                              temp_service_path);
3089           else
3090             GNUNET_asprintf (&arg, "%s:%s/blacklist",
3091                              pg->peers[pg_iter].daemon->hostname,
3092                              temp_service_path);
3093           procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp", "scp",
3094                                                       mytemp, arg, NULL);
3095           GNUNET_assert(procarr[pg_iter] != NULL);
3096           GNUNET_OS_process_wait (procarr[pg_iter]); /* FIXME: add scheduled blacklist file copy that parallelizes file copying! */
3097
3098 #if VERBOSE_TESTING
3099           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3100               _("Copying file with command scp %s %s\n"), mytemp,
3101               arg);
3102 #endif
3103           GNUNET_free (arg);
3104         }
3105       GNUNET_free (temp_service_path);
3106       GNUNET_free (mytemp);
3107     }
3108
3109   count = 0;
3110   ret = GNUNET_SYSERR;
3111   while ((count < max_wait) && (ret != GNUNET_OK))
3112     {
3113       ret = GNUNET_OK;
3114       for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3115         {
3116 #if VERBOSE_TESTING
3117           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3118               _("Checking copy status of file %d\n"), pg_iter);
3119 #endif
3120           if (procarr[pg_iter] != NULL) /* Check for already completed! */
3121             {
3122               if (GNUNET_OS_process_status (procarr[pg_iter], &type,
3123                                             &return_code) != GNUNET_OK)
3124                 {
3125                   ret = GNUNET_SYSERR;
3126                 }
3127               else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
3128                 {
3129                   ret = GNUNET_SYSERR;
3130                 }
3131               else
3132                 {
3133                   GNUNET_OS_process_close (procarr[pg_iter]);
3134                   procarr[pg_iter] = NULL;
3135 #if VERBOSE_TESTING
3136                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3137                       _("File %d copied\n"), pg_iter);
3138 #endif
3139                 }
3140             }
3141         }
3142       count++;
3143       if (ret == GNUNET_SYSERR)
3144         {
3145           /* FIXME: why sleep here? -CG */
3146           sleep (1);
3147         }
3148     }
3149
3150 #if VERBOSE_TESTING
3151   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3152       _("Finished copying all blacklist files!\n"));
3153 #endif
3154   GNUNET_free (procarr);
3155   return ret;
3156 }
3157
3158 /* Forward Declaration */
3159 static void
3160 schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
3161
3162 /**
3163  * Choose a random peer's next connection to create, and
3164  * call schedule_connect to set up the connect task.
3165  *
3166  * @param ct_ctx the overall connection context
3167  */
3168 static void
3169 preschedule_connect(struct GNUNET_TESTING_PeerGroup *pg)
3170 {
3171   struct ConnectTopologyContext *ct_ctx = &pg->ct_ctx;
3172   struct PeerConnection *connection_iter;
3173   struct ConnectContext *connect_context;
3174   uint32_t random_peer;
3175
3176   if (ct_ctx->remaining_connections == 0)
3177     return;
3178   random_peer
3179       = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, pg->total);
3180   while (pg->peers[random_peer].connect_peers_head == NULL)
3181     random_peer = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
3182                                             pg->total);
3183
3184   connection_iter = pg->peers[random_peer].connect_peers_head;
3185   connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
3186   connect_context->first_index = random_peer;
3187   connect_context->second_index = connection_iter->index;
3188   connect_context->ct_ctx = ct_ctx;
3189   GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
3190   GNUNET_CONTAINER_DLL_remove(pg->peers[random_peer].connect_peers_head, pg->peers[random_peer].connect_peers_tail, connection_iter);
3191   GNUNET_free(connection_iter);
3192   ct_ctx->remaining_connections--;
3193 }
3194
3195 #if USE_SEND_HELLOS
3196 /* Forward declaration */
3197 static void schedule_send_hellos (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
3198
3199 /**
3200  * Close connections and free the hello context.
3201  *
3202  * @param cls the 'struct SendHelloContext *'
3203  * @param tc scheduler context
3204  */
3205 static void
3206 free_hello_context (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3207   {
3208     struct SendHelloContext *send_hello_context = cls;
3209     if (send_hello_context->peer->daemon->server != NULL)
3210       {
3211         GNUNET_CORE_disconnect(send_hello_context->peer->daemon->server);
3212         send_hello_context->peer->daemon->server = NULL;
3213       }
3214     if (send_hello_context->peer->daemon->th != NULL)
3215       {
3216         GNUNET_TRANSPORT_disconnect(send_hello_context->peer->daemon->th);
3217         send_hello_context->peer->daemon->th = NULL;
3218       }
3219     if (send_hello_context->core_connect_task != GNUNET_SCHEDULER_NO_TASK)
3220       {
3221         GNUNET_SCHEDULER_cancel(send_hello_context->core_connect_task);
3222         send_hello_context->core_connect_task = GNUNET_SCHEDULER_NO_TASK;
3223       }
3224     send_hello_context->pg->outstanding_connects--;
3225     GNUNET_free(send_hello_context);
3226   }
3227
3228 /**
3229  * For peers that haven't yet connected, notify
3230  * the caller that they have failed (timeout).
3231  *
3232  * @param cls the 'struct SendHelloContext *'
3233  * @param tc scheduler context
3234  */
3235 static void
3236 notify_remaining_connections_failed (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3237   {
3238     struct SendHelloContext *send_hello_context = cls;
3239     struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg;
3240     struct PeerConnection *connection;
3241
3242     GNUNET_CORE_disconnect(send_hello_context->peer->daemon->server);
3243     send_hello_context->peer->daemon->server = NULL;
3244
3245     connection = send_hello_context->peer->connect_peers_head;
3246
3247     while (connection != NULL)
3248       {
3249         if (pg->notify_connection != NULL)
3250           {
3251             pg->notify_connection(pg->notify_connection_cls,
3252                 &send_hello_context->peer->daemon->id,
3253                 &pg->peers[connection->index].daemon->id,
3254                 0, /* FIXME */
3255                 send_hello_context->peer->daemon->cfg,
3256                 pg->peers[connection->index].daemon->cfg,
3257                 send_hello_context->peer->daemon,
3258                 pg->peers[connection->index].daemon,
3259                 "Peers failed to connect (timeout)");
3260           }
3261         GNUNET_CONTAINER_DLL_remove(send_hello_context->peer->connect_peers_head, send_hello_context->peer->connect_peers_tail, connection);
3262         GNUNET_free(connection);
3263         connection = connection->next;
3264       }
3265     GNUNET_SCHEDULER_add_now(&free_hello_context, send_hello_context);
3266 #if BAD
3267     other_peer = &pg->peers[connection->index];
3268 #endif
3269   }
3270
3271 /**
3272  * For peers that haven't yet connected, send
3273  * CORE connect requests.
3274  *
3275  * @param cls the 'struct SendHelloContext *'
3276  * @param tc scheduler context
3277  */
3278 static void
3279 send_core_connect_requests (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3280   {
3281     struct SendHelloContext *send_hello_context = cls;
3282     struct PeerConnection *conn;
3283     GNUNET_assert(send_hello_context->peer->daemon->server != NULL);
3284
3285     send_hello_context->core_connect_task = GNUNET_SCHEDULER_NO_TASK;
3286
3287     send_hello_context->connect_attempts++;
3288     if (send_hello_context->connect_attempts < send_hello_context->pg->ct_ctx.connect_attempts)
3289       {
3290         conn = send_hello_context->peer->connect_peers_head;
3291         while (conn != NULL)
3292           {
3293             GNUNET_CORE_peer_request_connect(send_hello_context->peer->daemon->server,
3294                 &send_hello_context->pg->peers[conn->index].daemon->id,
3295                 NULL,
3296                 NULL);
3297             conn = conn->next;
3298           }
3299         send_hello_context->core_connect_task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_divide(send_hello_context->pg->ct_ctx.connect_timeout, send_hello_context->pg->ct_ctx.connect_attempts) ,
3300             &send_core_connect_requests,
3301             send_hello_context);
3302       }
3303     else
3304       {
3305         GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Timeout before all connections created, marking rest as failed!\n");
3306         GNUNET_SCHEDULER_add_now(&notify_remaining_connections_failed, send_hello_context);
3307       }
3308
3309   }
3310
3311 /**
3312  * Success, connection is up.  Signal client our success.
3313  *
3314  * @param cls our "struct SendHelloContext"
3315  * @param peer identity of the peer that has connected
3316  * @param atsi performance information
3317  *
3318  * FIXME: remove peers from BOTH lists, call notify twice, should
3319  * double the speed of connections as long as the list iteration
3320  * doesn't take too long!
3321  */
3322 static void
3323 core_connect_notify (void *cls,
3324     const struct GNUNET_PeerIdentity *peer,
3325     const struct GNUNET_TRANSPORT_ATS_Information *atsi)
3326   {
3327     struct SendHelloContext *send_hello_context = cls;
3328     struct PeerConnection *connection;
3329     struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg;
3330 #if BAD
3331     struct PeerData *other_peer;
3332 #endif
3333 #if DEBUG_TESTING
3334     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3335         "Connected peer %s to peer %s\n",
3336         ctx->d1->shortname, GNUNET_i2s(peer));
3337 #endif
3338
3339     if (0 == memcmp(&send_hello_context->peer->daemon->id, peer, sizeof(struct GNUNET_PeerIdentity)))
3340     return;
3341
3342     connection = send_hello_context->peer->connect_peers_head;
3343 #if BAD
3344     other_peer = NULL;
3345 #endif
3346
3347     while ((connection != NULL) &&
3348         (0 != memcmp(&pg->peers[connection->index].daemon->id, peer, sizeof(struct GNUNET_PeerIdentity))))
3349       {
3350         connection = connection->next;
3351       }
3352
3353     if (connection == NULL)
3354       {
3355         GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Connected peer %s to %s, not in list (no problem(?))\n", GNUNET_i2s(peer), send_hello_context->peer->daemon->shortname);
3356       }
3357     else
3358       {
3359 #if BAD
3360         other_peer = &pg->peers[connection->index];
3361 #endif
3362         if (pg->notify_connection != NULL)
3363           {
3364             pg->notify_connection(pg->notify_connection_cls,
3365                 &send_hello_context->peer->daemon->id,
3366                 peer,
3367                 0, /* FIXME */
3368                 send_hello_context->peer->daemon->cfg,
3369                 pg->peers[connection->index].daemon->cfg,
3370                 send_hello_context->peer->daemon,
3371                 pg->peers[connection->index].daemon,
3372                 NULL);
3373           }
3374         GNUNET_CONTAINER_DLL_remove(send_hello_context->peer->connect_peers_head, send_hello_context->peer->connect_peers_tail, connection);
3375         GNUNET_free(connection);
3376       }
3377
3378 #if BAD
3379     /* Notify of reverse connection and remove from other peers list of outstanding */
3380     if (other_peer != NULL)
3381       {
3382         connection = other_peer->connect_peers_head;
3383         while ((connection != NULL) &&
3384             (0 != memcmp(&send_hello_context->peer->daemon->id, &pg->peers[connection->index].daemon->id, sizeof(struct GNUNET_PeerIdentity))))
3385           {
3386             connection = connection->next;
3387           }
3388         if (connection != NULL)
3389           {
3390             if (pg->notify_connection != NULL)
3391               {
3392                 pg->notify_connection(pg->notify_connection_cls,
3393                     peer,
3394                     &send_hello_context->peer->daemon->id,
3395                     0, /* FIXME */
3396                     pg->peers[connection->index].daemon->cfg,
3397                     send_hello_context->peer->daemon->cfg,
3398                     pg->peers[connection->index].daemon,
3399                     send_hello_context->peer->daemon,
3400                     NULL);
3401               }
3402
3403             GNUNET_CONTAINER_DLL_remove(other_peer->connect_peers_head, other_peer->connect_peers_tail, connection);
3404             GNUNET_free(connection);
3405           }
3406       }
3407 #endif
3408
3409     if (send_hello_context->peer->connect_peers_head == NULL)
3410       {
3411         GNUNET_SCHEDULER_add_now(&free_hello_context, send_hello_context);
3412       }
3413   }
3414
3415 /**
3416  * Notify of a successful connection to the core service.
3417  *
3418  * @param cls a struct SendHelloContext *
3419  * @param server handle to the core service
3420  * @param my_identity the peer identity of this peer
3421  * @param publicKey the public key of the peer
3422  */
3423 void
3424 core_init (void *cls,
3425     struct GNUNET_CORE_Handle * server,
3426     const struct GNUNET_PeerIdentity *
3427     my_identity,
3428     const struct
3429     GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
3430     publicKey)
3431   {
3432     struct SendHelloContext *send_hello_context = cls;
3433     send_hello_context->core_ready = GNUNET_YES;
3434   }
3435
3436 /**
3437  * Function called once a hello has been sent
3438  * to the transport, move on to the next one
3439  * or go away forever.
3440  *
3441  * @param cls the 'struct SendHelloContext *'
3442  * @param tc scheduler context
3443  */
3444 static void
3445 hello_sent_callback (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3446   {
3447     struct SendHelloContext *send_hello_context = cls;
3448     //unsigned int pg_iter;
3449     if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
3450       {
3451         GNUNET_free(send_hello_context);
3452         return;
3453       }
3454
3455     send_hello_context->pg->remaining_hellos--;
3456 #if DEBUG_TESTING
3457     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sent HELLO, have %d remaining!\n", send_hello_context->pg->remaining_hellos);
3458 #endif
3459     if (send_hello_context->peer_pos == NULL) /* All HELLOs (for this peer!) have been transmitted! */
3460       {
3461 #if DEBUG_TESTING
3462         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "All hellos for this peer sent, disconnecting transport!\n");
3463 #endif
3464         GNUNET_assert(send_hello_context->peer->daemon->th != NULL);
3465         GNUNET_TRANSPORT_disconnect(send_hello_context->peer->daemon->th);
3466         send_hello_context->peer->daemon->th = NULL;
3467
3468         /*if (send_hello_context->pg->remaining_hellos == 0)
3469          {
3470          for (pg_iter = 0; pg_iter < send_hello_context->pg->max_outstanding_connections; pg_iter++)
3471          {
3472          preschedule_connect(&send_hello_context->pg->ct_ctx);
3473          }
3474          }
3475          */
3476         GNUNET_assert (send_hello_context->peer->daemon->server == NULL);
3477         send_hello_context->peer->daemon->server = GNUNET_CORE_connect(send_hello_context->peer->cfg,
3478             1,
3479             send_hello_context,
3480             &core_init,
3481             &core_connect_notify,
3482             NULL,
3483             NULL,
3484             NULL, GNUNET_NO,
3485             NULL, GNUNET_NO,
3486             no_handlers);
3487
3488         send_hello_context->core_connect_task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_divide(send_hello_context->pg->ct_ctx.connect_timeout, send_hello_context->pg->ct_ctx.connect_attempts),
3489             &send_core_connect_requests,
3490             send_hello_context);
3491       }
3492     else
3493     GNUNET_SCHEDULER_add_now(&schedule_send_hellos, send_hello_context);
3494   }
3495
3496 /**
3497  * Connect to a peer, give it all the HELLO's of those peers
3498  * we will later ask it to connect to.
3499  *
3500  * @param ct_ctx the overall connection context
3501  */
3502 static void schedule_send_hellos (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3503   {
3504     struct SendHelloContext *send_hello_context = cls;
3505     struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg;
3506
3507     if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
3508       {
3509         GNUNET_free(send_hello_context);
3510         return;
3511       }
3512
3513     GNUNET_assert(send_hello_context->peer_pos != NULL); /* All of the HELLO sends to be scheduled have been scheduled! */
3514
3515     if (((send_hello_context->peer->daemon->th == NULL) &&
3516             (pg->outstanding_connects > pg->max_outstanding_connections)) ||
3517         (pg->stop_connects == GNUNET_YES))
3518       {
3519 #if VERBOSE_TESTING > 2
3520         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3521             _
3522             ("Delaying connect, we have too many outstanding connections!\n"));
3523 #endif
3524         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
3525             (GNUNET_TIME_UNIT_MILLISECONDS, 100),
3526             &schedule_send_hellos, send_hello_context);
3527       }
3528     else
3529       {
3530 #if VERBOSE_TESTING > 2
3531         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3532             _("Creating connection, outstanding_connections is %d\n"),
3533             outstanding_connects);
3534 #endif
3535         if (send_hello_context->peer->daemon->th == NULL)
3536           {
3537             pg->outstanding_connects++; /* Actual TRANSPORT, CORE connections! */
3538             send_hello_context->peer->daemon->th
3539                 = GNUNET_TRANSPORT_connect (send_hello_context->peer->cfg, NULL,
3540                                             send_hello_context, NULL, NULL, NULL);
3541           }
3542 #if DEBUG_TESTING
3543         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3544             _("Offering Hello of peer %s to peer %s\n"),
3545             send_hello_context->peer->daemon->shortname, pg->peers[send_hello_context->peer_pos->index].daemon->shortname);
3546 #endif
3547         GNUNET_TRANSPORT_offer_hello(send_hello_context->peer->daemon->th,
3548             (const struct GNUNET_MessageHeader *)pg->peers[send_hello_context->peer_pos->index].daemon->hello,
3549             &hello_sent_callback,
3550             send_hello_context);
3551         send_hello_context->peer_pos = send_hello_context->peer_pos->next;
3552         GNUNET_assert(send_hello_context->peer->daemon->th != NULL);
3553       }
3554   }
3555 #endif
3556
3557 /**
3558  * Internal notification of a connection, kept so that we can ensure some connections
3559  * happen instead of flooding all testing daemons with requests to connect.
3560  */
3561 static void
3562 internal_connect_notify(void *cls, const struct GNUNET_PeerIdentity *first,
3563                         const struct GNUNET_PeerIdentity *second,
3564                         uint32_t distance,
3565                         const struct GNUNET_CONFIGURATION_Handle *first_cfg,
3566                         const struct GNUNET_CONFIGURATION_Handle *second_cfg,
3567                         struct GNUNET_TESTING_Daemon *first_daemon,
3568                         struct GNUNET_TESTING_Daemon *second_daemon,
3569                         const char *emsg)
3570 {
3571   struct ConnectContext *connect_ctx = cls;
3572   struct ConnectTopologyContext *ct_ctx = connect_ctx->ct_ctx;
3573   struct GNUNET_TESTING_PeerGroup *pg = ct_ctx->pg;
3574   struct PeerConnection *connection;
3575
3576   GNUNET_assert (0 < pg->outstanding_connects);
3577   pg->outstanding_connects--;
3578
3579   /*
3580    * Check whether the inverse connection has been scheduled yet,
3581    * if not, we can remove it from the other peers list and avoid
3582    * even trying to connect them again!
3583    */
3584   connection = pg->peers[connect_ctx->second_index].connect_peers_head;
3585 #if BAD
3586   other_peer = NULL;
3587 #endif
3588
3589   while ((connection != NULL) && (0
3590       != memcmp (first, &pg->peers[connection->index].daemon->id,
3591                  sizeof(struct GNUNET_PeerIdentity))))
3592     {
3593       connection = connection->next;
3594     }
3595
3596   if (connection != NULL) /* Can safely remove! */
3597     {
3598       GNUNET_assert (0 < ct_ctx->remaining_connections);
3599       ct_ctx->remaining_connections--;
3600       if (pg->notify_connection != NULL) /* Notify of reverse connection */
3601         pg->notify_connection (pg->notify_connection_cls, second, first,
3602                                distance, second_cfg, first_cfg, second_daemon,
3603                                first_daemon, emsg);
3604
3605       GNUNET_CONTAINER_DLL_remove(pg->peers[connect_ctx->second_index].connect_peers_head, pg->peers[connect_ctx->second_index].connect_peers_tail, connection);
3606       GNUNET_free(connection);
3607     }
3608
3609   if (ct_ctx->remaining_connections == 0)
3610     {
3611       if (ct_ctx->notify_connections_done != NULL)
3612         {
3613           ct_ctx->notify_connections_done (ct_ctx->notify_cls, NULL);
3614           ct_ctx->notify_connections_done = NULL;
3615         }
3616     }
3617   else
3618     preschedule_connect (pg);
3619
3620   if (pg->notify_connection != NULL)
3621     pg->notify_connection (pg->notify_connection_cls, first, second, distance,
3622                            first_cfg, second_cfg, first_daemon, second_daemon,
3623                            emsg);
3624
3625   GNUNET_free(connect_ctx);
3626 }
3627
3628 /**
3629  * Either delay a connection (because there are too many outstanding)
3630  * or schedule it for right now.
3631  *
3632  * @param cls a connection context
3633  * @param tc the task runtime context
3634  */
3635 static void
3636 schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3637 {
3638   struct ConnectContext *connect_context = cls;
3639   struct GNUNET_TESTING_PeerGroup *pg = connect_context->ct_ctx->pg;
3640
3641   if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
3642     return;
3643
3644   if ((pg->outstanding_connects > pg->max_outstanding_connections)
3645       || (pg->stop_connects == GNUNET_YES))
3646     {
3647 #if VERBOSE_TESTING
3648       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3649           _
3650           ("Delaying connect, we have too many outstanding connections!\n"));
3651 #endif
3652       GNUNET_SCHEDULER_add_delayed (
3653                                     GNUNET_TIME_relative_multiply (
3654                                                                    GNUNET_TIME_UNIT_MILLISECONDS,
3655                                                                    100),
3656                                     &schedule_connect, connect_context);
3657     }
3658   else
3659     {
3660 #if VERBOSE_TESTING
3661       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3662           _("Creating connection, outstanding_connections is %d (max %d)\n"),
3663           pg->outstanding_connects, pg->max_outstanding_connections);
3664 #endif
3665       pg->outstanding_connects++;
3666       pg->total_connects_scheduled++;
3667       GNUNET_TESTING_daemons_connect (
3668                                       pg->peers[connect_context->first_index].daemon,
3669                                       pg->peers[connect_context->second_index].daemon,
3670                                       connect_context->ct_ctx->connect_timeout,
3671                                       connect_context->ct_ctx->connect_attempts,
3672 #if USE_SEND_HELLOS
3673                                       GNUNET_NO,
3674 #else
3675                                       GNUNET_YES,
3676 #endif
3677                                       &internal_connect_notify, connect_context); /* FIXME: free connect context! */
3678     }
3679 }
3680
3681 #if !OLD
3682 /**
3683  * Iterator for actually scheduling connections to be created
3684  * between two peers.
3685  *
3686  * @param cls closure, a GNUNET_TESTING_Daemon
3687  * @param key the key the second Daemon was stored under
3688  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3689  *
3690  * @return GNUNET_YES to continue iteration
3691  */
3692 static int
3693 connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3694   {
3695     struct ConnectTopologyContext *ct_ctx = cls;
3696     struct PeerData *first = ct_ctx->first;
3697     struct GNUNET_TESTING_Daemon *second = value;
3698     struct ConnectContext *connect_context;
3699
3700     connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
3701     connect_context->first = first->daemon;
3702     connect_context->second = second;
3703     connect_context->ct_ctx = ct_ctx;
3704     GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
3705
3706     return GNUNET_YES;
3707   }
3708 #endif
3709
3710 #if !OLD
3711 /**
3712  * Iterator for copying all entries in the allowed hashmap to the
3713  * connect hashmap.
3714  *
3715  * @param cls closure, a GNUNET_TESTING_Daemon
3716  * @param key the key the second Daemon was stored under
3717  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3718  *
3719  * @return GNUNET_YES to continue iteration
3720  */
3721 static int
3722 copy_topology_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3723   {
3724     struct PeerData *first = cls;
3725
3726     GNUNET_assert (GNUNET_OK ==
3727         GNUNET_CONTAINER_multihashmap_put (first->connect_peers, key,
3728             value,
3729             GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3730
3731     return GNUNET_YES;
3732   }
3733 #endif
3734
3735 /**
3736  * Make the peers to connect the same as those that are allowed to be
3737  * connected.
3738  *
3739  * @param pg the peer group
3740  */
3741 static int
3742 copy_allowed_topology(struct GNUNET_TESTING_PeerGroup *pg)
3743 {
3744   unsigned int pg_iter;
3745   int ret;
3746   int total;
3747 #if OLD
3748   struct PeerConnection *iter;
3749 #endif
3750   total = 0;
3751   ret = 0;
3752   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3753     {
3754 #if OLD
3755       iter = pg->peers[pg_iter].allowed_peers_head;
3756       while (iter != NULL)
3757         {
3758           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3759                       "Creating connection between %d and %d\n", pg_iter,
3760                       iter->index);
3761           total += add_connections (pg, pg_iter, iter->index, CONNECT,
3762                                     GNUNET_YES);
3763           //total += add_actual_connections(pg, pg_iter, iter->index);
3764           iter = iter->next;
3765         }
3766 #else
3767       ret =
3768       GNUNET_CONTAINER_multihashmap_iterate (pg->
3769           peers[pg_iter].allowed_peers,
3770           &copy_topology_iterator,
3771           &pg->peers[pg_iter]);
3772 #endif
3773       if (GNUNET_SYSERR == ret)
3774         return GNUNET_SYSERR;
3775
3776       total = total + ret;
3777     }
3778
3779   return total;
3780 }
3781
3782 /**
3783  * Connect the topology as specified by the PeerConnection's
3784  * of each peer in the peer group
3785  *
3786  * @param pg the peer group we are dealing with
3787  * @param connect_timeout how long try connecting two peers
3788  * @param connect_attempts how many times (max) to attempt
3789  * @param notify_callback callback to notify when finished
3790  * @param notify_cls closure for notify callback
3791  *
3792  * @return the number of connections that will be attempted
3793  */
3794 static int
3795 connect_topology(struct GNUNET_TESTING_PeerGroup *pg,
3796                  struct GNUNET_TIME_Relative connect_timeout,
3797                  unsigned int connect_attempts,
3798                  GNUNET_TESTING_NotifyCompletion notify_callback,
3799                  void *notify_cls)
3800 {
3801   unsigned int pg_iter;
3802   unsigned int total;
3803
3804 #if OLD
3805   struct PeerConnection *connection_iter;
3806 #endif
3807 #if USE_SEND_HELLOS
3808   struct SendHelloContext *send_hello_context;
3809 #endif
3810
3811   total = 0;
3812   pg->ct_ctx.notify_connections_done = notify_callback;
3813   pg->ct_ctx.notify_cls = notify_cls;
3814   pg->ct_ctx.pg = pg;
3815
3816   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3817     {
3818 #if OLD
3819       connection_iter = pg->peers[pg_iter].connect_peers_head;
3820       while (connection_iter != NULL)
3821         {
3822           connection_iter = connection_iter->next;
3823           total++;
3824         }
3825 #else
3826       total +=
3827       GNUNET_CONTAINER_multihashmap_size (pg->peers[pg_iter].connect_peers);
3828 #endif
3829     }
3830
3831   if (total == 0)
3832     return total;
3833
3834   pg->ct_ctx.connect_timeout = connect_timeout;
3835   pg->ct_ctx.connect_attempts = connect_attempts;
3836   pg->ct_ctx.remaining_connections = total;
3837
3838 #if USE_SEND_HELLOS
3839   /* First give all peers the HELLO's of other peers (connect to first peer's transport service, give HELLO's of other peers, continue...) */
3840   pg->remaining_hellos = total;
3841   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3842     {
3843       send_hello_context = GNUNET_malloc(sizeof(struct SendHelloContext));
3844       send_hello_context->peer = &pg->peers[pg_iter];
3845       send_hello_context->peer_pos = pg->peers[pg_iter].connect_peers_head;
3846       send_hello_context->pg = pg;
3847       GNUNET_SCHEDULER_add_now(&schedule_send_hellos, send_hello_context);
3848     }
3849 #else
3850   for (pg_iter = 0; pg_iter < pg->max_outstanding_connections; pg_iter++)
3851     {
3852       preschedule_connect (pg);
3853     }
3854 #endif
3855   return total;
3856
3857 }
3858
3859 /**
3860  * Takes a peer group and creates a topology based on the
3861  * one specified.  Creates a topology means generates friend
3862  * files for the peers so they can only connect to those allowed
3863  * by the topology.  This will only have an effect once peers
3864  * are started if the FRIENDS_ONLY option is set in the base
3865  * config.  Also takes an optional restrict topology which
3866  * disallows connections based on particular transports
3867  * UNLESS they are specified in the restricted topology.
3868  *
3869  * @param pg the peer group struct representing the running peers
3870  * @param topology which topology to connect the peers in
3871  * @param restrict_topology disallow restrict_transports transport
3872  *                          connections to peers NOT in this topology
3873  *                          use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
3874  * @param restrict_transports space delimited list of transports to blacklist
3875  *                            to create restricted topology
3876  *
3877  * @return the maximum number of connections were all allowed peers
3878  *         connected to each other
3879  */
3880 unsigned int
3881 GNUNET_TESTING_create_topology(struct GNUNET_TESTING_PeerGroup *pg,
3882                                enum GNUNET_TESTING_Topology topology,
3883                                enum GNUNET_TESTING_Topology restrict_topology,
3884                                const char *restrict_transports)
3885 {
3886   int ret;
3887
3888   unsigned int num_connections;
3889   int unblacklisted_connections;
3890   char *filename;
3891   struct PeerConnection *conn_iter;
3892   struct PeerConnection *temp_conn;
3893   unsigned int off;
3894
3895 #if !OLD
3896   unsigned int i;
3897   for (i = 0; i < pg->total; i++)
3898     {
3899       pg->peers[i].allowed_peers =
3900       GNUNET_CONTAINER_multihashmap_create (100);
3901       pg->peers[i].connect_peers =
3902       GNUNET_CONTAINER_multihashmap_create (100);
3903       pg->peers[i].blacklisted_peers =
3904       GNUNET_CONTAINER_multihashmap_create (100);
3905       pg->peers[i].pg = pg;
3906     }
3907 #endif
3908
3909   switch (topology)
3910     {
3911   case GNUNET_TESTING_TOPOLOGY_CLIQUE:
3912 #if VERBOSE_TESTING
3913     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating clique topology\n"));
3914 #endif
3915     num_connections = create_clique (pg, &add_connections, ALLOWED, GNUNET_NO);
3916     break;
3917   case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
3918 #if VERBOSE_TESTING
3919     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3920         _("Creating small world (ring) topology\n"));
3921 #endif
3922     num_connections = create_small_world_ring (pg, &add_connections, ALLOWED);
3923     break;
3924   case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
3925 #if VERBOSE_TESTING
3926     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3927         _("Creating small world (2d-torus) topology\n"));
3928 #endif
3929     num_connections = create_small_world (pg, &add_connections, ALLOWED);
3930     break;
3931   case GNUNET_TESTING_TOPOLOGY_RING:
3932 #if VERBOSE_TESTING
3933     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating ring topology\n"));
3934 #endif
3935     num_connections = create_ring (pg, &add_connections, ALLOWED);
3936     break;
3937   case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
3938 #if VERBOSE_TESTING
3939     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating 2d torus topology\n"));
3940 #endif
3941     num_connections = create_2d_torus (pg, &add_connections, ALLOWED);
3942     break;
3943   case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
3944 #if VERBOSE_TESTING
3945     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3946         _("Creating Erdos-Renyi topology\n"));
3947 #endif
3948     num_connections = create_erdos_renyi (pg, &add_connections, ALLOWED);
3949     break;
3950   case GNUNET_TESTING_TOPOLOGY_INTERNAT:
3951 #if VERBOSE_TESTING
3952     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating InterNAT topology\n"));
3953 #endif
3954     num_connections = create_nated_internet (pg, &add_connections, ALLOWED);
3955     break;
3956   case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
3957 #if VERBOSE_TESTING
3958     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3959         _("Creating Scale Free topology\n"));
3960 #endif
3961     num_connections = create_scale_free (pg, &add_connections, ALLOWED);
3962     break;
3963   case GNUNET_TESTING_TOPOLOGY_LINE:
3964 #if VERBOSE_TESTING
3965     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3966         _("Creating straight line topology\n"));
3967 #endif
3968     num_connections = create_line (pg, &add_connections, ALLOWED);
3969     break;
3970   case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
3971 #if VERBOSE_TESTING
3972     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3973         _("Creating topology from file!\n"));
3974 #endif
3975     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "testing",
3976                                                             "topology_file",
3977                                                             &filename))
3978       num_connections = create_from_file (pg, filename, &add_connections,
3979                                           ALLOWED);
3980     else
3981       {
3982         GNUNET_log (
3983                     GNUNET_ERROR_TYPE_WARNING,
3984                     "Missing configuration option TESTING:TOPOLOGY_FILE for creating topology from file!\n");
3985         num_connections = 0;
3986       }
3987     break;
3988   case GNUNET_TESTING_TOPOLOGY_NONE:
3989 #if VERBOSE_TESTING
3990     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3991         _
3992         ("Creating no allowed topology (all peers can connect at core level)\n"));
3993 #endif
3994     num_connections = pg->total * pg->total; /* Clique is allowed! */
3995     break;
3996   default:
3997     num_connections = 0;
3998     break;
3999     }
4000
4001   if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING",
4002                                                           "F2F"))
4003     {
4004       ret = create_and_copy_friend_files (pg);
4005       if (ret != GNUNET_OK)
4006         {
4007 #if VERBOSE_TESTING
4008           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4009               _("Failed during friend file copying!\n"));
4010 #endif
4011           return GNUNET_SYSERR;
4012         }
4013       else
4014         {
4015 #if VERBOSE_TESTING
4016           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4017               _("Friend files created/copied successfully!\n"));
4018 #endif
4019         }
4020     }
4021
4022   /* Use the create clique method to initially set all connections as blacklisted. */
4023   if ((restrict_topology != GNUNET_TESTING_TOPOLOGY_NONE) && (restrict_topology
4024       != GNUNET_TESTING_TOPOLOGY_FROM_FILE))
4025     create_clique (pg, &add_connections, BLACKLIST, GNUNET_NO);
4026   else
4027     return num_connections;
4028
4029   unblacklisted_connections = 0;
4030   /* Un-blacklist connections as per the topology specified */
4031   switch (restrict_topology)
4032     {
4033   case GNUNET_TESTING_TOPOLOGY_CLIQUE:
4034 #if VERBOSE_TESTING
4035     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4036         _("Blacklisting all but clique topology\n"));
4037 #endif
4038     unblacklisted_connections = create_clique (pg, &remove_connections,
4039                                                BLACKLIST, GNUNET_NO);
4040     break;
4041   case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
4042 #if VERBOSE_TESTING
4043     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4044         _("Blacklisting all but small world (ring) topology\n"));
4045 #endif
4046     unblacklisted_connections = create_small_world_ring (pg,
4047                                                          &remove_connections,
4048                                                          BLACKLIST);
4049     break;
4050   case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
4051 #if VERBOSE_TESTING
4052     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4053         _
4054         ("Blacklisting all but small world (2d-torus) topology\n"));
4055 #endif
4056     unblacklisted_connections = create_small_world (pg, &remove_connections,
4057                                                     BLACKLIST);
4058     break;
4059   case GNUNET_TESTING_TOPOLOGY_RING:
4060 #if VERBOSE_TESTING
4061     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4062         _("Blacklisting all but ring topology\n"));
4063 #endif
4064     unblacklisted_connections
4065         = create_ring (pg, &remove_connections, BLACKLIST);
4066     break;
4067   case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
4068 #if VERBOSE_TESTING
4069     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4070         _("Blacklisting all but 2d torus topology\n"));
4071 #endif
4072     unblacklisted_connections = create_2d_torus (pg, &remove_connections,
4073                                                  BLACKLIST);
4074     break;
4075   case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
4076 #if VERBOSE_TESTING
4077     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4078         _("Blacklisting all but Erdos-Renyi topology\n"));
4079 #endif
4080     unblacklisted_connections = create_erdos_renyi (pg, &remove_connections,
4081                                                     BLACKLIST);
4082     break;
4083   case GNUNET_TESTING_TOPOLOGY_INTERNAT:
4084 #if VERBOSE_TESTING
4085     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4086         _("Blacklisting all but InterNAT topology\n"));
4087 #endif
4088
4089 #if TOPOLOGY_HACK
4090     for (off = 0; off < pg->total; off++)
4091       {
4092         conn_iter = pg->peers[off].allowed_peers_head;
4093         while (conn_iter != NULL)
4094           {
4095             temp_conn = conn_iter->next;
4096             GNUNET_free(conn_iter);
4097             conn_iter = temp_conn;
4098           }
4099         pg->peers[off].allowed_peers_head = NULL;
4100         pg->peers[off].allowed_peers_tail = NULL;
4101
4102         conn_iter = pg->peers[off].connect_peers_head;
4103         while (conn_iter != NULL)
4104           {
4105             temp_conn = conn_iter->next;
4106             GNUNET_free(conn_iter);
4107             conn_iter = temp_conn;
4108           }
4109         pg->peers[off].connect_peers_head = NULL;
4110         pg->peers[off].connect_peers_tail = NULL;
4111       }
4112     unblacklisted_connections
4113         = create_nated_internet_copy (pg, &remove_connections, BLACKLIST);
4114 #else
4115     unblacklisted_connections =
4116     create_nated_internet (pg, &remove_connections, BLACKLIST);
4117 #endif
4118
4119     break;
4120   case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
4121 #if VERBOSE_TESTING
4122     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4123         _("Blacklisting all but Scale Free topology\n"));
4124 #endif
4125     unblacklisted_connections = create_scale_free (pg, &remove_connections,
4126                                                    BLACKLIST);
4127     break;
4128   case GNUNET_TESTING_TOPOLOGY_LINE:
4129 #if VERBOSE_TESTING
4130     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4131         _("Blacklisting all but straight line topology\n"));
4132 #endif
4133     unblacklisted_connections
4134         = create_line (pg, &remove_connections, BLACKLIST);
4135   default:
4136     break;
4137     }
4138
4139   if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
4140     {
4141       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Creating blacklist with `%s'\n",
4142                   restrict_transports);
4143       ret = create_and_copy_blacklist_files (pg, restrict_transports);
4144       if (ret != GNUNET_OK)
4145         {
4146 #if VERBOSE_TESTING
4147           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4148               _("Failed during blacklist file copying!\n"));
4149 #endif
4150           return 0;
4151         }
4152       else
4153         {
4154 #if VERBOSE_TESTING
4155           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4156               _("Blacklist files created/copied successfully!\n"));
4157 #endif
4158         }
4159     }
4160   return num_connections;
4161 }
4162
4163 #if !OLD
4164 /**
4165  * Iterator for choosing random peers to connect.
4166  *
4167  * @param cls closure, a RandomContext
4168  * @param key the key the second Daemon was stored under
4169  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
4170  *
4171  * @return GNUNET_YES to continue iteration
4172  */
4173 static int
4174 random_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
4175   {
4176     struct RandomContext *random_ctx = cls;
4177     double random_number;
4178     uint32_t second_pos;
4179     GNUNET_HashCode first_hash;
4180     random_number =
4181     ((double)
4182         GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
4183             UINT64_MAX)) / ((double) UINT64_MAX);
4184     if (random_number < random_ctx->percentage)
4185       {
4186         GNUNET_assert (GNUNET_OK ==
4187             GNUNET_CONTAINER_multihashmap_put (random_ctx->
4188                 first->connect_peers_working_set,
4189                 key, value,
4190                 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4191       }
4192
4193     /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
4194     uid_from_hash (key, &second_pos);
4195     hash_from_uid (random_ctx->first_uid, &first_hash);
4196     GNUNET_assert (random_ctx->pg->total > second_pos);
4197     GNUNET_assert (GNUNET_YES ==
4198         GNUNET_CONTAINER_multihashmap_remove (random_ctx->
4199             pg->peers
4200             [second_pos].connect_peers,
4201             &first_hash,
4202             random_ctx->
4203             first->daemon));
4204
4205     return GNUNET_YES;
4206   }
4207
4208 /**
4209  * Iterator for adding at least X peers to a peers connection set.
4210  *
4211  * @param cls closure, MinimumContext
4212  * @param key the key the second Daemon was stored under
4213  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
4214  *
4215  * @return GNUNET_YES to continue iteration
4216  */
4217 static int
4218 minimum_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
4219   {
4220     struct MinimumContext *min_ctx = cls;
4221     uint32_t second_pos;
4222     GNUNET_HashCode first_hash;
4223     unsigned int i;
4224
4225     if (GNUNET_CONTAINER_multihashmap_size
4226         (min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
4227       {
4228         for (i = 0; i < min_ctx->num_to_add; i++)
4229           {
4230             if (min_ctx->pg_array[i] == min_ctx->current)
4231               {
4232                 GNUNET_assert (GNUNET_OK ==
4233                     GNUNET_CONTAINER_multihashmap_put
4234                     (min_ctx->first->connect_peers_working_set, key,
4235                         value,
4236                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4237                 uid_from_hash (key, &second_pos);
4238                 hash_from_uid (min_ctx->first_uid, &first_hash);
4239                 GNUNET_assert (min_ctx->pg->total > second_pos);
4240                 GNUNET_assert (GNUNET_OK ==
4241                     GNUNET_CONTAINER_multihashmap_put (min_ctx->
4242                         pg->peers
4243                         [second_pos].connect_peers_working_set,
4244                         &first_hash,
4245                         min_ctx->first->
4246                         daemon,
4247                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4248                 /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
4249                 GNUNET_assert (GNUNET_YES ==
4250                     GNUNET_CONTAINER_multihashmap_remove
4251                     (min_ctx->pg->peers[second_pos].connect_peers,
4252                         &first_hash, min_ctx->first->daemon));
4253               }
4254           }
4255         min_ctx->current++;
4256         return GNUNET_YES;
4257       }
4258     else
4259     return GNUNET_NO; /* We can stop iterating, we have enough peers! */
4260
4261   }
4262
4263 /**
4264  * Iterator for adding peers to a connection set based on a depth first search.
4265  *
4266  * @param cls closure, MinimumContext
4267  * @param key the key the second daemon was stored under
4268  * @param value the GNUNET_TESTING_Daemon that the first is to connect to
4269  *
4270  * @return GNUNET_YES to continue iteration
4271  */
4272 static int
4273 dfs_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
4274   {
4275     struct DFSContext *dfs_ctx = cls;
4276     GNUNET_HashCode first_hash;
4277
4278     if (dfs_ctx->current == dfs_ctx->chosen)
4279       {
4280         GNUNET_assert (GNUNET_OK ==
4281             GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
4282                 first->connect_peers_working_set,
4283                 key, value,
4284                 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4285         uid_from_hash (key, &dfs_ctx->second_uid);
4286         hash_from_uid (dfs_ctx->first_uid, &first_hash);
4287         GNUNET_assert (GNUNET_OK ==
4288             GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
4289                 pg->peers
4290                 [dfs_ctx->second_uid].connect_peers_working_set,
4291                 &first_hash,
4292                 dfs_ctx->
4293                 first->daemon,
4294                 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4295         GNUNET_assert (GNUNET_YES ==
4296             GNUNET_CONTAINER_multihashmap_remove (dfs_ctx->
4297                 pg->peers
4298                 [dfs_ctx->second_uid].connect_peers,
4299                 &first_hash,
4300                 dfs_ctx->
4301                 first->daemon));
4302         /* Can't remove second from first yet because we are currently iterating, hence the return value in the DFSContext! */
4303         return GNUNET_NO; /* We have found our peer, don't iterate more */
4304       }
4305
4306     dfs_ctx->current++;
4307     return GNUNET_YES;
4308   }
4309 #endif
4310
4311 /**
4312  * From the set of connections possible, choose percentage percent of connections
4313  * to actually connect.
4314  *
4315  * @param pg the peergroup we are dealing with
4316  * @param percentage what percent of total connections to make
4317  */
4318 void
4319 choose_random_connections(struct GNUNET_TESTING_PeerGroup *pg,
4320                           double percentage)
4321 {
4322   uint32_t pg_iter;
4323 #if OLD
4324   struct PeerConnection *conn_iter;
4325   double random_number;
4326 #else
4327   struct RandomContext random_ctx;
4328 #endif
4329
4330   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4331     {
4332 #if OLD
4333       conn_iter = pg->peers[pg_iter].connect_peers_head;
4334       while (conn_iter != NULL)
4335         {
4336           random_number
4337               = ((double) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
4338                                                     UINT64_MAX))
4339                   / ((double) UINT64_MAX);
4340           if (random_number < percentage)
4341             {
4342               add_connections (pg, pg_iter, conn_iter->index, WORKING_SET,
4343                                GNUNET_YES);
4344             }
4345           conn_iter = conn_iter->next;
4346         }
4347 #else
4348       random_ctx.first_uid = pg_iter;
4349       random_ctx.first = &pg->peers[pg_iter];
4350       random_ctx.percentage = percentage;
4351       random_ctx.pg = pg;
4352       pg->peers[pg_iter].connect_peers_working_set 
4353         = GNUNET_CONTAINER_multihashmap_create (pg->total);
4354       GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
4355                                              &random_connect_iterator,
4356                                              &random_ctx);
4357       /* Now remove the old connections */
4358       GNUNET_CONTAINER_multihashmap_destroy (pg->
4359                                              peers[pg_iter].connect_peers);
4360       /* And replace with the random set */
4361       pg->peers[pg_iter].connect_peers 
4362         = pg->peers[pg_iter].connect_peers_working_set;
4363 #endif
4364     }
4365
4366   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4367     {
4368       conn_iter = pg->peers[pg_iter].connect_peers_head;
4369       while (pg->peers[pg_iter].connect_peers_head != NULL)
4370         remove_connections (pg, pg_iter,
4371                             pg->peers[pg_iter].connect_peers_head->index,
4372                             CONNECT, GNUNET_YES);
4373
4374       pg->peers[pg_iter].connect_peers_head
4375           = pg->peers[pg_iter].connect_peers_working_set_head;
4376       pg->peers[pg_iter].connect_peers_tail
4377           = pg->peers[pg_iter].connect_peers_working_set_tail;
4378       pg->peers[pg_iter].connect_peers_working_set_head = NULL;
4379       pg->peers[pg_iter].connect_peers_working_set_tail = NULL;
4380     }
4381 }
4382
4383 /**
4384  * Count the number of connections in a linked list of connections.
4385  *
4386  * @param conn_list the connection list to get the count of
4387  *
4388  * @return the number of elements in the list
4389  */
4390 static unsigned int
4391 count_connections(struct PeerConnection *conn_list)
4392 {
4393   struct PeerConnection *iter;
4394   unsigned int count;
4395   count = 0;
4396   iter = conn_list;
4397   while (iter != NULL)
4398     {
4399       iter = iter->next;
4400       count++;
4401     }
4402   return count;
4403 }
4404
4405 static unsigned int
4406 count_workingset_connections(struct GNUNET_TESTING_PeerGroup *pg)
4407 {
4408   unsigned int count;
4409   unsigned int pg_iter;
4410 #if OLD
4411   struct PeerConnection *conn_iter;
4412 #endif
4413   count = 0;
4414
4415   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4416     {
4417 #if OLD
4418       conn_iter = pg->peers[pg_iter].connect_peers_working_set_head;
4419       while (conn_iter != NULL)
4420         {
4421           count++;
4422           conn_iter = conn_iter->next;
4423         }
4424 #else
4425       count +=
4426       GNUNET_CONTAINER_multihashmap_size (pg->
4427           peers
4428           [pg_iter].connect_peers_working_set);
4429 #endif
4430     }
4431
4432   return count;
4433 }
4434
4435 static unsigned int
4436 count_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg)
4437 {
4438   unsigned int count;
4439   unsigned int pg_iter;
4440 #if OLD
4441   struct PeerConnection *conn_iter;
4442 #endif
4443
4444   count = 0;
4445   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4446     {
4447 #if OLD
4448       conn_iter = pg->peers[pg_iter].allowed_peers_head;
4449       while (conn_iter != NULL)
4450         {
4451           count++;
4452           conn_iter = conn_iter->next;
4453         }
4454 #else
4455       count +=
4456       GNUNET_CONTAINER_multihashmap_size (pg->
4457           peers
4458           [pg_iter].allowed_peers);
4459 #endif
4460     }
4461
4462   return count;
4463 }
4464
4465 /**
4466  * From the set of connections possible, choose at least num connections per
4467  * peer.
4468  *
4469  * @param pg the peergroup we are dealing with
4470  * @param num how many connections at least should each peer have (if possible)?
4471  */
4472 static void
4473 choose_minimum(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
4474 {
4475 #if !OLD
4476   struct MinimumContext minimum_ctx;
4477 #else
4478   struct PeerConnection *conn_iter;
4479   unsigned int temp_list_size;
4480   unsigned int i;
4481   unsigned int count;
4482   uint32_t random; /* Random list entry to connect peer to */
4483 #endif
4484   uint32_t pg_iter;
4485
4486 #if OLD
4487   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4488     {
4489       temp_list_size
4490           = count_connections (pg->peers[pg_iter].connect_peers_head);
4491       if (temp_list_size == 0)
4492         {
4493           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4494                       "Peer %d has 0 connections!?!?\n", pg_iter);
4495           break;
4496         }
4497       for (i = 0; i < num; i++)
4498         {
4499           random = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
4500                                              temp_list_size);
4501           conn_iter = pg->peers[pg_iter].connect_peers_head;
4502           for (count = 0; count < random; count++)
4503             conn_iter = conn_iter->next;
4504           /* We now have a random connection, connect it! */
4505           GNUNET_assert(conn_iter != NULL);
4506           add_connections (pg, pg_iter, conn_iter->index, WORKING_SET,
4507                            GNUNET_YES);
4508         }
4509     }
4510 #else
4511   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4512     {
4513       pg->peers[pg_iter].connect_peers_working_set =
4514       GNUNET_CONTAINER_multihashmap_create (num);
4515     }
4516
4517   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4518     {
4519       minimum_ctx.first_uid = pg_iter;
4520       minimum_ctx.pg_array =
4521       GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
4522           GNUNET_CONTAINER_multihashmap_size
4523           (pg->peers[pg_iter].connect_peers));
4524       minimum_ctx.first = &pg->peers[pg_iter];
4525       minimum_ctx.pg = pg;
4526       minimum_ctx.num_to_add = num;
4527       minimum_ctx.current = 0;
4528       GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
4529           &minimum_connect_iterator,
4530           &minimum_ctx);
4531     }
4532
4533   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4534     {
4535       /* Remove the "old" connections */
4536       GNUNET_CONTAINER_multihashmap_destroy (pg->
4537           peers[pg_iter].connect_peers);
4538       /* And replace with the working set */
4539       pg->peers[pg_iter].connect_peers =
4540       pg->peers[pg_iter].connect_peers_working_set;
4541     }
4542 #endif
4543   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4544     {
4545       while (pg->peers[pg_iter].connect_peers_head != NULL)
4546         {
4547           conn_iter = pg->peers[pg_iter].connect_peers_head;
4548           GNUNET_CONTAINER_DLL_remove(pg->peers[pg_iter].connect_peers_head,
4549               pg->peers[pg_iter].connect_peers_tail,
4550               conn_iter);
4551           GNUNET_free(conn_iter);
4552           /*remove_connections(pg, pg_iter, pg->peers[pg_iter].connect_peers_head->index, CONNECT, GNUNET_YES);*/
4553         }
4554
4555       pg->peers[pg_iter].connect_peers_head
4556           = pg->peers[pg_iter].connect_peers_working_set_head;
4557       pg->peers[pg_iter].connect_peers_tail
4558           = pg->peers[pg_iter].connect_peers_working_set_tail;
4559       pg->peers[pg_iter].connect_peers_working_set_head = NULL;
4560       pg->peers[pg_iter].connect_peers_working_set_tail = NULL;
4561     }
4562 }
4563
4564 #if !OLD
4565 struct FindClosestContext
4566   {
4567     /**
4568      * The currently known closest peer.
4569      */
4570     struct GNUNET_TESTING_Daemon *closest;
4571
4572     /**
4573      * The info for the peer we are adding connections for.
4574      */
4575     struct PeerData *curr_peer;
4576
4577     /**
4578      * The distance (bits) between the current
4579      * peer and the currently known closest.
4580      */
4581     unsigned int closest_dist;
4582
4583     /**
4584      * The offset of the closest known peer in
4585      * the peer group.
4586      */
4587     unsigned int closest_num;
4588   };
4589
4590 /**
4591  * Iterator over hash map entries of the allowed
4592  * peer connections.  Find the closest, not already
4593  * connected peer and return it.
4594  *
4595  * @param cls closure (struct FindClosestContext)
4596  * @param key current key code (hash of offset in pg)
4597  * @param value value in the hash map - a GNUNET_TESTING_Daemon
4598  * @return GNUNET_YES if we should continue to
4599  *         iterate,
4600  *         GNUNET_NO if not.
4601  */
4602 static int
4603 find_closest_peers (void *cls, const GNUNET_HashCode * key, void *value)
4604   {
4605     struct FindClosestContext *closest_ctx = cls;
4606     struct GNUNET_TESTING_Daemon *daemon = value;
4607
4608     if (((closest_ctx->closest == NULL) ||
4609             (GNUNET_CRYPTO_hash_matching_bits
4610                 (&daemon->id.hashPubKey,
4611                     &closest_ctx->curr_peer->daemon->id.hashPubKey) >
4612                 closest_ctx->closest_dist))
4613         && (GNUNET_YES !=
4614             GNUNET_CONTAINER_multihashmap_contains (closest_ctx->
4615                 curr_peer->connect_peers,
4616                 key)))
4617       {
4618         closest_ctx->closest_dist =
4619         GNUNET_CRYPTO_hash_matching_bits (&daemon->id.hashPubKey,
4620             &closest_ctx->curr_peer->daemon->
4621             id.hashPubKey);
4622         closest_ctx->closest = daemon;
4623         uid_from_hash (key, &closest_ctx->closest_num);
4624       }
4625     return GNUNET_YES;
4626   }
4627
4628 /**
4629  * From the set of connections possible, choose at num connections per
4630  * peer based on depth which are closest out of those allowed.  Guaranteed
4631  * to add num peers to connect to, provided there are that many peers
4632  * in the underlay topology to connect to.
4633  *
4634  * @param pg the peergroup we are dealing with
4635  * @param num how many connections at least should each peer have (if possible)?
4636  * @param proc processor to actually add the connections
4637  * @param list the peer list to use
4638  */
4639 void
4640 add_closest (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num,
4641     GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
4642   {
4643 #if OLD
4644
4645 #else
4646     struct FindClosestContext closest_ctx;
4647 #endif
4648     uint32_t pg_iter;
4649     uint32_t i;
4650
4651     for (i = 0; i < num; i++) /* Each time find a closest peer (from those available) */
4652       {
4653         for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4654           {
4655             closest_ctx.curr_peer = &pg->peers[pg_iter];
4656             closest_ctx.closest = NULL;
4657             closest_ctx.closest_dist = 0;
4658             closest_ctx.closest_num = 0;
4659             GNUNET_CONTAINER_multihashmap_iterate (pg->
4660                 peers[pg_iter].allowed_peers,
4661                 &find_closest_peers,
4662                 &closest_ctx);
4663             if (closest_ctx.closest != NULL)
4664               {
4665                 GNUNET_assert (closest_ctx.closest_num < pg->total);
4666                 proc (pg, pg_iter, closest_ctx.closest_num, list);
4667               }
4668           }
4669       }
4670   }
4671 #endif
4672
4673 /**
4674  * From the set of connections possible, choose at least num connections per
4675  * peer based on depth first traversal of peer connections.  If DFS leaves
4676  * peers unconnected, ensure those peers get connections.
4677  *
4678  * @param pg the peergroup we are dealing with
4679  * @param num how many connections at least should each peer have (if possible)?
4680  */
4681 void
4682 perform_dfs(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
4683 {
4684   uint32_t pg_iter;
4685   uint32_t dfs_count;
4686   uint32_t starting_peer;
4687   uint32_t least_connections;
4688   uint32_t random_connection;
4689 #if OLD
4690   unsigned int temp_count;
4691   struct PeerConnection *peer_iter;
4692 #else
4693   struct DFSContext dfs_ctx;
4694   GNUNET_HashCode second_hash;
4695 #endif
4696
4697 #if OLD
4698   starting_peer = 0;
4699   dfs_count = 0;
4700   while ((count_workingset_connections (pg) < num * pg->total)
4701       && (count_allowed_connections (pg) > 0))
4702     {
4703       if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
4704         {
4705           least_connections = -1; /* Set to very high number */
4706           for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4707             {
4708               temp_count
4709                   = count_connections (
4710                                        pg->peers[pg_iter].connect_peers_working_set_head);
4711               if (temp_count < least_connections)
4712                 {
4713                   starting_peer = pg_iter;
4714                   least_connections = temp_count;
4715                 }
4716             }
4717         }
4718
4719       temp_count
4720           = count_connections (pg->peers[starting_peer].connect_peers_head);
4721       if (temp_count == 0)
4722         continue; /* FIXME: infinite loop? */
4723
4724       random_connection = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
4725                                                     temp_count);
4726       temp_count = 0;
4727       peer_iter = pg->peers[starting_peer].connect_peers_head;
4728       while (temp_count < random_connection)
4729         {
4730           peer_iter = peer_iter->next;
4731           temp_count++;
4732         }
4733       GNUNET_assert(peer_iter != NULL);
4734       add_connections (pg, starting_peer, peer_iter->index, WORKING_SET,
4735                        GNUNET_NO);
4736       remove_connections (pg, starting_peer, peer_iter->index, CONNECT,
4737                           GNUNET_YES);
4738       starting_peer = peer_iter->index;
4739       dfs_count++;
4740     }
4741
4742 #else
4743   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4744     {
4745       pg->peers[pg_iter].connect_peers_working_set =
4746       GNUNET_CONTAINER_multihashmap_create (num);
4747     }
4748
4749   starting_peer = 0;
4750   dfs_count = 0;
4751   while ((count_workingset_connections (pg) < num * pg->total)
4752       && (count_allowed_connections (pg) > 0))
4753     {
4754       if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
4755         {
4756           least_connections = -1; /* Set to very high number */
4757           for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4758             {
4759               if (GNUNET_CONTAINER_multihashmap_size
4760                   (pg->peers[pg_iter].connect_peers_working_set) <
4761                   least_connections)
4762                 {
4763                   starting_peer = pg_iter;
4764                   least_connections =
4765                   GNUNET_CONTAINER_multihashmap_size (pg->
4766                       peers
4767                       [pg_iter].connect_peers_working_set);
4768                 }
4769             }
4770         }
4771
4772       if (GNUNET_CONTAINER_multihashmap_size (pg->peers[starting_peer].connect_peers) == 0) /* Ensure there is at least one peer left to connect! */
4773         {
4774           dfs_count = 0;
4775           continue;
4776         }
4777
4778       /* Choose a random peer from the chosen peers set of connections to add */
4779       dfs_ctx.chosen =
4780       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
4781           GNUNET_CONTAINER_multihashmap_size
4782           (pg->peers[starting_peer].connect_peers));
4783       dfs_ctx.first_uid = starting_peer;
4784       dfs_ctx.first = &pg->peers[starting_peer];
4785       dfs_ctx.pg = pg;
4786       dfs_ctx.current = 0;
4787
4788       GNUNET_CONTAINER_multihashmap_iterate (pg->
4789           peers
4790           [starting_peer].connect_peers,
4791           &dfs_connect_iterator, &dfs_ctx);
4792       /* Remove the second from the first, since we will be continuing the search and may encounter the first peer again! */
4793       hash_from_uid (dfs_ctx.second_uid, &second_hash);
4794       GNUNET_assert (GNUNET_YES ==
4795           GNUNET_CONTAINER_multihashmap_remove (pg->peers
4796               [starting_peer].connect_peers,
4797               &second_hash,
4798               pg->
4799               peers
4800               [dfs_ctx.second_uid].daemon));
4801       starting_peer = dfs_ctx.second_uid;
4802     }
4803
4804   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4805     {
4806       /* Remove the "old" connections */
4807       GNUNET_CONTAINER_multihashmap_destroy (pg->
4808           peers[pg_iter].connect_peers);
4809       /* And replace with the working set */
4810       pg->peers[pg_iter].connect_peers =
4811       pg->peers[pg_iter].connect_peers_working_set;
4812     }
4813 #endif
4814 }
4815
4816 /**
4817  * Internal callback for topology information for a particular peer.
4818  */
4819 static void
4820 internal_topology_callback(void *cls, const struct GNUNET_PeerIdentity *peer,
4821                            const struct GNUNET_TRANSPORT_ATS_Information *atsi)
4822 {
4823   struct CoreContext *core_ctx = cls;
4824   struct TopologyIterateContext *iter_ctx = core_ctx->iter_context;
4825
4826   if (peer == NULL) /* Either finished, or something went wrong */
4827     {
4828       iter_ctx->completed++;
4829       iter_ctx->connected--;
4830       /* One core context allocated per iteration, must free! */
4831       GNUNET_free (core_ctx);
4832     }
4833   else
4834     {
4835       iter_ctx->topology_cb (iter_ctx->cls, &core_ctx->daemon->id, peer, NULL);
4836     }
4837
4838   if (iter_ctx->completed == iter_ctx->total)
4839     {
4840       iter_ctx->topology_cb (iter_ctx->cls, NULL, NULL, NULL);
4841       /* Once all are done, free the iteration context */
4842       GNUNET_free (iter_ctx);
4843     }
4844 }
4845
4846 /**
4847  * Check running topology iteration tasks, if below max start a new one, otherwise
4848  * schedule for some time in the future.
4849  */
4850 static void
4851 schedule_get_topology(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
4852 {
4853   struct CoreContext *core_context = cls;
4854   struct TopologyIterateContext *topology_context =
4855       (struct TopologyIterateContext *) core_context->iter_context;
4856   if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
4857     return;
4858
4859   if (topology_context->connected
4860       > topology_context->pg->max_outstanding_connections)
4861     {
4862 #if VERBOSE_TESTING > 2
4863       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4864           _
4865           ("Delaying connect, we have too many outstanding connections!\n"));
4866 #endif
4867       GNUNET_SCHEDULER_add_delayed (
4868                                     GNUNET_TIME_relative_multiply (
4869                                                                    GNUNET_TIME_UNIT_MILLISECONDS,
4870                                                                    100),
4871                                     &schedule_get_topology, core_context);
4872     }
4873   else
4874     {
4875 #if VERBOSE_TESTING > 2
4876       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4877           _("Creating connection, outstanding_connections is %d\n"),
4878           outstanding_connects);
4879 #endif
4880       topology_context->connected++;
4881
4882       if (GNUNET_OK != GNUNET_CORE_iterate_peers (core_context->daemon->cfg,
4883                                                   &internal_topology_callback,
4884                                                   core_context))
4885         {
4886           GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Topology iteration failed.\n");
4887           internal_topology_callback (core_context, NULL, NULL);
4888         }
4889     }
4890 }
4891
4892 /**
4893  * Iterate over all (running) peers in the peer group, retrieve
4894  * all connections that each currently has.
4895  */
4896 void
4897 GNUNET_TESTING_get_topology(struct GNUNET_TESTING_PeerGroup *pg,
4898                             GNUNET_TESTING_NotifyTopology cb, void *cls)
4899 {
4900   struct TopologyIterateContext *topology_context;
4901   struct CoreContext *core_ctx;
4902   unsigned int i;
4903   unsigned int total_count;
4904
4905   /* Allocate a single topology iteration context */
4906   topology_context = GNUNET_malloc (sizeof (struct TopologyIterateContext));
4907   topology_context->topology_cb = cb;
4908   topology_context->cls = cls;
4909   topology_context->pg = pg;
4910   total_count = 0;
4911   for (i = 0; i < pg->total; i++)
4912     {
4913       if (pg->peers[i].daemon->running == GNUNET_YES)
4914         {
4915           /* Allocate one core context per core we need to connect to */
4916           core_ctx = GNUNET_malloc (sizeof (struct CoreContext));
4917           core_ctx->daemon = pg->peers[i].daemon;
4918           /* Set back pointer to topology iteration context */
4919           core_ctx->iter_context = topology_context;
4920           GNUNET_SCHEDULER_add_now (&schedule_get_topology, core_ctx);
4921           total_count++;
4922         }
4923     }
4924   if (total_count == 0)
4925     {
4926       cb (cls, NULL, NULL, "Cannot iterate over topology, no running peers!");
4927       GNUNET_free (topology_context);
4928     }
4929   else
4930     topology_context->total = total_count;
4931   return;
4932 }
4933
4934 /**
4935  * Callback function to process statistic values.
4936  * This handler is here only really to insert a peer
4937  * identity (or daemon) so the statistics can be uniquely
4938  * tied to a single running peer.
4939  *
4940  * @param cls closure
4941  * @param subsystem name of subsystem that created the statistic
4942  * @param name the name of the datum
4943  * @param value the current value
4944  * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
4945  * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
4946  */
4947 static int
4948 internal_stats_callback(void *cls, const char *subsystem, const char *name,
4949                         uint64_t value, int is_persistent)
4950 {
4951   struct StatsCoreContext *core_context = cls;
4952   struct StatsIterateContext *stats_context =
4953       (struct StatsIterateContext *) core_context->iter_context;
4954
4955   return stats_context->proc (stats_context->cls, &core_context->daemon->id,
4956                               subsystem, name, value, is_persistent);
4957 }
4958
4959 /**
4960  * Internal continuation call for statistics iteration.
4961  *
4962  * @param cls closure, the CoreContext for this iteration
4963  * @param success whether or not the statistics iterations
4964  *        was canceled or not (we don't care)
4965  */
4966 static void
4967 internal_stats_cont(void *cls, int success)
4968 {
4969   struct StatsCoreContext *core_context = cls;
4970   struct StatsIterateContext *stats_context =
4971       (struct StatsIterateContext *) core_context->iter_context;
4972
4973   stats_context->connected--;
4974   stats_context->completed++;
4975
4976   if (stats_context->completed == stats_context->total)
4977     {
4978       stats_context->cont (stats_context->cls, GNUNET_YES);
4979       GNUNET_free (stats_context);
4980     }
4981
4982   if (core_context->stats_handle != NULL)
4983     GNUNET_STATISTICS_destroy (core_context->stats_handle, GNUNET_NO);
4984
4985   GNUNET_free (core_context);
4986 }
4987
4988 /**
4989  * Check running topology iteration tasks, if below max start a new one, otherwise
4990  * schedule for some time in the future.
4991  */
4992 static void
4993 schedule_get_statistics(void *cls,
4994                         const struct GNUNET_SCHEDULER_TaskContext *tc)
4995 {
4996   struct StatsCoreContext *core_context = cls;
4997   struct StatsIterateContext *stats_context =
4998       (struct StatsIterateContext *) core_context->iter_context;
4999
5000   if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
5001     return;
5002
5003   if (stats_context->connected > stats_context->pg->max_outstanding_connections)
5004     {
5005 #if VERBOSE_TESTING > 2
5006       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5007           _
5008           ("Delaying connect, we have too many outstanding connections!\n"));
5009 #endif
5010       GNUNET_SCHEDULER_add_delayed (
5011                                     GNUNET_TIME_relative_multiply (
5012                                                                    GNUNET_TIME_UNIT_MILLISECONDS,
5013                                                                    100),
5014                                     &schedule_get_statistics, core_context);
5015     }
5016   else
5017     {
5018 #if VERBOSE_TESTING > 2
5019       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5020           _("Creating connection, outstanding_connections is %d\n"),
5021           outstanding_connects);
5022 #endif
5023
5024       stats_context->connected++;
5025       core_context->stats_handle
5026           = GNUNET_STATISTICS_create ("testing", core_context->daemon->cfg);
5027       if (core_context->stats_handle == NULL)
5028         {
5029           internal_stats_cont (core_context, GNUNET_NO);
5030           return;
5031         }
5032
5033       core_context->stats_get_handle
5034           = GNUNET_STATISTICS_get (core_context->stats_handle, NULL, NULL,
5035                                    GNUNET_TIME_relative_get_forever (),
5036                                    &internal_stats_cont,
5037                                    &internal_stats_callback, core_context);
5038       if (core_context->stats_get_handle == NULL)
5039         internal_stats_cont (core_context, GNUNET_NO);
5040
5041     }
5042 }
5043
5044 struct DuplicateStats
5045 {
5046   /**
5047    * Next item in the list
5048    */
5049   struct DuplicateStats *next;
5050
5051   /**
5052    * Nasty string, concatenation of relevant information.
5053    */
5054   char *unique_string;
5055 };
5056
5057 /**
5058  * Check whether the combination of port/host/unix domain socket
5059  * already exists in the list of peers being checked for statistics.
5060  *
5061  * @param pg the peergroup in question
5062  * @param specific_peer the peer we're concerned with
5063  * @param stats_list the list to return to the caller
5064  *
5065  * @return GNUNET_YES if the statistics instance has been seen already,
5066  *         GNUNET_NO if not (and we may have added it to the list)
5067  */
5068 static int
5069 stats_check_existing(struct GNUNET_TESTING_PeerGroup *pg,
5070                      struct PeerData *specific_peer,
5071                      struct DuplicateStats **stats_list)
5072 {
5073   struct DuplicateStats *pos;
5074   char *unix_domain_socket;
5075   unsigned long long port;
5076   char *to_match;
5077   if (GNUNET_YES
5078       != GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "testing",
5079                                                "single_statistics_per_host"))
5080     return GNUNET_NO; /* Each peer has its own statistics instance, do nothing! */
5081
5082   pos = *stats_list;
5083   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (specific_peer->cfg,
5084                                                           "statistics",
5085                                                           "unixpath",
5086                                                           &unix_domain_socket))
5087     return GNUNET_NO;
5088
5089   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (specific_peer->cfg,
5090                                                           "statistics", "port",
5091                                                           &port))
5092     {
5093       GNUNET_free(unix_domain_socket);
5094       return GNUNET_NO;
5095     }
5096
5097   if (specific_peer->daemon->hostname != NULL)
5098     GNUNET_asprintf (&to_match, "%s%s%llu", specific_peer->daemon->hostname,
5099                      unix_domain_socket, port);
5100   else
5101     GNUNET_asprintf (&to_match, "%s%llu", unix_domain_socket, port);
5102
5103   while (pos != NULL)
5104     {
5105       if (0 == strcmp (to_match, pos->unique_string))
5106         {
5107           GNUNET_free (unix_domain_socket);
5108           GNUNET_free (to_match);
5109           return GNUNET_YES;
5110         }
5111       pos = pos->next;
5112     }
5113   pos = GNUNET_malloc (sizeof (struct DuplicateStats));
5114   pos->unique_string = to_match;
5115   pos->next = *stats_list;
5116   *stats_list = pos;
5117   GNUNET_free (unix_domain_socket);
5118   return GNUNET_NO;
5119 }
5120
5121 /**
5122  * Iterate over all (running) peers in the peer group, retrieve
5123  * all statistics from each.
5124  *
5125  * @param pg the peergroup to iterate statistics of
5126  * @param cont continuation to call once all stats have been retrieved
5127  * @param proc processing function for each statistic from each peer
5128  * @param cls closure to pass to proc
5129  *
5130  */
5131 void
5132 GNUNET_TESTING_get_statistics(struct GNUNET_TESTING_PeerGroup *pg,
5133                               GNUNET_STATISTICS_Callback cont,
5134                               GNUNET_TESTING_STATISTICS_Iterator proc,
5135                               void *cls)
5136 {
5137   struct StatsIterateContext *stats_context;
5138   struct StatsCoreContext *core_ctx;
5139   unsigned int i;
5140   unsigned int total_count;
5141   struct DuplicateStats *stats_list;
5142   struct DuplicateStats *pos;
5143   stats_list = NULL;
5144
5145   /* Allocate a single stats iteration context */
5146   stats_context = GNUNET_malloc (sizeof (struct StatsIterateContext));
5147   stats_context->cont = cont;
5148   stats_context->proc = proc;
5149   stats_context->cls = cls;
5150   stats_context->pg = pg;
5151   total_count = 0;
5152
5153   for (i = 0; i < pg->total; i++)
5154     {
5155       if ((pg->peers[i].daemon->running == GNUNET_YES) && (GNUNET_NO
5156           == stats_check_existing (pg, &pg->peers[i], &stats_list)))
5157         {
5158           /* Allocate one core context per core we need to connect to */
5159           core_ctx = GNUNET_malloc (sizeof (struct StatsCoreContext));
5160           core_ctx->daemon = pg->peers[i].daemon;
5161           /* Set back pointer to topology iteration context */
5162           core_ctx->iter_context = stats_context;
5163           GNUNET_SCHEDULER_add_now (&schedule_get_statistics, core_ctx);
5164           total_count++;
5165         }
5166     }
5167
5168   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5169               "Retrieving stats from %u total instances.\n", total_count);
5170   stats_context->total = total_count;
5171   if (stats_list != NULL)
5172     {
5173       pos = stats_list;
5174       while (pos != NULL)
5175         {
5176           GNUNET_free (pos->unique_string);
5177           stats_list = pos->next;
5178           GNUNET_free (pos);
5179           pos = stats_list->next;
5180         }
5181     }
5182   return;
5183 }
5184
5185 /**
5186  * Stop the connection process temporarily.
5187  *
5188  * @param pg the peer group to stop connecting
5189  */
5190 void
5191 GNUNET_TESTING_stop_connections(struct GNUNET_TESTING_PeerGroup *pg)
5192 {
5193   pg->stop_connects = GNUNET_YES;
5194 }
5195
5196 /**
5197  * Resume the connection process temporarily.
5198  *
5199  * @param pg the peer group to resume connecting
5200  */
5201 void
5202 GNUNET_TESTING_resume_connections(struct GNUNET_TESTING_PeerGroup *pg)
5203 {
5204   pg->stop_connects = GNUNET_NO;
5205 }
5206
5207 /**
5208  * There are many ways to connect peers that are supported by this function.
5209  * To connect peers in the same topology that was created via the
5210  * GNUNET_TESTING_create_topology, the topology variable must be set to
5211  * GNUNET_TESTING_TOPOLOGY_NONE.  If the topology variable is specified,
5212  * a new instance of that topology will be generated and attempted to be
5213  * connected.  This could result in some connections being impossible,
5214  * because some topologies are non-deterministic.
5215  *
5216  * @param pg the peer group struct representing the running peers
5217  * @param topology which topology to connect the peers in
5218  * @param options options for connecting the topology
5219  * @param option_modifier modifier for options that take a parameter
5220  * @param connect_timeout how long to wait before giving up on connecting
5221  *                        two peers
5222  * @param connect_attempts how many times to attempt to connect two peers
5223  *                         over the connect_timeout duration
5224  * @param notify_callback notification to be called once all connections completed
5225  * @param notify_cls closure for notification callback
5226  *
5227  * @return the number of connections that will be attempted, GNUNET_SYSERR on error
5228  */
5229 int
5230 GNUNET_TESTING_connect_topology(
5231                                 struct GNUNET_TESTING_PeerGroup *pg,
5232                                 enum GNUNET_TESTING_Topology topology,
5233                                 enum GNUNET_TESTING_TopologyOption options,
5234                                 double option_modifier,
5235                                 struct GNUNET_TIME_Relative connect_timeout,
5236                                 unsigned int connect_attempts,
5237                                 GNUNET_TESTING_NotifyCompletion notify_callback,
5238                                 void *notify_cls)
5239 {
5240   switch (topology)
5241     {
5242   case GNUNET_TESTING_TOPOLOGY_CLIQUE:
5243 #if VERBOSE_TOPOLOGY
5244     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5245                 _("Creating clique CONNECT topology\n"));
5246 #endif
5247     create_clique (pg, &add_connections, CONNECT, GNUNET_NO);
5248     break;
5249   case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
5250 #if VERBOSE_TOPOLOGY
5251     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5252                 _("Creating small world (ring) CONNECT topology\n"));
5253 #endif
5254     create_small_world_ring (pg, &add_connections, CONNECT);
5255     break;
5256   case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
5257 #if VERBOSE_TOPOLOGY
5258     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5259                 _("Creating small world (2d-torus) CONNECT topology\n"));
5260 #endif
5261     create_small_world (pg, &add_connections, CONNECT);
5262     break;
5263   case GNUNET_TESTING_TOPOLOGY_RING:
5264 #if VERBOSE_TOPOLOGY
5265     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating ring CONNECT topology\n"));
5266 #endif
5267     create_ring (pg, &add_connections, CONNECT);
5268     break;
5269   case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
5270 #if VERBOSE_TOPOLOGY
5271     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5272                 _("Creating 2d torus CONNECT topology\n"));
5273 #endif
5274     create_2d_torus (pg, &add_connections, CONNECT);
5275     break;
5276   case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
5277 #if VERBOSE_TOPOLOGY
5278     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5279                 _("Creating Erdos-Renyi CONNECT topology\n"));
5280 #endif
5281     create_erdos_renyi (pg, &add_connections, CONNECT);
5282     break;
5283   case GNUNET_TESTING_TOPOLOGY_INTERNAT:
5284 #if VERBOSE_TOPOLOGY
5285     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5286                 _("Creating InterNAT CONNECT topology\n"));
5287 #endif
5288     create_nated_internet (pg, &add_connections, CONNECT);
5289     break;
5290   case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
5291 #if VERBOSE_TOPOLOGY
5292     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5293                 _("Creating Scale Free CONNECT topology\n"));
5294 #endif
5295     create_scale_free (pg, &add_connections, CONNECT);
5296     break;
5297   case GNUNET_TESTING_TOPOLOGY_LINE:
5298 #if VERBOSE_TOPOLOGY
5299     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5300                 _("Creating straight line CONNECT topology\n"));
5301 #endif
5302     create_line (pg, &add_connections, CONNECT);
5303     break;
5304   case GNUNET_TESTING_TOPOLOGY_NONE:
5305 #if VERBOSE_TOPOLOGY
5306     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating no CONNECT topology\n"));
5307 #endif
5308     copy_allowed_topology (pg);
5309     break;
5310   default:
5311     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _
5312     ("Unknown topology specification, can't connect peers!\n"));
5313     return GNUNET_SYSERR;
5314     }
5315
5316   switch (options)
5317     {
5318   case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM:
5319 #if VERBOSE_TOPOLOGY
5320     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _
5321     ("Connecting random subset (%'.2f percent) of possible peers\n"), 100
5322         * option_modifier);
5323 #endif
5324     choose_random_connections (pg, option_modifier);
5325     break;
5326   case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM:
5327 #if VERBOSE_TOPOLOGY
5328     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5329                 _("Connecting a minimum of %u peers each (if possible)\n"),
5330                 (unsigned int) option_modifier);
5331 #endif
5332     choose_minimum (pg, (unsigned int) option_modifier);
5333     break;
5334   case GNUNET_TESTING_TOPOLOGY_OPTION_DFS:
5335 #if VERBOSE_TOPOLOGY
5336     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _
5337     ("Using DFS to connect a minimum of %u peers each (if possible)\n"),
5338                 (unsigned int) option_modifier);
5339 #endif
5340 #if FIXME
5341     perform_dfs (pg, (int) option_modifier);
5342 #endif
5343     break;
5344   case GNUNET_TESTING_TOPOLOGY_OPTION_ADD_CLOSEST:
5345 #if VERBOSE_TOPOLOGY
5346     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _
5347     ("Finding additional %u closest peers each (if possible)\n"),
5348                 (unsigned int) option_modifier);
5349 #endif
5350 #if FIXME
5351     add_closest (pg, (unsigned int) option_modifier,
5352         &add_connections, CONNECT);
5353 #endif
5354     break;
5355   case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
5356     break;
5357   case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
5358     break;
5359   default:
5360     break;
5361     }
5362
5363   return connect_topology (pg, connect_timeout, connect_attempts,
5364                            notify_callback, notify_cls);
5365 }
5366
5367 /**
5368  * Lookup and return the number of SSH connections to a host.
5369  *
5370  * @param hostname the hostname to lookup in the list
5371  * @param pg the peergroup that the host belongs to
5372  *
5373  * @return the number of current ssh connections to the host
5374  */
5375 static unsigned int
5376 count_outstanding_at_host(const char *hostname,
5377                           struct GNUNET_TESTING_PeerGroup *pg)
5378 {
5379   struct OutstandingSSH *pos;
5380   pos = pg->ssh_head;
5381   while ((pos != NULL) && (strcmp (pos->hostname, hostname) != 0))
5382     pos = pos->next;
5383   GNUNET_assert(pos != NULL);
5384   return pos->outstanding;
5385 }
5386
5387 /**
5388  * Increment the number of SSH connections to a host by one.
5389  *
5390  * @param hostname the hostname to lookup in the list
5391  * @param pg the peergroup that the host belongs to
5392  *
5393  */
5394 static void
5395 increment_outstanding_at_host(const char *hostname,
5396                               struct GNUNET_TESTING_PeerGroup *pg)
5397 {
5398   struct OutstandingSSH *pos;
5399   pos = pg->ssh_head;
5400   while ((pos != NULL) && (strcmp (pos->hostname, hostname) != 0))
5401     pos = pos->next;
5402   GNUNET_assert(pos != NULL);
5403   pos->outstanding++;
5404 }
5405
5406 /**
5407  * Decrement the number of SSH connections to a host by one.
5408  *
5409  * @param hostname the hostname to lookup in the list
5410  * @param pg the peergroup that the host belongs to
5411  *
5412  */
5413 static void
5414 decrement_outstanding_at_host(const char *hostname,
5415                               struct GNUNET_TESTING_PeerGroup *pg)
5416 {
5417   struct OutstandingSSH *pos;
5418   pos = pg->ssh_head;
5419   while ((pos != NULL) && (strcmp (pos->hostname, hostname) != 0))
5420     pos = pos->next;
5421   GNUNET_assert(pos != NULL);
5422   pos->outstanding--;
5423 }
5424
5425 /**
5426  * Callback that is called whenever a hostkey is generated
5427  * for a peer.  Call the real callback and decrement the
5428  * starting counter for the peergroup.
5429  *
5430  * @param cls closure
5431  * @param id identifier for the daemon, NULL on error
5432  * @param d handle for the daemon
5433  * @param emsg error message (NULL on success)
5434  */
5435 static void
5436 internal_hostkey_callback(void *cls, const struct GNUNET_PeerIdentity *id,
5437                           struct GNUNET_TESTING_Daemon *d, const char *emsg)
5438 {
5439   struct InternalStartContext *internal_context = cls;
5440   internal_context->peer->pg->starting--;
5441   internal_context->peer->pg->started++;
5442   if (internal_context->hostname != NULL)
5443     decrement_outstanding_at_host (internal_context->hostname,
5444                                    internal_context->peer->pg);
5445   if (internal_context->hostkey_callback != NULL)
5446     internal_context->hostkey_callback (internal_context->hostkey_cls, id, d,
5447                                         emsg);
5448   else if (internal_context->peer->pg->started
5449       == internal_context->peer->pg->total)
5450     {
5451       internal_context->peer->pg->started = 0; /* Internal startup may use this counter! */
5452       GNUNET_TESTING_daemons_continue_startup (internal_context->peer->pg);
5453     }
5454 }
5455
5456 /**
5457  * Callback that is called whenever a peer has finished starting.
5458  * Call the real callback and decrement the starting counter
5459  * for the peergroup.
5460  *
5461  * @param cls closure
5462  * @param id identifier for the daemon, NULL on error
5463  * @param cfg config
5464  * @param d handle for the daemon
5465  * @param emsg error message (NULL on success)
5466  */
5467 static void
5468 internal_startup_callback(void *cls, const struct GNUNET_PeerIdentity *id,
5469                           const struct GNUNET_CONFIGURATION_Handle *cfg,
5470                           struct GNUNET_TESTING_Daemon *d, const char *emsg)
5471 {
5472   struct InternalStartContext *internal_context = cls;
5473   internal_context->peer->pg->starting--;
5474   if (internal_context->hostname != NULL)
5475     decrement_outstanding_at_host (internal_context->hostname,
5476                                    internal_context->peer->pg);
5477   if (internal_context->start_cb != NULL)
5478     internal_context->start_cb (internal_context->start_cb_cls, id, cfg, d,
5479                                 emsg);
5480 }
5481
5482 static void
5483 internal_continue_startup(void *cls,
5484                           const struct GNUNET_SCHEDULER_TaskContext *tc)
5485 {
5486   struct InternalStartContext *internal_context = cls;
5487
5488   if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
5489     {
5490       return;
5491     }
5492
5493   if ((internal_context->peer->pg->starting
5494       < internal_context->peer->pg->max_concurrent_ssh)
5495       || ((internal_context->hostname != NULL)
5496           && (count_outstanding_at_host (internal_context->hostname,
5497                                          internal_context->peer->pg)
5498               < internal_context->peer->pg->max_concurrent_ssh)))
5499     {
5500       if (internal_context->hostname != NULL)
5501         increment_outstanding_at_host (internal_context->hostname,
5502                                        internal_context->peer->pg);
5503       internal_context->peer->pg->starting++;
5504       GNUNET_TESTING_daemon_continue_startup (internal_context->peer->daemon);
5505     }
5506   else
5507     {
5508       GNUNET_SCHEDULER_add_delayed (
5509                                     GNUNET_TIME_relative_multiply (
5510                                                                    GNUNET_TIME_UNIT_MILLISECONDS,
5511                                                                    100),
5512                                     &internal_continue_startup,
5513                                     internal_context);
5514     }
5515 }
5516
5517 /**
5518  * Callback for informing us about a successful
5519  * or unsuccessful churn start call.
5520  *
5521  * @param cls a ChurnContext
5522  * @param id the peer identity of the started peer
5523  * @param cfg the handle to the configuration of the peer
5524  * @param d handle to the daemon for the peer
5525  * @param emsg NULL on success, non-NULL on failure
5526  *
5527  */
5528 void
5529 churn_start_callback(void *cls, const struct GNUNET_PeerIdentity *id,
5530                      const struct GNUNET_CONFIGURATION_Handle *cfg,
5531                      struct GNUNET_TESTING_Daemon *d, const char *emsg)
5532 {
5533   struct ChurnRestartContext *startup_ctx = cls;
5534   struct ChurnContext *churn_ctx = startup_ctx->churn_ctx;
5535
5536   unsigned int total_left;
5537   char *error_message;
5538
5539   error_message = NULL;
5540   if (emsg != NULL)
5541     {
5542       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5543                   "Churn stop callback failed with error `%s'\n", emsg);
5544       churn_ctx->num_failed_start++;
5545     }
5546   else
5547     {
5548       churn_ctx->num_to_start--;
5549     }
5550
5551 #if DEBUG_CHURN
5552   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5553       "Started peer, %d left.\n", churn_ctx->num_to_start);
5554 #endif
5555
5556   total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop)
5557       + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
5558
5559   if (total_left == 0)
5560     {
5561       if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
5562         GNUNET_asprintf (
5563                          &error_message,
5564                          "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
5565                          churn_ctx->num_failed_start,
5566                          churn_ctx->num_failed_stop);
5567       churn_ctx->cb (churn_ctx->cb_cls, error_message);
5568       GNUNET_free_non_null (error_message);
5569       GNUNET_free (churn_ctx);
5570       GNUNET_free (startup_ctx);
5571     }
5572 }
5573
5574 static void
5575 schedule_churn_restart(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5576 {
5577   struct PeerRestartContext *peer_restart_ctx = cls;
5578   struct ChurnRestartContext *startup_ctx = peer_restart_ctx->churn_restart_ctx;
5579
5580   if (startup_ctx->outstanding > startup_ctx->pg->max_concurrent_ssh)
5581     GNUNET_SCHEDULER_add_delayed (
5582                                   GNUNET_TIME_relative_multiply (
5583                                                                  GNUNET_TIME_UNIT_MILLISECONDS,
5584                                                                  100),
5585                                   &schedule_churn_restart, peer_restart_ctx);
5586   else
5587     {
5588       if (startup_ctx->churn_ctx->service != NULL)
5589         GNUNET_TESTING_daemon_start_stopped_service (peer_restart_ctx->daemon,
5590                                                      startup_ctx->churn_ctx->service,
5591                                                      startup_ctx->timeout,
5592                                                      &churn_start_callback, startup_ctx);
5593       else
5594         GNUNET_TESTING_daemon_start_stopped (peer_restart_ctx->daemon,
5595                                              startup_ctx->timeout,
5596                                              &churn_start_callback, startup_ctx);
5597       GNUNET_free (peer_restart_ctx);
5598     }
5599 }
5600
5601
5602 static void
5603 internal_start(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5604 {
5605   struct InternalStartContext *internal_context = cls;
5606
5607   if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
5608     {
5609       return;
5610     }
5611
5612   if ((internal_context->peer->pg->starting
5613       < internal_context->peer->pg->max_concurrent_ssh)
5614       || ((internal_context->hostname != NULL)
5615           && (count_outstanding_at_host (internal_context->hostname,
5616                                          internal_context->peer->pg)
5617               < internal_context->peer->pg->max_concurrent_ssh)))
5618     {
5619       if (internal_context->hostname != NULL)
5620         increment_outstanding_at_host (internal_context->hostname,
5621                                        internal_context->peer->pg);
5622       internal_context->peer->pg->starting++;
5623       internal_context->peer->daemon
5624           = GNUNET_TESTING_daemon_start (internal_context->peer->cfg,
5625                                          internal_context->timeout,
5626                                          GNUNET_NO,
5627                                          internal_context->hostname,
5628                                          internal_context->username,
5629                                          internal_context->sshport,
5630                                          internal_context->hostkey,
5631                                          &internal_hostkey_callback,
5632                                          internal_context,
5633                                          &internal_startup_callback,
5634                                          internal_context);
5635     }
5636   else
5637     {
5638       GNUNET_SCHEDULER_add_delayed (
5639                                     GNUNET_TIME_relative_multiply (
5640                                                                    GNUNET_TIME_UNIT_MILLISECONDS,
5641                                                                    100),
5642                                     &internal_start, internal_context);
5643     }
5644 }
5645 #if USE_START_HELPER
5646
5647 struct PeerStartHelperContext
5648 {
5649   struct GNUNET_TESTING_PeerGroup *pg;
5650
5651   struct HostData *host;
5652
5653   struct GNUNET_OS_Process *proc;
5654 };
5655
5656 static void
5657 check_peers_started (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5658 {
5659   struct PeerStartHelperContext *helper = cls;
5660   enum GNUNET_OS_ProcessStatusType type;
5661   unsigned long code;
5662   unsigned int i;
5663   GNUNET_TESTING_NotifyDaemonRunning cb;
5664
5665   if (GNUNET_NO == GNUNET_OS_process_status (helper->proc, &type, &code)) /* Still running, wait some more! */
5666   {
5667     GNUNET_SCHEDULER_add_delayed(GNUNET_CONSTANTS_EXEC_WAIT, &check_peers_started, helper);
5668     return;
5669   }
5670
5671   helper->pg->starting--;
5672   if (helper->pg->starting == 0) /* All peers have finished starting! */
5673     {
5674       /* Call the peer started callback for each peer, set proper FSM state (?) */
5675       for (i = 0; i < helper->pg->total; i++)
5676         {
5677           cb = helper->pg->peers[i].daemon->cb;
5678           helper->pg->peers[i].daemon->cb = NULL;
5679           helper->pg->peers[i].daemon->running = GNUNET_YES;
5680           helper->pg->peers[i].daemon->phase = SP_START_DONE;
5681           if (NULL != cb)
5682           {
5683             if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
5684               cb (helper->pg->peers[i].daemon->cb_cls,
5685                   &helper->pg->peers[i].daemon->id,
5686                   helper->pg->peers[i].daemon->cfg, helper->pg->peers[i].daemon,
5687                   "Failed to execute peerStartHelper.pl, or return code bad!");
5688             else
5689               cb (helper->pg->peers[i].daemon->cb_cls,
5690                   &helper->pg->peers[i].daemon->id,
5691                   helper->pg->peers[i].daemon->cfg, helper->pg->peers[i].daemon,
5692                   NULL);
5693
5694           }
5695
5696         }
5697     }
5698   GNUNET_OS_process_close(helper->proc);
5699 }
5700
5701 static void
5702 start_peer_helper (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5703 {
5704   struct PeerStartHelperContext *helper = cls;
5705   char *baseservicehome;
5706   char *tempdir;
5707   char *arg;
5708   /* ssh user@host peerStartHelper /path/to/basedirectory */
5709   GNUNET_assert(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (helper->pg->cfg, "PATHS", "SERVICEHOME",
5710                                                                     &baseservicehome));
5711   GNUNET_asprintf(&tempdir, "%s/%s/", baseservicehome, helper->host->hostname);
5712   if (NULL != helper->host->username)
5713     GNUNET_asprintf (&arg, "%s@%s", helper->host->username, helper->host->hostname);
5714   else
5715     GNUNET_asprintf (&arg, "%s", helper->host->hostname);
5716
5717   /* FIXME: Doesn't support ssh_port option! */
5718   helper->proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh", arg,
5719                                   "peerStartHelper.pl", tempdir,  NULL);
5720   GNUNET_assert(helper->proc != NULL);
5721   GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "starting peers with cmd ssh %s %s %s\n", arg, "peerStartHelper.pl", tempdir);
5722   GNUNET_SCHEDULER_add_now (&check_peers_started, helper);
5723   GNUNET_free (tempdir);
5724   GNUNET_free (baseservicehome);
5725   GNUNET_free (arg);
5726 }
5727 #endif
5728
5729 /**
5730  * Function which continues a peer group starting up
5731  * after successfully generating hostkeys for each peer.
5732  *
5733  * @param pg the peer group to continue starting
5734  *
5735  */
5736 void
5737 GNUNET_TESTING_daemons_continue_startup(struct GNUNET_TESTING_PeerGroup *pg)
5738 {
5739   unsigned int i;
5740
5741 #if USE_START_HELPER
5742   if ((pg->num_hosts > 0) && (pg->hostkey_data != NULL))
5743     {
5744       struct PeerStartHelperContext *helper;
5745       pg->starting = pg->num_hosts;
5746       for (i = 0; i < pg->num_hosts; i++)
5747         {
5748           helper = GNUNET_malloc(sizeof(struct PeerStartHelperContext));
5749           helper->pg = pg;
5750           helper->host = &pg->hosts[i];
5751           GNUNET_SCHEDULER_add_now(&start_peer_helper, helper);
5752         }
5753     }
5754   else
5755     {
5756       pg->starting = 0;
5757       for (i = 0; i < pg->total; i++)
5758         {
5759           GNUNET_SCHEDULER_add_now (&internal_continue_startup,
5760                                     &pg->peers[i].internal_context);
5761         }
5762     }
5763 #else
5764   pg->starting = 0;
5765   for (i = 0; i < pg->total; i++)
5766     {
5767       GNUNET_SCHEDULER_add_now (&internal_continue_startup,
5768                                 &pg->peers[i].internal_context);
5769     }
5770 #endif
5771 }
5772
5773 #if USE_START_HELPER
5774 static void
5775 call_hostkey_callbacks (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5776 {
5777   struct GNUNET_TESTING_PeerGroup *pg = cls;
5778   unsigned int i;
5779   for (i = 0; i < pg->total; i++)
5780     {
5781       if (pg->peers[i].internal_context.hostkey_callback != NULL)
5782         pg->peers[i].internal_context.hostkey_callback (pg->peers[i].internal_context.hostkey_cls,
5783                                                            &pg->peers[i].daemon->id,
5784                                                            pg->peers[i].daemon,
5785                                                            NULL);
5786     }
5787
5788   if (pg->peers[0].internal_context.hostkey_callback == NULL)
5789     GNUNET_TESTING_daemons_continue_startup (pg);
5790 }
5791 #endif
5792
5793 /**
5794  * Start count gnunet instances with the same set of transports and
5795  * applications.  The port numbers (any option called "PORT") will be
5796  * adjusted to ensure that no two peers running on the same system
5797  * have the same port(s) in their respective configurations.
5798  *
5799  * @param cfg configuration template to use
5800  * @param total number of daemons to start
5801  * @param max_concurrent_connections for testing, how many peers can
5802  *                                   we connect to simultaneously
5803  * @param max_concurrent_ssh when starting with ssh, how many ssh
5804  *        connections will we allow at once (based on remote hosts allowed!)
5805  * @param timeout total time allowed for peers to start
5806  * @param hostkey_callback function to call on each peers hostkey generation
5807  *        if NULL, peers will be started by this call, if non-null,
5808  *        GNUNET_TESTING_daemons_continue_startup must be called after
5809  *        successful hostkey generation
5810  * @param hostkey_cls closure for hostkey callback
5811  * @param cb function to call on each daemon that was started
5812  * @param cb_cls closure for cb
5813  * @param connect_callback function to call each time two hosts are connected
5814  * @param connect_callback_cls closure for connect_callback
5815  * @param hostnames linked list of host structs to use to start peers on
5816  *                  (NULL to run on localhost only)
5817  *
5818  * @return NULL on error, otherwise handle to control peer group
5819  */
5820 struct GNUNET_TESTING_PeerGroup *
5821 GNUNET_TESTING_daemons_start(const struct GNUNET_CONFIGURATION_Handle *cfg,
5822                              unsigned int total,
5823                              unsigned int max_concurrent_connections,
5824                              unsigned int max_concurrent_ssh,
5825                              struct GNUNET_TIME_Relative timeout,
5826                              GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback,
5827                              void *hostkey_cls,
5828                              GNUNET_TESTING_NotifyDaemonRunning cb,
5829                              void *cb_cls,
5830                              GNUNET_TESTING_NotifyConnection connect_callback,
5831                              void *connect_callback_cls,
5832                              const struct GNUNET_TESTING_Host *hostnames)
5833 {
5834   struct GNUNET_TESTING_PeerGroup *pg;
5835   const struct GNUNET_TESTING_Host *hostpos;
5836   const char *hostname;
5837   const char *username;
5838   char *baseservicehome;
5839   char *newservicehome;
5840   char *tmpdir;
5841   char *hostkeys_file;
5842   char *arg;
5843   char *ssh_port_str;
5844   struct GNUNET_DISK_FileHandle *fd;
5845   struct GNUNET_CONFIGURATION_Handle *pcfg;
5846   unsigned int off;
5847   struct OutstandingSSH *ssh_entry;
5848   unsigned int hostcnt;
5849   unsigned int i;
5850   uint16_t minport;
5851   uint16_t sshport;
5852   uint32_t upnum;
5853   uint32_t fdnum;
5854   uint64_t fs;
5855   uint64_t total_hostkeys;
5856   struct GNUNET_OS_Process *proc;
5857
5858   username = NULL;
5859   if (0 == total)
5860     {
5861       GNUNET_break (0);
5862       return NULL;
5863     }
5864
5865   upnum = 0;
5866   fdnum = 0;
5867   pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
5868   pg->cfg = cfg;
5869   pg->notify_connection = connect_callback;
5870   pg->notify_connection_cls = connect_callback_cls;
5871   pg->total = total;
5872   pg->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
5873   pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
5874   pg->max_outstanding_connections = max_concurrent_connections;
5875   pg->max_concurrent_ssh = max_concurrent_ssh;
5876   if (NULL != hostnames)
5877     {
5878       off = 0;
5879       hostpos = hostnames;
5880       while (hostpos != NULL)
5881         {
5882           hostpos = hostpos->next;
5883           off++;
5884         }
5885       pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
5886       off = 0;
5887
5888       hostpos = hostnames;
5889       while (hostpos != NULL)
5890         {
5891           pg->hosts[off].minport = LOW_PORT;
5892           pg->hosts[off].hostname = GNUNET_strdup (hostpos->hostname);
5893           if (hostpos->username != NULL)
5894             pg->hosts[off].username = GNUNET_strdup (hostpos->username);
5895           pg->hosts[off].sshport = hostpos->port;
5896           hostpos = hostpos->next;
5897           off++;
5898         }
5899
5900       if (off == 0)
5901         {
5902           pg->hosts = NULL;
5903         }
5904       hostcnt = off;
5905       minport = 0;
5906       pg->num_hosts = off;
5907     }
5908   else
5909     {
5910       hostcnt = 0;
5911       minport = LOW_PORT;
5912     }
5913
5914   /* Create the servicehome directory for each remote peer */
5915   GNUNET_assert(GNUNET_OK ==
5916                 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME",
5917                                                        &baseservicehome));
5918   for (i = 0; i < pg->num_hosts; i++)
5919     {
5920       ssh_entry = GNUNET_malloc(sizeof(struct OutstandingSSH));
5921       ssh_entry->hostname = pg->hosts[i].hostname; /* Don't free! */
5922       GNUNET_CONTAINER_DLL_insert(pg->ssh_head, pg->ssh_tail, ssh_entry);
5923       GNUNET_asprintf(&tmpdir, "%s/%s", baseservicehome, pg->hosts[i].hostname);
5924       if (NULL != pg->hosts[i].username)
5925         GNUNET_asprintf (&arg, "%s@%s", pg->hosts[i].username,
5926                          pg->hosts[i].hostname);
5927       else
5928         GNUNET_asprintf (&arg, "%s", pg->hosts[i].hostname);
5929       if (pg->hosts[i].sshport != 0)
5930         {
5931           GNUNET_asprintf (&ssh_port_str, "%d", pg->hosts[i].sshport);
5932           proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh", "-P",
5933                                           ssh_port_str,
5934 #if !DEBUG_TESTING
5935                                           "-q",
5936 #endif
5937                                           arg, "mkdir -p", tmpdir,
5938                                           NULL);
5939         }
5940       else
5941         proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh", arg,
5942                                         "mkdir -p", tmpdir, NULL);
5943       GNUNET_assert(proc != NULL);
5944       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5945                   "Creating remote dir with command ssh %s %s %s\n", arg,
5946                   " mkdir -p ", tmpdir);
5947       GNUNET_free(tmpdir);
5948       GNUNET_free(arg);
5949       GNUNET_OS_process_wait (proc);
5950       GNUNET_OS_process_close(proc);
5951     }
5952   GNUNET_free(baseservicehome);
5953   baseservicehome = NULL;
5954
5955   if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING",
5956                                                            "HOSTKEYSFILE",
5957                                                            &hostkeys_file))
5958     {
5959       if (GNUNET_YES != GNUNET_DISK_file_test (hostkeys_file))
5960         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
5961                     _("Could not read hostkeys file!\n"));
5962       else
5963         {
5964           /* Check hostkey file size, read entire thing into memory */
5965           fd = GNUNET_DISK_file_open (hostkeys_file,
5966                                       GNUNET_DISK_OPEN_READ,
5967                                       GNUNET_DISK_PERM_NONE);
5968           if (NULL == fd)
5969             {
5970               GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 
5971                                         "open", 
5972                                         hostkeys_file);
5973               GNUNET_free (hostkeys_file);
5974               for (i=0;i<pg->num_hosts;i++)
5975                 {
5976                   GNUNET_free (pg->hosts[i].hostname);
5977                   GNUNET_free_non_null (pg->hosts[i].username);
5978                 }
5979               GNUNET_free (pg->peers);
5980               GNUNET_free (pg->hosts);
5981               GNUNET_free (pg);
5982               return NULL;
5983             }
5984
5985           if (GNUNET_YES != GNUNET_DISK_file_size (hostkeys_file, &fs,
5986                                                    GNUNET_YES))
5987             fs = 0;
5988
5989           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5990                       "Found file size %llu for hostkeys, expect hostkeys to be size %d\n",
5991                       fs, HOSTKEYFILESIZE);
5992
5993           if (0 != (fs % HOSTKEYFILESIZE))
5994             {
5995               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5996                           "File size %llu seems incorrect for hostkeys...\n",
5997                           fs);
5998             }
5999           else
6000             {
6001               total_hostkeys = fs / HOSTKEYFILESIZE;
6002               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
6003                           "Will read %llu hostkeys from file\n",
6004                           total_hostkeys);
6005               pg->hostkey_data = GNUNET_malloc_large (fs);
6006               GNUNET_assert (fs == GNUNET_DISK_file_read (fd, pg->hostkey_data, fs));
6007             }
6008           GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fd));
6009         }
6010       GNUNET_free(hostkeys_file);
6011     }
6012
6013   for (off = 0; off < total; off++)
6014     {
6015       if (hostcnt > 0)
6016         {
6017           hostname = pg->hosts[off % hostcnt].hostname;
6018           username = pg->hosts[off % hostcnt].username;
6019           sshport = pg->hosts[off % hostcnt].sshport;
6020           pcfg = make_config (cfg, off, &pg->hosts[off % hostcnt].minport,
6021                               &upnum, hostname, &fdnum);
6022         }
6023       else
6024         {
6025           hostname = NULL;
6026           username = NULL;
6027           sshport = 0;
6028           pcfg = make_config (cfg, off, &minport, &upnum, hostname, &fdnum);
6029         }
6030
6031       if (NULL == pcfg)
6032         {
6033           GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 
6034                       _("Could not create configuration for peer number %u on `%s'!\n"),
6035                       off, 
6036                       hostname == NULL ? "localhost" : hostname);
6037           continue;
6038         }
6039
6040       if (GNUNET_YES
6041           == GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS",
6042                                                     "SERVICEHOME",
6043                                                     &baseservicehome))
6044         {
6045           if (hostname != NULL)
6046             GNUNET_asprintf (&newservicehome, "%s/%s/%d/", baseservicehome, hostname, off);
6047           else
6048             GNUNET_asprintf (&newservicehome, "%s/%d/", baseservicehome, off);
6049           GNUNET_free (baseservicehome);
6050           baseservicehome = NULL;
6051         }
6052       else
6053         {
6054           tmpdir = getenv ("TMPDIR");
6055           tmpdir = tmpdir ? tmpdir : "/tmp";
6056           if (hostname != NULL)
6057             GNUNET_asprintf (&newservicehome, "%s/%s/%s/%d/", tmpdir, hostname,
6058                              "gnunet-testing-test-test", off);
6059           else
6060             GNUNET_asprintf (&newservicehome, "%s/%s/%d/", tmpdir,
6061                              "gnunet-testing-test-test", off);
6062         }
6063       GNUNET_CONFIGURATION_set_value_string (pcfg, "PATHS", "SERVICEHOME",
6064                                              newservicehome);
6065       GNUNET_free (newservicehome);
6066       pg->peers[off].cfg = pcfg;
6067       pg->peers[off].pg = pg;
6068       pg->peers[off].internal_context.peer = &pg->peers[off];
6069       pg->peers[off].internal_context.timeout = timeout;
6070       pg->peers[off].internal_context.hostname = hostname;
6071       pg->peers[off].internal_context.username = username;
6072       pg->peers[off].internal_context.sshport = sshport;
6073       if (pg->hostkey_data != NULL)
6074         pg->peers[off].internal_context.hostkey = &pg->hostkey_data[off
6075             * HOSTKEYFILESIZE];
6076       pg->peers[off].internal_context.hostkey_callback = hostkey_callback;
6077       pg->peers[off].internal_context.hostkey_cls = hostkey_cls;
6078       pg->peers[off].internal_context.start_cb = cb;
6079       pg->peers[off].internal_context.start_cb_cls = cb_cls;
6080 #if !USE_START_HELPER
6081       GNUNET_SCHEDULER_add_now (&internal_start,
6082                                 &pg->peers[off].internal_context);
6083 #else
6084       if ((pg->hostkey_data != NULL) && (hostcnt > 0))
6085         {
6086           pg->peers[off].daemon
6087             = GNUNET_TESTING_daemon_start (pcfg,
6088                                            timeout,
6089                                            GNUNET_YES,
6090                                            hostname,
6091                                            username,
6092                                            sshport,
6093                                            pg->peers[off].internal_context.hostkey,
6094                                            &internal_hostkey_callback,
6095                                            &pg->peers[off].internal_context,
6096                                            &internal_startup_callback,
6097                                            &pg->peers[off].internal_context);
6098           /**
6099            * At this point, given that we had a hostkeyfile,
6100            * we can call the hostkey callback!
6101            * But first, we should copy (rsync) all of the configs
6102            * and hostkeys to the remote peers.  Then let topology
6103            * creation happen, then call the peer start helper processes,
6104            * then set pg->whatever_phase for each peer and let them
6105            * enter the fsm to get the HELLO's for peers and start connecting.
6106            */
6107         }
6108       else
6109         {
6110           GNUNET_SCHEDULER_add_now (&internal_start,
6111                                     &pg->peers[off].internal_context);
6112         }
6113
6114 #endif
6115     }
6116
6117 #if USE_START_HELPER /* Now the peergroup has been set up, hostkeys and configs written to files. */
6118   if ((pg->hostkey_data != NULL) && (hostcnt > 0))
6119     {
6120       for (off = 0; off < hostcnt; off++)
6121         {
6122           /* FIXME: grab baseservicehome! */
6123           GNUNET_asprintf (&newservicehome, 
6124                            "%s/%s/",
6125                            baseservicehome, 
6126                            pg->hosts[off].hostname);
6127
6128           if (NULL != username)
6129             GNUNET_asprintf (&arg, 
6130                              "%s@%s:%s/%s", 
6131                              username, 
6132                              pg->hosts[off].hostname, 
6133                              baseservicehome, 
6134                              pg->hosts[off].hostname);
6135           else
6136             GNUNET_asprintf (&arg, 
6137                              "%s:%s/%s", 
6138                              pg->hosts[off].hostname,
6139                              baseservicehome, 
6140                              pg->hosts[off].hostname);
6141           
6142           // FIXME: free--- GNUNET_free (baseservicehome);
6143           
6144           /* FIXME: Doesn't support ssh_port option! */
6145           proc = GNUNET_OS_start_process (NULL, NULL,
6146                                           "rsync",
6147                                           "rsync", "-r", newservicehome, arg, NULL);
6148
6149           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
6150                       "copying directory with command rsync -r %s %s\n", 
6151                       newservicehome, arg);
6152
6153           GNUNET_free (arg);
6154           if (NULL == proc)
6155             {
6156               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6157                           _("Could not start `%s' process to copy configuration directory.\n"),
6158                           "scp");
6159               GNUNET_assert(0);
6160             }
6161           GNUNET_OS_process_wait (proc);
6162           GNUNET_OS_process_close (proc);
6163         }
6164       /* Now all the configuration files and hostkeys are copied to the remote host.  Call the hostkey callback for each peer! */
6165       GNUNET_SCHEDULER_add_now (&call_hostkey_callbacks, pg);
6166     }
6167 #endif
6168   return pg;
6169 }
6170
6171 /*
6172  * Get a daemon by number, so callers don't have to do nasty
6173  * offsetting operation.
6174  */
6175 struct GNUNET_TESTING_Daemon *
6176 GNUNET_TESTING_daemon_get(struct GNUNET_TESTING_PeerGroup *pg,
6177                           unsigned int position)
6178 {
6179   if (position < pg->total)
6180     return pg->peers[position].daemon;
6181   return NULL;
6182 }
6183
6184 /*
6185  * Get a daemon by peer identity, so callers can
6186  * retrieve the daemon without knowing it's offset.
6187  *
6188  * @param pg the peer group to retrieve the daemon from
6189  * @param peer_id the peer identity of the daemon to retrieve
6190  *
6191  * @return the daemon on success, or NULL if no such peer identity is found
6192  */
6193 struct GNUNET_TESTING_Daemon *
6194 GNUNET_TESTING_daemon_get_by_id(struct GNUNET_TESTING_PeerGroup *pg,
6195                                 struct GNUNET_PeerIdentity *peer_id)
6196 {
6197   unsigned int i;
6198
6199   for (i = 0; i < pg->total; i++)
6200     {
6201       if (0 == memcmp (&pg->peers[i].daemon->id, peer_id,
6202                        sizeof(struct GNUNET_PeerIdentity)))
6203         return pg->peers[i].daemon;
6204     }
6205   return NULL;
6206 }
6207
6208 /**
6209  * Prototype of a function that will be called when a
6210  * particular operation was completed the testing library.
6211  *
6212  * @param cls closure (a struct RestartContext)
6213  * @param id id of the peer that was restarted
6214  * @param cfg handle to the configuration of the peer
6215  * @param d handle to the daemon that was restarted
6216  * @param emsg NULL on success
6217  */
6218 static void
6219 restart_callback(void *cls, const struct GNUNET_PeerIdentity *id,
6220                  const struct GNUNET_CONFIGURATION_Handle *cfg,
6221                  struct GNUNET_TESTING_Daemon *d, const char *emsg)
6222 {
6223   struct RestartContext *restart_context = cls;
6224
6225   if (emsg == NULL)
6226     {
6227       restart_context->peers_restarted++;
6228     }
6229   else
6230     {
6231       restart_context->peers_restart_failed++;
6232     }
6233
6234   if (restart_context->peers_restarted == restart_context->peer_group->total)
6235     {
6236       restart_context->callback (restart_context->callback_cls, NULL);
6237       GNUNET_free (restart_context);
6238     }
6239   else if (restart_context->peers_restart_failed
6240       + restart_context->peers_restarted == restart_context->peer_group->total)
6241     {
6242       restart_context->callback (restart_context->callback_cls,
6243                                  "Failed to restart peers!");
6244       GNUNET_free (restart_context);
6245     }
6246
6247 }
6248
6249 /**
6250  * Callback for informing us about a successful
6251  * or unsuccessful churn stop call.
6252  *
6253  * @param cls a ChurnContext
6254  * @param emsg NULL on success, non-NULL on failure
6255  *
6256  */
6257 static void
6258 churn_stop_callback(void *cls, const char *emsg)
6259 {
6260   struct ShutdownContext *shutdown_ctx = cls;
6261   struct ChurnContext *churn_ctx = shutdown_ctx->cb_cls;
6262   unsigned int total_left;
6263   char *error_message;
6264
6265   error_message = NULL;
6266   shutdown_ctx->outstanding--;
6267
6268   if (emsg != NULL)
6269     {
6270       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6271                   "Churn stop callback failed with error `%s'\n", emsg);
6272       churn_ctx->num_failed_stop++;
6273     }
6274   else
6275     {
6276       churn_ctx->num_to_stop--;
6277     }
6278
6279 #if DEBUG_CHURN
6280   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6281       "Stopped peer, %d left.\n", churn_ctx->num_to_stop);
6282 #endif
6283   total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop)
6284       + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
6285
6286   if (total_left == 0)
6287     {
6288       if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
6289         {
6290           GNUNET_asprintf (
6291                            &error_message,
6292                            "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
6293                            churn_ctx->num_failed_start,
6294                            churn_ctx->num_failed_stop);
6295         }
6296       churn_ctx->cb (churn_ctx->cb_cls, error_message);
6297       GNUNET_free_non_null (error_message);
6298       GNUNET_free (churn_ctx);
6299       GNUNET_free (shutdown_ctx);
6300     }
6301 }
6302
6303 /**
6304  * Count the number of running peers.
6305  *
6306  * @param pg handle for the peer group
6307  *
6308  * @return the number of currently running peers in the peer group
6309  */
6310 unsigned int
6311 GNUNET_TESTING_daemons_running(struct GNUNET_TESTING_PeerGroup *pg)
6312 {
6313   unsigned int i;
6314   unsigned int running = 0;
6315   for (i = 0; i < pg->total; i++)
6316     {
6317       if (pg->peers[i].daemon->running == GNUNET_YES)
6318         {
6319           GNUNET_assert (running != -1);
6320           running++;
6321         }
6322     }
6323   return running;
6324 }
6325
6326 /**
6327  * Task to rate limit the number of outstanding peer shutdown
6328  * requests.  This is necessary for making sure we don't do
6329  * too many ssh connections at once, but is generally nicer
6330  * to any system as well (graduated task starts, as opposed
6331  * to calling gnunet-arm N times all at once).
6332  */
6333 static void
6334 schedule_churn_shutdown_task(void *cls,
6335                              const struct GNUNET_SCHEDULER_TaskContext *tc)
6336 {
6337   struct PeerShutdownContext *peer_shutdown_ctx = cls;
6338   struct ShutdownContext *shutdown_ctx;
6339   struct ChurnContext *churn_ctx;
6340   GNUNET_assert (peer_shutdown_ctx != NULL);
6341   shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
6342   GNUNET_assert (shutdown_ctx != NULL);
6343   churn_ctx = (struct ChurnContext *) shutdown_ctx->cb_cls;
6344   if (shutdown_ctx->outstanding > churn_ctx->pg->max_concurrent_ssh)
6345     GNUNET_SCHEDULER_add_delayed (
6346                                   GNUNET_TIME_relative_multiply (
6347                                                                  GNUNET_TIME_UNIT_MILLISECONDS,
6348                                                                  100),
6349                                   &schedule_churn_shutdown_task,
6350                                   peer_shutdown_ctx);
6351   else
6352     {
6353       shutdown_ctx->outstanding++;
6354       if (churn_ctx->service != NULL)
6355         GNUNET_TESTING_daemon_stop_service (peer_shutdown_ctx->daemon,
6356                                             churn_ctx->service,
6357                                             shutdown_ctx->timeout, shutdown_ctx->cb,
6358                                             shutdown_ctx);
6359       else
6360         GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
6361                                     shutdown_ctx->timeout, shutdown_ctx->cb,
6362                                     shutdown_ctx, GNUNET_NO, GNUNET_YES);
6363       GNUNET_free (peer_shutdown_ctx);
6364     }
6365 }
6366
6367
6368 /**
6369  * Simulate churn by stopping some peers (and possibly
6370  * re-starting others if churn is called multiple times).  This
6371  * function can only be used to create leave-join churn (peers "never"
6372  * leave for good).  First "voff" random peers that are currently
6373  * online will be taken offline; then "von" random peers that are then
6374  * offline will be put back online.  No notifications will be
6375  * generated for any of these operations except for the callback upon
6376  * completion.
6377  *
6378  * @param pg handle for the peer group
6379  * @param service the service to churn off/on, NULL to churn peer
6380  * @param voff number of peers that should go offline
6381  * @param von number of peers that should come back online;
6382  *            must be zero on first call (since "testbed_start"
6383  *            always starts all of the peers)
6384  * @param timeout how long to wait for operations to finish before
6385  *        giving up
6386  * @param cb function to call at the end
6387  * @param cb_cls closure for cb
6388  */
6389 void
6390 GNUNET_TESTING_daemons_churn(struct GNUNET_TESTING_PeerGroup *pg,
6391                              char *service,
6392                              unsigned int voff, unsigned int von,
6393                              struct GNUNET_TIME_Relative timeout,
6394                              GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
6395 {
6396   struct ChurnContext *churn_ctx;
6397   struct ShutdownContext *shutdown_ctx;
6398   struct PeerShutdownContext *peer_shutdown_ctx;
6399   struct PeerRestartContext *peer_restart_ctx;
6400   struct ChurnRestartContext *churn_startup_ctx;
6401
6402   unsigned int running;
6403   unsigned int stopped;
6404   unsigned int total_running;
6405   unsigned int total_stopped;
6406   unsigned int i;
6407   unsigned int *running_arr;
6408   unsigned int *stopped_arr;
6409   unsigned int *running_permute;
6410   unsigned int *stopped_permute;
6411   char *pos;
6412
6413   shutdown_ctx = NULL;
6414   peer_shutdown_ctx = NULL;
6415   peer_restart_ctx = NULL;
6416   churn_startup_ctx = NULL;
6417
6418   running = 0;
6419   stopped = 0;
6420
6421   if ((von == 0) && (voff == 0)) /* No peers at all? */
6422     {
6423       cb (cb_cls, NULL);
6424       return;
6425     }
6426
6427   for (i = 0; i < pg->total; i++)
6428     {
6429       if (service == NULL)
6430         {
6431           if (pg->peers[i].daemon->running == GNUNET_YES)
6432             {
6433               GNUNET_assert (running != -1);
6434               running++;
6435             }
6436           else
6437             {
6438               GNUNET_assert (stopped != -1);
6439               stopped++;
6440             }
6441         }
6442       else
6443         {
6444           /* FIXME: make churned services a list! */
6445           pos = pg->peers[i].daemon->churned_services;
6446           /* FIXME: while (pos != NULL) */
6447           if (pos != NULL)
6448             {
6449               if (0 == strcasecmp(pos, service))
6450                 {
6451                   GNUNET_assert (stopped != -1);
6452                   stopped++;
6453                   break;
6454                 }
6455               /* FIXME: pos = pos->next; */
6456             }
6457           if (pos == NULL)
6458             {
6459               GNUNET_assert (running != -1);
6460               running++;
6461             }
6462         }
6463     }
6464
6465   if (voff > running)
6466     {
6467       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6468                   "Trying to stop more peers than are currently running!\n");
6469       cb (cb_cls, "Trying to stop more peers than are currently running!");
6470       return;
6471     }
6472
6473   if (von > stopped)
6474     {
6475       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6476                   "Trying to start more peers than are currently stopped!\n");
6477       cb (cb_cls, "Trying to start more peers than are currently stopped!");
6478       return;
6479     }
6480
6481   churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
6482
6483   running_arr = NULL;
6484   if (running > 0)
6485     running_arr = GNUNET_malloc (running * sizeof (unsigned int));
6486
6487   stopped_arr = NULL;
6488   if (stopped > 0)
6489     stopped_arr = GNUNET_malloc (stopped * sizeof (unsigned int));
6490
6491   running_permute = NULL;
6492   stopped_permute = NULL;
6493
6494   if (running > 0)
6495     running_permute = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
6496                                                     running);
6497   if (stopped > 0)
6498     stopped_permute = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
6499                                                     stopped);
6500
6501   total_running = running;
6502   total_stopped = stopped;
6503   running = 0;
6504   stopped = 0;
6505
6506   churn_ctx->num_to_start = von;
6507   churn_ctx->num_to_stop = voff;
6508   churn_ctx->cb = cb;
6509   churn_ctx->cb_cls = cb_cls;
6510   churn_ctx->pg = pg;
6511
6512   for (i = 0; i < pg->total; i++)
6513     {
6514       if (service == NULL)
6515         {
6516           if (pg->peers[i].daemon->running == GNUNET_YES)
6517             {
6518               GNUNET_assert ((running_arr != NULL) && (total_running > running));
6519               running_arr[running] = i;
6520               running++;
6521             }
6522           else
6523             {
6524               GNUNET_assert ((stopped_arr != NULL) && (total_stopped > stopped));
6525               stopped_arr[stopped] = i;
6526               stopped++;
6527             }
6528         }
6529       else
6530         {
6531           /* FIXME: make churned services a list! */
6532           pos = pg->peers[i].daemon->churned_services;
6533           /* FIXME: while (pos != NULL) */
6534           if (pos != NULL)
6535             {
6536               if (0 == strcasecmp(pos, service))
6537                 {
6538                   GNUNET_assert ((stopped_arr != NULL) && (total_stopped > stopped));
6539                   stopped_arr[stopped] = i;
6540                   stopped++;
6541                   break;
6542                 }
6543               /* FIXME: pos = pos->next; */
6544             }
6545           if (pos == NULL)
6546             {
6547               GNUNET_assert ((running_arr != NULL) && (total_running > running));
6548               running_arr[running] = i;
6549               running++;
6550             }
6551         }
6552     }
6553
6554   GNUNET_assert (running >= voff);
6555   if (voff > 0)
6556     {
6557       shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
6558       shutdown_ctx->cb = &churn_stop_callback;
6559       shutdown_ctx->cb_cls = churn_ctx;
6560       shutdown_ctx->total_peers = voff;
6561       shutdown_ctx->timeout = timeout;
6562     }
6563
6564   for (i = 0; i < voff; i++)
6565     {
6566 #if DEBUG_CHURN
6567       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Stopping peer %d!\n",
6568           running_permute[i]);
6569 #endif
6570       GNUNET_assert (running_arr != NULL);
6571       peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
6572       peer_shutdown_ctx->daemon
6573           = pg->peers[running_arr[running_permute[i]]].daemon;
6574       peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
6575       GNUNET_SCHEDULER_add_now (&schedule_churn_shutdown_task,
6576                                 peer_shutdown_ctx);
6577     }
6578
6579   GNUNET_assert (stopped >= von);
6580   if (von > 0)
6581     {
6582       churn_startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
6583       churn_startup_ctx->churn_ctx = churn_ctx;
6584       churn_startup_ctx->timeout = timeout;
6585       churn_startup_ctx->pg = pg;
6586     }
6587   for (i = 0; i < von; i++)
6588     {
6589 #if DEBUG_CHURN
6590       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Starting up peer %d!\n",
6591           stopped_permute[i]);
6592 #endif
6593       GNUNET_assert (stopped_arr != NULL);
6594       peer_restart_ctx = GNUNET_malloc (sizeof (struct PeerRestartContext));
6595       peer_restart_ctx->churn_restart_ctx = churn_startup_ctx;
6596       peer_restart_ctx->daemon
6597           = pg->peers[stopped_arr[stopped_permute[i]]].daemon;
6598       GNUNET_SCHEDULER_add_now (&schedule_churn_restart, peer_restart_ctx);
6599     }
6600
6601   GNUNET_free_non_null (running_arr);
6602   GNUNET_free_non_null (stopped_arr);
6603   GNUNET_free_non_null (running_permute);
6604   GNUNET_free_non_null (stopped_permute);
6605 }
6606
6607
6608 /**
6609  * Restart all peers in the given group.
6610  *
6611  * @param pg the handle to the peer group
6612  * @param callback function to call on completion (or failure)
6613  * @param callback_cls closure for the callback function
6614  */
6615 void
6616 GNUNET_TESTING_daemons_restart(struct GNUNET_TESTING_PeerGroup *pg,
6617                                GNUNET_TESTING_NotifyCompletion callback,
6618                                void *callback_cls)
6619 {
6620   struct RestartContext *restart_context;
6621   unsigned int off;
6622
6623   if (pg->total > 0)
6624     {
6625       restart_context = GNUNET_malloc (sizeof (struct RestartContext));
6626       restart_context->peer_group = pg;
6627       restart_context->peers_restarted = 0;
6628       restart_context->callback = callback;
6629       restart_context->callback_cls = callback_cls;
6630
6631       for (off = 0; off < pg->total; off++)
6632         {
6633           GNUNET_TESTING_daemon_restart (pg->peers[off].daemon,
6634                                          &restart_callback, restart_context);
6635         }
6636     }
6637 }
6638
6639
6640 /**
6641  * Start or stop an individual peer from the given group.
6642  *
6643  * @param pg handle to the peer group
6644  * @param offset which peer to start or stop
6645  * @param desired_status GNUNET_YES to have it running, GNUNET_NO to stop it
6646  * @param timeout how long to wait for shutdown
6647  * @param cb function to call at the end
6648  * @param cb_cls closure for cb
6649  */
6650 void
6651 GNUNET_TESTING_daemons_vary(struct GNUNET_TESTING_PeerGroup *pg,
6652                             unsigned int offset, int desired_status,
6653                             struct GNUNET_TIME_Relative timeout,
6654                             GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
6655 {
6656   struct ShutdownContext *shutdown_ctx;
6657   struct ChurnRestartContext *startup_ctx;
6658   struct ChurnContext *churn_ctx;
6659
6660   if (GNUNET_NO == desired_status)
6661     {
6662       if (NULL != pg->peers[offset].daemon)
6663         {
6664           shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
6665           churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
6666           churn_ctx->num_to_start = 0;
6667           churn_ctx->num_to_stop = 1;
6668           churn_ctx->cb = cb;
6669           churn_ctx->cb_cls = cb_cls;
6670           shutdown_ctx->cb_cls = churn_ctx;
6671           GNUNET_TESTING_daemon_stop (pg->peers[offset].daemon, timeout,
6672                                       &churn_stop_callback, shutdown_ctx,
6673                                       GNUNET_NO, GNUNET_YES);
6674         }
6675     }
6676   else if (GNUNET_YES == desired_status)
6677     {
6678       if (NULL == pg->peers[offset].daemon)
6679         {
6680           startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
6681           churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
6682           churn_ctx->num_to_start = 1;
6683           churn_ctx->num_to_stop = 0;
6684           churn_ctx->cb = cb;
6685           churn_ctx->cb_cls = cb_cls;
6686           startup_ctx->churn_ctx = churn_ctx;
6687           GNUNET_TESTING_daemon_start_stopped (pg->peers[offset].daemon,
6688                                                timeout, &churn_start_callback,
6689                                                startup_ctx);
6690         }
6691     }
6692   else
6693     GNUNET_break (0);
6694 }
6695
6696
6697 /**
6698  * Callback for shutting down peers in a peer group.
6699  *
6700  * @param cls closure (struct ShutdownContext)
6701  * @param emsg NULL on success
6702  */
6703 static void
6704 internal_shutdown_callback(void *cls, const char *emsg)
6705 {
6706   struct PeerShutdownContext *peer_shutdown_ctx = cls;
6707   struct ShutdownContext *shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
6708   unsigned int off;
6709   struct OutstandingSSH *ssh_pos;
6710
6711   shutdown_ctx->outstanding--;
6712   if (peer_shutdown_ctx->daemon->hostname != NULL)
6713     decrement_outstanding_at_host (peer_shutdown_ctx->daemon->hostname,
6714                                    shutdown_ctx->pg);
6715
6716   if (emsg == NULL)
6717     {
6718       shutdown_ctx->peers_down++;
6719     }
6720   else
6721     {
6722       shutdown_ctx->peers_failed++;
6723     }
6724
6725   if ((shutdown_ctx->cb != NULL) && (shutdown_ctx->peers_down
6726       + shutdown_ctx->peers_failed == shutdown_ctx->total_peers))
6727     {
6728       if (shutdown_ctx->peers_failed > 0)
6729         shutdown_ctx->cb (shutdown_ctx->cb_cls,
6730                           "Not all peers successfully shut down!");
6731       else
6732         shutdown_ctx->cb (shutdown_ctx->cb_cls, NULL);
6733
6734       GNUNET_free (shutdown_ctx->pg->peers);
6735       GNUNET_free_non_null(shutdown_ctx->pg->hostkey_data);
6736       for (off = 0; off < shutdown_ctx->pg->num_hosts; off++)
6737         {
6738           GNUNET_free (shutdown_ctx->pg->hosts[off].hostname);
6739           GNUNET_free_non_null (shutdown_ctx->pg->hosts[off].username);
6740         }
6741       GNUNET_free_non_null (shutdown_ctx->pg->hosts);
6742       while (NULL != (ssh_pos = shutdown_ctx->pg->ssh_head))
6743         {
6744           GNUNET_CONTAINER_DLL_remove(shutdown_ctx->pg->ssh_head, shutdown_ctx->pg->ssh_tail, ssh_pos);
6745           GNUNET_free(ssh_pos);
6746         }
6747       GNUNET_free (shutdown_ctx->pg);
6748       GNUNET_free (shutdown_ctx);
6749     }
6750   GNUNET_free(peer_shutdown_ctx);
6751 }
6752
6753
6754 /**
6755  * Task to rate limit the number of outstanding peer shutdown
6756  * requests.  This is necessary for making sure we don't do
6757  * too many ssh connections at once, but is generally nicer
6758  * to any system as well (graduated task starts, as opposed
6759  * to calling gnunet-arm N times all at once).
6760  */
6761 static void
6762 schedule_shutdown_task(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
6763 {
6764   struct PeerShutdownContext *peer_shutdown_ctx = cls;
6765   struct ShutdownContext *shutdown_ctx;
6766
6767   GNUNET_assert (peer_shutdown_ctx != NULL);
6768   shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
6769   GNUNET_assert (shutdown_ctx != NULL);
6770
6771   if ((shutdown_ctx->outstanding < shutdown_ctx->pg->max_concurrent_ssh)
6772       || ((peer_shutdown_ctx->daemon->hostname != NULL)
6773           && (count_outstanding_at_host (peer_shutdown_ctx->daemon->hostname,
6774                                          shutdown_ctx->pg)
6775               < shutdown_ctx->pg->max_concurrent_ssh)))
6776     {
6777       if (peer_shutdown_ctx->daemon->hostname != NULL)
6778         increment_outstanding_at_host (peer_shutdown_ctx->daemon->hostname,
6779                                        shutdown_ctx->pg);
6780       shutdown_ctx->outstanding++;
6781       GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
6782                                   shutdown_ctx->timeout,
6783                                   &internal_shutdown_callback, peer_shutdown_ctx,
6784                                   shutdown_ctx->delete_files, GNUNET_NO);
6785     }
6786   else
6787     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
6788                                                                  100),
6789                                   &schedule_shutdown_task, peer_shutdown_ctx);
6790
6791 }
6792
6793
6794 /**
6795  * Shutdown all peers started in the given group.
6796  *
6797  * @param pg handle to the peer group
6798  * @param timeout how long to wait for shutdown
6799  * @param cb callback to notify upon success or failure
6800  * @param cb_cls closure for cb
6801  */
6802 void
6803 GNUNET_TESTING_daemons_stop(struct GNUNET_TESTING_PeerGroup *pg,
6804                             struct GNUNET_TIME_Relative timeout,
6805                             GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
6806 {
6807   unsigned int off;
6808   struct ShutdownContext *shutdown_ctx;
6809   struct PeerShutdownContext *peer_shutdown_ctx;
6810 #if OLD
6811   struct PeerConnection *conn_iter;
6812   struct PeerConnection *temp_conn;
6813 #endif
6814
6815   GNUNET_assert (pg->total > 0);
6816
6817   shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
6818   shutdown_ctx->delete_files = GNUNET_CONFIGURATION_get_value_yesno (pg->cfg,
6819                                                                      "TESTING",
6820                                                                      "DELETE_FILES");
6821   shutdown_ctx->cb = cb;
6822   shutdown_ctx->cb_cls = cb_cls;
6823   shutdown_ctx->total_peers = pg->total;
6824   shutdown_ctx->timeout = timeout;
6825   shutdown_ctx->pg = pg;
6826   /* shtudown_ctx->outstanding = 0; */
6827
6828   for (off = 0; off < pg->total; off++)
6829     {
6830       GNUNET_assert (NULL != pg->peers[off].daemon);
6831       peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
6832       peer_shutdown_ctx->daemon = pg->peers[off].daemon;
6833       peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
6834       GNUNET_SCHEDULER_add_now (&schedule_shutdown_task, peer_shutdown_ctx);
6835
6836       if (NULL != pg->peers[off].cfg)
6837       {
6838         GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
6839         pg->peers[off].cfg = NULL;
6840       }
6841 #if OLD
6842       conn_iter = pg->peers[off].allowed_peers_head;
6843       while (conn_iter != NULL)
6844         {
6845           temp_conn = conn_iter->next;
6846           GNUNET_free(conn_iter);
6847           conn_iter = temp_conn;
6848         }
6849
6850       conn_iter = pg->peers[off].connect_peers_head;
6851       while (conn_iter != NULL)
6852         {
6853           temp_conn = conn_iter->next;
6854           GNUNET_free(conn_iter);
6855           conn_iter = temp_conn;
6856         }
6857
6858       conn_iter = pg->peers[off].blacklisted_peers_head;
6859       while (conn_iter != NULL)
6860         {
6861           temp_conn = conn_iter->next;
6862           GNUNET_free(conn_iter);
6863           conn_iter = temp_conn;
6864         }
6865
6866       conn_iter = pg->peers[off].connect_peers_working_set_head;
6867       while (conn_iter != NULL)
6868         {
6869           temp_conn = conn_iter->next;
6870           GNUNET_free(conn_iter);
6871           conn_iter = temp_conn;
6872         }
6873 #else
6874       if (pg->peers[off].allowed_peers != NULL)
6875         GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].allowed_peers);
6876       if (pg->peers[off].connect_peers != NULL)
6877         GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].connect_peers);
6878       if (pg->peers[off].blacklisted_peers != NULL)
6879         GNUNET_CONTAINER_multihashmap_destroy (pg->
6880                                                peers[off].blacklisted_peers);
6881 #endif
6882     }
6883 }
6884
6885 /* end of testing_group.c */