2 This file is part of GNUnet
3 (C) 2008, 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file testing/testing_group.c
23 * @brief convenience API for writing testcases for GNUnet
24 * @author Nathan Evans
25 * @author Christian Grothoff
29 #include "gnunet_constants.h"
30 #include "gnunet_arm_service.h"
31 #include "gnunet_testing_lib.h"
32 #include "gnunet_core_service.h"
34 #define VERBOSE_TESTING GNUNET_NO
36 #define VERBOSE_TOPOLOGY GNUNET_NO
38 #define DEBUG_CHURN GNUNET_NO
40 #define USE_START_HELPER GNUNET_YES
44 /* Before connecting peers, send all of the HELLOs */
45 #define USE_SEND_HELLOS GNUNET_NO
47 #define TOPOLOGY_HACK GNUNET_YES
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
56 #define LOW_PORT 12000
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).
63 #define HIGH_PORT 56000
65 /* Maximum time to delay connect attempt */
66 #define MAX_CONNECT_DELAY 300
69 * Which list of peers do we need to modify?
73 /** Modify allowed peers */
76 /** Modify connect peers */
79 /** Modify blacklist peers */
82 /** Modify workingset peers */
87 * Prototype of a function called whenever two peers would be connected
88 * in a certain topology.
91 (*GNUNET_TESTING_ConnectionProcessor)(struct GNUNET_TESTING_PeerGroup * pg,
92 unsigned int first, unsigned int second,
93 enum PeerLists list, unsigned int check);
96 * Context for handling churning a peer group
101 * The peergroup we are dealing with.
103 struct GNUNET_TESTING_PeerGroup *pg;
106 * Name of the service to churn on/off, NULL
107 * to churn entire peer.
112 * Callback used to notify of churning finished
114 GNUNET_TESTING_NotifyCompletion cb;
117 * Closure for callback
122 * Number of peers that still need to be started
124 unsigned int num_to_start;
127 * Number of peers that still need to be stopped
129 unsigned int num_to_stop;
132 * Number of peers that failed to start
134 unsigned int num_failed_start;
137 * Number of peers that failed to stop
139 unsigned int num_failed_stop;
142 struct RestartContext
145 * The group of peers being restarted
147 struct GNUNET_TESTING_PeerGroup *peer_group;
150 * How many peers have been restarted thus far
152 unsigned int peers_restarted;
155 * How many peers got an error when restarting
157 unsigned int peers_restart_failed;
160 * The function to call once all peers have been restarted
162 GNUNET_TESTING_NotifyCompletion callback;
165 * Closure for callback function
171 struct SendHelloContext
174 * Global handle to the peer group.
176 struct GNUNET_TESTING_PeerGroup *pg;
179 * The data about this specific peer.
181 struct PeerData *peer;
184 * The next HELLO that needs sent to this peer.
186 struct PeerConnection *peer_pos;
189 * Are we connected to CORE yet?
191 unsigned int core_ready;
194 * How many attempts should we make for failed connections?
196 unsigned int connect_attempts;
199 * Task for scheduling core connect requests to be sent.
201 GNUNET_SCHEDULER_TaskIdentifier core_connect_task;
204 struct ShutdownContext
206 struct GNUNET_TESTING_PeerGroup *pg;
208 * Total peers to wait for
210 unsigned int total_peers;
213 * Number of peers successfully shut down
215 unsigned int peers_down;
218 * Number of peers failed to shut down
220 unsigned int peers_failed;
223 * Number of peers we have started shutting
224 * down. If too many, wait on them.
226 unsigned int outstanding;
229 * Timeout for shutdown.
231 struct GNUNET_TIME_Relative timeout;
234 * Callback to call when all peers either
235 * shutdown or failed to shutdown
237 GNUNET_TESTING_NotifyCompletion cb;
245 * Should we delete all of the files from the peers?
251 * Individual shutdown context for a particular peer.
253 struct PeerShutdownContext
256 * Pointer to the high level shutdown context.
258 struct ShutdownContext *shutdown_ctx;
261 * The daemon handle for the peer to shut down.
263 struct GNUNET_TESTING_Daemon *daemon;
267 * Individual shutdown context for a particular peer.
269 struct PeerRestartContext
272 * Pointer to the high level restart context.
274 struct ChurnRestartContext *churn_restart_ctx;
277 * The daemon handle for the peer to shut down.
279 struct GNUNET_TESTING_Daemon *daemon;
282 struct CreateTopologyContext
286 * Function to call with number of connections
288 GNUNET_TESTING_NotifyConnections cont;
291 * Closure for connection notification
298 /** Waiting to read number of peers */
301 /** Should find next peer index */
304 /** Should find colon */
307 /** Should read other peer index, space, or endline */
312 struct PeerConnection
317 struct PeerConnection *prev;
322 struct PeerConnection *next;
325 * Index of daemon in pg->peers
332 struct InternalStartContext
335 * Pointer to peerdata
337 struct PeerData *peer;
340 * Timeout for peer startup
342 struct GNUNET_TIME_Relative timeout;
345 * Client callback for hostkey notification
347 GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback;
350 * Closure for hostkey_callback
355 * Client callback for peer start notification
357 GNUNET_TESTING_NotifyDaemonRunning start_cb;
365 * Hostname, where to start the peer
367 const char *hostname;
370 * Username to use when connecting to the
373 const char *username;
376 * Pointer to starting memory location of a hostkey
381 * Port to use for ssh.
387 struct ChurnRestartContext
390 * PeerGroup that we are working with.
392 struct GNUNET_TESTING_PeerGroup *pg;
395 * Number of restarts currently in flight.
397 unsigned int outstanding;
400 * Handle to the underlying churn context.
402 struct ChurnContext *churn_ctx;
405 * How long to allow the operation to take.
407 struct GNUNET_TIME_Relative timeout;
410 struct OutstandingSSH
412 struct OutstandingSSH *next;
414 struct OutstandingSSH *prev;
417 * Number of current ssh connections.
419 uint32_t outstanding;
422 * The hostname of this peer.
424 const char *hostname;
428 * Data we keep per peer.
433 * (Initial) configuration of the host.
434 * (initial because clients could change
435 * it and we would not know about those
438 struct GNUNET_CONFIGURATION_Handle *cfg;
441 * Handle for controlling the daemon.
443 struct GNUNET_TESTING_Daemon *daemon;
446 * The peergroup this peer belongs to.
448 struct GNUNET_TESTING_PeerGroup *pg;
452 * Linked list of allowed peer connections.
454 struct PeerConnection *allowed_peers_head;
457 * Linked list of allowed peer connections.
459 struct PeerConnection *allowed_peers_tail;
462 * Linked list of blacklisted peer connections.
464 struct PeerConnection *blacklisted_peers_head;
467 * Linked list of blacklisted peer connections.
469 struct PeerConnection *blacklisted_peers_tail;
472 * Linked list of connect peer connections.
474 struct PeerConnection *connect_peers_head;
477 * Linked list of connect peer connections.
479 struct PeerConnection *connect_peers_tail;
482 * Linked list of connect peer connections.
484 struct PeerConnection *connect_peers_working_set_head;
487 * Linked list of connect peer connections.
489 struct PeerConnection *connect_peers_working_set_tail;
493 * Hash map of allowed peer connections (F2F created topology)
495 struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
498 * Hash map of blacklisted peers
500 struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
503 * Hash map of peer connections
505 struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
508 * Temporary hash map of peer connections
510 struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
514 * Temporary variable for topology creation, should be reset before
515 * creating any topology so the count is valid once finished.
520 * Context to keep track of peers being started, to
521 * stagger hostkey generation and peer startup.
523 struct InternalStartContext internal_context;
527 * Linked list of per-host data.
537 * SSH username to use when connecting to this host.
542 * SSH port to use when connecting to this host.
547 * Lowest port that we have not yet used
553 struct TopologyIterateContext
556 * The peergroup we are working with.
558 struct GNUNET_TESTING_PeerGroup *pg;
561 * Callback for notifying of two connected peers.
563 GNUNET_TESTING_NotifyTopology topology_cb;
566 * Closure for topology_cb
571 * Number of peers currently connected to.
573 unsigned int connected;
576 * Number of peers we have finished iterating.
578 unsigned int completed;
581 * Number of peers total.
586 struct StatsIterateContext
589 * The peergroup that we are dealing with.
591 struct GNUNET_TESTING_PeerGroup *pg;
594 * Continuation to call once all stats information has been retrieved.
596 GNUNET_STATISTICS_Callback cont;
599 * Proc function to call on each value received.
601 GNUNET_TESTING_STATISTICS_Iterator proc;
604 * Closure for topology_cb
609 * Number of peers currently connected to.
611 unsigned int connected;
614 * Number of peers we have finished iterating.
616 unsigned int completed;
619 * Number of peers total.
627 struct GNUNET_TESTING_Daemon *daemon;
630 struct StatsCoreContext
633 struct GNUNET_TESTING_Daemon *daemon;
635 * Handle to the statistics service.
637 struct GNUNET_STATISTICS_Handle *stats_handle;
640 * Handle for getting statistics.
642 struct GNUNET_STATISTICS_GetHandle *stats_get_handle;
645 struct ConnectTopologyContext
648 * How many connections are left to create.
650 unsigned int remaining_connections;
653 * Handle to group of peers.
655 struct GNUNET_TESTING_PeerGroup *pg;
658 * How long to try this connection before timing out.
660 struct GNUNET_TIME_Relative connect_timeout;
663 * How many times to retry connecting the two peers.
665 unsigned int connect_attempts;
668 * Temp value set for each iteration.
670 //struct PeerData *first;
673 * Notification that all peers are connected.
675 GNUNET_TESTING_NotifyCompletion notify_connections_done;
678 * Closure for notify.
684 * Handle to a group of GNUnet peers.
686 struct GNUNET_TESTING_PeerGroup
689 * Configuration template.
691 const struct GNUNET_CONFIGURATION_Handle *cfg;
694 * Function to call on each started daemon.
696 //GNUNET_TESTING_NotifyDaemonRunning cb;
704 * Function to call on each topology connection created
706 GNUNET_TESTING_NotifyConnection notify_connection;
709 * Callback for notify_connection
711 void *notify_connection_cls;
714 * Array of information about hosts.
716 struct HostData *hosts;
719 * Number of hosts (size of HostData)
721 unsigned int num_hosts;
724 * Array of "total" peers.
726 struct PeerData *peers;
729 * Number of peers in this group.
734 * At what time should we fail the peer startup process?
736 struct GNUNET_TIME_Absolute max_timeout;
739 * How many peers are being started right now?
741 unsigned int starting;
744 * How many peers have already been started?
746 unsigned int started;
749 * Number of possible connections to peers
752 unsigned int max_outstanding_connections;
755 * Number of ssh connections to peers (max).
757 unsigned int max_concurrent_ssh;
760 * Number of connects we are waiting on, allows us to rate limit
763 unsigned int outstanding_connects;
766 * Number of HELLOs we have yet to send.
768 unsigned int remaining_hellos;
771 * How many connects have already been scheduled?
773 unsigned int total_connects_scheduled;
776 * Hostkeys loaded from a file.
781 * Head of DLL to keep track of the number of outstanding
782 * ssh connections per peer.
784 struct OutstandingSSH *ssh_head;
787 * Tail of DLL to keep track of the number of outstanding
788 * ssh connections per peer.
790 struct OutstandingSSH *ssh_tail;
793 * Stop scheduling peers connecting.
795 unsigned int stop_connects;
798 * Connection context for peer group.
800 struct ConnectTopologyContext ct_ctx;
806 * The altered configuration.
808 struct GNUNET_CONFIGURATION_Handle *ret;
811 * The original configuration to alter.
813 const struct GNUNET_CONFIGURATION_Handle *orig;
816 * The hostname that this peer will run on.
818 const char *hostname;
821 * The next possible port to assign.
826 * Unique number for unix domain sockets.
831 * Unique number for this peer/host to offset
832 * things that are grouped by host.
837 struct ConnectContext
840 * Index of peer to connect second to.
842 uint32_t first_index;
845 * Index of peer to connect first to.
847 uint32_t second_index;
850 * Higher level topology connection context.
852 struct ConnectTopologyContext *ct_ctx;
855 * Whether this connection has been accounted for in the schedule_connect call.
860 struct UnblacklistContext
865 struct GNUNET_TESTING_PeerGroup *pg;
868 * uid of the first peer
878 struct GNUNET_TESTING_PeerGroup *pg;
881 * uid of the first peer
886 * Peer data for first peer.
888 struct PeerData *first;
891 * Random percentage to use
896 struct MinimumContext
901 struct GNUNET_TESTING_PeerGroup *pg;
904 * uid of the first peer
909 * Peer data for first peer.
911 struct PeerData *first;
914 * Number of conns per peer
916 unsigned int num_to_add;
919 * Permuted array of all possible connections. Only add the Nth
920 * peer if it's in the Nth position.
922 unsigned int *pg_array;
925 * What number is the current element we are iterating over?
927 unsigned int current;
935 struct GNUNET_TESTING_PeerGroup *pg;
938 * uid of the first peer
943 * uid of the second peer
948 * Peer data for first peer.
950 struct PeerData *first;
953 * Which peer has been chosen as the one to add?
958 * What number is the current element we are iterating over?
960 unsigned int current;
964 * Simple struct to keep track of progress, and print a
965 * nice little percentage meter for long running tasks.
975 unsigned int completed;
979 char *startup_string;
984 * Convert unique ID to hash code.
986 * @param uid unique ID to convert
987 * @param hash set to uid (extended with zeros)
990 hash_from_uid (uint32_t uid, GNUNET_HashCode * hash)
992 memset (hash, 0, sizeof (GNUNET_HashCode));
993 *((uint32_t *) hash) = uid;
997 * Convert hash code to unique ID.
999 * @param uid unique ID to convert
1000 * @param hash set to uid (extended with zeros)
1003 uid_from_hash (const GNUNET_HashCode * hash, uint32_t * uid)
1005 memcpy (uid, hash, sizeof (uint32_t));
1010 static struct GNUNET_CORE_MessageHandler no_handlers[] =
1016 * Create a meter to keep track of the progress of some task.
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
1023 * @return the progress meter
1025 static struct ProgressMeter *
1026 create_meter(unsigned int total, char * start_string, int print)
1028 struct ProgressMeter *ret;
1029 ret = GNUNET_malloc(sizeof(struct ProgressMeter));
1032 ret->modnum = total / 4;
1033 if (ret->modnum == 0) /* Divide by zero check */
1035 ret->dotnum = (total / 50) + 1;
1036 if (start_string != NULL)
1037 ret->startup_string = GNUNET_strdup(start_string);
1039 ret->startup_string = GNUNET_strdup("");
1045 * Update progress meter (increment by one).
1047 * @param meter the meter to update and print info for
1049 * @return GNUNET_YES if called the total requested,
1050 * GNUNET_NO if more items expected
1053 update_meter(struct ProgressMeter *meter)
1055 if (meter->print == GNUNET_YES)
1057 if (meter->completed % meter->modnum == 0)
1059 if (meter->completed == 0)
1061 fprintf (stdout, "%sProgress: [0%%", meter->startup_string);
1064 fprintf (stdout, "%d%%", (int) (((float) meter->completed
1065 / meter->total) * 100));
1067 else if (meter->completed % meter->dotnum == 0)
1068 fprintf (stdout, ".");
1070 if (meter->completed + 1 == meter->total)
1071 fprintf (stdout, "%d%%]\n", 100);
1076 if (meter->completed == meter->total)
1078 if (meter->completed > meter->total)
1079 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Progress meter overflow!!\n");
1084 * Reset progress meter.
1086 * @param meter the meter to reset
1088 * @return GNUNET_YES if meter reset,
1089 * GNUNET_SYSERR on error
1092 reset_meter(struct ProgressMeter *meter)
1095 return GNUNET_SYSERR;
1097 meter->completed = 0;
1102 * Release resources for meter
1104 * @param meter the meter to free
1107 free_meter(struct ProgressMeter *meter)
1109 GNUNET_free_non_null (meter->startup_string);
1110 GNUNET_free (meter);
1114 * Get a topology from a string input.
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
1123 GNUNET_TESTING_topology_get(enum GNUNET_TESTING_Topology *topology,
1124 const char *topology_string)
1127 * Strings representing topologies in enum
1129 static const char *topology_strings[] =
1132 * A clique (everyone connected to everyone else).
1137 * Small-world network (2d torus plus random links).
1142 * Small-world network (ring plus random links).
1162 * Certain percentage of peers are unable to communicate directly
1163 * replicating NAT conditions
1168 * Scale free topology.
1173 * Straight line topology.
1178 * All peers are disconnected.
1183 * Read the topology from a file.
1190 if (topology_string == NULL)
1192 while (topology_strings[curr] != NULL)
1194 if (strcasecmp (topology_strings[curr], topology_string) == 0)
1201 *topology = GNUNET_TESTING_TOPOLOGY_NONE;
1206 * Get connect topology option from string input.
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
1215 GNUNET_TESTING_topology_option_get(
1216 enum GNUNET_TESTING_TopologyOption *topology_option,
1217 const char *topology_string)
1220 * Options for connecting a topology as strings.
1222 static const char *topology_option_strings[] =
1225 * Try to connect all peers specified in the topology.
1230 * Choose a random subset of connections to create.
1232 "CONNECT_RANDOM_SUBSET",
1235 * Create at least X connections for each peer.
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
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.
1255 * No options specified.
1262 if (topology_string == NULL)
1264 while (NULL != topology_option_strings[curr])
1266 if (strcasecmp (topology_option_strings[curr], topology_string) == 0)
1268 *topology_option = curr;
1273 *topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_NONE;
1278 * Function to iterate over options. Copies
1279 * the options to the target configuration,
1280 * updating PORT values as needed.
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
1288 update_config(void *cls, const char *section, const char *option,
1291 struct UpdateContext *ctx = cls;
1295 char *single_variable;
1296 char *per_host_variable;
1297 unsigned long long num_per_host;
1299 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
1300 GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
1302 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
1304 if ((ival != 0) && (GNUNET_YES
1305 != GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
1308 GNUNET_snprintf (cval, sizeof(cval), "%u", ctx->nport++);
1311 else if ((ival != 0) && (GNUNET_YES
1312 == GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
1314 && GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
1318 GNUNET_snprintf (cval, sizeof(cval), "%u", ival + ctx->fdnum
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);
1328 if (0 == strcmp (option, "UNIXPATH"))
1330 if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (ctx->orig,
1334 GNUNET_snprintf (uval, sizeof(uval), "/tmp/test-service-%s-%u",
1335 section, ctx->upnum++);
1338 else if ((GNUNET_YES
1339 == GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
1342 && (num_per_host > 0))
1345 GNUNET_snprintf (uval, sizeof(uval), "/tmp/test-service-%s-%u",
1346 section, ctx->fdnum % num_per_host);
1351 if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
1353 value = ctx->hostname;
1355 GNUNET_free (single_variable);
1356 GNUNET_free (per_host_variable);
1357 GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
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.
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)
1375 * @return new configuration, NULL on error
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,
1382 struct UpdateContext uc;
1385 char *allowed_hosts;
1386 unsigned long long skew_variance;
1387 unsigned long long skew_offset;
1388 long long actual_offset;
1394 uc.ret = GNUNET_CONFIGURATION_create ();
1395 uc.hostname = hostname;
1398 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
1399 if (uc.nport >= HIGH_PORT)
1402 GNUNET_CONFIGURATION_destroy (uc.ret);
1406 if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
1409 && (skew_variance > 0))
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);
1418 if (GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "control_host",
1419 &control_host) == GNUNET_OK)
1421 if (hostname != NULL)
1422 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1; %s;", control_host,
1425 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", control_host);
1427 GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "ACCEPT_FROM",
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",
1433 GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics",
1434 "ACCEPT_FROM", allowed_hosts);
1436 GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "UNIXPATH", "");
1437 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport", "UNIXPATH",
1439 GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "UNIXPATH", "");
1440 GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics", "UNIXPATH",
1443 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp", "USE_LOCALADDR",
1445 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp", "USE_LOCALADDR",
1447 GNUNET_free_non_null (control_host);
1448 GNUNET_free (allowed_hosts);
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)
1455 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", hostname);
1456 GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "BINDTO",
1458 GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "INTERNAL_ADDRESS",
1460 GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "EXTERNAL_ADDRESS",
1462 GNUNET_CONFIGURATION_set_value_string (uc.ret, "disablev6", "BINDTO",
1464 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp", "USE_LOCALADDR",
1466 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp", "USE_LOCALADDR",
1468 GNUNET_CONFIGURATION_set_value_string (uc.ret, "arm", "ACCEPT_FROM",
1470 GNUNET_free (allowed_hosts);
1474 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp", "USE_LOCALADDR",
1476 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp", "USE_LOCALADDR",
1478 GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "BINDTO",
1480 GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "INTERNAL_ADDRESS",
1482 GNUNET_CONFIGURATION_set_value_string (uc.ret, "nat", "EXTERNAL_ADDRESS",
1484 GNUNET_CONFIGURATION_set_value_string (uc.ret, "disablev6", "BINDTO",
1488 *port = (uint16_t) uc.nport;
1496 * Remove entries from the peer connection list
1498 * @param pg the peer group we are working with
1499 * @param first index of the first peer
1500 * @param second index of the second peer
1501 * @param list the peer list to use
1502 * @param check UNUSED
1504 * @return the number of connections added (can be 0, 1 or 2)
1508 remove_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first,
1509 unsigned int second, enum PeerLists list, unsigned int check)
1513 struct PeerConnection **first_list;
1514 struct PeerConnection **second_list;
1515 struct PeerConnection *first_iter;
1516 struct PeerConnection *second_iter;
1517 struct PeerConnection **first_tail;
1518 struct PeerConnection **second_tail;
1521 GNUNET_HashCode hash_first;
1522 GNUNET_HashCode hash_second;
1524 hash_from_uid (first, &hash_first);
1525 hash_from_uid (second, &hash_second);
1533 first_list = &pg->peers[first].allowed_peers_head;
1534 second_list = &pg->peers[second].allowed_peers_head;
1535 first_tail = &pg->peers[first].allowed_peers_tail;
1536 second_tail = &pg->peers[second].allowed_peers_tail;
1539 first_list = &pg->peers[first].connect_peers_head;
1540 second_list = &pg->peers[second].connect_peers_head;
1541 first_tail = &pg->peers[first].connect_peers_tail;
1542 second_tail = &pg->peers[second].connect_peers_tail;
1545 first_list = &pg->peers[first].blacklisted_peers_head;
1546 second_list = &pg->peers[second].blacklisted_peers_head;
1547 first_tail = &pg->peers[first].blacklisted_peers_tail;
1548 second_tail = &pg->peers[second].blacklisted_peers_tail;
1551 first_list = &pg->peers[first].connect_peers_working_set_head;
1552 second_list = &pg->peers[second].connect_peers_working_set_head;
1553 first_tail = &pg->peers[first].connect_peers_working_set_tail;
1554 second_tail = &pg->peers[second].connect_peers_working_set_tail;
1561 first_iter = *first_list;
1562 while (first_iter != NULL)
1564 if (first_iter->index == second)
1566 GNUNET_CONTAINER_DLL_remove(*first_list, *first_tail, first_iter);
1567 GNUNET_free(first_iter);
1571 first_iter = first_iter->next;
1574 second_iter = *second_list;
1575 while (second_iter != NULL)
1577 if (second_iter->index == first)
1579 GNUNET_CONTAINER_DLL_remove(*second_list, *second_tail, second_iter);
1580 GNUNET_free(second_iter);
1584 second_iter = second_iter->next;
1588 GNUNET_CONTAINER_multihashmap_contains (pg->peers[first].blacklisted_peers,
1591 GNUNET_CONTAINER_multihashmap_remove_all (pg->peers[first].blacklisted_peers,
1596 GNUNET_CONTAINER_multihashmap_contains (pg->peers[second].blacklisted_peers,
1599 GNUNET_CONTAINER_multihashmap_remove_all (pg->peers[second].blacklisted_peers,
1608 * Add entries to the some list
1610 * @param pg the peer group we are working with
1611 * @param first index of the first peer
1612 * @param second index of the second peer
1613 * @param list the list type that we should modify
1614 * @param check GNUNET_YES to check lists before adding
1615 * GNUNET_NO to force add
1617 * @return the number of connections added (can be 0, 1 or 2)
1621 add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first,
1622 unsigned int second, enum PeerLists list, unsigned int check)
1628 struct PeerConnection **first_list;
1629 struct PeerConnection **second_list;
1630 struct PeerConnection *first_iter;
1631 struct PeerConnection *second_iter;
1632 struct PeerConnection *new_first;
1633 struct PeerConnection *new_second;
1634 struct PeerConnection **first_tail;
1635 struct PeerConnection **second_tail;
1640 first_list = &pg->peers[first].allowed_peers_head;
1641 second_list = &pg->peers[second].allowed_peers_head;
1642 first_tail = &pg->peers[first].allowed_peers_tail;
1643 second_tail = &pg->peers[second].allowed_peers_tail;
1646 first_list = &pg->peers[first].connect_peers_head;
1647 second_list = &pg->peers[second].connect_peers_head;
1648 first_tail = &pg->peers[first].connect_peers_tail;
1649 second_tail = &pg->peers[second].connect_peers_tail;
1652 first_list = &pg->peers[first].blacklisted_peers_head;
1653 second_list = &pg->peers[second].blacklisted_peers_head;
1654 first_tail = &pg->peers[first].blacklisted_peers_tail;
1655 second_tail = &pg->peers[second].blacklisted_peers_tail;
1658 first_list = &pg->peers[first].connect_peers_working_set_head;
1659 second_list = &pg->peers[second].connect_peers_working_set_head;
1660 first_tail = &pg->peers[first].connect_peers_working_set_tail;
1661 second_tail = &pg->peers[second].connect_peers_working_set_tail;
1668 add_first = GNUNET_YES;
1669 add_second = GNUNET_YES;
1671 if (check == GNUNET_YES)
1673 first_iter = *first_list;
1674 while (first_iter != NULL)
1676 if (first_iter->index == second)
1678 add_first = GNUNET_NO;
1681 first_iter = first_iter->next;
1684 second_iter = *second_list;
1685 while (second_iter != NULL)
1687 if (second_iter->index == first)
1689 add_second = GNUNET_NO;
1692 second_iter = second_iter->next;
1699 new_first = GNUNET_malloc (sizeof (struct PeerConnection));
1700 new_first->index = second;
1701 GNUNET_CONTAINER_DLL_insert(*first_list, *first_tail, new_first);
1702 pg->peers[first].num_connections++;
1708 new_second = GNUNET_malloc (sizeof (struct PeerConnection));
1709 new_second->index = first;
1710 GNUNET_CONTAINER_DLL_insert(*second_list, *second_tail, new_second);
1711 pg->peers[second].num_connections++;
1719 * Scale free network construction as described in:
1721 * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
1723 * Start with a network of "one" peer, then progressively add
1724 * peers up to the total number. At each step, iterate over
1725 * all possible peers and connect new peer based on number of
1726 * existing connections of the target peer.
1728 * @param pg the peer group we are dealing with
1729 * @param proc the connection processor to use
1730 * @param list the peer list to use
1732 * @return the number of connections created
1735 create_scale_free(struct GNUNET_TESTING_PeerGroup *pg,
1736 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1739 unsigned int total_connections;
1740 unsigned int outer_count;
1742 unsigned int previous_total_connections;
1746 GNUNET_assert (pg->total > 1);
1748 /* Add a connection between the first two nodes */
1749 total_connections = proc (pg, 0, 1, list, GNUNET_YES);
1751 for (outer_count = 1; outer_count < pg->total; outer_count++)
1753 previous_total_connections = total_connections;
1754 for (i = 0; i < outer_count; i++)
1756 probability = pg->peers[i].num_connections
1757 / (double) previous_total_connections;
1759 = ((double) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1761 / ((double) UINT64_MAX);
1763 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1764 "Considering connecting peer %d to peer %d\n",
1767 if (random < probability)
1770 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1771 "Connecting peer %d to peer %d\n", outer_count, i);
1773 total_connections += proc (pg, outer_count, i, list, GNUNET_YES);
1778 return total_connections;
1782 * Create a topology given a peer group (set of running peers)
1783 * and a connection processor. Creates a small world topology
1784 * according to the rewired ring construction. The basic
1785 * behavior is that a ring topology is created, but with some
1786 * probability instead of connecting a peer to the next
1787 * neighbor in the ring a connection will be created to a peer
1788 * selected uniformly at random. We use the TESTING
1789 * PERCENTAGE option to specify what number of
1790 * connections each peer should have. Default is 2,
1791 * which makes the ring, any given number is multiplied by
1792 * the log of the network size; i.e. a PERCENTAGE of 2 makes
1793 * each peer have on average 2logn connections. The additional
1794 * connections are made at increasing distance around the ring
1795 * from the original peer, or to random peers based on the re-
1796 * wiring probability. The TESTING
1797 * PROBABILITY option is used as the probability that a given
1798 * connection is rewired.
1800 * @param pg the peergroup to create the topology on
1801 * @param proc the connection processor to call to actually set
1802 * up connections between two peers
1803 * @param list the peer list to use
1805 * @return the number of connections that were set up
1809 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg,
1810 GNUNET_TESTING_ConnectionProcessor proc,
1811 enum PeerLists list)
1815 unsigned int natLog;
1816 unsigned int randomPeer;
1817 double random, logNModifier, probability;
1818 unsigned int smallWorldConnections;
1823 unsigned int useAnd;
1824 int connect_attempts;
1826 logNModifier = 0.5; /* FIXME: default value? */
1827 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
1831 if (sscanf (p_string, "%lf", &logNModifier) != 1)
1833 GNUNET_ERROR_TYPE_WARNING,
1835 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1836 p_string, "LOGNMODIFIER", "TESTING");
1837 GNUNET_free (p_string);
1839 probability = 0.5; /* FIXME: default percentage? */
1840 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
1844 if (sscanf (p_string, "%lf", &probability) != 1)
1846 GNUNET_ERROR_TYPE_WARNING,
1848 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1849 p_string, "PERCENTAGE", "TESTING");
1850 GNUNET_free (p_string);
1852 natLog = log (pg->total);
1853 connsPerPeer = ceil (natLog * logNModifier);
1855 if (connsPerPeer % 2 == 1)
1858 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Target is %d connections per peer."),
1861 smallWorldConnections = 0;
1862 connect_attempts = 0;
1863 for (i = 0; i < pg->total; i++)
1866 max = i + connsPerPeer / 2;
1867 min = i - connsPerPeer / 2;
1869 if (max > pg->total - 1)
1871 max = max - pg->total;
1877 min = pg->total - 1 + min;
1881 for (j = 0; j < connsPerPeer / 2; j++)
1884 = ((double) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1886 / ((double) UINT64_MAX));
1887 if (random < probability)
1889 /* Connect to uniformly selected random peer */
1891 = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1893 while ((((randomPeer < max) && (randomPeer > min)) && (useAnd
1894 == 0)) || (((randomPeer > min) || (randomPeer < max))
1898 = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1901 smallWorldConnections += proc (pg, i, randomPeer, list,
1906 nodeToConnect = i + j + 1;
1907 if (nodeToConnect > pg->total - 1)
1909 nodeToConnect = nodeToConnect - pg->total;
1911 connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
1917 connect_attempts += smallWorldConnections;
1919 return connect_attempts;
1923 * Create a topology given a peer group (set of running peers)
1924 * and a connection processor.
1926 * @param pg the peergroup to create the topology on
1927 * @param proc the connection processor to call to actually set
1928 * up connections between two peers
1929 * @param list the peer list to use
1931 * @return the number of connections that were set up
1935 create_nated_internet(struct GNUNET_TESTING_PeerGroup *pg,
1936 GNUNET_TESTING_ConnectionProcessor proc,
1937 enum PeerLists list)
1939 unsigned int outer_count, inner_count;
1940 unsigned int cutoff;
1941 int connect_attempts;
1942 double nat_percentage;
1945 nat_percentage = 0.6; /* FIXME: default percentage? */
1946 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
1950 if (sscanf (p_string, "%lf", &nat_percentage) != 1)
1952 GNUNET_ERROR_TYPE_WARNING,
1954 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1955 p_string, "PERCENTAGE", "TESTING");
1956 GNUNET_free (p_string);
1959 cutoff = (unsigned int) (nat_percentage * pg->total);
1960 connect_attempts = 0;
1961 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1963 for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
1965 if ((outer_count > cutoff) || (inner_count > cutoff))
1968 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1969 "Connecting peer %d to peer %d\n",
1970 outer_count, inner_count);
1972 connect_attempts += proc (pg, outer_count, inner_count, list,
1977 return connect_attempts;
1982 * Create a topology given a peer group (set of running peers)
1983 * and a connection processor.
1985 * @param pg the peergroup to create the topology on
1986 * @param proc the connection processor to call to actually set
1987 * up connections between two peers
1988 * @param list the peer list to use
1990 * @return the number of connections that were set up
1994 create_nated_internet_copy(struct GNUNET_TESTING_PeerGroup *pg,
1995 GNUNET_TESTING_ConnectionProcessor proc,
1996 enum PeerLists list)
1998 unsigned int outer_count, inner_count;
1999 unsigned int cutoff;
2000 int connect_attempts;
2001 double nat_percentage;
2004 struct ProgressMeter *conn_meter;
2006 nat_percentage = 0.6; /* FIXME: default percentage? */
2007 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
2011 if (sscanf (p_string, "%lf", &nat_percentage) != 1)
2013 GNUNET_ERROR_TYPE_WARNING,
2015 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
2016 p_string, "PERCENTAGE", "TESTING");
2017 GNUNET_free (p_string);
2020 cutoff = (unsigned int) (nat_percentage * pg->total);
2022 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2024 for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
2026 if ((outer_count > cutoff) || (inner_count > cutoff))
2032 conn_meter = create_meter (count, "NAT COPY", GNUNET_YES);
2033 connect_attempts = 0;
2034 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2036 for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
2038 if ((outer_count > cutoff) || (inner_count > cutoff))
2041 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2042 "Connecting peer %d to peer %d\n",
2043 outer_count, inner_count);
2045 connect_attempts += proc (pg, outer_count, inner_count, list,
2047 add_connections (pg, outer_count, inner_count, ALLOWED, GNUNET_NO);
2048 update_meter (conn_meter);
2052 free_meter (conn_meter);
2054 return connect_attempts;
2059 * Create a topology given a peer group (set of running peers)
2060 * and a connection processor.
2062 * @param pg the peergroup to create the topology on
2063 * @param proc the connection processor to call to actually set
2064 * up connections between two peers
2065 * @param list the peer list to use
2067 * @return the number of connections that were set up
2071 create_small_world(struct GNUNET_TESTING_PeerGroup *pg,
2072 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2074 unsigned int i, j, k;
2075 unsigned int square;
2078 unsigned int toggle = 1;
2079 unsigned int nodeToConnect;
2080 unsigned int natLog;
2081 unsigned int node1Row;
2082 unsigned int node1Col;
2083 unsigned int node2Row;
2084 unsigned int node2Col;
2085 unsigned int distance;
2086 double probability, random, percentage;
2087 unsigned int smallWorldConnections;
2088 unsigned int small_world_it;
2090 int connect_attempts;
2091 square = floor (sqrt (pg->total));
2095 percentage = 0.5; /* FIXME: default percentage? */
2096 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
2100 if (sscanf (p_string, "%lf", &percentage) != 1)
2102 GNUNET_ERROR_TYPE_WARNING,
2104 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
2105 p_string, "PERCENTAGE", "TESTING");
2106 GNUNET_free (p_string);
2108 if (percentage < 0.0)
2111 GNUNET_ERROR_TYPE_WARNING,
2113 ("Invalid value `%s' for option `%s' in section `%s': got %f, needed value greater than 0\n"),
2114 "PERCENTAGE", "TESTING", percentage);
2117 probability = 0.5; /* FIXME: default percentage? */
2118 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
2122 if (sscanf (p_string, "%lf", &probability) != 1)
2124 GNUNET_ERROR_TYPE_WARNING,
2126 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
2127 p_string, "PROBABILITY", "TESTING");
2128 GNUNET_free (p_string);
2130 if (square * square != pg->total)
2132 while (rows * cols < pg->total)
2134 if (toggle % 2 == 0)
2143 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2145 ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
2149 connect_attempts = 0;
2150 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
2151 * to the node to its right and above. Once this is over, we'll have our torus!
2152 * Special case for the last node (if the rows and columns are not equal), connect
2153 * to the first in the row to maintain topology.
2155 for (i = 0; i < pg->total; i++)
2157 /* First connect to the node to the right */
2158 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
2159 nodeToConnect = i + 1;
2160 else if (i + 1 == pg->total)
2161 nodeToConnect = rows * cols - cols;
2163 nodeToConnect = i - cols + 1;
2165 connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
2169 nodeToConnect = (rows * cols) - cols + i;
2170 if (nodeToConnect >= pg->total)
2171 nodeToConnect -= cols;
2174 nodeToConnect = i - cols;
2176 if (nodeToConnect < pg->total)
2177 connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
2179 natLog = log (pg->total);
2180 #if VERBOSE_TESTING > 2
2181 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2182 _("natural log of %d is %d, will run %d iterations\n"),
2183 pg->total, natLog, (int) (natLog * percentage));
2184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2185 _("Total connections added thus far: %u!\n"), connect_attempts);
2187 smallWorldConnections = 0;
2188 small_world_it = (unsigned int) (natLog * percentage);
2189 if (small_world_it < 1)
2191 GNUNET_assert (small_world_it > 0 && small_world_it < (unsigned int) -1);
2192 for (i = 0; i < small_world_it; i++)
2194 for (j = 0; j < pg->total; j++)
2196 /* Determine the row and column of node at position j on the 2d torus */
2197 node1Row = j / cols;
2198 node1Col = j - (node1Row * cols);
2199 for (k = 0; k < pg->total; k++)
2201 /* Determine the row and column of node at position k on the 2d torus */
2202 node2Row = k / cols;
2203 node2Col = k - (node2Row * cols);
2204 /* Simple Cartesian distance */
2205 distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
2208 /* Calculate probability as 1 over the square of the distance */
2209 probability = 1.0 / (distance * distance);
2210 /* Choose a random value between 0 and 1 */
2212 = ((double) GNUNET_CRYPTO_random_u64 (
2213 GNUNET_CRYPTO_QUALITY_WEAK,
2215 / ((double) UINT64_MAX);
2216 /* If random < probability, then connect the two nodes */
2217 if (random < probability)
2218 smallWorldConnections += proc (pg, j, k, list, GNUNET_YES);
2224 connect_attempts += smallWorldConnections;
2225 #if VERBOSE_TESTING > 2
2226 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2227 _("Total connections added for small world: %d!\n"),
2228 smallWorldConnections);
2230 return connect_attempts;
2234 * Create a topology given a peer group (set of running peers)
2235 * and a connection processor.
2237 * @param pg the peergroup to create the topology on
2238 * @param proc the connection processor to call to actually set
2239 * up connections between two peers
2240 * @param list the peer list to use
2242 * @return the number of connections that were set up
2246 create_erdos_renyi(struct GNUNET_TESTING_PeerGroup *pg,
2247 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2250 unsigned int outer_count;
2251 unsigned int inner_count;
2252 int connect_attempts;
2256 probability = 0.5; /* FIXME: default percentage? */
2257 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "TESTING",
2261 if (sscanf (p_string, "%lf", &probability) != 1)
2263 GNUNET_ERROR_TYPE_WARNING,
2265 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
2266 p_string, "PROBABILITY", "TESTING");
2267 GNUNET_free (p_string);
2269 connect_attempts = 0;
2270 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2272 for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
2275 = ((double) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2277 / ((double) UINT64_MAX);
2279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2280 _("rand is %f probability is %f\n"), temp_rand,
2283 if (temp_rand < probability)
2285 connect_attempts += proc (pg, outer_count, inner_count, list,
2291 return connect_attempts;
2295 * Create a topology given a peer group (set of running peers)
2296 * and a connection processor. This particular function creates
2297 * the connections for a 2d-torus, plus additional "closest"
2298 * connections per peer.
2300 * @param pg the peergroup to create the topology on
2301 * @param proc the connection processor to call to actually set
2302 * up connections between two peers
2303 * @param list the peer list to use
2305 * @return the number of connections that were set up
2309 create_2d_torus(struct GNUNET_TESTING_PeerGroup *pg,
2310 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2313 unsigned int square;
2316 unsigned int toggle = 1;
2317 unsigned int nodeToConnect;
2318 int connect_attempts;
2320 connect_attempts = 0;
2322 square = floor (sqrt (pg->total));
2326 if (square * square != pg->total)
2328 while (rows * cols < pg->total)
2330 if (toggle % 2 == 0)
2339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2341 ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
2344 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
2345 * to the node to its right and above. Once this is over, we'll have our torus!
2346 * Special case for the last node (if the rows and columns are not equal), connect
2347 * to the first in the row to maintain topology.
2349 for (i = 0; i < pg->total; i++)
2351 /* First connect to the node to the right */
2352 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
2353 nodeToConnect = i + 1;
2354 else if (i + 1 == pg->total)
2355 nodeToConnect = rows * cols - cols;
2357 nodeToConnect = i - cols + 1;
2359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2360 "Connecting peer %d to peer %d\n", i, nodeToConnect);
2362 connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
2364 /* Second connect to the node immediately above */
2367 nodeToConnect = (rows * cols) - cols + i;
2368 if (nodeToConnect >= pg->total)
2369 nodeToConnect -= cols;
2372 nodeToConnect = i - cols;
2374 if (nodeToConnect < pg->total)
2377 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2378 "Connecting peer %d to peer %d\n", i, nodeToConnect);
2380 connect_attempts += proc (pg, i, nodeToConnect, list, GNUNET_YES);
2385 return connect_attempts;
2389 * Create a topology given a peer group (set of running peers)
2390 * and a connection processor.
2392 * @param pg the peergroup to create the topology on
2393 * @param proc the connection processor to call to actually set
2394 * up connections between two peers
2395 * @param list the peer list to use
2396 * @param check does the connection processor need to check before
2397 * performing an action on the list?
2399 * @return the number of connections that were set up
2403 create_clique(struct GNUNET_TESTING_PeerGroup *pg,
2404 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list,
2407 unsigned int outer_count;
2408 unsigned int inner_count;
2409 int connect_attempts;
2410 struct ProgressMeter *conn_meter;
2411 connect_attempts = 0;
2413 conn_meter = create_meter ((((pg->total * pg->total) + pg->total) / 2)
2414 - pg->total, "Create Clique ", GNUNET_NO);
2415 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2417 for (inner_count = outer_count + 1; inner_count < pg->total; inner_count++)
2420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2421 "Connecting peer %d to peer %d\n",
2422 outer_count, inner_count);
2424 connect_attempts += proc (pg, outer_count, inner_count, list, check);
2425 update_meter (conn_meter);
2428 reset_meter (conn_meter);
2429 free_meter (conn_meter);
2430 return connect_attempts;
2435 * Iterator over hash map entries.
2437 * @param cls closure the peer group
2438 * @param key the key stored in the hashmap is the
2439 * index of the peer to connect to
2440 * @param value value in the hash map, handle to the peer daemon
2441 * @return GNUNET_YES if we should continue to
2446 unblacklist_iterator (void *cls,
2447 const GNUNET_HashCode * key,
2450 struct UnblacklistContext *un_ctx = cls;
2451 uint32_t second_pos;
2453 uid_from_hash (key, &second_pos);
2455 unblacklist_connections(un_ctx->pg, un_ctx->first_uid, second_pos);
2463 * Create a blacklist topology based on the allowed topology
2464 * which disallows any connections not in the allowed topology
2465 * at the transport level.
2467 * @param pg the peergroup to create the topology on
2468 * @param proc the connection processor to call to allow
2469 * up connections between two peers
2471 * @return the number of connections that were set up
2475 copy_allowed (struct GNUNET_TESTING_PeerGroup *pg,
2476 GNUNET_TESTING_ConnectionProcessor proc)
2480 struct PeerConnection *iter;
2482 struct UnblacklistContext un_ctx;
2487 for (count = 0; count < pg->total - 1; count++)
2490 iter = pg->peers[count].allowed_peers_head;
2491 while (iter != NULL)
2493 remove_connections (pg, count, iter->index, BLACKLIST, GNUNET_YES);
2494 //unblacklist_connections(pg, count, iter->index);
2498 un_ctx.first_uid = count;
2499 total += GNUNET_CONTAINER_multihashmap_iterate(pg->peers[count].allowed_peers,
2500 &unblacklist_iterator,
2504 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2505 "Unblacklisted %u peers\n",
2512 * Create a topology given a peer group (set of running peers)
2513 * and a connection processor.
2515 * @param pg the peergroup to create the topology on
2516 * @param proc the connection processor to call to actually set
2517 * up connections between two peers
2518 * @param list which list should be modified
2520 * @return the number of connections that were set up
2524 create_line(struct GNUNET_TESTING_PeerGroup *pg,
2525 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2528 unsigned int connect_attempts;
2530 connect_attempts = 0;
2531 /* Connect each peer to the next highest numbered peer */
2532 for (count = 0; count < pg->total - 1; count++)
2535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2536 "Connecting peer %d to peer %d\n",
2539 connect_attempts += proc (pg, count, count + 1, list, GNUNET_YES);
2542 return connect_attempts;
2546 * Create a topology given a peer group (set of running peers)
2547 * and a connection processor.
2549 * @param pg the peergroup to create the topology on
2550 * @param filename the file to read topology information from
2551 * @param proc the connection processor to call to actually set
2552 * up connections between two peers
2553 * @param list the peer list to use
2555 * @return the number of connections that were set up
2559 create_from_file(struct GNUNET_TESTING_PeerGroup *pg, char *filename,
2560 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2562 int connect_attempts;
2563 unsigned int first_peer_index;
2564 unsigned int second_peer_index;
2569 unsigned int total_peers;
2570 enum States curr_state;
2572 connect_attempts = 0;
2573 if (GNUNET_OK != GNUNET_DISK_file_test (filename))
2574 GNUNET_DISK_fn_write (filename, NULL, 0, GNUNET_DISK_PERM_USER_READ);
2576 if ((0 != STAT (filename, &frstat)) || (frstat.st_size == 0))
2578 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2579 "Could not open file `%s' specified for topology!", filename);
2580 return connect_attempts;
2583 data = GNUNET_malloc_large (frstat.st_size);
2584 GNUNET_assert(data != NULL);
2585 if (frstat.st_size != GNUNET_DISK_fn_read (filename, data, frstat.st_size))
2588 GNUNET_ERROR_TYPE_ERROR,
2589 "Could not read file %s specified for host list, ending test!",
2592 return connect_attempts;
2597 first_peer_index = 0;
2598 /* First line should contain a single integer, specifying the number of peers */
2599 /* Each subsequent line should contain this format PEER_INDEX:OTHER_PEER_INDEX[,...] */
2600 curr_state = NUM_PEERS;
2601 while (count < frstat.st_size - 1)
2603 if ((buf[count] == '\n') || (buf[count] == ' '))
2613 total_peers = strtoul(&buf[count], NULL, 10);
2616 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2617 "Failed to read number of peers from topology file!\n");
2619 return connect_attempts;
2621 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2622 "Read %u total peers in topology\n", total_peers);
2623 GNUNET_assert(total_peers == pg->total);
2624 curr_state = PEER_INDEX;
2625 while ((buf[count] != '\n') && (count < frstat.st_size - 1))
2631 first_peer_index = strtoul(&buf[count], NULL, 10);
2634 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2635 "Failed to read peer index from topology file!\n");
2637 return connect_attempts;
2639 while ((buf[count] != ':') && (count < frstat.st_size - 1))
2642 curr_state = OTHER_PEER_INDEX;
2645 if (1 == sscanf (&buf[count], ":"))
2646 curr_state = OTHER_PEER_INDEX;
2649 case OTHER_PEER_INDEX:
2651 second_peer_index = strtoul(&buf[count], NULL, 10);
2654 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2655 "Failed to peer index from topology file!\n");
2657 return connect_attempts;
2659 /* Assume file is written with first peer 1, but array index is 0 */
2660 connect_attempts += proc (pg, first_peer_index - 1, second_peer_index
2661 - 1, list, GNUNET_YES);
2662 while ((buf[count] != '\n') && (buf[count] != ',') && (count
2663 < frstat.st_size - 1))
2665 if (buf[count] == '\n')
2667 curr_state = PEER_INDEX;
2669 else if (buf[count] != ',')
2671 curr_state = OTHER_PEER_INDEX;
2676 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2677 "Found bad data in topology file while in state %d!\n",
2681 return connect_attempts;
2685 return connect_attempts;
2689 * Create a topology given a peer group (set of running peers)
2690 * and a connection processor.
2692 * @param pg the peergroup to create the topology on
2693 * @param proc the connection processor to call to actually set
2694 * up connections between two peers
2695 * @param list the peer list to use
2697 * @return the number of connections that were set up
2701 create_ring(struct GNUNET_TESTING_PeerGroup *pg,
2702 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2705 int connect_attempts;
2707 connect_attempts = 0;
2709 /* Connect each peer to the next highest numbered peer */
2710 for (count = 0; count < pg->total - 1; count++)
2713 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2714 "Connecting peer %d to peer %d\n", count, count + 1);
2716 connect_attempts += proc (pg, count, count + 1, list, GNUNET_YES);
2719 /* Connect the last peer to the first peer */
2720 connect_attempts += proc (pg, pg->total - 1, 0, list, GNUNET_YES);
2722 return connect_attempts;
2727 * Iterator for writing friends of a peer to a file.
2729 * @param cls closure, an open writable file handle
2730 * @param key the key the daemon was stored under
2731 * @param value the GNUNET_TESTING_Daemon that needs to be written.
2733 * @return GNUNET_YES to continue iteration
2735 * TODO: Could replace friend_file_iterator and blacklist_file_iterator
2736 * with a single file_iterator that takes a closure which contains
2737 * the prefix to write before the peer. Then this could be used
2738 * for blacklisting multiple transports and writing the friend
2739 * file. I'm sure *someone* will complain loudly about other
2740 * things that negate these functions even existing so no point in
2744 friend_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2746 FILE *temp_friend_handle = cls;
2747 struct GNUNET_TESTING_Daemon *peer = value;
2748 struct GNUNET_PeerIdentity *temppeer;
2749 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2751 temppeer = &peer->id;
2752 GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2753 fprintf (temp_friend_handle, "%s\n", (char *) &peer_enc);
2758 struct BlacklistContext
2761 * The (open) file handle to write to
2763 FILE *temp_file_handle;
2766 * The transport that this peer will be blacklisted on.
2772 * Iterator for writing blacklist data to appropriate files.
2774 * @param cls closure, an open writable file handle
2775 * @param key the key the daemon was stored under
2776 * @param value the GNUNET_TESTING_Daemon that needs to be written.
2778 * @return GNUNET_YES to continue iteration
2781 blacklist_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2783 struct BlacklistContext *blacklist_ctx = cls;
2784 struct GNUNET_TESTING_Daemon *peer = value;
2785 struct GNUNET_PeerIdentity *temppeer;
2786 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2788 temppeer = &peer->id;
2789 GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2790 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Writing entry %s:%s to file\n", blacklist_ctx->transport, (char *) &peer_enc);
2791 fprintf (blacklist_ctx->temp_file_handle, "%s:%s\n",
2792 blacklist_ctx->transport, (char *) &peer_enc);
2799 * Create the friend files based on the PeerConnection's
2800 * of each peer in the peer group, and copy the files
2801 * to the appropriate place
2803 * @param pg the peer group we are dealing with
2806 create_and_copy_friend_files(struct GNUNET_TESTING_PeerGroup *pg)
2808 FILE *temp_friend_handle;
2809 unsigned int pg_iter;
2810 char *temp_service_path;
2811 struct GNUNET_OS_Process **procarr;
2815 enum GNUNET_OS_ProcessStatusType type;
2816 unsigned long return_code;
2824 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2825 struct PeerConnection *conn_iter;
2827 procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
2828 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2830 mytemp = GNUNET_DISK_mktemp ("friends");
2831 GNUNET_assert (mytemp != NULL);
2832 temp_friend_handle = fopen (mytemp, "wt");
2833 GNUNET_assert (temp_friend_handle != NULL);
2835 conn_iter = pg->peers[pg_iter].allowed_peers_head;
2836 while (conn_iter != NULL)
2838 GNUNET_CRYPTO_hash_to_enc (
2839 &pg->peers[conn_iter->index].daemon->id.hashPubKey,
2841 fprintf (temp_friend_handle, "%s\n", (char *) &peer_enc);
2842 conn_iter = conn_iter->next;
2845 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].allowed_peers,
2846 &friend_file_iterator,
2847 temp_friend_handle);
2849 fclose (temp_friend_handle);
2852 != GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter].
2854 "PATHS", "SERVICEHOME",
2855 &temp_service_path))
2857 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2858 _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
2859 "SERVICEHOME", "PATHS");
2860 if (UNLINK (mytemp) != 0)
2861 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2864 GNUNET_free (mytemp);
2868 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
2870 GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
2871 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv", "mv",
2873 GNUNET_assert(procarr[pg_iter] != NULL);
2875 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2876 "Copying file with command cp %s %s\n",
2880 ret = GNUNET_OS_process_wait (procarr[pg_iter]); /* FIXME: schedule this, throttle! */
2881 GNUNET_OS_process_close (procarr[pg_iter]);
2884 else /* Remote, scp the file to the correct place */
2886 if (NULL != pg->peers[pg_iter].daemon->username)
2887 GNUNET_asprintf (&arg, "%s@%s:%s/friends",
2888 pg->peers[pg_iter].daemon->username,
2889 pg->peers[pg_iter].daemon->hostname,
2892 GNUNET_asprintf (&arg, "%s:%s/friends",
2893 pg->peers[pg_iter].daemon->hostname,
2895 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp", "scp",
2897 GNUNET_assert(procarr[pg_iter] != NULL);
2898 ret = GNUNET_OS_process_wait (procarr[pg_iter]); /* FIXME: schedule this, throttle! */
2899 GNUNET_OS_process_close (procarr[pg_iter]);
2900 if (ret != GNUNET_OK)
2902 /* FIXME: free contents of 'procarr' array */
2903 GNUNET_free (procarr);
2904 GNUNET_free (temp_service_path);
2905 GNUNET_free (mytemp);
2909 procarr[pg_iter] = NULL;
2911 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2912 "Copying file with command scp %s %s\n",
2918 GNUNET_free (temp_service_path);
2919 GNUNET_free (mytemp);
2924 ret = GNUNET_SYSERR;
2925 while ((count < max_wait) && (ret != GNUNET_OK))
2928 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2931 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2932 "Checking copy status of file %d\n",
2935 if (procarr[pg_iter] != NULL) /* Check for already completed! */
2937 if (GNUNET_OS_process_status
2938 (procarr[pg_iter], &type, &return_code) != GNUNET_OK)
2940 ret = GNUNET_SYSERR;
2942 else if ((type != GNUNET_OS_PROCESS_EXITED)
2943 || (return_code != 0))
2945 ret = GNUNET_SYSERR;
2949 GNUNET_OS_process_close (procarr[pg_iter]);
2950 procarr[pg_iter] = NULL;
2952 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2960 if (ret == GNUNET_SYSERR)
2962 /* FIXME: why sleep here? -CG */
2968 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2969 _("Finished copying all friend files!\n"));
2972 GNUNET_free (procarr);
2977 * Create the blacklist files based on the PeerConnection's
2978 * of each peer in the peer group, and copy the files
2979 * to the appropriate place.
2981 * @param pg the peer group we are dealing with
2982 * @param transports space delimited list of transports to blacklist
2985 create_and_copy_blacklist_files(struct GNUNET_TESTING_PeerGroup *pg,
2986 const char *transports)
2988 FILE *temp_file_handle;
2989 unsigned int pg_iter;
2990 char *temp_service_path;
2991 struct GNUNET_OS_Process **procarr;
2994 enum GNUNET_OS_ProcessStatusType type;
2995 unsigned long return_code;
3002 char *temp_transports;
3004 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
3005 struct PeerConnection *conn_iter;
3007 static struct BlacklistContext blacklist_ctx;
3010 procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
3011 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3013 mytemp = GNUNET_DISK_mktemp ("blacklist");
3014 GNUNET_assert (mytemp != NULL);
3015 temp_file_handle = fopen (mytemp, "wt");
3016 GNUNET_assert (temp_file_handle != NULL);
3017 temp_transports = GNUNET_strdup (transports);
3019 blacklist_ctx.temp_file_handle = temp_file_handle;
3021 transport_len = strlen (temp_transports) + 1;
3024 for (i = 0; i < transport_len; i++)
3026 if ((temp_transports[i] == ' ') && (pos == NULL))
3027 continue; /* At start of string (whitespace) */
3028 else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */
3030 temp_transports[i] = '\0';
3032 conn_iter = pg->peers[pg_iter].blacklisted_peers_head;
3033 while (conn_iter != NULL)
3035 GNUNET_CRYPTO_hash_to_enc (
3036 &pg->peers[conn_iter->index].daemon->id.hashPubKey,
3038 fprintf (temp_file_handle, "%s:%s\n", pos, (char *) &peer_enc);
3039 conn_iter = conn_iter->next;
3042 blacklist_ctx.transport = pos;
3043 (void) GNUNET_CONTAINER_multihashmap_iterate (pg->
3045 [pg_iter].blacklisted_peers,
3046 &blacklist_file_iterator,
3050 } /* At beginning of actual string */
3051 else if (pos == NULL)
3053 pos = &temp_transports[i];
3057 GNUNET_free (temp_transports);
3058 fclose (temp_file_handle);
3061 != GNUNET_CONFIGURATION_get_value_string (
3062 pg->peers[pg_iter]. daemon->cfg,
3063 "PATHS", "SERVICEHOME",
3064 &temp_service_path))
3067 GNUNET_ERROR_TYPE_WARNING,
3069 ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
3070 "SERVICEHOME", "PATHS");
3071 if (UNLINK (mytemp) != 0)
3072 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
3074 GNUNET_free (mytemp);
3078 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
3080 GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
3081 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv", "mv",
3084 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3085 _("Copying file with command cp %s %s\n"), mytemp, arg);
3090 else /* Remote, scp the file to the correct place */
3092 if (NULL != pg->peers[pg_iter].daemon->username)
3093 GNUNET_asprintf (&arg, "%s@%s:%s/blacklist",
3094 pg->peers[pg_iter].daemon->username,
3095 pg->peers[pg_iter].daemon->hostname,
3098 GNUNET_asprintf (&arg, "%s:%s/blacklist",
3099 pg->peers[pg_iter].daemon->hostname,
3101 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp", "scp",
3103 GNUNET_assert(procarr[pg_iter] != NULL);
3104 GNUNET_OS_process_wait (procarr[pg_iter]); /* FIXME: add scheduled blacklist file copy that parallelizes file copying! */
3107 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3108 _("Copying file with command scp %s %s\n"), mytemp,
3113 GNUNET_free (temp_service_path);
3114 GNUNET_free (mytemp);
3118 ret = GNUNET_SYSERR;
3119 while ((count < max_wait) && (ret != GNUNET_OK))
3122 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3125 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3126 _("Checking copy status of file %d\n"), pg_iter);
3128 if (procarr[pg_iter] != NULL) /* Check for already completed! */
3130 if (GNUNET_OS_process_status (procarr[pg_iter], &type,
3131 &return_code) != GNUNET_OK)
3133 ret = GNUNET_SYSERR;
3135 else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
3137 ret = GNUNET_SYSERR;
3141 GNUNET_OS_process_close (procarr[pg_iter]);
3142 procarr[pg_iter] = NULL;
3144 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3145 _("File %d copied\n"), pg_iter);
3151 if (ret == GNUNET_SYSERR)
3153 /* FIXME: why sleep here? -CG */
3159 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3160 _("Finished copying all blacklist files!\n"));
3162 GNUNET_free (procarr);
3166 /* Forward Declaration */
3168 schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
3171 * Choose a random peer's next connection to create, and
3172 * call schedule_connect to set up the connect task.
3174 * @param ct_ctx the overall connection context
3177 preschedule_connect(struct GNUNET_TESTING_PeerGroup *pg)
3179 struct ConnectTopologyContext *ct_ctx = &pg->ct_ctx;
3180 struct PeerConnection *connection_iter;
3181 struct ConnectContext *connect_context;
3182 uint32_t random_peer;
3184 if (ct_ctx->remaining_connections == 0)
3187 = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, pg->total);
3188 while (pg->peers[random_peer].connect_peers_head == NULL)
3189 random_peer = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
3192 connection_iter = pg->peers[random_peer].connect_peers_head;
3193 connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
3194 connect_context->first_index = random_peer;
3195 connect_context->second_index = connection_iter->index;
3196 connect_context->ct_ctx = ct_ctx;
3197 GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
3198 GNUNET_CONTAINER_DLL_remove(pg->peers[random_peer].connect_peers_head, pg->peers[random_peer].connect_peers_tail, connection_iter);
3199 GNUNET_free(connection_iter);
3200 ct_ctx->remaining_connections--;
3204 /* Forward declaration */
3205 static void schedule_send_hellos (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
3208 * Close connections and free the hello context.
3210 * @param cls the 'struct SendHelloContext *'
3211 * @param tc scheduler context
3214 free_hello_context (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3216 struct SendHelloContext *send_hello_context = cls;
3217 if (send_hello_context->peer->daemon->server != NULL)
3219 GNUNET_CORE_disconnect(send_hello_context->peer->daemon->server);
3220 send_hello_context->peer->daemon->server = NULL;
3222 if (send_hello_context->peer->daemon->th != NULL)
3224 GNUNET_TRANSPORT_disconnect(send_hello_context->peer->daemon->th);
3225 send_hello_context->peer->daemon->th = NULL;
3227 if (send_hello_context->core_connect_task != GNUNET_SCHEDULER_NO_TASK)
3229 GNUNET_SCHEDULER_cancel(send_hello_context->core_connect_task);
3230 send_hello_context->core_connect_task = GNUNET_SCHEDULER_NO_TASK;
3232 send_hello_context->pg->outstanding_connects--;
3233 GNUNET_free(send_hello_context);
3237 * For peers that haven't yet connected, notify
3238 * the caller that they have failed (timeout).
3240 * @param cls the 'struct SendHelloContext *'
3241 * @param tc scheduler context
3244 notify_remaining_connections_failed (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3246 struct SendHelloContext *send_hello_context = cls;
3247 struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg;
3248 struct PeerConnection *connection;
3250 GNUNET_CORE_disconnect(send_hello_context->peer->daemon->server);
3251 send_hello_context->peer->daemon->server = NULL;
3253 connection = send_hello_context->peer->connect_peers_head;
3255 while (connection != NULL)
3257 if (pg->notify_connection != NULL)
3259 pg->notify_connection(pg->notify_connection_cls,
3260 &send_hello_context->peer->daemon->id,
3261 &pg->peers[connection->index].daemon->id,
3263 send_hello_context->peer->daemon->cfg,
3264 pg->peers[connection->index].daemon->cfg,
3265 send_hello_context->peer->daemon,
3266 pg->peers[connection->index].daemon,
3267 "Peers failed to connect (timeout)");
3269 GNUNET_CONTAINER_DLL_remove(send_hello_context->peer->connect_peers_head, send_hello_context->peer->connect_peers_tail, connection);
3270 GNUNET_free(connection);
3271 connection = connection->next;
3273 GNUNET_SCHEDULER_add_now(&free_hello_context, send_hello_context);
3275 other_peer = &pg->peers[connection->index];
3280 * For peers that haven't yet connected, send
3281 * CORE connect requests.
3283 * @param cls the 'struct SendHelloContext *'
3284 * @param tc scheduler context
3287 send_core_connect_requests (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3289 struct SendHelloContext *send_hello_context = cls;
3290 struct PeerConnection *conn;
3291 GNUNET_assert(send_hello_context->peer->daemon->server != NULL);
3293 send_hello_context->core_connect_task = GNUNET_SCHEDULER_NO_TASK;
3295 send_hello_context->connect_attempts++;
3296 if (send_hello_context->connect_attempts < send_hello_context->pg->ct_ctx.connect_attempts)
3298 conn = send_hello_context->peer->connect_peers_head;
3299 while (conn != NULL)
3301 GNUNET_CORE_peer_request_connect(send_hello_context->peer->daemon->server,
3302 &send_hello_context->pg->peers[conn->index].daemon->id,
3307 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) ,
3308 &send_core_connect_requests,
3309 send_hello_context);
3313 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Timeout before all connections created, marking rest as failed!\n");
3314 GNUNET_SCHEDULER_add_now(¬ify_remaining_connections_failed, send_hello_context);
3320 * Success, connection is up. Signal client our success.
3322 * @param cls our "struct SendHelloContext"
3323 * @param peer identity of the peer that has connected
3324 * @param atsi performance information
3326 * FIXME: remove peers from BOTH lists, call notify twice, should
3327 * double the speed of connections as long as the list iteration
3328 * doesn't take too long!
3331 core_connect_notify (void *cls,
3332 const struct GNUNET_PeerIdentity *peer,
3333 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
3335 struct SendHelloContext *send_hello_context = cls;
3336 struct PeerConnection *connection;
3337 struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg;
3339 struct PeerData *other_peer;
3342 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3343 "Connected peer %s to peer %s\n",
3344 ctx->d1->shortname, GNUNET_i2s(peer));
3347 if (0 == memcmp(&send_hello_context->peer->daemon->id, peer, sizeof(struct GNUNET_PeerIdentity)))
3350 connection = send_hello_context->peer->connect_peers_head;
3355 while ((connection != NULL) &&
3356 (0 != memcmp(&pg->peers[connection->index].daemon->id, peer, sizeof(struct GNUNET_PeerIdentity))))
3358 connection = connection->next;
3361 if (connection == NULL)
3363 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);
3368 other_peer = &pg->peers[connection->index];
3370 if (pg->notify_connection != NULL)
3372 pg->notify_connection(pg->notify_connection_cls,
3373 &send_hello_context->peer->daemon->id,
3376 send_hello_context->peer->daemon->cfg,
3377 pg->peers[connection->index].daemon->cfg,
3378 send_hello_context->peer->daemon,
3379 pg->peers[connection->index].daemon,
3382 GNUNET_CONTAINER_DLL_remove(send_hello_context->peer->connect_peers_head, send_hello_context->peer->connect_peers_tail, connection);
3383 GNUNET_free(connection);
3387 /* Notify of reverse connection and remove from other peers list of outstanding */
3388 if (other_peer != NULL)
3390 connection = other_peer->connect_peers_head;
3391 while ((connection != NULL) &&
3392 (0 != memcmp(&send_hello_context->peer->daemon->id, &pg->peers[connection->index].daemon->id, sizeof(struct GNUNET_PeerIdentity))))
3394 connection = connection->next;
3396 if (connection != NULL)
3398 if (pg->notify_connection != NULL)
3400 pg->notify_connection(pg->notify_connection_cls,
3402 &send_hello_context->peer->daemon->id,
3404 pg->peers[connection->index].daemon->cfg,
3405 send_hello_context->peer->daemon->cfg,
3406 pg->peers[connection->index].daemon,
3407 send_hello_context->peer->daemon,
3411 GNUNET_CONTAINER_DLL_remove(other_peer->connect_peers_head, other_peer->connect_peers_tail, connection);
3412 GNUNET_free(connection);
3417 if (send_hello_context->peer->connect_peers_head == NULL)
3419 GNUNET_SCHEDULER_add_now(&free_hello_context, send_hello_context);
3424 * Notify of a successful connection to the core service.
3426 * @param cls a struct SendHelloContext *
3427 * @param server handle to the core service
3428 * @param my_identity the peer identity of this peer
3429 * @param publicKey the public key of the peer
3432 core_init (void *cls,
3433 struct GNUNET_CORE_Handle * server,
3434 const struct GNUNET_PeerIdentity *
3437 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
3440 struct SendHelloContext *send_hello_context = cls;
3441 send_hello_context->core_ready = GNUNET_YES;
3445 * Function called once a hello has been sent
3446 * to the transport, move on to the next one
3447 * or go away forever.
3449 * @param cls the 'struct SendHelloContext *'
3450 * @param tc scheduler context
3453 hello_sent_callback (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3455 struct SendHelloContext *send_hello_context = cls;
3456 //unsigned int pg_iter;
3457 if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
3459 GNUNET_free(send_hello_context);
3463 send_hello_context->pg->remaining_hellos--;
3465 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sent HELLO, have %d remaining!\n", send_hello_context->pg->remaining_hellos);
3467 if (send_hello_context->peer_pos == NULL) /* All HELLOs (for this peer!) have been transmitted! */
3470 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "All hellos for this peer sent, disconnecting transport!\n");
3472 GNUNET_assert(send_hello_context->peer->daemon->th != NULL);
3473 GNUNET_TRANSPORT_disconnect(send_hello_context->peer->daemon->th);
3474 send_hello_context->peer->daemon->th = NULL;
3476 /*if (send_hello_context->pg->remaining_hellos == 0)
3478 for (pg_iter = 0; pg_iter < send_hello_context->pg->max_outstanding_connections; pg_iter++)
3480 preschedule_connect(&send_hello_context->pg->ct_ctx);
3484 GNUNET_assert (send_hello_context->peer->daemon->server == NULL);
3485 send_hello_context->peer->daemon->server = GNUNET_CORE_connect(send_hello_context->peer->cfg,
3489 &core_connect_notify,
3496 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),
3497 &send_core_connect_requests,
3498 send_hello_context);
3501 GNUNET_SCHEDULER_add_now(&schedule_send_hellos, send_hello_context);
3505 * Connect to a peer, give it all the HELLO's of those peers
3506 * we will later ask it to connect to.
3508 * @param ct_ctx the overall connection context
3510 static void schedule_send_hellos (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3512 struct SendHelloContext *send_hello_context = cls;
3513 struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg;
3515 if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
3517 GNUNET_free(send_hello_context);
3521 GNUNET_assert(send_hello_context->peer_pos != NULL); /* All of the HELLO sends to be scheduled have been scheduled! */
3523 if (((send_hello_context->peer->daemon->th == NULL) &&
3524 (pg->outstanding_connects > pg->max_outstanding_connections)) ||
3525 (pg->stop_connects == GNUNET_YES))
3527 #if VERBOSE_TESTING > 2
3528 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3530 ("Delaying connect, we have too many outstanding connections!\n"));
3532 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
3533 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
3534 &schedule_send_hellos, send_hello_context);
3538 #if VERBOSE_TESTING > 2
3539 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3540 _("Creating connection, outstanding_connections is %d\n"),
3541 outstanding_connects);
3543 if (send_hello_context->peer->daemon->th == NULL)
3545 pg->outstanding_connects++; /* Actual TRANSPORT, CORE connections! */
3546 send_hello_context->peer->daemon->th
3547 = GNUNET_TRANSPORT_connect (send_hello_context->peer->cfg, NULL,
3548 send_hello_context, NULL, NULL, NULL);
3551 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3552 _("Offering Hello of peer %s to peer %s\n"),
3553 send_hello_context->peer->daemon->shortname, pg->peers[send_hello_context->peer_pos->index].daemon->shortname);
3555 GNUNET_TRANSPORT_offer_hello(send_hello_context->peer->daemon->th,
3556 (const struct GNUNET_MessageHeader *)pg->peers[send_hello_context->peer_pos->index].daemon->hello,
3557 &hello_sent_callback,
3558 send_hello_context);
3559 send_hello_context->peer_pos = send_hello_context->peer_pos->next;
3560 GNUNET_assert(send_hello_context->peer->daemon->th != NULL);
3566 * Internal notification of a connection, kept so that we can ensure some connections
3567 * happen instead of flooding all testing daemons with requests to connect.
3570 internal_connect_notify(void *cls, const struct GNUNET_PeerIdentity *first,
3571 const struct GNUNET_PeerIdentity *second,
3573 const struct GNUNET_CONFIGURATION_Handle *first_cfg,
3574 const struct GNUNET_CONFIGURATION_Handle *second_cfg,
3575 struct GNUNET_TESTING_Daemon *first_daemon,
3576 struct GNUNET_TESTING_Daemon *second_daemon,
3579 struct ConnectContext *connect_ctx = cls;
3580 struct ConnectTopologyContext *ct_ctx = connect_ctx->ct_ctx;
3581 struct GNUNET_TESTING_PeerGroup *pg = ct_ctx->pg;
3582 struct PeerConnection *connection;
3584 GNUNET_assert (0 < pg->outstanding_connects);
3585 pg->outstanding_connects--;
3588 * Check whether the inverse connection has been scheduled yet,
3589 * if not, we can remove it from the other peers list and avoid
3590 * even trying to connect them again!
3592 connection = pg->peers[connect_ctx->second_index].connect_peers_head;
3597 while ((connection != NULL) && (0
3598 != memcmp (first, &pg->peers[connection->index].daemon->id,
3599 sizeof(struct GNUNET_PeerIdentity))))
3601 connection = connection->next;
3604 if (connection != NULL) /* Can safely remove! */
3606 GNUNET_assert (0 < ct_ctx->remaining_connections);
3607 ct_ctx->remaining_connections--;
3608 if (pg->notify_connection != NULL) /* Notify of reverse connection */
3609 pg->notify_connection (pg->notify_connection_cls, second, first,
3610 distance, second_cfg, first_cfg, second_daemon,
3611 first_daemon, emsg);
3613 GNUNET_CONTAINER_DLL_remove(pg->peers[connect_ctx->second_index].connect_peers_head, pg->peers[connect_ctx->second_index].connect_peers_tail, connection);
3614 GNUNET_free(connection);
3617 if (ct_ctx->remaining_connections == 0)
3619 if (ct_ctx->notify_connections_done != NULL)
3621 ct_ctx->notify_connections_done (ct_ctx->notify_cls, NULL);
3622 ct_ctx->notify_connections_done = NULL;
3626 preschedule_connect (pg);
3628 if (pg->notify_connection != NULL)
3629 pg->notify_connection (pg->notify_connection_cls, first, second, distance,
3630 first_cfg, second_cfg, first_daemon, second_daemon,
3633 GNUNET_free(connect_ctx);
3637 * Either delay a connection (because there are too many outstanding)
3638 * or schedule it for right now.
3640 * @param cls a connection context
3641 * @param tc the task runtime context
3644 schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3646 struct ConnectContext *connect_context = cls;
3647 struct GNUNET_TESTING_PeerGroup *pg = connect_context->ct_ctx->pg;
3649 if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
3652 if ((pg->outstanding_connects > pg->max_outstanding_connections)
3653 || (pg->stop_connects == GNUNET_YES))
3656 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3658 ("Delaying connect, we have too many outstanding connections!\n"));
3660 GNUNET_SCHEDULER_add_delayed (
3661 GNUNET_TIME_relative_multiply (
3662 GNUNET_TIME_UNIT_MILLISECONDS,
3664 &schedule_connect, connect_context);
3669 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3670 _("Creating connection, outstanding_connections is %d (max %d)\n"),
3671 pg->outstanding_connects, pg->max_outstanding_connections);
3673 pg->outstanding_connects++;
3674 pg->total_connects_scheduled++;
3675 GNUNET_TESTING_daemons_connect (
3676 pg->peers[connect_context->first_index].daemon,
3677 pg->peers[connect_context->second_index].daemon,
3678 connect_context->ct_ctx->connect_timeout,
3679 connect_context->ct_ctx->connect_attempts,
3685 &internal_connect_notify, connect_context); /* FIXME: free connect context! */
3691 * Iterator for actually scheduling connections to be created
3692 * between two peers.
3694 * @param cls closure, a GNUNET_TESTING_Daemon
3695 * @param key the key the second Daemon was stored under
3696 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3698 * @return GNUNET_YES to continue iteration
3701 connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3703 struct ConnectTopologyContext *ct_ctx = cls;
3704 struct PeerData *first = ct_ctx->first;
3705 struct GNUNET_TESTING_Daemon *second = value;
3706 struct ConnectContext *connect_context;
3708 connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
3709 connect_context->first = first->daemon;
3710 connect_context->second = second;
3711 connect_context->ct_ctx = ct_ctx;
3712 GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
3720 * Iterator for copying all entries in the allowed hashmap to the
3723 * @param cls closure, a GNUNET_TESTING_Daemon
3724 * @param key the key the second Daemon was stored under
3725 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3727 * @return GNUNET_YES to continue iteration
3730 copy_topology_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3732 struct PeerData *first = cls;
3734 GNUNET_assert (GNUNET_OK ==
3735 GNUNET_CONTAINER_multihashmap_put (first->connect_peers, key,
3737 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3744 * Make the peers to connect the same as those that are allowed to be
3747 * @param pg the peer group
3750 copy_allowed_topology(struct GNUNET_TESTING_PeerGroup *pg)
3752 unsigned int pg_iter;
3756 struct PeerConnection *iter;
3760 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3763 iter = pg->peers[pg_iter].allowed_peers_head;
3764 while (iter != NULL)
3766 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3767 "Creating connection between %d and %d\n", pg_iter,
3769 total += add_connections (pg, pg_iter, iter->index, CONNECT,
3771 //total += add_actual_connections(pg, pg_iter, iter->index);
3776 GNUNET_CONTAINER_multihashmap_iterate (pg->
3777 peers[pg_iter].allowed_peers,
3778 ©_topology_iterator,
3779 &pg->peers[pg_iter]);
3781 if (GNUNET_SYSERR == ret)
3782 return GNUNET_SYSERR;
3784 total = total + ret;
3791 * Connect the topology as specified by the PeerConnection's
3792 * of each peer in the peer group
3794 * @param pg the peer group we are dealing with
3795 * @param connect_timeout how long try connecting two peers
3796 * @param connect_attempts how many times (max) to attempt
3797 * @param notify_callback callback to notify when finished
3798 * @param notify_cls closure for notify callback
3800 * @return the number of connections that will be attempted
3803 connect_topology(struct GNUNET_TESTING_PeerGroup *pg,
3804 struct GNUNET_TIME_Relative connect_timeout,
3805 unsigned int connect_attempts,
3806 GNUNET_TESTING_NotifyCompletion notify_callback,
3809 unsigned int pg_iter;
3813 struct PeerConnection *connection_iter;
3816 struct SendHelloContext *send_hello_context;
3820 pg->ct_ctx.notify_connections_done = notify_callback;
3821 pg->ct_ctx.notify_cls = notify_cls;
3824 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3827 connection_iter = pg->peers[pg_iter].connect_peers_head;
3828 while (connection_iter != NULL)
3830 connection_iter = connection_iter->next;
3835 GNUNET_CONTAINER_multihashmap_size (pg->peers[pg_iter].connect_peers);
3842 pg->ct_ctx.connect_timeout = connect_timeout;
3843 pg->ct_ctx.connect_attempts = connect_attempts;
3844 pg->ct_ctx.remaining_connections = total;
3847 /* First give all peers the HELLO's of other peers (connect to first peer's transport service, give HELLO's of other peers, continue...) */
3848 pg->remaining_hellos = total;
3849 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3851 send_hello_context = GNUNET_malloc(sizeof(struct SendHelloContext));
3852 send_hello_context->peer = &pg->peers[pg_iter];
3853 send_hello_context->peer_pos = pg->peers[pg_iter].connect_peers_head;
3854 send_hello_context->pg = pg;
3855 GNUNET_SCHEDULER_add_now(&schedule_send_hellos, send_hello_context);
3858 for (pg_iter = 0; pg_iter < pg->max_outstanding_connections; pg_iter++)
3860 preschedule_connect (pg);
3868 * Takes a peer group and creates a topology based on the
3869 * one specified. Creates a topology means generates friend
3870 * files for the peers so they can only connect to those allowed
3871 * by the topology. This will only have an effect once peers
3872 * are started if the FRIENDS_ONLY option is set in the base
3873 * config. Also takes an optional restrict topology which
3874 * disallows connections based on particular transports
3875 * UNLESS they are specified in the restricted topology.
3877 * @param pg the peer group struct representing the running peers
3878 * @param topology which topology to connect the peers in
3879 * @param restrict_topology disallow restrict_transports transport
3880 * connections to peers NOT in this topology
3881 * use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
3882 * @param restrict_transports space delimited list of transports to blacklist
3883 * to create restricted topology
3885 * @return the maximum number of connections were all allowed peers
3886 * connected to each other
3889 GNUNET_TESTING_create_topology(struct GNUNET_TESTING_PeerGroup *pg,
3890 enum GNUNET_TESTING_Topology topology,
3891 enum GNUNET_TESTING_Topology restrict_topology,
3892 const char *restrict_transports)
3896 unsigned int num_connections;
3897 int unblacklisted_connections;
3899 struct PeerConnection *conn_iter;
3900 struct PeerConnection *temp_conn;
3905 for (i = 0; i < pg->total; i++)
3907 pg->peers[i].allowed_peers =
3908 GNUNET_CONTAINER_multihashmap_create (100);
3909 pg->peers[i].connect_peers =
3910 GNUNET_CONTAINER_multihashmap_create (100);
3911 pg->peers[i].blacklisted_peers =
3912 GNUNET_CONTAINER_multihashmap_create (100);
3913 pg->peers[i].pg = pg;
3919 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
3921 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating clique topology\n"));
3923 num_connections = create_clique (pg, &add_connections, ALLOWED, GNUNET_NO);
3925 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
3927 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3928 _("Creating small world (ring) topology\n"));
3930 num_connections = create_small_world_ring (pg, &add_connections, ALLOWED);
3932 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
3934 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3935 _("Creating small world (2d-torus) topology\n"));
3937 num_connections = create_small_world (pg, &add_connections, ALLOWED);
3939 case GNUNET_TESTING_TOPOLOGY_RING:
3941 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating ring topology\n"));
3943 num_connections = create_ring (pg, &add_connections, ALLOWED);
3945 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
3947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating 2d torus topology\n"));
3949 num_connections = create_2d_torus (pg, &add_connections, ALLOWED);
3951 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
3953 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3954 _("Creating Erdos-Renyi topology\n"));
3956 num_connections = create_erdos_renyi (pg, &add_connections, ALLOWED);
3958 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
3960 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating InterNAT topology\n"));
3962 num_connections = create_nated_internet (pg, &add_connections, ALLOWED);
3964 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
3966 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3967 _("Creating Scale Free topology\n"));
3969 num_connections = create_scale_free (pg, &add_connections, ALLOWED);
3971 case GNUNET_TESTING_TOPOLOGY_LINE:
3973 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3974 _("Creating straight line topology\n"));
3976 num_connections = create_line (pg, &add_connections, ALLOWED);
3978 case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
3980 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3981 _("Creating topology from file!\n"));
3983 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg, "testing",
3986 num_connections = create_from_file (pg, filename, &add_connections,
3991 GNUNET_ERROR_TYPE_WARNING,
3992 "Missing configuration option TESTING:TOPOLOGY_FILE for creating topology from file!\n");
3993 num_connections = 0;
3996 case GNUNET_TESTING_TOPOLOGY_NONE:
3998 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4000 ("Creating no allowed topology (all peers can connect at core level)\n"));
4002 num_connections = pg->total * pg->total; /* Clique is allowed! */
4005 num_connections = 0;
4009 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING",
4012 ret = create_and_copy_friend_files (pg);
4013 if (ret != GNUNET_OK)
4016 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4017 _("Failed during friend file copying!\n"));
4019 return GNUNET_SYSERR;
4024 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4025 _("Friend files created/copied successfully!\n"));
4030 /* Use the create clique method to initially set all connections as blacklisted. */
4031 if ((restrict_topology != GNUNET_TESTING_TOPOLOGY_NONE) && (restrict_topology
4032 != GNUNET_TESTING_TOPOLOGY_FROM_FILE))
4033 create_clique (pg, &add_connections, BLACKLIST, GNUNET_NO);
4035 return num_connections;
4037 unblacklisted_connections = 0;
4038 /* Un-blacklist connections as per the topology specified */
4039 switch (restrict_topology)
4041 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
4043 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4044 _("Blacklisting all but clique topology\n"));
4046 unblacklisted_connections = create_clique (pg, &remove_connections,
4047 BLACKLIST, GNUNET_NO);
4049 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
4051 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4052 _("Blacklisting all but small world (ring) topology\n"));
4054 unblacklisted_connections = create_small_world_ring (pg,
4055 &remove_connections,
4058 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
4060 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4062 ("Blacklisting all but small world (2d-torus) topology\n"));
4064 unblacklisted_connections = create_small_world (pg, &remove_connections,
4067 case GNUNET_TESTING_TOPOLOGY_RING:
4069 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4070 _("Blacklisting all but ring topology\n"));
4072 unblacklisted_connections
4073 = create_ring (pg, &remove_connections, BLACKLIST);
4075 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
4077 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4078 _("Blacklisting all but 2d torus topology\n"));
4080 unblacklisted_connections = create_2d_torus (pg, &remove_connections,
4083 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
4085 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4086 _("Blacklisting all but Erdos-Renyi topology\n"));
4088 unblacklisted_connections = create_erdos_renyi (pg, &remove_connections,
4091 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
4093 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4094 _("Blacklisting all but InterNAT topology\n"));
4098 for (off = 0; off < pg->total; off++)
4100 conn_iter = pg->peers[off].allowed_peers_head;
4101 while (conn_iter != NULL)
4103 temp_conn = conn_iter->next;
4104 GNUNET_free(conn_iter);
4105 conn_iter = temp_conn;
4107 pg->peers[off].allowed_peers_head = NULL;
4108 pg->peers[off].allowed_peers_tail = NULL;
4110 conn_iter = pg->peers[off].connect_peers_head;
4111 while (conn_iter != NULL)
4113 temp_conn = conn_iter->next;
4114 GNUNET_free(conn_iter);
4115 conn_iter = temp_conn;
4117 pg->peers[off].connect_peers_head = NULL;
4118 pg->peers[off].connect_peers_tail = NULL;
4120 unblacklisted_connections
4121 = create_nated_internet_copy (pg, &remove_connections, BLACKLIST);
4123 unblacklisted_connections =
4124 create_nated_internet (pg, &remove_connections, BLACKLIST);
4128 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
4130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4131 _("Blacklisting all but Scale Free topology\n"));
4133 unblacklisted_connections = create_scale_free (pg, &remove_connections,
4136 case GNUNET_TESTING_TOPOLOGY_LINE:
4138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4139 _("Blacklisting all but straight line topology\n"));
4141 unblacklisted_connections
4142 = create_line (pg, &remove_connections, BLACKLIST);
4147 if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
4149 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Creating blacklist with `%s'\n",
4150 restrict_transports);
4151 ret = create_and_copy_blacklist_files (pg, restrict_transports);
4152 if (ret != GNUNET_OK)
4155 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4156 _("Failed during blacklist file copying!\n"));
4163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4164 _("Blacklist files created/copied successfully!\n"));
4168 return num_connections;
4173 * Iterator for choosing random peers to connect.
4175 * @param cls closure, a RandomContext
4176 * @param key the key the second Daemon was stored under
4177 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
4179 * @return GNUNET_YES to continue iteration
4182 random_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
4184 struct RandomContext *random_ctx = cls;
4185 double random_number;
4186 uint32_t second_pos;
4187 GNUNET_HashCode first_hash;
4190 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
4191 UINT64_MAX)) / ((double) UINT64_MAX);
4192 if (random_number < random_ctx->percentage)
4194 GNUNET_assert (GNUNET_OK ==
4195 GNUNET_CONTAINER_multihashmap_put (random_ctx->
4196 first->connect_peers_working_set,
4198 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4201 /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
4202 uid_from_hash (key, &second_pos);
4203 hash_from_uid (random_ctx->first_uid, &first_hash);
4204 GNUNET_assert (random_ctx->pg->total > second_pos);
4205 GNUNET_assert (GNUNET_YES ==
4206 GNUNET_CONTAINER_multihashmap_remove (random_ctx->
4208 [second_pos].connect_peers,
4217 * Iterator for adding at least X peers to a peers connection set.
4219 * @param cls closure, MinimumContext
4220 * @param key the key the second Daemon was stored under
4221 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
4223 * @return GNUNET_YES to continue iteration
4226 minimum_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
4228 struct MinimumContext *min_ctx = cls;
4229 uint32_t second_pos;
4230 GNUNET_HashCode first_hash;
4233 if (GNUNET_CONTAINER_multihashmap_size
4234 (min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
4236 for (i = 0; i < min_ctx->num_to_add; i++)
4238 if (min_ctx->pg_array[i] == min_ctx->current)
4240 GNUNET_assert (GNUNET_OK ==
4241 GNUNET_CONTAINER_multihashmap_put
4242 (min_ctx->first->connect_peers_working_set, key,
4244 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4245 uid_from_hash (key, &second_pos);
4246 hash_from_uid (min_ctx->first_uid, &first_hash);
4247 GNUNET_assert (min_ctx->pg->total > second_pos);
4248 GNUNET_assert (GNUNET_OK ==
4249 GNUNET_CONTAINER_multihashmap_put (min_ctx->
4251 [second_pos].connect_peers_working_set,
4255 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4256 /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
4257 GNUNET_assert (GNUNET_YES ==
4258 GNUNET_CONTAINER_multihashmap_remove
4259 (min_ctx->pg->peers[second_pos].connect_peers,
4260 &first_hash, min_ctx->first->daemon));
4267 return GNUNET_NO; /* We can stop iterating, we have enough peers! */
4272 * Iterator for adding peers to a connection set based on a depth first search.
4274 * @param cls closure, MinimumContext
4275 * @param key the key the second daemon was stored under
4276 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
4278 * @return GNUNET_YES to continue iteration
4281 dfs_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
4283 struct DFSContext *dfs_ctx = cls;
4284 GNUNET_HashCode first_hash;
4286 if (dfs_ctx->current == dfs_ctx->chosen)
4288 GNUNET_assert (GNUNET_OK ==
4289 GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
4290 first->connect_peers_working_set,
4292 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4293 uid_from_hash (key, &dfs_ctx->second_uid);
4294 hash_from_uid (dfs_ctx->first_uid, &first_hash);
4295 GNUNET_assert (GNUNET_OK ==
4296 GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
4298 [dfs_ctx->second_uid].connect_peers_working_set,
4302 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4303 GNUNET_assert (GNUNET_YES ==
4304 GNUNET_CONTAINER_multihashmap_remove (dfs_ctx->
4306 [dfs_ctx->second_uid].connect_peers,
4310 /* Can't remove second from first yet because we are currently iterating, hence the return value in the DFSContext! */
4311 return GNUNET_NO; /* We have found our peer, don't iterate more */
4320 * From the set of connections possible, choose percentage percent of connections
4321 * to actually connect.
4323 * @param pg the peergroup we are dealing with
4324 * @param percentage what percent of total connections to make
4327 choose_random_connections(struct GNUNET_TESTING_PeerGroup *pg,
4332 struct PeerConnection *conn_iter;
4333 double random_number;
4335 struct RandomContext random_ctx;
4338 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4341 conn_iter = pg->peers[pg_iter].connect_peers_head;
4342 while (conn_iter != NULL)
4345 = ((double) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
4347 / ((double) UINT64_MAX);
4348 if (random_number < percentage)
4350 add_connections (pg, pg_iter, conn_iter->index, WORKING_SET,
4353 conn_iter = conn_iter->next;
4356 random_ctx.first_uid = pg_iter;
4357 random_ctx.first = &pg->peers[pg_iter];
4358 random_ctx.percentage = percentage;
4360 pg->peers[pg_iter].connect_peers_working_set
4361 = GNUNET_CONTAINER_multihashmap_create (pg->total);
4362 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
4363 &random_connect_iterator,
4365 /* Now remove the old connections */
4366 GNUNET_CONTAINER_multihashmap_destroy (pg->
4367 peers[pg_iter].connect_peers);
4368 /* And replace with the random set */
4369 pg->peers[pg_iter].connect_peers
4370 = pg->peers[pg_iter].connect_peers_working_set;
4374 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4376 conn_iter = pg->peers[pg_iter].connect_peers_head;
4377 while (pg->peers[pg_iter].connect_peers_head != NULL)
4378 remove_connections (pg, pg_iter,
4379 pg->peers[pg_iter].connect_peers_head->index,
4380 CONNECT, GNUNET_YES);
4382 pg->peers[pg_iter].connect_peers_head
4383 = pg->peers[pg_iter].connect_peers_working_set_head;
4384 pg->peers[pg_iter].connect_peers_tail
4385 = pg->peers[pg_iter].connect_peers_working_set_tail;
4386 pg->peers[pg_iter].connect_peers_working_set_head = NULL;
4387 pg->peers[pg_iter].connect_peers_working_set_tail = NULL;
4392 * Count the number of connections in a linked list of connections.
4394 * @param conn_list the connection list to get the count of
4396 * @return the number of elements in the list
4399 count_connections(struct PeerConnection *conn_list)
4401 struct PeerConnection *iter;
4405 while (iter != NULL)
4414 count_workingset_connections(struct GNUNET_TESTING_PeerGroup *pg)
4417 unsigned int pg_iter;
4419 struct PeerConnection *conn_iter;
4423 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4426 conn_iter = pg->peers[pg_iter].connect_peers_working_set_head;
4427 while (conn_iter != NULL)
4430 conn_iter = conn_iter->next;
4434 GNUNET_CONTAINER_multihashmap_size (pg->
4436 [pg_iter].connect_peers_working_set);
4444 count_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg)
4447 unsigned int pg_iter;
4449 struct PeerConnection *conn_iter;
4453 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4456 conn_iter = pg->peers[pg_iter].allowed_peers_head;
4457 while (conn_iter != NULL)
4460 conn_iter = conn_iter->next;
4464 GNUNET_CONTAINER_multihashmap_size (pg->
4466 [pg_iter].allowed_peers);
4474 * From the set of connections possible, choose at least num connections per
4477 * @param pg the peergroup we are dealing with
4478 * @param num how many connections at least should each peer have (if possible)?
4481 choose_minimum(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
4484 struct MinimumContext minimum_ctx;
4486 struct PeerConnection *conn_iter;
4487 unsigned int temp_list_size;
4490 uint32_t random; /* Random list entry to connect peer to */
4495 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4498 = count_connections (pg->peers[pg_iter].connect_peers_head);
4499 if (temp_list_size == 0)
4501 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4502 "Peer %d has 0 connections!?!?\n", pg_iter);
4505 for (i = 0; i < num; i++)
4507 random = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
4509 conn_iter = pg->peers[pg_iter].connect_peers_head;
4510 for (count = 0; count < random; count++)
4511 conn_iter = conn_iter->next;
4512 /* We now have a random connection, connect it! */
4513 GNUNET_assert(conn_iter != NULL);
4514 add_connections (pg, pg_iter, conn_iter->index, WORKING_SET,
4519 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4521 pg->peers[pg_iter].connect_peers_working_set =
4522 GNUNET_CONTAINER_multihashmap_create (num);
4525 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4527 minimum_ctx.first_uid = pg_iter;
4528 minimum_ctx.pg_array =
4529 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
4530 GNUNET_CONTAINER_multihashmap_size
4531 (pg->peers[pg_iter].connect_peers));
4532 minimum_ctx.first = &pg->peers[pg_iter];
4533 minimum_ctx.pg = pg;
4534 minimum_ctx.num_to_add = num;
4535 minimum_ctx.current = 0;
4536 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
4537 &minimum_connect_iterator,
4541 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4543 /* Remove the "old" connections */
4544 GNUNET_CONTAINER_multihashmap_destroy (pg->
4545 peers[pg_iter].connect_peers);
4546 /* And replace with the working set */
4547 pg->peers[pg_iter].connect_peers =
4548 pg->peers[pg_iter].connect_peers_working_set;
4551 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4553 while (pg->peers[pg_iter].connect_peers_head != NULL)
4555 conn_iter = pg->peers[pg_iter].connect_peers_head;
4556 GNUNET_CONTAINER_DLL_remove(pg->peers[pg_iter].connect_peers_head,
4557 pg->peers[pg_iter].connect_peers_tail,
4559 GNUNET_free(conn_iter);
4560 /*remove_connections(pg, pg_iter, pg->peers[pg_iter].connect_peers_head->index, CONNECT, GNUNET_YES);*/
4563 pg->peers[pg_iter].connect_peers_head
4564 = pg->peers[pg_iter].connect_peers_working_set_head;
4565 pg->peers[pg_iter].connect_peers_tail
4566 = pg->peers[pg_iter].connect_peers_working_set_tail;
4567 pg->peers[pg_iter].connect_peers_working_set_head = NULL;
4568 pg->peers[pg_iter].connect_peers_working_set_tail = NULL;
4573 struct FindClosestContext
4576 * The currently known closest peer.
4578 struct GNUNET_TESTING_Daemon *closest;
4581 * The info for the peer we are adding connections for.
4583 struct PeerData *curr_peer;
4586 * The distance (bits) between the current
4587 * peer and the currently known closest.
4589 unsigned int closest_dist;
4592 * The offset of the closest known peer in
4595 unsigned int closest_num;
4599 * Iterator over hash map entries of the allowed
4600 * peer connections. Find the closest, not already
4601 * connected peer and return it.
4603 * @param cls closure (struct FindClosestContext)
4604 * @param key current key code (hash of offset in pg)
4605 * @param value value in the hash map - a GNUNET_TESTING_Daemon
4606 * @return GNUNET_YES if we should continue to
4611 find_closest_peers (void *cls, const GNUNET_HashCode * key, void *value)
4613 struct FindClosestContext *closest_ctx = cls;
4614 struct GNUNET_TESTING_Daemon *daemon = value;
4616 if (((closest_ctx->closest == NULL) ||
4617 (GNUNET_CRYPTO_hash_matching_bits
4618 (&daemon->id.hashPubKey,
4619 &closest_ctx->curr_peer->daemon->id.hashPubKey) >
4620 closest_ctx->closest_dist))
4622 GNUNET_CONTAINER_multihashmap_contains (closest_ctx->
4623 curr_peer->connect_peers,
4626 closest_ctx->closest_dist =
4627 GNUNET_CRYPTO_hash_matching_bits (&daemon->id.hashPubKey,
4628 &closest_ctx->curr_peer->daemon->
4630 closest_ctx->closest = daemon;
4631 uid_from_hash (key, &closest_ctx->closest_num);
4637 * From the set of connections possible, choose at num connections per
4638 * peer based on depth which are closest out of those allowed. Guaranteed
4639 * to add num peers to connect to, provided there are that many peers
4640 * in the underlay topology to connect to.
4642 * @param pg the peergroup we are dealing with
4643 * @param num how many connections at least should each peer have (if possible)?
4644 * @param proc processor to actually add the connections
4645 * @param list the peer list to use
4648 add_closest (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num,
4649 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
4654 struct FindClosestContext closest_ctx;
4659 for (i = 0; i < num; i++) /* Each time find a closest peer (from those available) */
4661 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4663 closest_ctx.curr_peer = &pg->peers[pg_iter];
4664 closest_ctx.closest = NULL;
4665 closest_ctx.closest_dist = 0;
4666 closest_ctx.closest_num = 0;
4667 GNUNET_CONTAINER_multihashmap_iterate (pg->
4668 peers[pg_iter].allowed_peers,
4669 &find_closest_peers,
4671 if (closest_ctx.closest != NULL)
4673 GNUNET_assert (closest_ctx.closest_num < pg->total);
4674 proc (pg, pg_iter, closest_ctx.closest_num, list);
4682 * From the set of connections possible, choose at least num connections per
4683 * peer based on depth first traversal of peer connections. If DFS leaves
4684 * peers unconnected, ensure those peers get connections.
4686 * @param pg the peergroup we are dealing with
4687 * @param num how many connections at least should each peer have (if possible)?
4690 perform_dfs(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
4694 uint32_t starting_peer;
4695 uint32_t least_connections;
4696 uint32_t random_connection;
4698 unsigned int temp_count;
4699 struct PeerConnection *peer_iter;
4701 struct DFSContext dfs_ctx;
4702 GNUNET_HashCode second_hash;
4708 while ((count_workingset_connections (pg) < num * pg->total)
4709 && (count_allowed_connections (pg) > 0))
4711 if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
4713 least_connections = -1; /* Set to very high number */
4714 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4717 = count_connections (
4718 pg->peers[pg_iter].connect_peers_working_set_head);
4719 if (temp_count < least_connections)
4721 starting_peer = pg_iter;
4722 least_connections = temp_count;
4728 = count_connections (pg->peers[starting_peer].connect_peers_head);
4729 if (temp_count == 0)
4730 continue; /* FIXME: infinite loop? */
4732 random_connection = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
4735 peer_iter = pg->peers[starting_peer].connect_peers_head;
4736 while (temp_count < random_connection)
4738 peer_iter = peer_iter->next;
4741 GNUNET_assert(peer_iter != NULL);
4742 add_connections (pg, starting_peer, peer_iter->index, WORKING_SET,
4744 remove_connections (pg, starting_peer, peer_iter->index, CONNECT,
4746 starting_peer = peer_iter->index;
4751 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4753 pg->peers[pg_iter].connect_peers_working_set =
4754 GNUNET_CONTAINER_multihashmap_create (num);
4759 while ((count_workingset_connections (pg) < num * pg->total)
4760 && (count_allowed_connections (pg) > 0))
4762 if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
4764 least_connections = -1; /* Set to very high number */
4765 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4767 if (GNUNET_CONTAINER_multihashmap_size
4768 (pg->peers[pg_iter].connect_peers_working_set) <
4771 starting_peer = pg_iter;
4773 GNUNET_CONTAINER_multihashmap_size (pg->
4775 [pg_iter].connect_peers_working_set);
4780 if (GNUNET_CONTAINER_multihashmap_size (pg->peers[starting_peer].connect_peers) == 0) /* Ensure there is at least one peer left to connect! */
4786 /* Choose a random peer from the chosen peers set of connections to add */
4788 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
4789 GNUNET_CONTAINER_multihashmap_size
4790 (pg->peers[starting_peer].connect_peers));
4791 dfs_ctx.first_uid = starting_peer;
4792 dfs_ctx.first = &pg->peers[starting_peer];
4794 dfs_ctx.current = 0;
4796 GNUNET_CONTAINER_multihashmap_iterate (pg->
4798 [starting_peer].connect_peers,
4799 &dfs_connect_iterator, &dfs_ctx);
4800 /* Remove the second from the first, since we will be continuing the search and may encounter the first peer again! */
4801 hash_from_uid (dfs_ctx.second_uid, &second_hash);
4802 GNUNET_assert (GNUNET_YES ==
4803 GNUNET_CONTAINER_multihashmap_remove (pg->peers
4804 [starting_peer].connect_peers,
4808 [dfs_ctx.second_uid].daemon));
4809 starting_peer = dfs_ctx.second_uid;
4812 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4814 /* Remove the "old" connections */
4815 GNUNET_CONTAINER_multihashmap_destroy (pg->
4816 peers[pg_iter].connect_peers);
4817 /* And replace with the working set */
4818 pg->peers[pg_iter].connect_peers =
4819 pg->peers[pg_iter].connect_peers_working_set;
4825 * Internal callback for topology information for a particular peer.
4828 internal_topology_callback(void *cls, const struct GNUNET_PeerIdentity *peer,
4829 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
4831 struct CoreContext *core_ctx = cls;
4832 struct TopologyIterateContext *iter_ctx = core_ctx->iter_context;
4834 if (peer == NULL) /* Either finished, or something went wrong */
4836 iter_ctx->completed++;
4837 iter_ctx->connected--;
4838 /* One core context allocated per iteration, must free! */
4839 GNUNET_free (core_ctx);
4843 iter_ctx->topology_cb (iter_ctx->cls, &core_ctx->daemon->id, peer, NULL);
4846 if (iter_ctx->completed == iter_ctx->total)
4848 iter_ctx->topology_cb (iter_ctx->cls, NULL, NULL, NULL);
4849 /* Once all are done, free the iteration context */
4850 GNUNET_free (iter_ctx);
4855 * Check running topology iteration tasks, if below max start a new one, otherwise
4856 * schedule for some time in the future.
4859 schedule_get_topology(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
4861 struct CoreContext *core_context = cls;
4862 struct TopologyIterateContext *topology_context =
4863 (struct TopologyIterateContext *) core_context->iter_context;
4864 if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
4867 if (topology_context->connected
4868 > topology_context->pg->max_outstanding_connections)
4870 #if VERBOSE_TESTING > 2
4871 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4873 ("Delaying connect, we have too many outstanding connections!\n"));
4875 GNUNET_SCHEDULER_add_delayed (
4876 GNUNET_TIME_relative_multiply (
4877 GNUNET_TIME_UNIT_MILLISECONDS,
4879 &schedule_get_topology, core_context);
4883 #if VERBOSE_TESTING > 2
4884 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4885 _("Creating connection, outstanding_connections is %d\n"),
4886 outstanding_connects);
4888 topology_context->connected++;
4890 if (GNUNET_OK != GNUNET_CORE_iterate_peers (core_context->daemon->cfg,
4891 &internal_topology_callback,
4894 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Topology iteration failed.\n");
4895 internal_topology_callback (core_context, NULL, NULL);
4901 * Iterate over all (running) peers in the peer group, retrieve
4902 * all connections that each currently has.
4905 GNUNET_TESTING_get_topology(struct GNUNET_TESTING_PeerGroup *pg,
4906 GNUNET_TESTING_NotifyTopology cb, void *cls)
4908 struct TopologyIterateContext *topology_context;
4909 struct CoreContext *core_ctx;
4911 unsigned int total_count;
4913 /* Allocate a single topology iteration context */
4914 topology_context = GNUNET_malloc (sizeof (struct TopologyIterateContext));
4915 topology_context->topology_cb = cb;
4916 topology_context->cls = cls;
4917 topology_context->pg = pg;
4919 for (i = 0; i < pg->total; i++)
4921 if (pg->peers[i].daemon->running == GNUNET_YES)
4923 /* Allocate one core context per core we need to connect to */
4924 core_ctx = GNUNET_malloc (sizeof (struct CoreContext));
4925 core_ctx->daemon = pg->peers[i].daemon;
4926 /* Set back pointer to topology iteration context */
4927 core_ctx->iter_context = topology_context;
4928 GNUNET_SCHEDULER_add_now (&schedule_get_topology, core_ctx);
4932 if (total_count == 0)
4934 cb (cls, NULL, NULL, "Cannot iterate over topology, no running peers!");
4935 GNUNET_free (topology_context);
4938 topology_context->total = total_count;
4943 * Callback function to process statistic values.
4944 * This handler is here only really to insert a peer
4945 * identity (or daemon) so the statistics can be uniquely
4946 * tied to a single running peer.
4948 * @param cls closure
4949 * @param subsystem name of subsystem that created the statistic
4950 * @param name the name of the datum
4951 * @param value the current value
4952 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
4953 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
4956 internal_stats_callback(void *cls, const char *subsystem, const char *name,
4957 uint64_t value, int is_persistent)
4959 struct StatsCoreContext *core_context = cls;
4960 struct StatsIterateContext *stats_context =
4961 (struct StatsIterateContext *) core_context->iter_context;
4963 return stats_context->proc (stats_context->cls, &core_context->daemon->id,
4964 subsystem, name, value, is_persistent);
4968 * Internal continuation call for statistics iteration.
4970 * @param cls closure, the CoreContext for this iteration
4971 * @param success whether or not the statistics iterations
4972 * was canceled or not (we don't care)
4975 internal_stats_cont(void *cls, int success)
4977 struct StatsCoreContext *core_context = cls;
4978 struct StatsIterateContext *stats_context =
4979 (struct StatsIterateContext *) core_context->iter_context;
4981 stats_context->connected--;
4982 stats_context->completed++;
4984 if (stats_context->completed == stats_context->total)
4986 stats_context->cont (stats_context->cls, GNUNET_YES);
4987 GNUNET_free (stats_context);
4990 if (core_context->stats_handle != NULL)
4991 GNUNET_STATISTICS_destroy (core_context->stats_handle, GNUNET_NO);
4993 GNUNET_free (core_context);
4997 * Check running topology iteration tasks, if below max start a new one, otherwise
4998 * schedule for some time in the future.
5001 schedule_get_statistics(void *cls,
5002 const struct GNUNET_SCHEDULER_TaskContext *tc)
5004 struct StatsCoreContext *core_context = cls;
5005 struct StatsIterateContext *stats_context =
5006 (struct StatsIterateContext *) core_context->iter_context;
5008 if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
5011 if (stats_context->connected > stats_context->pg->max_outstanding_connections)
5013 #if VERBOSE_TESTING > 2
5014 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5016 ("Delaying connect, we have too many outstanding connections!\n"));
5018 GNUNET_SCHEDULER_add_delayed (
5019 GNUNET_TIME_relative_multiply (
5020 GNUNET_TIME_UNIT_MILLISECONDS,
5022 &schedule_get_statistics, core_context);
5026 #if VERBOSE_TESTING > 2
5027 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5028 _("Creating connection, outstanding_connections is %d\n"),
5029 outstanding_connects);
5032 stats_context->connected++;
5033 core_context->stats_handle
5034 = GNUNET_STATISTICS_create ("testing", core_context->daemon->cfg);
5035 if (core_context->stats_handle == NULL)
5037 internal_stats_cont (core_context, GNUNET_NO);
5041 core_context->stats_get_handle
5042 = GNUNET_STATISTICS_get (core_context->stats_handle, NULL, NULL,
5043 GNUNET_TIME_relative_get_forever (),
5044 &internal_stats_cont,
5045 &internal_stats_callback, core_context);
5046 if (core_context->stats_get_handle == NULL)
5047 internal_stats_cont (core_context, GNUNET_NO);
5052 struct DuplicateStats
5055 * Next item in the list
5057 struct DuplicateStats *next;
5060 * Nasty string, concatenation of relevant information.
5062 char *unique_string;
5066 * Check whether the combination of port/host/unix domain socket
5067 * already exists in the list of peers being checked for statistics.
5069 * @param pg the peergroup in question
5070 * @param specific_peer the peer we're concerned with
5071 * @param stats_list the list to return to the caller
5073 * @return GNUNET_YES if the statistics instance has been seen already,
5074 * GNUNET_NO if not (and we may have added it to the list)
5077 stats_check_existing(struct GNUNET_TESTING_PeerGroup *pg,
5078 struct PeerData *specific_peer,
5079 struct DuplicateStats **stats_list)
5081 struct DuplicateStats *pos;
5082 char *unix_domain_socket;
5083 unsigned long long port;
5086 != GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "testing",
5087 "single_statistics_per_host"))
5088 return GNUNET_NO; /* Each peer has its own statistics instance, do nothing! */
5091 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (specific_peer->cfg,
5094 &unix_domain_socket))
5097 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (specific_peer->cfg,
5098 "statistics", "port",
5101 GNUNET_free(unix_domain_socket);
5105 if (specific_peer->daemon->hostname != NULL)
5106 GNUNET_asprintf (&to_match, "%s%s%llu", specific_peer->daemon->hostname,
5107 unix_domain_socket, port);
5109 GNUNET_asprintf (&to_match, "%s%llu", unix_domain_socket, port);
5113 if (0 == strcmp (to_match, pos->unique_string))
5115 GNUNET_free (unix_domain_socket);
5116 GNUNET_free (to_match);
5121 pos = GNUNET_malloc (sizeof (struct DuplicateStats));
5122 pos->unique_string = to_match;
5123 pos->next = *stats_list;
5125 GNUNET_free (unix_domain_socket);
5130 * Iterate over all (running) peers in the peer group, retrieve
5131 * all statistics from each.
5133 * @param pg the peergroup to iterate statistics of
5134 * @param cont continuation to call once all stats have been retrieved
5135 * @param proc processing function for each statistic from each peer
5136 * @param cls closure to pass to proc
5140 GNUNET_TESTING_get_statistics(struct GNUNET_TESTING_PeerGroup *pg,
5141 GNUNET_STATISTICS_Callback cont,
5142 GNUNET_TESTING_STATISTICS_Iterator proc,
5145 struct StatsIterateContext *stats_context;
5146 struct StatsCoreContext *core_ctx;
5148 unsigned int total_count;
5149 struct DuplicateStats *stats_list;
5150 struct DuplicateStats *pos;
5153 /* Allocate a single stats iteration context */
5154 stats_context = GNUNET_malloc (sizeof (struct StatsIterateContext));
5155 stats_context->cont = cont;
5156 stats_context->proc = proc;
5157 stats_context->cls = cls;
5158 stats_context->pg = pg;
5161 for (i = 0; i < pg->total; i++)
5163 if ((pg->peers[i].daemon->running == GNUNET_YES) && (GNUNET_NO
5164 == stats_check_existing (pg, &pg->peers[i], &stats_list)))
5166 /* Allocate one core context per core we need to connect to */
5167 core_ctx = GNUNET_malloc (sizeof (struct StatsCoreContext));
5168 core_ctx->daemon = pg->peers[i].daemon;
5169 /* Set back pointer to topology iteration context */
5170 core_ctx->iter_context = stats_context;
5171 GNUNET_SCHEDULER_add_now (&schedule_get_statistics, core_ctx);
5176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5177 "Retrieving stats from %u total instances.\n", total_count);
5178 stats_context->total = total_count;
5179 if (stats_list != NULL)
5184 GNUNET_free (pos->unique_string);
5185 stats_list = pos->next;
5187 pos = stats_list->next;
5194 * Stop the connection process temporarily.
5196 * @param pg the peer group to stop connecting
5199 GNUNET_TESTING_stop_connections(struct GNUNET_TESTING_PeerGroup *pg)
5201 pg->stop_connects = GNUNET_YES;
5205 * Resume the connection process temporarily.
5207 * @param pg the peer group to resume connecting
5210 GNUNET_TESTING_resume_connections(struct GNUNET_TESTING_PeerGroup *pg)
5212 pg->stop_connects = GNUNET_NO;
5216 * There are many ways to connect peers that are supported by this function.
5217 * To connect peers in the same topology that was created via the
5218 * GNUNET_TESTING_create_topology, the topology variable must be set to
5219 * GNUNET_TESTING_TOPOLOGY_NONE. If the topology variable is specified,
5220 * a new instance of that topology will be generated and attempted to be
5221 * connected. This could result in some connections being impossible,
5222 * because some topologies are non-deterministic.
5224 * @param pg the peer group struct representing the running peers
5225 * @param topology which topology to connect the peers in
5226 * @param options options for connecting the topology
5227 * @param option_modifier modifier for options that take a parameter
5228 * @param connect_timeout how long to wait before giving up on connecting
5230 * @param connect_attempts how many times to attempt to connect two peers
5231 * over the connect_timeout duration
5232 * @param notify_callback notification to be called once all connections completed
5233 * @param notify_cls closure for notification callback
5235 * @return the number of connections that will be attempted, GNUNET_SYSERR on error
5238 GNUNET_TESTING_connect_topology(
5239 struct GNUNET_TESTING_PeerGroup *pg,
5240 enum GNUNET_TESTING_Topology topology,
5241 enum GNUNET_TESTING_TopologyOption options,
5242 double option_modifier,
5243 struct GNUNET_TIME_Relative connect_timeout,
5244 unsigned int connect_attempts,
5245 GNUNET_TESTING_NotifyCompletion notify_callback,
5250 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
5251 #if VERBOSE_TOPOLOGY
5252 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5253 _("Creating clique CONNECT topology\n"));
5255 create_clique (pg, &add_connections, CONNECT, GNUNET_NO);
5257 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
5258 #if VERBOSE_TOPOLOGY
5259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5260 _("Creating small world (ring) CONNECT topology\n"));
5262 create_small_world_ring (pg, &add_connections, CONNECT);
5264 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
5265 #if VERBOSE_TOPOLOGY
5266 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5267 _("Creating small world (2d-torus) CONNECT topology\n"));
5269 create_small_world (pg, &add_connections, CONNECT);
5271 case GNUNET_TESTING_TOPOLOGY_RING:
5272 #if VERBOSE_TOPOLOGY
5273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating ring CONNECT topology\n"));
5275 create_ring (pg, &add_connections, CONNECT);
5277 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
5278 #if VERBOSE_TOPOLOGY
5279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5280 _("Creating 2d torus CONNECT topology\n"));
5282 create_2d_torus (pg, &add_connections, CONNECT);
5284 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
5285 #if VERBOSE_TOPOLOGY
5286 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5287 _("Creating Erdos-Renyi CONNECT topology\n"));
5289 create_erdos_renyi (pg, &add_connections, CONNECT);
5291 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
5292 #if VERBOSE_TOPOLOGY
5293 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5294 _("Creating InterNAT CONNECT topology\n"));
5296 create_nated_internet (pg, &add_connections, CONNECT);
5298 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
5299 #if VERBOSE_TOPOLOGY
5300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5301 _("Creating Scale Free CONNECT topology\n"));
5303 create_scale_free (pg, &add_connections, CONNECT);
5305 case GNUNET_TESTING_TOPOLOGY_LINE:
5306 #if VERBOSE_TOPOLOGY
5307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5308 _("Creating straight line CONNECT topology\n"));
5310 create_line (pg, &add_connections, CONNECT);
5312 case GNUNET_TESTING_TOPOLOGY_NONE:
5313 #if VERBOSE_TOPOLOGY
5314 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating no CONNECT topology\n"));
5316 copy_allowed_topology (pg);
5319 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _
5320 ("Unknown topology specification, can't connect peers!\n"));
5321 return GNUNET_SYSERR;
5326 case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM:
5327 #if VERBOSE_TOPOLOGY
5328 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _
5329 ("Connecting random subset (%'.2f percent) of possible peers\n"), 100
5332 choose_random_connections (pg, option_modifier);
5334 case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM:
5335 #if VERBOSE_TOPOLOGY
5336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5337 _("Connecting a minimum of %u peers each (if possible)\n"),
5338 (unsigned int) option_modifier);
5340 choose_minimum (pg, (unsigned int) option_modifier);
5342 case GNUNET_TESTING_TOPOLOGY_OPTION_DFS:
5343 #if VERBOSE_TOPOLOGY
5344 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _
5345 ("Using DFS to connect a minimum of %u peers each (if possible)\n"),
5346 (unsigned int) option_modifier);
5349 perform_dfs (pg, (int) option_modifier);
5352 case GNUNET_TESTING_TOPOLOGY_OPTION_ADD_CLOSEST:
5353 #if VERBOSE_TOPOLOGY
5354 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _
5355 ("Finding additional %u closest peers each (if possible)\n"),
5356 (unsigned int) option_modifier);
5359 add_closest (pg, (unsigned int) option_modifier,
5360 &add_connections, CONNECT);
5363 case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
5365 case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
5371 return connect_topology (pg, connect_timeout, connect_attempts,
5372 notify_callback, notify_cls);
5376 * Lookup and return the number of SSH connections to a host.
5378 * @param hostname the hostname to lookup in the list
5379 * @param pg the peergroup that the host belongs to
5381 * @return the number of current ssh connections to the host
5384 count_outstanding_at_host(const char *hostname,
5385 struct GNUNET_TESTING_PeerGroup *pg)
5387 struct OutstandingSSH *pos;
5389 while ((pos != NULL) && (strcmp (pos->hostname, hostname) != 0))
5391 GNUNET_assert(pos != NULL);
5392 return pos->outstanding;
5396 * Increment the number of SSH connections to a host by one.
5398 * @param hostname the hostname to lookup in the list
5399 * @param pg the peergroup that the host belongs to
5403 increment_outstanding_at_host(const char *hostname,
5404 struct GNUNET_TESTING_PeerGroup *pg)
5406 struct OutstandingSSH *pos;
5408 while ((pos != NULL) && (strcmp (pos->hostname, hostname) != 0))
5410 GNUNET_assert(pos != NULL);
5415 * Decrement the number of SSH connections to a host by one.
5417 * @param hostname the hostname to lookup in the list
5418 * @param pg the peergroup that the host belongs to
5422 decrement_outstanding_at_host(const char *hostname,
5423 struct GNUNET_TESTING_PeerGroup *pg)
5425 struct OutstandingSSH *pos;
5427 while ((pos != NULL) && (strcmp (pos->hostname, hostname) != 0))
5429 GNUNET_assert(pos != NULL);
5434 * Callback that is called whenever a hostkey is generated
5435 * for a peer. Call the real callback and decrement the
5436 * starting counter for the peergroup.
5438 * @param cls closure
5439 * @param id identifier for the daemon, NULL on error
5440 * @param d handle for the daemon
5441 * @param emsg error message (NULL on success)
5444 internal_hostkey_callback(void *cls, const struct GNUNET_PeerIdentity *id,
5445 struct GNUNET_TESTING_Daemon *d, const char *emsg)
5447 struct InternalStartContext *internal_context = cls;
5448 internal_context->peer->pg->starting--;
5449 internal_context->peer->pg->started++;
5450 if (internal_context->hostname != NULL)
5451 decrement_outstanding_at_host (internal_context->hostname,
5452 internal_context->peer->pg);
5453 if (internal_context->hostkey_callback != NULL)
5454 internal_context->hostkey_callback (internal_context->hostkey_cls, id, d,
5456 else if (internal_context->peer->pg->started
5457 == internal_context->peer->pg->total)
5459 internal_context->peer->pg->started = 0; /* Internal startup may use this counter! */
5460 GNUNET_TESTING_daemons_continue_startup (internal_context->peer->pg);
5465 * Callback that is called whenever a peer has finished starting.
5466 * Call the real callback and decrement the starting counter
5467 * for the peergroup.
5469 * @param cls closure
5470 * @param id identifier for the daemon, NULL on error
5472 * @param d handle for the daemon
5473 * @param emsg error message (NULL on success)
5476 internal_startup_callback(void *cls, const struct GNUNET_PeerIdentity *id,
5477 const struct GNUNET_CONFIGURATION_Handle *cfg,
5478 struct GNUNET_TESTING_Daemon *d, const char *emsg)
5480 struct InternalStartContext *internal_context = cls;
5481 internal_context->peer->pg->starting--;
5482 if (internal_context->hostname != NULL)
5483 decrement_outstanding_at_host (internal_context->hostname,
5484 internal_context->peer->pg);
5485 if (internal_context->start_cb != NULL)
5486 internal_context->start_cb (internal_context->start_cb_cls, id, cfg, d,
5491 internal_continue_startup(void *cls,
5492 const struct GNUNET_SCHEDULER_TaskContext *tc)
5494 struct InternalStartContext *internal_context = cls;
5496 if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
5501 if ((internal_context->peer->pg->starting
5502 < internal_context->peer->pg->max_concurrent_ssh)
5503 || ((internal_context->hostname != NULL)
5504 && (count_outstanding_at_host (internal_context->hostname,
5505 internal_context->peer->pg)
5506 < internal_context->peer->pg->max_concurrent_ssh)))
5508 if (internal_context->hostname != NULL)
5509 increment_outstanding_at_host (internal_context->hostname,
5510 internal_context->peer->pg);
5511 internal_context->peer->pg->starting++;
5512 GNUNET_TESTING_daemon_continue_startup (internal_context->peer->daemon);
5516 GNUNET_SCHEDULER_add_delayed (
5517 GNUNET_TIME_relative_multiply (
5518 GNUNET_TIME_UNIT_MILLISECONDS,
5520 &internal_continue_startup,
5526 * Callback for informing us about a successful
5527 * or unsuccessful churn start call.
5529 * @param cls a ChurnContext
5530 * @param id the peer identity of the started peer
5531 * @param cfg the handle to the configuration of the peer
5532 * @param d handle to the daemon for the peer
5533 * @param emsg NULL on success, non-NULL on failure
5537 churn_start_callback(void *cls, const struct GNUNET_PeerIdentity *id,
5538 const struct GNUNET_CONFIGURATION_Handle *cfg,
5539 struct GNUNET_TESTING_Daemon *d, const char *emsg)
5541 struct ChurnRestartContext *startup_ctx = cls;
5542 struct ChurnContext *churn_ctx = startup_ctx->churn_ctx;
5544 unsigned int total_left;
5545 char *error_message;
5547 error_message = NULL;
5550 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5551 "Churn stop callback failed with error `%s'\n", emsg);
5552 churn_ctx->num_failed_start++;
5556 churn_ctx->num_to_start--;
5559 total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop)
5560 + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
5562 if (total_left == 0)
5564 if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
5567 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
5568 churn_ctx->num_failed_start,
5569 churn_ctx->num_failed_stop);
5570 churn_ctx->cb (churn_ctx->cb_cls, error_message);
5571 GNUNET_free_non_null (error_message);
5572 GNUNET_free (churn_ctx);
5573 GNUNET_free (startup_ctx);
5578 schedule_churn_restart(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5580 struct PeerRestartContext *peer_restart_ctx = cls;
5581 struct ChurnRestartContext *startup_ctx = peer_restart_ctx->churn_restart_ctx;
5583 if (startup_ctx->outstanding > startup_ctx->pg->max_concurrent_ssh)
5584 GNUNET_SCHEDULER_add_delayed (
5585 GNUNET_TIME_relative_multiply (
5586 GNUNET_TIME_UNIT_MILLISECONDS,
5588 &schedule_churn_restart, peer_restart_ctx);
5591 if (startup_ctx->churn_ctx->service != NULL)
5592 GNUNET_TESTING_daemon_start_stopped_service (peer_restart_ctx->daemon,
5593 startup_ctx->churn_ctx->service,
5594 startup_ctx->timeout,
5595 &churn_start_callback, startup_ctx);
5597 GNUNET_TESTING_daemon_start_stopped (peer_restart_ctx->daemon,
5598 startup_ctx->timeout,
5599 &churn_start_callback, startup_ctx);
5600 GNUNET_free (peer_restart_ctx);
5606 internal_start(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5608 struct InternalStartContext *internal_context = cls;
5610 if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
5615 if ((internal_context->peer->pg->starting
5616 < internal_context->peer->pg->max_concurrent_ssh)
5617 || ((internal_context->hostname != NULL)
5618 && (count_outstanding_at_host (internal_context->hostname,
5619 internal_context->peer->pg)
5620 < internal_context->peer->pg->max_concurrent_ssh)))
5622 if (internal_context->hostname != NULL)
5623 increment_outstanding_at_host (internal_context->hostname,
5624 internal_context->peer->pg);
5625 internal_context->peer->pg->starting++;
5626 internal_context->peer->daemon
5627 = GNUNET_TESTING_daemon_start (internal_context->peer->cfg,
5628 internal_context->timeout,
5630 internal_context->hostname,
5631 internal_context->username,
5632 internal_context->sshport,
5633 internal_context->hostkey,
5634 &internal_hostkey_callback,
5636 &internal_startup_callback,
5641 GNUNET_SCHEDULER_add_delayed (
5642 GNUNET_TIME_relative_multiply (
5643 GNUNET_TIME_UNIT_MILLISECONDS,
5645 &internal_start, internal_context);
5648 #if USE_START_HELPER
5650 struct PeerStartHelperContext
5652 struct GNUNET_TESTING_PeerGroup *pg;
5654 struct HostData *host;
5656 struct GNUNET_OS_Process *proc;
5660 check_peers_started (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5662 struct PeerStartHelperContext *helper = cls;
5663 enum GNUNET_OS_ProcessStatusType type;
5666 GNUNET_TESTING_NotifyDaemonRunning cb;
5668 if (GNUNET_NO == GNUNET_OS_process_status (helper->proc, &type, &code)) /* Still running, wait some more! */
5670 GNUNET_SCHEDULER_add_delayed(GNUNET_CONSTANTS_EXEC_WAIT, &check_peers_started, helper);
5674 helper->pg->starting--;
5675 if (helper->pg->starting == 0) /* All peers have finished starting! */
5677 /* Call the peer started callback for each peer, set proper FSM state (?) */
5678 for (i = 0; i < helper->pg->total; i++)
5680 cb = helper->pg->peers[i].daemon->cb;
5681 helper->pg->peers[i].daemon->cb = NULL;
5682 helper->pg->peers[i].daemon->running = GNUNET_YES;
5683 helper->pg->peers[i].daemon->phase = SP_START_DONE;
5686 if ((type != GNUNET_OS_PROCESS_EXITED) || (code != 0))
5687 cb (helper->pg->peers[i].daemon->cb_cls,
5688 &helper->pg->peers[i].daemon->id,
5689 helper->pg->peers[i].daemon->cfg, helper->pg->peers[i].daemon,
5690 "Failed to execute peerStartHelper.pl, or return code bad!");
5692 cb (helper->pg->peers[i].daemon->cb_cls,
5693 &helper->pg->peers[i].daemon->id,
5694 helper->pg->peers[i].daemon->cfg, helper->pg->peers[i].daemon,
5701 GNUNET_OS_process_close(helper->proc);
5705 start_peer_helper (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5707 struct PeerStartHelperContext *helper = cls;
5708 char *baseservicehome;
5711 /* ssh user@host peerStartHelper /path/to/basedirectory */
5712 GNUNET_assert(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (helper->pg->cfg, "PATHS", "SERVICEHOME",
5714 GNUNET_asprintf(&tempdir, "%s/%s/", baseservicehome, helper->host->hostname);
5715 if (NULL != helper->host->username)
5716 GNUNET_asprintf (&arg, "%s@%s", helper->host->username, helper->host->hostname);
5718 GNUNET_asprintf (&arg, "%s", helper->host->hostname);
5720 /* FIXME: Doesn't support ssh_port option! */
5721 helper->proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh", arg,
5722 "peerStartHelper.pl", tempdir, NULL);
5723 GNUNET_assert(helper->proc != NULL);
5724 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "starting peers with cmd ssh %s %s %s\n", arg, "peerStartHelper.pl", tempdir);
5725 GNUNET_SCHEDULER_add_now (&check_peers_started, helper);
5726 GNUNET_free (tempdir);
5727 GNUNET_free (baseservicehome);
5733 * Function which continues a peer group starting up
5734 * after successfully generating hostkeys for each peer.
5736 * @param pg the peer group to continue starting
5740 GNUNET_TESTING_daemons_continue_startup(struct GNUNET_TESTING_PeerGroup *pg)
5744 #if USE_START_HELPER
5745 if ((pg->num_hosts > 0) && (pg->hostkey_data != NULL))
5747 struct PeerStartHelperContext *helper;
5748 pg->starting = pg->num_hosts;
5749 for (i = 0; i < pg->num_hosts; i++)
5751 helper = GNUNET_malloc(sizeof(struct PeerStartHelperContext));
5753 helper->host = &pg->hosts[i];
5754 GNUNET_SCHEDULER_add_now(&start_peer_helper, helper);
5760 for (i = 0; i < pg->total; i++)
5762 GNUNET_SCHEDULER_add_now (&internal_continue_startup,
5763 &pg->peers[i].internal_context);
5768 for (i = 0; i < pg->total; i++)
5770 GNUNET_SCHEDULER_add_now (&internal_continue_startup,
5771 &pg->peers[i].internal_context);
5776 #if USE_START_HELPER
5778 call_hostkey_callbacks (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5780 struct GNUNET_TESTING_PeerGroup *pg = cls;
5782 for (i = 0; i < pg->total; i++)
5784 if (pg->peers[i].internal_context.hostkey_callback != NULL)
5785 pg->peers[i].internal_context.hostkey_callback (pg->peers[i].internal_context.hostkey_cls,
5786 &pg->peers[i].daemon->id,
5787 pg->peers[i].daemon,
5791 if (pg->peers[0].internal_context.hostkey_callback == NULL)
5792 GNUNET_TESTING_daemons_continue_startup (pg);
5797 * Start count gnunet instances with the same set of transports and
5798 * applications. The port numbers (any option called "PORT") will be
5799 * adjusted to ensure that no two peers running on the same system
5800 * have the same port(s) in their respective configurations.
5802 * @param cfg configuration template to use
5803 * @param total number of daemons to start
5804 * @param max_concurrent_connections for testing, how many peers can
5805 * we connect to simultaneously
5806 * @param max_concurrent_ssh when starting with ssh, how many ssh
5807 * connections will we allow at once (based on remote hosts allowed!)
5808 * @param timeout total time allowed for peers to start
5809 * @param hostkey_callback function to call on each peers hostkey generation
5810 * if NULL, peers will be started by this call, if non-null,
5811 * GNUNET_TESTING_daemons_continue_startup must be called after
5812 * successful hostkey generation
5813 * @param hostkey_cls closure for hostkey callback
5814 * @param cb function to call on each daemon that was started
5815 * @param cb_cls closure for cb
5816 * @param connect_callback function to call each time two hosts are connected
5817 * @param connect_callback_cls closure for connect_callback
5818 * @param hostnames linked list of host structs to use to start peers on
5819 * (NULL to run on localhost only)
5821 * @return NULL on error, otherwise handle to control peer group
5823 struct GNUNET_TESTING_PeerGroup *
5824 GNUNET_TESTING_daemons_start(const struct GNUNET_CONFIGURATION_Handle *cfg,
5826 unsigned int max_concurrent_connections,
5827 unsigned int max_concurrent_ssh,
5828 struct GNUNET_TIME_Relative timeout,
5829 GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback,
5831 GNUNET_TESTING_NotifyDaemonRunning cb,
5833 GNUNET_TESTING_NotifyConnection connect_callback,
5834 void *connect_callback_cls,
5835 const struct GNUNET_TESTING_Host *hostnames)
5837 struct GNUNET_TESTING_PeerGroup *pg;
5838 const struct GNUNET_TESTING_Host *hostpos;
5839 const char *hostname;
5840 const char *username;
5841 char *baseservicehome;
5842 char *newservicehome;
5844 char *hostkeys_file;
5847 struct GNUNET_DISK_FileHandle *fd;
5848 struct GNUNET_CONFIGURATION_Handle *pcfg;
5850 struct OutstandingSSH *ssh_entry;
5851 unsigned int hostcnt;
5858 uint64_t total_hostkeys;
5859 struct GNUNET_OS_Process *proc;
5870 pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
5872 pg->notify_connection = connect_callback;
5873 pg->notify_connection_cls = connect_callback_cls;
5875 pg->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
5876 pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
5877 pg->max_outstanding_connections = max_concurrent_connections;
5878 pg->max_concurrent_ssh = max_concurrent_ssh;
5879 if (NULL != hostnames)
5882 hostpos = hostnames;
5883 while (hostpos != NULL)
5885 hostpos = hostpos->next;
5888 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
5891 hostpos = hostnames;
5892 while (hostpos != NULL)
5894 pg->hosts[off].minport = LOW_PORT;
5895 pg->hosts[off].hostname = GNUNET_strdup (hostpos->hostname);
5896 if (hostpos->username != NULL)
5897 pg->hosts[off].username = GNUNET_strdup (hostpos->username);
5898 pg->hosts[off].sshport = hostpos->port;
5899 hostpos = hostpos->next;
5909 pg->num_hosts = off;
5917 /* Create the servicehome directory for each remote peer */
5918 GNUNET_assert(GNUNET_OK ==
5919 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME",
5921 for (i = 0; i < pg->num_hosts; i++)
5923 ssh_entry = GNUNET_malloc(sizeof(struct OutstandingSSH));
5924 ssh_entry->hostname = pg->hosts[i].hostname; /* Don't free! */
5925 GNUNET_CONTAINER_DLL_insert(pg->ssh_head, pg->ssh_tail, ssh_entry);
5926 GNUNET_asprintf(&tmpdir, "%s/%s", baseservicehome, pg->hosts[i].hostname);
5927 if (NULL != pg->hosts[i].username)
5928 GNUNET_asprintf (&arg, "%s@%s", pg->hosts[i].username,
5929 pg->hosts[i].hostname);
5931 GNUNET_asprintf (&arg, "%s", pg->hosts[i].hostname);
5932 if (pg->hosts[i].sshport != 0)
5934 GNUNET_asprintf (&ssh_port_str, "%d", pg->hosts[i].sshport);
5935 proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh", "-P",
5940 arg, "mkdir -p", tmpdir,
5944 proc = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh", arg,
5945 "mkdir -p", tmpdir, NULL);
5946 GNUNET_assert(proc != NULL);
5947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5948 "Creating remote dir with command ssh %s %s %s\n", arg,
5949 " mkdir -p ", tmpdir);
5950 GNUNET_free(tmpdir);
5952 GNUNET_OS_process_wait (proc);
5953 GNUNET_OS_process_close(proc);
5955 GNUNET_free(baseservicehome);
5956 baseservicehome = NULL;
5958 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING",
5962 if (GNUNET_YES != GNUNET_DISK_file_test (hostkeys_file))
5963 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5964 _("Could not read hostkeys file!\n"));
5967 /* Check hostkey file size, read entire thing into memory */
5968 fd = GNUNET_DISK_file_open (hostkeys_file,
5969 GNUNET_DISK_OPEN_READ,
5970 GNUNET_DISK_PERM_NONE);
5973 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
5976 GNUNET_free (hostkeys_file);
5977 for (i=0;i<pg->num_hosts;i++)
5979 GNUNET_free (pg->hosts[i].hostname);
5980 GNUNET_free_non_null (pg->hosts[i].username);
5982 GNUNET_free (pg->peers);
5983 GNUNET_free (pg->hosts);
5988 if (GNUNET_YES != GNUNET_DISK_file_size (hostkeys_file, &fs,
5992 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5993 "Found file size %llu for hostkeys, expect hostkeys to be size %d\n",
5994 fs, HOSTKEYFILESIZE);
5996 if (0 != (fs % HOSTKEYFILESIZE))
5998 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5999 "File size %llu seems incorrect for hostkeys...\n",
6004 total_hostkeys = fs / HOSTKEYFILESIZE;
6005 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
6006 "Will read %llu hostkeys from file\n",
6008 pg->hostkey_data = GNUNET_malloc_large (fs);
6009 GNUNET_assert (fs == GNUNET_DISK_file_read (fd, pg->hostkey_data, fs));
6011 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fd));
6013 GNUNET_free(hostkeys_file);
6016 for (off = 0; off < total; off++)
6020 hostname = pg->hosts[off % hostcnt].hostname;
6021 username = pg->hosts[off % hostcnt].username;
6022 sshport = pg->hosts[off % hostcnt].sshport;
6023 pcfg = make_config (cfg, off, &pg->hosts[off % hostcnt].minport,
6024 &upnum, hostname, &fdnum);
6031 pcfg = make_config (cfg, off, &minport, &upnum, hostname, &fdnum);
6036 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6037 _("Could not create configuration for peer number %u on `%s'!\n"),
6039 hostname == NULL ? "localhost" : hostname);
6044 == GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS",
6048 if (hostname != NULL)
6049 GNUNET_asprintf (&newservicehome, "%s/%s/%d/", baseservicehome, hostname, off);
6051 GNUNET_asprintf (&newservicehome, "%s/%d/", baseservicehome, off);
6052 GNUNET_free (baseservicehome);
6053 baseservicehome = NULL;
6057 tmpdir = getenv ("TMPDIR");
6058 tmpdir = tmpdir ? tmpdir : "/tmp";
6059 if (hostname != NULL)
6060 GNUNET_asprintf (&newservicehome, "%s/%s/%s/%d/", tmpdir, hostname,
6061 "gnunet-testing-test-test", off);
6063 GNUNET_asprintf (&newservicehome, "%s/%s/%d/", tmpdir,
6064 "gnunet-testing-test-test", off);
6066 GNUNET_CONFIGURATION_set_value_string (pcfg, "PATHS", "SERVICEHOME",
6068 GNUNET_free (newservicehome);
6069 pg->peers[off].cfg = pcfg;
6070 pg->peers[off].pg = pg;
6071 pg->peers[off].internal_context.peer = &pg->peers[off];
6072 pg->peers[off].internal_context.timeout = timeout;
6073 pg->peers[off].internal_context.hostname = hostname;
6074 pg->peers[off].internal_context.username = username;
6075 pg->peers[off].internal_context.sshport = sshport;
6076 if (pg->hostkey_data != NULL)
6077 pg->peers[off].internal_context.hostkey = &pg->hostkey_data[off
6079 pg->peers[off].internal_context.hostkey_callback = hostkey_callback;
6080 pg->peers[off].internal_context.hostkey_cls = hostkey_cls;
6081 pg->peers[off].internal_context.start_cb = cb;
6082 pg->peers[off].internal_context.start_cb_cls = cb_cls;
6083 #if !USE_START_HELPER
6084 GNUNET_SCHEDULER_add_now (&internal_start,
6085 &pg->peers[off].internal_context);
6087 if ((pg->hostkey_data != NULL) && (hostcnt > 0))
6089 pg->peers[off].daemon
6090 = GNUNET_TESTING_daemon_start (pcfg,
6096 pg->peers[off].internal_context.hostkey,
6097 &internal_hostkey_callback,
6098 &pg->peers[off].internal_context,
6099 &internal_startup_callback,
6100 &pg->peers[off].internal_context);
6102 * At this point, given that we had a hostkeyfile,
6103 * we can call the hostkey callback!
6104 * But first, we should copy (rsync) all of the configs
6105 * and hostkeys to the remote peers. Then let topology
6106 * creation happen, then call the peer start helper processes,
6107 * then set pg->whatever_phase for each peer and let them
6108 * enter the fsm to get the HELLO's for peers and start connecting.
6113 GNUNET_SCHEDULER_add_now (&internal_start,
6114 &pg->peers[off].internal_context);
6120 #if USE_START_HELPER /* Now the peergroup has been set up, hostkeys and configs written to files. */
6121 if ((pg->hostkey_data != NULL) && (hostcnt > 0))
6123 for (off = 0; off < hostcnt; off++)
6128 hostname = pg->hosts[off % hostcnt].hostname;
6129 username = pg->hosts[off % hostcnt].username;
6130 sshport = pg->hosts[off % hostcnt].sshport;
6131 pcfg = make_config (cfg, off, &pg->hosts[off % hostcnt].minport,
6132 &upnum, hostname, &fdnum);
6142 == GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS",
6146 if (hostname != NULL)
6147 GNUNET_asprintf (&newservicehome, "%s/%s/%d/", baseservicehome, hostname, off);
6149 GNUNET_asprintf (&newservicehome, "%s/%d/", baseservicehome, off);
6150 GNUNET_free (baseservicehome);
6151 baseservicehome = NULL;
6155 tmpdir = getenv ("TMPDIR");
6156 tmpdir = tmpdir ? tmpdir : "/tmp";
6157 if (hostname != NULL)
6158 GNUNET_asprintf (&newservicehome, "%s/%s/%s/%d/", tmpdir, hostname,
6159 "gnunet-testing-test-test", off);
6161 GNUNET_asprintf (&newservicehome, "%s/%s/%d/", tmpdir,
6162 "gnunet-testing-test-test", off);
6165 if (NULL != username)
6166 GNUNET_asprintf (&arg,
6169 pg->hosts[off].hostname,
6171 pg->hosts[off].hostname);
6173 GNUNET_asprintf (&arg,
6175 pg->hosts[off].hostname,
6177 pg->hosts[off].hostname);
6179 /* FIXME: Doesn't support ssh_port option! */
6180 proc = GNUNET_OS_start_process (NULL, NULL,
6182 "rsync", "-r", newservicehome, arg, NULL);
6184 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6185 "copying directory with command rsync -r %s %s\n",
6186 newservicehome, arg);
6187 GNUNET_free(newservicehome);
6191 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6192 _("Could not start `%s' process to copy configuration directory.\n"),
6196 GNUNET_OS_process_wait (proc);
6197 GNUNET_OS_process_close (proc);
6199 /* Now all the configuration files and hostkeys are copied to the remote host. Call the hostkey callback for each peer! */
6200 GNUNET_SCHEDULER_add_now (&call_hostkey_callbacks, pg);
6207 * Get a daemon by number, so callers don't have to do nasty
6208 * offsetting operation.
6210 struct GNUNET_TESTING_Daemon *
6211 GNUNET_TESTING_daemon_get(struct GNUNET_TESTING_PeerGroup *pg,
6212 unsigned int position)
6214 if (position < pg->total)
6215 return pg->peers[position].daemon;
6220 * Get a daemon by peer identity, so callers can
6221 * retrieve the daemon without knowing it's offset.
6223 * @param pg the peer group to retrieve the daemon from
6224 * @param peer_id the peer identity of the daemon to retrieve
6226 * @return the daemon on success, or NULL if no such peer identity is found
6228 struct GNUNET_TESTING_Daemon *
6229 GNUNET_TESTING_daemon_get_by_id(struct GNUNET_TESTING_PeerGroup *pg,
6230 struct GNUNET_PeerIdentity *peer_id)
6234 for (i = 0; i < pg->total; i++)
6236 if (0 == memcmp (&pg->peers[i].daemon->id, peer_id,
6237 sizeof(struct GNUNET_PeerIdentity)))
6238 return pg->peers[i].daemon;
6244 * Prototype of a function that will be called when a
6245 * particular operation was completed the testing library.
6247 * @param cls closure (a struct RestartContext)
6248 * @param id id of the peer that was restarted
6249 * @param cfg handle to the configuration of the peer
6250 * @param d handle to the daemon that was restarted
6251 * @param emsg NULL on success
6254 restart_callback(void *cls, const struct GNUNET_PeerIdentity *id,
6255 const struct GNUNET_CONFIGURATION_Handle *cfg,
6256 struct GNUNET_TESTING_Daemon *d, const char *emsg)
6258 struct RestartContext *restart_context = cls;
6262 restart_context->peers_restarted++;
6266 restart_context->peers_restart_failed++;
6269 if (restart_context->peers_restarted == restart_context->peer_group->total)
6271 restart_context->callback (restart_context->callback_cls, NULL);
6272 GNUNET_free (restart_context);
6274 else if (restart_context->peers_restart_failed
6275 + restart_context->peers_restarted == restart_context->peer_group->total)
6277 restart_context->callback (restart_context->callback_cls,
6278 "Failed to restart peers!");
6279 GNUNET_free (restart_context);
6285 * Callback for informing us about a successful
6286 * or unsuccessful churn stop call.
6288 * @param cls a ChurnContext
6289 * @param emsg NULL on success, non-NULL on failure
6293 churn_stop_callback(void *cls, const char *emsg)
6295 struct ShutdownContext *shutdown_ctx = cls;
6296 struct ChurnContext *churn_ctx = shutdown_ctx->cb_cls;
6297 unsigned int total_left;
6298 char *error_message;
6300 error_message = NULL;
6301 shutdown_ctx->outstanding--;
6305 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6306 "Churn stop callback failed with error `%s'\n", emsg);
6307 churn_ctx->num_failed_stop++;
6311 churn_ctx->num_to_stop--;
6314 total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop)
6315 + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
6317 if (total_left == 0)
6319 if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
6323 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
6324 churn_ctx->num_failed_start,
6325 churn_ctx->num_failed_stop);
6327 churn_ctx->cb (churn_ctx->cb_cls, error_message);
6328 GNUNET_free_non_null (error_message);
6329 GNUNET_free (churn_ctx);
6330 GNUNET_free (shutdown_ctx);
6335 * Count the number of running peers.
6337 * @param pg handle for the peer group
6339 * @return the number of currently running peers in the peer group
6342 GNUNET_TESTING_daemons_running(struct GNUNET_TESTING_PeerGroup *pg)
6345 unsigned int running = 0;
6346 for (i = 0; i < pg->total; i++)
6348 if (pg->peers[i].daemon->running == GNUNET_YES)
6350 GNUNET_assert (running != -1);
6358 * Task to rate limit the number of outstanding peer shutdown
6359 * requests. This is necessary for making sure we don't do
6360 * too many ssh connections at once, but is generally nicer
6361 * to any system as well (graduated task starts, as opposed
6362 * to calling gnunet-arm N times all at once).
6365 schedule_churn_shutdown_task(void *cls,
6366 const struct GNUNET_SCHEDULER_TaskContext *tc)
6368 struct PeerShutdownContext *peer_shutdown_ctx = cls;
6369 struct ShutdownContext *shutdown_ctx;
6370 struct ChurnContext *churn_ctx;
6371 GNUNET_assert (peer_shutdown_ctx != NULL);
6372 shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
6373 GNUNET_assert (shutdown_ctx != NULL);
6374 churn_ctx = (struct ChurnContext *) shutdown_ctx->cb_cls;
6375 if (shutdown_ctx->outstanding > churn_ctx->pg->max_concurrent_ssh)
6376 GNUNET_SCHEDULER_add_delayed (
6377 GNUNET_TIME_relative_multiply (
6378 GNUNET_TIME_UNIT_MILLISECONDS,
6380 &schedule_churn_shutdown_task,
6384 shutdown_ctx->outstanding++;
6385 if (churn_ctx->service != NULL)
6386 GNUNET_TESTING_daemon_stop_service (peer_shutdown_ctx->daemon,
6388 shutdown_ctx->timeout, shutdown_ctx->cb,
6391 GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
6392 shutdown_ctx->timeout, shutdown_ctx->cb,
6393 shutdown_ctx, GNUNET_NO, GNUNET_YES);
6394 GNUNET_free (peer_shutdown_ctx);
6400 * Simulate churn by stopping some peers (and possibly
6401 * re-starting others if churn is called multiple times). This
6402 * function can only be used to create leave-join churn (peers "never"
6403 * leave for good). First "voff" random peers that are currently
6404 * online will be taken offline; then "von" random peers that are then
6405 * offline will be put back online. No notifications will be
6406 * generated for any of these operations except for the callback upon
6409 * @param pg handle for the peer group
6410 * @param service the service to churn off/on, NULL to churn peer
6411 * @param voff number of peers that should go offline
6412 * @param von number of peers that should come back online;
6413 * must be zero on first call (since "testbed_start"
6414 * always starts all of the peers)
6415 * @param timeout how long to wait for operations to finish before
6417 * @param cb function to call at the end
6418 * @param cb_cls closure for cb
6421 GNUNET_TESTING_daemons_churn(struct GNUNET_TESTING_PeerGroup *pg,
6423 unsigned int voff, unsigned int von,
6424 struct GNUNET_TIME_Relative timeout,
6425 GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
6427 struct ChurnContext *churn_ctx;
6428 struct ShutdownContext *shutdown_ctx;
6429 struct PeerShutdownContext *peer_shutdown_ctx;
6430 struct PeerRestartContext *peer_restart_ctx;
6431 struct ChurnRestartContext *churn_startup_ctx;
6433 unsigned int running;
6434 unsigned int stopped;
6435 unsigned int total_running;
6436 unsigned int total_stopped;
6438 unsigned int *running_arr;
6439 unsigned int *stopped_arr;
6440 unsigned int *running_permute;
6441 unsigned int *stopped_permute;
6444 shutdown_ctx = NULL;
6445 peer_shutdown_ctx = NULL;
6446 peer_restart_ctx = NULL;
6447 churn_startup_ctx = NULL;
6452 if ((von == 0) && (voff == 0)) /* No peers at all? */
6458 for (i = 0; i < pg->total; i++)
6460 if (service == NULL)
6462 if (pg->peers[i].daemon->running == GNUNET_YES)
6464 GNUNET_assert (running != -1);
6469 GNUNET_assert (stopped != -1);
6475 /* FIXME: make churned services a list! */
6476 pos = pg->peers[i].daemon->churned_services;
6477 /* FIXME: while (pos != NULL) */
6481 if (0 == strcasecmp(pos, service))
6487 GNUNET_assert (stopped != -1);
6489 /* FIXME: pos = pos->next; */
6493 GNUNET_assert (running != -1);
6501 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6502 "Trying to stop more peers (%d) than are currently running (%d)!\n", voff, running);
6503 cb (cb_cls, "Trying to stop more peers than are currently running!");
6509 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6510 "Trying to start more peers (%d) than are currently stopped (%d)!\n", von, stopped);
6511 cb (cb_cls, "Trying to start more peers than are currently stopped!");
6515 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
6517 if (service != NULL)
6518 churn_ctx->service = GNUNET_strdup(service);
6521 running_arr = GNUNET_malloc (running * sizeof (unsigned int));
6525 stopped_arr = GNUNET_malloc (stopped * sizeof (unsigned int));
6527 running_permute = NULL;
6528 stopped_permute = NULL;
6531 running_permute = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
6534 stopped_permute = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
6537 total_running = running;
6538 total_stopped = stopped;
6542 churn_ctx->num_to_start = von;
6543 churn_ctx->num_to_stop = voff;
6545 churn_ctx->cb_cls = cb_cls;
6548 for (i = 0; i < pg->total; i++)
6550 if (service == NULL)
6552 if (pg->peers[i].daemon->running == GNUNET_YES)
6554 GNUNET_assert ((running_arr != NULL) && (total_running > running));
6555 running_arr[running] = i;
6560 GNUNET_assert ((stopped_arr != NULL) && (total_stopped > stopped));
6561 stopped_arr[stopped] = i;
6567 /* FIXME: make churned services a list! */
6568 pos = pg->peers[i].daemon->churned_services;
6569 /* FIXME: while (pos != NULL) */
6572 GNUNET_assert ((stopped_arr != NULL) && (total_stopped > stopped));
6573 stopped_arr[stopped] = i;
6575 /* FIXME: pos = pos->next; */
6579 GNUNET_assert ((running_arr != NULL) && (total_running > running));
6580 running_arr[running] = i;
6586 GNUNET_assert (running >= voff);
6589 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
6590 shutdown_ctx->cb = &churn_stop_callback;
6591 shutdown_ctx->cb_cls = churn_ctx;
6592 shutdown_ctx->total_peers = voff;
6593 shutdown_ctx->timeout = timeout;
6596 for (i = 0; i < voff; i++)
6599 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Stopping peer %d!\n", running_arr[running_permute[i]]);
6601 GNUNET_assert (running_arr != NULL);
6602 peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
6603 peer_shutdown_ctx->daemon
6604 = pg->peers[running_arr[running_permute[i]]].daemon;
6605 peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
6606 GNUNET_SCHEDULER_add_now (&schedule_churn_shutdown_task,
6610 GNUNET_assert (stopped >= von);
6613 churn_startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
6614 churn_startup_ctx->churn_ctx = churn_ctx;
6615 churn_startup_ctx->timeout = timeout;
6616 churn_startup_ctx->pg = pg;
6618 for (i = 0; i < von; i++)
6621 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Starting up peer %d!\n", stopped_arr[stopped_permute[i]]);
6623 GNUNET_assert (stopped_arr != NULL);
6624 peer_restart_ctx = GNUNET_malloc (sizeof (struct PeerRestartContext));
6625 peer_restart_ctx->churn_restart_ctx = churn_startup_ctx;
6626 peer_restart_ctx->daemon
6627 = pg->peers[stopped_arr[stopped_permute[i]]].daemon;
6628 GNUNET_SCHEDULER_add_now (&schedule_churn_restart, peer_restart_ctx);
6631 GNUNET_free_non_null (running_arr);
6632 GNUNET_free_non_null (stopped_arr);
6633 GNUNET_free_non_null (running_permute);
6634 GNUNET_free_non_null (stopped_permute);
6639 * Restart all peers in the given group.
6641 * @param pg the handle to the peer group
6642 * @param callback function to call on completion (or failure)
6643 * @param callback_cls closure for the callback function
6646 GNUNET_TESTING_daemons_restart(struct GNUNET_TESTING_PeerGroup *pg,
6647 GNUNET_TESTING_NotifyCompletion callback,
6650 struct RestartContext *restart_context;
6655 restart_context = GNUNET_malloc (sizeof (struct RestartContext));
6656 restart_context->peer_group = pg;
6657 restart_context->peers_restarted = 0;
6658 restart_context->callback = callback;
6659 restart_context->callback_cls = callback_cls;
6661 for (off = 0; off < pg->total; off++)
6663 GNUNET_TESTING_daemon_restart (pg->peers[off].daemon,
6664 &restart_callback, restart_context);
6671 * Start or stop an individual peer from the given group.
6673 * @param pg handle to the peer group
6674 * @param offset which peer to start or stop
6675 * @param desired_status GNUNET_YES to have it running, GNUNET_NO to stop it
6676 * @param timeout how long to wait for shutdown
6677 * @param cb function to call at the end
6678 * @param cb_cls closure for cb
6681 GNUNET_TESTING_daemons_vary(struct GNUNET_TESTING_PeerGroup *pg,
6682 unsigned int offset, int desired_status,
6683 struct GNUNET_TIME_Relative timeout,
6684 GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
6686 struct ShutdownContext *shutdown_ctx;
6687 struct ChurnRestartContext *startup_ctx;
6688 struct ChurnContext *churn_ctx;
6690 if (GNUNET_NO == desired_status)
6692 if (NULL != pg->peers[offset].daemon)
6694 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
6695 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
6696 churn_ctx->num_to_start = 0;
6697 churn_ctx->num_to_stop = 1;
6699 churn_ctx->cb_cls = cb_cls;
6700 shutdown_ctx->cb_cls = churn_ctx;
6701 GNUNET_TESTING_daemon_stop (pg->peers[offset].daemon, timeout,
6702 &churn_stop_callback, shutdown_ctx,
6703 GNUNET_NO, GNUNET_YES);
6706 else if (GNUNET_YES == desired_status)
6708 if (NULL == pg->peers[offset].daemon)
6710 startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
6711 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
6712 churn_ctx->num_to_start = 1;
6713 churn_ctx->num_to_stop = 0;
6715 churn_ctx->cb_cls = cb_cls;
6716 startup_ctx->churn_ctx = churn_ctx;
6717 GNUNET_TESTING_daemon_start_stopped (pg->peers[offset].daemon,
6718 timeout, &churn_start_callback,
6728 * Callback for shutting down peers in a peer group.
6730 * @param cls closure (struct ShutdownContext)
6731 * @param emsg NULL on success
6734 internal_shutdown_callback(void *cls, const char *emsg)
6736 struct PeerShutdownContext *peer_shutdown_ctx = cls;
6737 struct ShutdownContext *shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
6739 struct OutstandingSSH *ssh_pos;
6741 shutdown_ctx->outstanding--;
6742 if (peer_shutdown_ctx->daemon->hostname != NULL)
6743 decrement_outstanding_at_host (peer_shutdown_ctx->daemon->hostname,
6748 shutdown_ctx->peers_down++;
6752 shutdown_ctx->peers_failed++;
6755 if ((shutdown_ctx->cb != NULL) && (shutdown_ctx->peers_down
6756 + shutdown_ctx->peers_failed == shutdown_ctx->total_peers))
6758 if (shutdown_ctx->peers_failed > 0)
6759 shutdown_ctx->cb (shutdown_ctx->cb_cls,
6760 "Not all peers successfully shut down!");
6762 shutdown_ctx->cb (shutdown_ctx->cb_cls, NULL);
6764 GNUNET_free (shutdown_ctx->pg->peers);
6765 GNUNET_free_non_null(shutdown_ctx->pg->hostkey_data);
6766 for (off = 0; off < shutdown_ctx->pg->num_hosts; off++)
6768 GNUNET_free (shutdown_ctx->pg->hosts[off].hostname);
6769 GNUNET_free_non_null (shutdown_ctx->pg->hosts[off].username);
6771 GNUNET_free_non_null (shutdown_ctx->pg->hosts);
6772 while (NULL != (ssh_pos = shutdown_ctx->pg->ssh_head))
6774 GNUNET_CONTAINER_DLL_remove(shutdown_ctx->pg->ssh_head, shutdown_ctx->pg->ssh_tail, ssh_pos);
6775 GNUNET_free(ssh_pos);
6777 GNUNET_free (shutdown_ctx->pg);
6778 GNUNET_free (shutdown_ctx);
6780 GNUNET_free(peer_shutdown_ctx);
6785 * Task to rate limit the number of outstanding peer shutdown
6786 * requests. This is necessary for making sure we don't do
6787 * too many ssh connections at once, but is generally nicer
6788 * to any system as well (graduated task starts, as opposed
6789 * to calling gnunet-arm N times all at once).
6792 schedule_shutdown_task(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
6794 struct PeerShutdownContext *peer_shutdown_ctx = cls;
6795 struct ShutdownContext *shutdown_ctx;
6797 GNUNET_assert (peer_shutdown_ctx != NULL);
6798 shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
6799 GNUNET_assert (shutdown_ctx != NULL);
6801 if ((shutdown_ctx->outstanding < shutdown_ctx->pg->max_concurrent_ssh)
6802 || ((peer_shutdown_ctx->daemon->hostname != NULL)
6803 && (count_outstanding_at_host (peer_shutdown_ctx->daemon->hostname,
6805 < shutdown_ctx->pg->max_concurrent_ssh)))
6807 if (peer_shutdown_ctx->daemon->hostname != NULL)
6808 increment_outstanding_at_host (peer_shutdown_ctx->daemon->hostname,
6810 shutdown_ctx->outstanding++;
6811 GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
6812 shutdown_ctx->timeout,
6813 &internal_shutdown_callback, peer_shutdown_ctx,
6814 shutdown_ctx->delete_files, GNUNET_NO);
6817 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
6819 &schedule_shutdown_task, peer_shutdown_ctx);
6824 * Read a testing hosts file based on a configuration.
6825 * Returns a DLL of hosts (caller must free!) on success
6826 * or NULL on failure.
6828 * @param cfg a configuration with a testing section
6830 * @return DLL of hosts on success, NULL on failure
6832 struct GNUNET_TESTING_Host *
6833 GNUNET_TESTING_hosts_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
6835 struct GNUNET_TESTING_Host *hosts;
6836 struct GNUNET_TESTING_Host *temphost;
6844 /* Check for a hostfile containing user@host:port triples */
6846 != GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "hostfile",
6853 if (hostfile != NULL)
6855 if (GNUNET_OK != GNUNET_DISK_file_test (hostfile))
6856 GNUNET_DISK_fn_write (hostfile, NULL, 0, GNUNET_DISK_PERM_USER_READ
6857 | GNUNET_DISK_PERM_USER_WRITE);
6858 if ((0 != STAT (hostfile, &frstat)) || (frstat.st_size == 0))
6860 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6861 "Could not open file specified for host list, ending test!");
6862 GNUNET_free(hostfile);
6866 data = GNUNET_malloc_large (frstat.st_size);
6867 GNUNET_assert(data != NULL);
6869 != GNUNET_DISK_fn_read (hostfile, data, frstat.st_size))
6872 GNUNET_ERROR_TYPE_ERROR,
6873 "Could not read file %s specified for host list, ending test!",
6875 GNUNET_free (hostfile);
6880 GNUNET_free_non_null(hostfile);
6884 while (count < frstat.st_size - 1)
6887 if (((data[count] == '\n')) && (buf != &data[count]))
6890 temphost = GNUNET_malloc(sizeof(struct GNUNET_TESTING_Host));
6891 ret = sscanf (buf, "%a[a-zA-Z0-9_]@%a[a-zA-Z0-9.]:%hd",
6892 &temphost->username, &temphost->hostname,
6897 GNUNET_ERROR_TYPE_DEBUG,
6898 "Successfully read host %s, port %d and user %s from file\n",
6899 temphost->hostname, temphost->port,
6900 temphost->username);
6904 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6905 "Error reading line `%s' in hostfile\n", buf);
6906 GNUNET_free(temphost);
6907 buf = &data[count + 1];
6910 temphost->next = hosts;
6912 buf = &data[count + 1];
6914 else if ((data[count] == '\n') || (data[count] == '\0'))
6915 buf = &data[count + 1];
6918 GNUNET_free_non_null(data);
6924 * Shutdown all peers started in the given group.
6926 * @param pg handle to the peer group
6927 * @param timeout how long to wait for shutdown
6928 * @param cb callback to notify upon success or failure
6929 * @param cb_cls closure for cb
6932 GNUNET_TESTING_daemons_stop(struct GNUNET_TESTING_PeerGroup *pg,
6933 struct GNUNET_TIME_Relative timeout,
6934 GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
6937 struct ShutdownContext *shutdown_ctx;
6938 struct PeerShutdownContext *peer_shutdown_ctx;
6940 struct PeerConnection *conn_iter;
6941 struct PeerConnection *temp_conn;
6944 GNUNET_assert (pg->total > 0);
6946 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
6947 shutdown_ctx->delete_files = GNUNET_CONFIGURATION_get_value_yesno (pg->cfg,
6950 shutdown_ctx->cb = cb;
6951 shutdown_ctx->cb_cls = cb_cls;
6952 shutdown_ctx->total_peers = pg->total;
6953 shutdown_ctx->timeout = timeout;
6954 shutdown_ctx->pg = pg;
6955 /* shtudown_ctx->outstanding = 0; */
6957 for (off = 0; off < pg->total; off++)
6959 GNUNET_assert (NULL != pg->peers[off].daemon);
6960 peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
6961 peer_shutdown_ctx->daemon = pg->peers[off].daemon;
6962 peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
6963 GNUNET_SCHEDULER_add_now (&schedule_shutdown_task, peer_shutdown_ctx);
6965 if (NULL != pg->peers[off].cfg)
6967 GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
6968 pg->peers[off].cfg = NULL;
6971 conn_iter = pg->peers[off].allowed_peers_head;
6972 while (conn_iter != NULL)
6974 temp_conn = conn_iter->next;
6975 GNUNET_free(conn_iter);
6976 conn_iter = temp_conn;
6979 conn_iter = pg->peers[off].connect_peers_head;
6980 while (conn_iter != NULL)
6982 temp_conn = conn_iter->next;
6983 GNUNET_free(conn_iter);
6984 conn_iter = temp_conn;
6987 conn_iter = pg->peers[off].blacklisted_peers_head;
6988 while (conn_iter != NULL)
6990 temp_conn = conn_iter->next;
6991 GNUNET_free(conn_iter);
6992 conn_iter = temp_conn;
6995 conn_iter = pg->peers[off].connect_peers_working_set_head;
6996 while (conn_iter != NULL)
6998 temp_conn = conn_iter->next;
6999 GNUNET_free(conn_iter);
7000 conn_iter = temp_conn;
7003 if (pg->peers[off].allowed_peers != NULL)
7004 GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].allowed_peers);
7005 if (pg->peers[off].connect_peers != NULL)
7006 GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].connect_peers);
7007 if (pg->peers[off].blacklisted_peers != NULL)
7008 GNUNET_CONTAINER_multihashmap_destroy (pg->
7009 peers[off].blacklisted_peers);
7014 /* end of testing_group.c */