aboutsummaryrefslogtreecommitdiff
path: root/src/testing/testing_group.c
diff options
context:
space:
mode:
authorBertrand Marc <beberking@gmail.com>2012-05-02 21:43:37 +0200
committerBertrand Marc <beberking@gmail.com>2012-05-02 21:43:37 +0200
commit2b81464a43485fcc8ce079fafdee7b7a171835f4 (patch)
tree394774c0f735199b57d51a2d3840356317853fe1 /src/testing/testing_group.c
Imported Upstream version 0.9.2upstream/0.9.2
Diffstat (limited to 'src/testing/testing_group.c')
-rw-r--r--src/testing/testing_group.c7170
1 files changed, 7170 insertions, 0 deletions
diff --git a/src/testing/testing_group.c b/src/testing/testing_group.c
new file mode 100644
index 0000000..2d0e9ef
--- /dev/null
+++ b/src/testing/testing_group.c
@@ -0,0 +1,7170 @@
+/*
+ This file is part of GNUnet
+ (C) 2008, 2009 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file testing/testing_group.c
+ * @brief convenience API for writing testcases for GNUnet
+ * @author Nathan Evans
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_constants.h"
+#include "gnunet_arm_service.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_core_service.h"
+
+#define VERBOSE_TESTING GNUNET_NO
+
+#define VERBOSE_TOPOLOGY GNUNET_NO
+
+#define DEBUG_CHURN GNUNET_EXTRA_LOGGING
+
+#define USE_START_HELPER GNUNET_YES
+
+#define OLD 1
+
+/* Before connecting peers, send all of the HELLOs */
+#define USE_SEND_HELLOS GNUNET_NO
+
+#define TOPOLOGY_HACK GNUNET_YES
+
+
+/**
+ * Lowest port used for GNUnet testing. Should be high enough to not
+ * conflict with other applications running on the hosts but be low
+ * enough to not conflict with client-ports (typically starting around
+ * 32k).
+ */
+#define LOW_PORT 12000
+
+/**
+ * Highest port used for GNUnet testing. Should be low enough to not
+ * conflict with the port range for "local" ports (client apps; see
+ * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
+ */
+#define HIGH_PORT 56000
+
+/* Maximum time to delay connect attempt */
+#define MAX_CONNECT_DELAY 300
+
+/**
+ * Which list of peers do we need to modify?
+ */
+enum PeerLists
+{
+ /** Modify allowed peers */
+ ALLOWED,
+
+ /** Modify connect peers */
+ CONNECT,
+
+ /** Modify blacklist peers */
+ BLACKLIST,
+
+ /** Modify workingset peers */
+ WORKING_SET
+};
+
+/**
+ * Prototype of a function called whenever two peers would be connected
+ * in a certain topology.
+ */
+typedef unsigned int (*GNUNET_TESTING_ConnectionProcessor) (struct
+ GNUNET_TESTING_PeerGroup
+ * pg,
+ unsigned int first,
+ unsigned int second,
+ enum PeerLists list,
+ unsigned int check);
+
+/**
+ * Context for handling churning a peer group
+ */
+struct ChurnContext
+{
+ /**
+ * The peergroup we are dealing with.
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * Name of the service to churn on/off, NULL
+ * to churn entire peer.
+ */
+ char *service;
+
+ /**
+ * Callback used to notify of churning finished
+ */
+ GNUNET_TESTING_NotifyCompletion cb;
+
+ /**
+ * Closure for callback
+ */
+ void *cb_cls;
+
+ /**
+ * Number of peers that still need to be started
+ */
+ unsigned int num_to_start;
+
+ /**
+ * Number of peers that still need to be stopped
+ */
+ unsigned int num_to_stop;
+
+ /**
+ * Number of peers that failed to start
+ */
+ unsigned int num_failed_start;
+
+ /**
+ * Number of peers that failed to stop
+ */
+ unsigned int num_failed_stop;
+};
+
+struct RestartContext
+{
+ /**
+ * The group of peers being restarted
+ */
+ struct GNUNET_TESTING_PeerGroup *peer_group;
+
+ /**
+ * How many peers have been restarted thus far
+ */
+ unsigned int peers_restarted;
+
+ /**
+ * How many peers got an error when restarting
+ */
+ unsigned int peers_restart_failed;
+
+ /**
+ * The function to call once all peers have been restarted
+ */
+ GNUNET_TESTING_NotifyCompletion callback;
+
+ /**
+ * Closure for callback function
+ */
+ void *callback_cls;
+
+};
+
+struct SendHelloContext
+{
+ /**
+ * Global handle to the peer group.
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * The data about this specific peer.
+ */
+ struct PeerData *peer;
+
+ /**
+ * The next HELLO that needs sent to this peer.
+ */
+ struct PeerConnection *peer_pos;
+
+ /**
+ * Are we connected to CORE yet?
+ */
+ unsigned int core_ready;
+
+ /**
+ * How many attempts should we make for failed connections?
+ */
+ unsigned int connect_attempts;
+
+ /**
+ * Task for scheduling core connect requests to be sent.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier core_connect_task;
+};
+
+struct ShutdownContext
+{
+ struct GNUNET_TESTING_PeerGroup *pg;
+ /**
+ * Total peers to wait for
+ */
+ unsigned int total_peers;
+
+ /**
+ * Number of peers successfully shut down
+ */
+ unsigned int peers_down;
+
+ /**
+ * Number of peers failed to shut down
+ */
+ unsigned int peers_failed;
+
+ /**
+ * Number of peers we have started shutting
+ * down. If too many, wait on them.
+ */
+ unsigned int outstanding;
+
+ /**
+ * Timeout for shutdown.
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * Callback to call when all peers either
+ * shutdown or failed to shutdown
+ */
+ GNUNET_TESTING_NotifyCompletion cb;
+
+ /**
+ * Closure for cb
+ */
+ void *cb_cls;
+
+ /**
+ * Should we delete all of the files from the peers?
+ */
+ int delete_files;
+};
+
+/**
+ * Individual shutdown context for a particular peer.
+ */
+struct PeerShutdownContext
+{
+ /**
+ * Pointer to the high level shutdown context.
+ */
+ struct ShutdownContext *shutdown_ctx;
+
+ /**
+ * The daemon handle for the peer to shut down.
+ */
+ struct GNUNET_TESTING_Daemon *daemon;
+};
+
+/**
+ * Individual shutdown context for a particular peer.
+ */
+struct PeerRestartContext
+{
+ /**
+ * Pointer to the high level restart context.
+ */
+ struct ChurnRestartContext *churn_restart_ctx;
+
+ /**
+ * The daemon handle for the peer to shut down.
+ */
+ struct GNUNET_TESTING_Daemon *daemon;
+};
+
+struct ServiceStartContext
+{
+ struct GNUNET_TESTING_PeerGroup *pg;
+ unsigned int remaining;
+ GNUNET_TESTING_NotifyCompletion cb;
+ unsigned int outstanding;
+ char *service;
+ struct GNUNET_TIME_Relative timeout;
+ void *cb_cls;
+};
+
+/**
+ * Individual shutdown context for a particular peer.
+ */
+struct PeerServiceStartContext
+{
+ /**
+ * Pointer to the high level start context.
+ */
+ struct ServiceStartContext *start_ctx;
+
+ /**
+ * The daemon handle for the peer to start the service on.
+ */
+ struct GNUNET_TESTING_Daemon *daemon;
+};
+
+struct CreateTopologyContext
+{
+
+ /**
+ * Function to call with number of connections
+ */
+ GNUNET_TESTING_NotifyConnections cont;
+
+ /**
+ * Closure for connection notification
+ */
+ void *cls;
+};
+
+enum States
+{
+ /** Waiting to read number of peers */
+ NUM_PEERS,
+
+ /** Should find next peer index */
+ PEER_INDEX,
+
+ /** Should find colon */
+ COLON,
+
+ /** Should read other peer index, space, or endline */
+ OTHER_PEER_INDEX
+};
+
+#if OLD
+struct PeerConnection
+{
+ /**
+ * Doubly Linked list
+ */
+ struct PeerConnection *prev;
+
+ /*
+ * Doubly Linked list
+ */
+ struct PeerConnection *next;
+
+ /*
+ * Index of daemon in pg->peers
+ */
+ uint32_t index;
+
+};
+#endif
+
+struct InternalStartContext
+{
+ /**
+ * Pointer to peerdata
+ */
+ struct PeerData *peer;
+
+ /**
+ * Timeout for peer startup
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * Client callback for hostkey notification
+ */
+ GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback;
+
+ /**
+ * Closure for hostkey_callback
+ */
+ void *hostkey_cls;
+
+ /**
+ * Client callback for peer start notification
+ */
+ GNUNET_TESTING_NotifyDaemonRunning start_cb;
+
+ /**
+ * Closure for cb
+ */
+ void *start_cb_cls;
+
+ /**
+ * Hostname, where to start the peer
+ */
+ const char *hostname;
+
+ /**
+ * Username to use when connecting to the
+ * host via ssh.
+ */
+ const char *username;
+
+ /**
+ * Pointer to starting memory location of a hostkey
+ */
+ const char *hostkey;
+
+ /**
+ * Port to use for ssh.
+ */
+ uint16_t sshport;
+
+};
+
+struct ChurnRestartContext
+{
+ /**
+ * PeerGroup that we are working with.
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * Number of restarts currently in flight.
+ */
+ unsigned int outstanding;
+
+ /**
+ * Handle to the underlying churn context.
+ */
+ struct ChurnContext *churn_ctx;
+
+ /**
+ * How long to allow the operation to take.
+ */
+ struct GNUNET_TIME_Relative timeout;
+};
+
+struct OutstandingSSH
+{
+ struct OutstandingSSH *next;
+
+ struct OutstandingSSH *prev;
+
+ /**
+ * Number of current ssh connections.
+ */
+ uint32_t outstanding;
+
+ /**
+ * The hostname of this peer.
+ */
+ const char *hostname;
+};
+
+/**
+ * Data we keep per peer.
+ */
+struct PeerData
+{
+ /**
+ * (Initial) configuration of the host.
+ * (initial because clients could change
+ * it and we would not know about those
+ * updates).
+ */
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Handle for controlling the daemon.
+ */
+ struct GNUNET_TESTING_Daemon *daemon;
+
+ /**
+ * The peergroup this peer belongs to.
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+#if OLD
+ /**
+ * Linked list of allowed peer connections.
+ */
+ struct PeerConnection *allowed_peers_head;
+
+ /**
+ * Linked list of allowed peer connections.
+ */
+ struct PeerConnection *allowed_peers_tail;
+
+ /**
+ * Linked list of blacklisted peer connections.
+ */
+ struct PeerConnection *blacklisted_peers_head;
+
+ /**
+ * Linked list of blacklisted peer connections.
+ */
+ struct PeerConnection *blacklisted_peers_tail;
+
+ /**
+ * Linked list of connect peer connections.
+ */
+ struct PeerConnection *connect_peers_head;
+
+ /**
+ * Linked list of connect peer connections.
+ */
+ struct PeerConnection *connect_peers_tail;
+
+ /**
+ * Linked list of connect peer connections.
+ */
+ struct PeerConnection *connect_peers_working_set_head;
+
+ /**
+ * Linked list of connect peer connections.
+ */
+ struct PeerConnection *connect_peers_working_set_tail;
+
+#else
+ /**
+ * Hash map of allowed peer connections (F2F created topology)
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
+
+ /**
+ * Hash map of blacklisted peers
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
+
+ /**
+ * Hash map of peer connections
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
+
+ /**
+ * Temporary hash map of peer connections
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
+#endif
+
+ /**
+ * Temporary variable for topology creation, should be reset before
+ * creating any topology so the count is valid once finished.
+ */
+ int num_connections;
+
+ /**
+ * Context to keep track of peers being started, to
+ * stagger hostkey generation and peer startup.
+ */
+ struct InternalStartContext internal_context;
+
+ /**
+ * Task ID for the queued internal_continue_startup task
+ */
+ GNUNET_SCHEDULER_TaskIdentifier startup_task;
+
+};
+
+/**
+ * Linked list of per-host data.
+ */
+struct HostData
+{
+ /**
+ * Name of the host.
+ */
+ char *hostname;
+
+ /**
+ * SSH username to use when connecting to this host.
+ */
+ char *username;
+
+ /**
+ * SSH port to use when connecting to this host.
+ */
+ uint16_t sshport;
+
+ /**
+ * Lowest port that we have not yet used
+ * for GNUnet.
+ */
+ uint16_t minport;
+};
+
+struct TopologyIterateContext
+{
+ /**
+ * The peergroup we are working with.
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * Callback for notifying of two connected peers.
+ */
+ GNUNET_TESTING_NotifyTopology topology_cb;
+
+ /**
+ * Closure for topology_cb
+ */
+ void *cls;
+
+ /**
+ * Number of peers currently connected to.
+ */
+ unsigned int connected;
+
+ /**
+ * Number of peers we have finished iterating.
+ */
+ unsigned int completed;
+
+ /**
+ * Number of peers total.
+ */
+ unsigned int total;
+};
+
+struct StatsIterateContext
+{
+ /**
+ * The peergroup that we are dealing with.
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * Continuation to call once all stats information has been retrieved.
+ */
+ GNUNET_STATISTICS_Callback cont;
+
+ /**
+ * Proc function to call on each value received.
+ */
+ GNUNET_TESTING_STATISTICS_Iterator proc;
+
+ /**
+ * Closure for topology_cb
+ */
+ void *cls;
+
+ /**
+ * Number of peers currently connected to.
+ */
+ unsigned int connected;
+
+ /**
+ * Number of peers we have finished iterating.
+ */
+ unsigned int completed;
+
+ /**
+ * Number of peers total.
+ */
+ unsigned int total;
+};
+
+struct CoreContext
+{
+ void *iter_context;
+ struct GNUNET_TESTING_Daemon *daemon;
+};
+
+struct StatsCoreContext
+{
+ void *iter_context;
+ struct GNUNET_TESTING_Daemon *daemon;
+ /**
+ * Handle to the statistics service.
+ */
+ struct GNUNET_STATISTICS_Handle *stats_handle;
+
+ /**
+ * Handle for getting statistics.
+ */
+ struct GNUNET_STATISTICS_GetHandle *stats_get_handle;
+};
+
+struct ConnectTopologyContext
+{
+ /**
+ * How many connections are left to create.
+ */
+ unsigned int remaining_connections;
+
+ /**
+ * Handle to group of peers.
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * How long to try this connection before timing out.
+ */
+ struct GNUNET_TIME_Relative connect_timeout;
+
+ /**
+ * How many times to retry connecting the two peers.
+ */
+ unsigned int connect_attempts;
+
+ /**
+ * Temp value set for each iteration.
+ */
+ //struct PeerData *first;
+
+ /**
+ * Notification that all peers are connected.
+ */
+ GNUNET_TESTING_NotifyCompletion notify_connections_done;
+
+ /**
+ * Closure for notify.
+ */
+ void *notify_cls;
+};
+
+struct ConnectContext;
+
+/**
+ * Handle to a group of GNUnet peers.
+ */
+struct GNUNET_TESTING_PeerGroup
+{
+ /**
+ * Configuration template.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ struct ConnectContext *cc_head;
+
+ struct ConnectContext *cc_tail;
+
+ /**
+ * Function to call on each started daemon.
+ */
+ //GNUNET_TESTING_NotifyDaemonRunning cb;
+
+ /**
+ * Closure for cb.
+ */
+ //void *cb_cls;
+
+ /*
+ * Function to call on each topology connection created
+ */
+ GNUNET_TESTING_NotifyConnection notify_connection;
+
+ /*
+ * Callback for notify_connection
+ */
+ void *notify_connection_cls;
+
+ /**
+ * Array of information about hosts.
+ */
+ struct HostData *hosts;
+
+ /**
+ * Number of hosts (size of HostData)
+ */
+ unsigned int num_hosts;
+
+ /**
+ * Array of "total" peers.
+ */
+ struct PeerData *peers;
+
+ /**
+ * Number of peers in this group.
+ */
+ unsigned int total;
+
+ /**
+ * At what time should we fail the peer startup process?
+ */
+ struct GNUNET_TIME_Absolute max_timeout;
+
+ /**
+ * How many peers are being started right now?
+ */
+ unsigned int starting;
+
+ /**
+ * How many peers have already been started?
+ */
+ unsigned int started;
+
+ /**
+ * Number of possible connections to peers
+ * at a time.
+ */
+ unsigned int max_outstanding_connections;
+
+ /**
+ * Number of ssh connections to peers (max).
+ */
+ unsigned int max_concurrent_ssh;
+
+ /**
+ * Number of connects we are waiting on, allows us to rate limit
+ * connect attempts.
+ */
+ unsigned int outstanding_connects;
+
+ /**
+ * Number of HELLOs we have yet to send.
+ */
+ unsigned int remaining_hellos;
+
+ /**
+ * How many connects have already been scheduled?
+ */
+ unsigned int total_connects_scheduled;
+
+ /**
+ * Hostkeys loaded from a file.
+ */
+ char *hostkey_data;
+
+ /**
+ * Head of DLL to keep track of the number of outstanding
+ * ssh connections per peer.
+ */
+ struct OutstandingSSH *ssh_head;
+
+ /**
+ * Tail of DLL to keep track of the number of outstanding
+ * ssh connections per peer.
+ */
+ struct OutstandingSSH *ssh_tail;
+
+ /**
+ * Stop scheduling peers connecting.
+ */
+ unsigned int stop_connects;
+
+ /**
+ * Connection context for peer group.
+ */
+ struct ConnectTopologyContext ct_ctx;
+};
+
+struct UpdateContext
+{
+ /**
+ * The altered configuration.
+ */
+ struct GNUNET_CONFIGURATION_Handle *ret;
+
+ /**
+ * The original configuration to alter.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *orig;
+
+ /**
+ * The hostname that this peer will run on.
+ */
+ const char *hostname;
+
+ /**
+ * The next possible port to assign.
+ */
+ unsigned int nport;
+
+ /**
+ * Unique number for unix domain sockets.
+ */
+ unsigned int upnum;
+
+ /**
+ * Unique number for this peer/host to offset
+ * things that are grouped by host.
+ */
+ unsigned int fdnum;
+};
+
+struct ConnectContext
+{
+
+ struct ConnectContext *next;
+
+ struct ConnectContext *prev;
+
+ /**
+ * Index of peer to connect second to.
+ */
+ uint32_t first_index;
+
+ /**
+ * Index of peer to connect first to.
+ */
+ uint32_t second_index;
+
+ /**
+ * Task associated with the attempt to connect.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier task;
+
+ /**
+ * Context in 'testing.c', to cancel connection attempt.
+ */
+ struct GNUNET_TESTING_ConnectContext *cc;
+
+ /**
+ * Higher level topology connection context.
+ */
+ struct ConnectTopologyContext *ct_ctx;
+
+ /**
+ * Whether this connection has been accounted for in the schedule_connect call.
+ */
+ int counted;
+};
+
+struct UnblacklistContext
+{
+ /**
+ * The peergroup
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * uid of the first peer
+ */
+ uint32_t first_uid;
+};
+
+struct RandomContext
+{
+ /**
+ * The peergroup
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * uid of the first peer
+ */
+ uint32_t first_uid;
+
+ /**
+ * Peer data for first peer.
+ */
+ struct PeerData *first;
+
+ /**
+ * Random percentage to use
+ */
+ double percentage;
+};
+
+struct MinimumContext
+{
+ /**
+ * The peergroup
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * uid of the first peer
+ */
+ uint32_t first_uid;
+
+ /**
+ * Peer data for first peer.
+ */
+ struct PeerData *first;
+
+ /**
+ * Number of conns per peer
+ */
+ unsigned int num_to_add;
+
+ /**
+ * Permuted array of all possible connections. Only add the Nth
+ * peer if it's in the Nth position.
+ */
+ unsigned int *pg_array;
+
+ /**
+ * What number is the current element we are iterating over?
+ */
+ unsigned int current;
+};
+
+struct DFSContext
+{
+ /**
+ * The peergroup
+ */
+ struct GNUNET_TESTING_PeerGroup *pg;
+
+ /**
+ * uid of the first peer
+ */
+ uint32_t first_uid;
+
+ /**
+ * uid of the second peer
+ */
+ uint32_t second_uid;
+
+ /**
+ * Peer data for first peer.
+ */
+ struct PeerData *first;
+
+ /**
+ * Which peer has been chosen as the one to add?
+ */
+ unsigned int chosen;
+
+ /**
+ * What number is the current element we are iterating over?
+ */
+ unsigned int current;
+};
+
+/**
+ * Simple struct to keep track of progress, and print a
+ * nice little percentage meter for long running tasks.
+ */
+struct ProgressMeter
+{
+ unsigned int total;
+
+ unsigned int modnum;
+
+ unsigned int dotnum;
+
+ unsigned int completed;
+
+ int print;
+
+ char *startup_string;
+};
+
+#if !OLD
+/**
+ * Convert unique ID to hash code.
+ *
+ * @param uid unique ID to convert
+ * @param hash set to uid (extended with zeros)
+ */
+static void
+hash_from_uid (uint32_t uid, GNUNET_HashCode * hash)
+{
+ memset (hash, 0, sizeof (GNUNET_HashCode));
+ *((uint32_t *) hash) = uid;
+}
+
+/**
+ * Convert hash code to unique ID.
+ *
+ * @param uid unique ID to convert
+ * @param hash set to uid (extended with zeros)
+ */
+static void
+uid_from_hash (const GNUNET_HashCode * hash, uint32_t * uid)
+{
+ memcpy (uid, hash, sizeof (uint32_t));
+}
+#endif
+
+#if USE_SEND_HELLOS
+static struct GNUNET_CORE_MessageHandler no_handlers[] = {
+ {NULL, 0, 0}
+};
+#endif
+
+/**
+ * Create a meter to keep track of the progress of some task.
+ *
+ * @param total the total number of items to complete
+ * @param start_string a string to prefix the meter with (if printing)
+ * @param print GNUNET_YES to print the meter, GNUNET_NO to count
+ * internally only
+ *
+ * @return the progress meter
+ */
+static struct ProgressMeter *
+create_meter (unsigned int total, char *start_string, int print)
+{
+ struct ProgressMeter *ret;
+
+ ret = GNUNET_malloc (sizeof (struct ProgressMeter));
+ ret->print = print;
+ ret->total = total;
+ ret->modnum = total / 4;
+ if (ret->modnum == 0) /* Divide by zero check */
+ ret->modnum = 1;
+ ret->dotnum = (total / 50) + 1;
+ if (start_string != NULL)
+ ret->startup_string = GNUNET_strdup (start_string);
+ else
+ ret->startup_string = GNUNET_strdup ("");
+
+ return ret;
+}
+
+/**
+ * Update progress meter (increment by one).
+ *
+ * @param meter the meter to update and print info for
+ *
+ * @return GNUNET_YES if called the total requested,
+ * GNUNET_NO if more items expected
+ */
+static int
+update_meter (struct ProgressMeter *meter)
+{
+ if (meter->print == GNUNET_YES)
+ {
+ if (meter->completed % meter->modnum == 0)
+ {
+ if (meter->completed == 0)
+ {
+ FPRINTF (stdout, "%sProgress: [0%%", meter->startup_string);
+ }
+ else
+ FPRINTF (stdout, "%d%%",
+ (int) (((float) meter->completed / meter->total) * 100));
+ }
+ else if (meter->completed % meter->dotnum == 0)
+ FPRINTF (stdout, "%s", ".");
+
+ if (meter->completed + 1 == meter->total)
+ FPRINTF (stdout, "%d%%]\n", 100);
+ fflush (stdout);
+ }
+ meter->completed++;
+
+ if (meter->completed == meter->total)
+ return GNUNET_YES;
+ if (meter->completed > meter->total)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Progress meter overflow!!\n");
+ return GNUNET_NO;
+}
+
+/**
+ * Reset progress meter.
+ *
+ * @param meter the meter to reset
+ *
+ * @return GNUNET_YES if meter reset,
+ * GNUNET_SYSERR on error
+ */
+static int
+reset_meter (struct ProgressMeter *meter)
+{
+ if (meter == NULL)
+ return GNUNET_SYSERR;
+
+ meter->completed = 0;
+ return GNUNET_YES;
+}
+
+/**
+ * Release resources for meter
+ *
+ * @param meter the meter to free
+ */
+static void
+free_meter (struct ProgressMeter *meter)
+{
+ GNUNET_free_non_null (meter->startup_string);
+ GNUNET_free (meter);
+}
+
+/**
+ * Get a topology from a string input.
+ *
+ * @param topology where to write the retrieved topology
+ * @param topology_string The string to attempt to
+ * get a configuration value from
+ * @return GNUNET_YES if topology string matched a
+ * known topology, GNUNET_NO if not
+ */
+int
+GNUNET_TESTING_topology_get (enum GNUNET_TESTING_Topology *topology,
+ const char *topology_string)
+{
+ /**
+ * Strings representing topologies in enum
+ */
+ static const char *topology_strings[] = {
+ /**
+ * A clique (everyone connected to everyone else).
+ */
+ "CLIQUE",
+
+ /**
+ * Small-world network (2d torus plus random links).
+ */
+ "SMALL_WORLD",
+
+ /**
+ * Small-world network (ring plus random links).
+ */
+ "SMALL_WORLD_RING",
+
+ /**
+ * Ring topology.
+ */
+ "RING",
+
+ /**
+ * 2-d torus.
+ */
+ "2D_TORUS",
+
+ /**
+ * Random graph.
+ */
+ "ERDOS_RENYI",
+
+ /**
+ * Certain percentage of peers are unable to communicate directly
+ * replicating NAT conditions
+ */
+ "INTERNAT",
+
+ /**
+ * Scale free topology.
+ */
+ "SCALE_FREE",
+
+ /**
+ * Straight line topology.
+ */
+ "LINE",
+
+ /**
+ * All peers are disconnected.
+ */
+ "NONE",
+
+ /**
+ * Read the topology from a file.
+ */
+ "FROM_FILE",
+
+ NULL
+ };
+
+ int curr = 0;
+
+ if (topology_string == NULL)
+ return GNUNET_NO;
+ while (topology_strings[curr] != NULL)
+ {
+ if (strcasecmp (topology_strings[curr], topology_string) == 0)
+ {
+ *topology = curr;
+ return GNUNET_YES;
+ }
+ curr++;
+ }
+ *topology = GNUNET_TESTING_TOPOLOGY_NONE;
+ return GNUNET_NO;
+}
+
+/**
+ * Get connect topology option from string input.
+ *
+ * @param topology_option where to write the retrieved topology
+ * @param topology_string The string to attempt to
+ * get a configuration value from
+ * @return GNUNET_YES if string matched a known
+ * topology option, GNUNET_NO if not
+ */
+int
+GNUNET_TESTING_topology_option_get (enum GNUNET_TESTING_TopologyOption
+ *topology_option,
+ const char *topology_string)
+{
+ /**
+ * Options for connecting a topology as strings.
+ */
+ static const char *topology_option_strings[] = {
+ /**
+ * Try to connect all peers specified in the topology.
+ */
+ "CONNECT_ALL",
+
+ /**
+ * Choose a random subset of connections to create.
+ */
+ "CONNECT_RANDOM_SUBSET",
+
+ /**
+ * Create at least X connections for each peer.
+ */
+ "CONNECT_MINIMUM",
+
+ /**
+ * Using a depth first search, create one connection
+ * per peer. If any are missed (graph disconnected)
+ * start over at those peers until all have at least one
+ * connection.
+ */
+ "CONNECT_DFS",
+
+ /**
+ * Find the N closest peers to each allowed peer in the
+ * topology and make sure a connection to those peers
+ * exists in the connect topology.
+ */
+ "CONNECT_CLOSEST",
+
+ /**
+ * No options specified.
+ */
+ "CONNECT_NONE",
+
+ NULL
+ };
+ int curr = 0;
+
+ if (topology_string == NULL)
+ return GNUNET_NO;
+ while (NULL != topology_option_strings[curr])
+ {
+ if (strcasecmp (topology_option_strings[curr], topology_string) == 0)
+ {
+ *topology_option = curr;
+ return GNUNET_YES;
+ }
+ curr++;
+ }
+ *topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_NONE;
+ return GNUNET_NO;
+}
+
+/**
+ * Function to iterate over options. Copies
+ * the options to the target configuration,
+ * updating PORT values as needed.
+ *
+ * @param cls closure
+ * @param section name of the section
+ * @param option name of the option
+ * @param value value of the option
+ */
+static void
+update_config (void *cls, const char *section, const char *option,
+ const char *value)
+{
+ struct UpdateContext *ctx = cls;
+ unsigned int ival;
+ char cval[12];
+ char uval[128];
+ char *single_variable;
+ char *per_host_variable;
+ unsigned long long num_per_host;
+
+ GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
+ GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
+
+ if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
+ {
+ if ((ival != 0) &&
+ (GNUNET_YES !=
+ GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
+ single_variable)))
+ {
+ GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
+ value = cval;
+ }
+ else if ((ival != 0) &&
+ (GNUNET_YES ==
+ GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
+ single_variable)) &&
+ GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
+ per_host_variable,
+ &num_per_host))
+ {
+ GNUNET_snprintf (cval, sizeof (cval), "%u",
+ ival + ctx->fdnum % num_per_host);
+ value = cval;
+ }
+
+ /* FIXME: REMOVE FOREVER HACK HACK HACK */
+ if (0 == strcasecmp (section, "transport-tcp"))
+ GNUNET_CONFIGURATION_set_value_string (ctx->ret, section,
+