style improvments wrt Mantis 1614 patch
[oweals/gnunet.git] / src / testing / testing_group.c
index f92d0c0140318c1e53d998fce7f7c1bc4498f48f..7322fc44a65a0165cc104fffeefb8e5fb90396bb 100644 (file)
@@ -51,7 +51,7 @@
  */
 #define HIGH_PORT 56000
 
-#define MAX_OUTSTANDING_CONNECTIONS 10
+#define MAX_OUTSTANDING_CONNECTIONS 40
 
 #define MAX_CONCURRENT_HOSTKEYS 10
 
@@ -67,7 +67,7 @@
  * Prototype of a function called whenever two peers would be connected
  * in a certain topology.
  */
-typedef int (*GNUNET_TESTING_ConnectionProcessor)(struct GNUNET_TESTING_PeerGroup *pg, 
+typedef unsigned int (*GNUNET_TESTING_ConnectionProcessor)(struct GNUNET_TESTING_PeerGroup *pg,
                                                  unsigned int first,
                                                  unsigned int second);
 
@@ -556,14 +556,50 @@ struct UpdateContext
   unsigned int fdnum;
 };
 
+struct ConnectTopologyContext
+{
+  /**
+   * How many connections are left to create.
+   */
+  unsigned int remaining_connections;
+
+  /**
+   * Handle to group of peers.
+   */
+  struct GNUNET_TESTING_PeerGroup *pg;
+
+  /**
+   * 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
 {
+  /**
+   * Peer to connect second to.
+   */
   struct GNUNET_TESTING_Daemon *first;
 
+  /**
+   * Peer to connect first to.
+   */
   struct GNUNET_TESTING_Daemon *second;
 
-  struct GNUNET_TESTING_PeerGroup *pg;
+  /**
+   * Higher level topology connection context.
+   */
+  struct ConnectTopologyContext *ct_ctx;
 };
 
 /**
@@ -608,7 +644,7 @@ static int outstanding_connects;
  *         known topology, GNUNET_NO if not
  */
 int
-GNUNET_TESTING_topology_get(enum GNUNET_TESTING_Topology *topology, char * topology_string)
+GNUNET_TESTING_topology_get(enum GNUNET_TESTING_Topology *topology, const char * topology_string)
 {
   /**
    * Strings representing topologies in enum
@@ -697,7 +733,7 @@ GNUNET_TESTING_topology_get(enum GNUNET_TESTING_Topology *topology, char * topol
  */
 int
 GNUNET_TESTING_topology_option_get (enum GNUNET_TESTING_TopologyOption *topology_option,
-                                   char * topology_string)
+                                   const char * topology_string)
 {
   /**
    * Options for connecting a topology as strings.
@@ -903,7 +939,6 @@ make_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
       GNUNET_CONFIGURATION_set_value_string(uc.ret, "transport-udp", "BINDTO", "127.0.0.1");
     }
 
-
   *port = (uint16_t) uc.nport;
   *upnum = uc.upnum;
   uc.fdnum++;
@@ -923,7 +958,7 @@ make_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
  *         technically should only be 0 or 2
  *
  */
-static int
+static unsigned int
 add_actual_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
 {
   int added;
@@ -980,7 +1015,7 @@ add_actual_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first,
  *         for being sure doesn't bother me!
  *
  */
-static int
+static unsigned int
 add_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
 {
   int added;
@@ -1070,7 +1105,7 @@ add_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first,
  * @return the number of connections added (can be 0, 1 or 2)
  *
  */
-static int
+static unsigned int
 blacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
 {
   int added;
@@ -1122,7 +1157,7 @@ blacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, u
  * @return the number of connections removed (can be 0, 1 or 2)
  *
  */
-static int
+static unsigned int
 unblacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
 {
   int removed;
@@ -1168,7 +1203,7 @@ unblacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first,
  *
  * @return the number of connections created
  */
-static int
+static unsigned int
 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
 
@@ -1223,7 +1258,7 @@ create_scale_free (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_Connectio
  * @return the number of connections that were set up
  *
  */
-int
+static unsigned int
 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int i, j;
@@ -1345,7 +1380,7 @@ create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_Conn
  * @return the number of connections that were set up
  *
  */
-static int
+static unsigned int
 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int outer_count, inner_count;
@@ -1407,7 +1442,7 @@ create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_Conne
  * @return the number of connections that were set up
  *
  */
-static int
+static unsigned int
 create_small_world (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int i, j, k;
@@ -1519,6 +1554,8 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_Connecti
 #endif
   smallWorldConnections = 0;
   small_world_it = (unsigned int)(natLog * percentage);
+  if (small_world_it < 1)
+    small_world_it = 1;
   GNUNET_assert(small_world_it > 0 && small_world_it < (unsigned int)-1);
   for (i = 0; i < small_world_it; i++)
     {
@@ -1569,7 +1606,7 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_Connecti
  * @return the number of connections that were set up
  *
  */
-static int
+static unsigned int
 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   double temp_rand;
@@ -1629,7 +1666,7 @@ create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_Connecti
  * @return the number of connections that were set up
  *
  */
-static int
+static unsigned int
 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int i;
@@ -1717,7 +1754,7 @@ create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionP
  * @return the number of connections that were set up
  *
  */
-static int
+static unsigned int
 create_clique (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int outer_count;
@@ -1754,7 +1791,7 @@ create_clique (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionPro
  * @return the number of connections that were set up
  *
  */
-static int
+static unsigned int
 create_line (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int count;
@@ -1787,7 +1824,7 @@ create_line (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProce
  * @return the number of connections that were set up
  *
  */
-static int
+static unsigned int
 create_ring (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int count;
@@ -1900,7 +1937,7 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
   FILE *temp_friend_handle;
   unsigned int pg_iter;
   char *temp_service_path;
-  pid_t *pidarr;
+  struct GNUNET_OS_Process **procarr;
   char *arg;
   char * mytemp;
   enum GNUNET_OS_ProcessStatusType type;
@@ -1909,7 +1946,7 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
   int ret;
   int max_wait = 10;
 
-  pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
+  procarr = GNUNET_malloc(sizeof(struct GNUNET_OS_Process *) * pg->total);
   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
     {
       mytemp = GNUNET_DISK_mktemp("friends");
@@ -1935,7 +1972,7 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
         {
           GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
-          pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
+          procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
                                          "mv", mytemp, arg, NULL);
 #if VERBOSE_TESTING
           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -1950,7 +1987,7 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
             GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
           else
             GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
-          pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
+          procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
                                          "scp", mytemp, arg, NULL);
 
 #if VERBOSE_TESTING
@@ -1974,9 +2011,9 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                       _("Checking copy status of file %d\n"), pg_iter);
 #endif
-          if (pidarr[pg_iter] != 0) /* Check for already completed! */
+          if (procarr[pg_iter] != NULL) /* Check for already completed! */
             {
-              if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
+              if (GNUNET_OS_process_status(procarr[pg_iter], &type, &return_code) != GNUNET_OK)
                 {
                   ret = GNUNET_SYSERR;
                 }
@@ -1986,7 +2023,8 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
                 }
               else
                 {
-                  pidarr[pg_iter] = 0;
+                  GNUNET_OS_process_close (procarr[pg_iter]);
+                  procarr[pg_iter] = NULL;
 #if VERBOSE_TESTING
             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                       _("File %d copied\n"), pg_iter);
@@ -2006,7 +2044,7 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 _("Finished copying all friend files!\n"));
 #endif
-  GNUNET_free(pidarr);
+  GNUNET_free(procarr);
   return ret;
 }
 
@@ -2020,13 +2058,13 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
  * @param transports space delimited list of transports to blacklist
  */
 static int
-create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *transports)
+create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, const char *transports)
 {
   FILE *temp_file_handle;
   static struct BlacklistContext blacklist_ctx;
   unsigned int pg_iter;
   char *temp_service_path;
-  pid_t *pidarr;
+  struct GNUNET_OS_Process **procarr;
   char *arg;
   char *mytemp;
   enum GNUNET_OS_ProcessStatusType type;
@@ -2039,7 +2077,7 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *tran
   char *pos;
   char *temp_transports;
 
-  pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
+  procarr = GNUNET_malloc(sizeof(struct GNUNET_OS_Process *) * pg->total);
   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
     {
       mytemp = GNUNET_DISK_mktemp("blacklist");
@@ -2087,7 +2125,7 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *tran
       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
         {
           GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
-          pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
+          procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
                                          "mv", mytemp, arg, NULL);
 #if VERBOSE_TESTING
           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2102,7 +2140,7 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *tran
             GNUNET_asprintf (&arg, "%s@%s:%s/blacklist", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
           else
             GNUNET_asprintf (&arg, "%s:%s/blacklist", pg->peers[pg_iter].daemon->hostname, temp_service_path);
-          pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
+          procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
                                          "scp", mytemp, arg, NULL);
 
 #if VERBOSE_TESTING
@@ -2126,9 +2164,9 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *tran
           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                       _("Checking copy status of file %d\n"), pg_iter);
 #endif
-          if (pidarr[pg_iter] != 0) /* Check for already completed! */
+          if (procarr[pg_iter] != NULL) /* Check for already completed! */
             {
-              if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
+              if (GNUNET_OS_process_status(procarr[pg_iter], &type, &return_code) != GNUNET_OK)
                 {
                   ret = GNUNET_SYSERR;
                 }
@@ -2138,7 +2176,8 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *tran
                 }
               else
                 {
-                  pidarr[pg_iter] = 0;
+                  GNUNET_OS_process_close (procarr[pg_iter]);
+                  procarr[pg_iter] = NULL;
 #if VERBOSE_TESTING
             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                       _("File %d copied\n"), pg_iter);
@@ -2158,7 +2197,7 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *tran
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 _("Finished copying all blacklist files!\n"));
 #endif
-  GNUNET_free(pidarr);
+  GNUNET_free(procarr);
   return ret;
 }
 
@@ -2177,10 +2216,19 @@ static void internal_connect_notify (void *cls,
                                      struct GNUNET_TESTING_Daemon *second_daemon,
                                      const char *emsg)
 {
-  struct GNUNET_TESTING_PeerGroup *pg = cls;
+  struct ConnectTopologyContext *ct_ctx = cls;
+  struct GNUNET_TESTING_PeerGroup *pg = ct_ctx->pg;
   outstanding_connects--;
+  ct_ctx->remaining_connections--;
+  if (ct_ctx->remaining_connections == 0)
+    {
+      if (ct_ctx->notify_connections_done != NULL)
+        ct_ctx->notify_connections_done(ct_ctx->notify_cls, NULL);
+      GNUNET_free(ct_ctx);
+    }
 
-  pg->notify_connection(pg->notify_connection_cls, first, second, distance, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
+  if (pg->notify_connection != NULL)
+    pg->notify_connection (pg->notify_connection_cls, first, second, distance, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
 }
 
 
@@ -2204,7 +2252,7 @@ static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContex
           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                       _("Delaying connect, we have too many outstanding connections!\n"));
 #endif
-      GNUNET_SCHEDULER_add_delayed(connect_context->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 100), &schedule_connect, connect_context);
+      GNUNET_SCHEDULER_add_delayed(connect_context->ct_ctx->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 100), &schedule_connect, connect_context);
     }
   else
     {
@@ -2218,7 +2266,7 @@ static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContex
                                       CONNECT_TIMEOUT,
                                       CONNECT_ATTEMPTS,
                                       &internal_connect_notify,
-                                      connect_context->pg);
+                                      connect_context->ct_ctx);
       GNUNET_free(connect_context);
     }
 }
@@ -2239,14 +2287,15 @@ connect_iterator (void *cls,
                   const GNUNET_HashCode * key,
                   void *value)
 {
-  struct PeerData *first = cls;
+  struct ConnectTopologyContext *ct_ctx = cls;
+  struct PeerData *first = ct_ctx->first;
   struct GNUNET_TESTING_Daemon *second = value;
   struct ConnectContext *connect_context;
 
   connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
-  connect_context->pg = first->pg;
   connect_context->first = first->daemon;
   connect_context->second = second;
+  connect_context->ct_ctx = ct_ctx;
   GNUNET_SCHEDULER_add_now(first->pg->sched, &schedule_connect, connect_context);
 
   return GNUNET_YES;
@@ -2310,32 +2359,50 @@ copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
  * @return the number of connections that will be attempted
  */
 static int
-connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
+connect_topology (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_NotifyCompletion notify_callback, void *notify_cls)
 {
   unsigned int pg_iter;
   int ret;
-  int total;
+  unsigned int total;
+  struct ConnectTopologyContext *ct_ctx;
 #if OLD
   struct PeerConnection *connection_iter;
   struct ConnectContext *connect_context;
 #endif
 
   total = 0;
+  ct_ctx = GNUNET_malloc(sizeof(struct ConnectTopologyContext));
+  ct_ctx->notify_connections_done = notify_callback;
+  ct_ctx->notify_cls = notify_cls;
+  ct_ctx->pg = pg;
+
   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
     {
-      ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &connect_iterator, &pg->peers[pg_iter]);
-      if (GNUNET_SYSERR == ret)
-        return GNUNET_SYSERR;
+      total += GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers);
+    }
 
+  if (total == 0)
+    {
+      GNUNET_free(ct_ctx);
+      return total;
+    }
+  ct_ctx->remaining_connections = total;
+  total = 0;
+
+  for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
+    {
+      ct_ctx->first = &pg->peers[pg_iter];
+      ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &connect_iterator, ct_ctx);
+      GNUNET_assert(GNUNET_SYSERR != ret && ret >= 0);
       total = total + ret;
 
 #if OLD
-      connection_iter = ;
+      connection_iter = FIXME;
       while (connection_iter != NULL)
         {
           connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
           connect_context->pg = pg;
-          connect_context->first = ;
+          connect_context->first = FIXME;
           connect_context->second = connection_iter->daemon;
           GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
           connection_iter = connection_iter->next;
@@ -2366,18 +2433,16 @@ connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
  * @return the maximum number of connections were all allowed peers
  *         connected to each other
  */
-int
+unsigned int
 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
                                 enum GNUNET_TESTING_Topology topology,
                                 enum GNUNET_TESTING_Topology restrict_topology,
-                                char *restrict_transports)
+                                const char *restrict_transports)
 {
   int ret;
-  int num_connections;
+  unsigned int num_connections;
   int unblacklisted_connections;
 
-  GNUNET_assert (pg->notify_connection != NULL);
-
   switch (topology)
     {
     case GNUNET_TESTING_TOPOLOGY_CLIQUE:
@@ -2481,6 +2546,7 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
   /* Use the create clique method to initially set all connections as blacklisted. */
   if (restrict_topology != GNUNET_TESTING_TOPOLOGY_NONE)
     create_clique (pg, &blacklist_connections);
+
   unblacklisted_connections = 0;
   /* Un-blacklist connections as per the topology specified */
   switch (restrict_topology)
@@ -2566,7 +2632,7 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                     _("Failed during blacklist file copying!\n"));
 #endif
-        return GNUNET_SYSERR;
+        return 0;
       }
     else
       {
@@ -3234,6 +3300,66 @@ schedule_get_statistics(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc
     }
 }
 
+struct DuplicateStats
+{
+  /**
+   * Next item in the list
+   */
+  struct DuplicateStats *next;
+
+  /**
+   * Nasty string, concatenation of relevant information.
+   */
+  char *unique_string;
+};
+
+/**
+ * Check whether the combination of port/host/unix domain socket
+ * already exists in the list of peers being checked for statistics.
+ *
+ * @param pg the peergroup in question
+ * @param specific_peer the peer we're concerned with
+ * @param stats_list the list to return to the caller
+ *
+ * @return GNUNET_YES if the statistics instance has been seen already,
+ *         GNUNET_NO if not (and we may have added it to the list)
+ */
+static int
+stats_check_existing(struct GNUNET_TESTING_PeerGroup *pg, struct PeerData *specific_peer, struct DuplicateStats **stats_list)
+{
+  struct DuplicateStats *pos;
+  char *unix_domain_socket;
+  unsigned long long port;
+  char *to_match;
+  if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno(pg->cfg, "testing", "single_statistics_per_host"))
+    return GNUNET_NO; /* Each peer has its own statistics instance, do nothing! */
+
+  pos = *stats_list;
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(specific_peer->cfg, "statistics", "unixpath", &unix_domain_socket))
+    return GNUNET_NO;
+
+  GNUNET_CONFIGURATION_get_value_number(specific_peer->cfg, "statistics", "port", &port);
+
+  if (specific_peer->daemon->hostname != NULL)
+    GNUNET_asprintf(&to_match, "%s%s%llu", specific_peer->daemon->hostname, unix_domain_socket, port);
+  else
+    GNUNET_asprintf(&to_match, "%s%llu", unix_domain_socket, port);
+
+  while (pos != NULL)
+    {
+      if (0 == strcmp(to_match, pos->unique_string))
+        {
+          GNUNET_free(to_match);
+          return GNUNET_YES;
+        }
+      pos = pos->next;
+    }
+  pos = GNUNET_malloc(sizeof(struct DuplicateStats));
+  pos->unique_string = to_match;
+  pos->next = *stats_list;
+  *stats_list = pos;
+  return GNUNET_NO;
+}
 
 /**
  * Iterate over all (running) peers in the peer group, retrieve
@@ -3248,6 +3374,9 @@ GNUNET_TESTING_get_statistics (struct GNUNET_TESTING_PeerGroup *pg,
   struct StatsCoreContext *core_ctx;
   unsigned int i;
   unsigned int total_count;
+  struct DuplicateStats *stats_list;
+  struct DuplicateStats *pos;
+  stats_list = NULL;
 
   /* Allocate a single stats iteration context */
   stats_context = GNUNET_malloc(sizeof(struct StatsIterateContext));
@@ -3255,9 +3384,10 @@ GNUNET_TESTING_get_statistics (struct GNUNET_TESTING_PeerGroup *pg,
   stats_context->proc = proc;
   stats_context->cls = cls;
   total_count = 0;
+
   for (i = 0; i < pg->total; i++)
     {
-      if (pg->peers[i].daemon->running == GNUNET_YES)
+      if ((pg->peers[i].daemon->running == GNUNET_YES) && (GNUNET_NO == stats_check_existing(pg, &pg->peers[i], &stats_list)))
         {
           /* Allocate one core context per core we need to connect to */
           core_ctx = GNUNET_malloc(sizeof(struct StatsCoreContext));
@@ -3268,7 +3398,20 @@ GNUNET_TESTING_get_statistics (struct GNUNET_TESTING_PeerGroup *pg,
           total_count++;
         }
     }
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Retrieving stats from %u total instances.\n", total_count);
   stats_context->total = total_count;
+  if (stats_list != NULL)
+    {
+      pos = stats_list;
+      while(pos != NULL)
+        {
+          GNUNET_free(pos->unique_string);
+          stats_list = pos->next;
+          GNUNET_free(pos);
+          pos = stats_list->next;
+        }
+    }
   return;
 }
 
@@ -3285,13 +3428,18 @@ GNUNET_TESTING_get_statistics (struct GNUNET_TESTING_PeerGroup *pg,
  * @param topology which topology to connect the peers in
  * @param options options for connecting the topology
  * @param option_modifier modifier for options that take a parameter
+ * @param notify_callback notification to be called once all connections completed
+ * @param notify_cls closure for notification callback
+ *
  * @return the number of connections that will be attempted, GNUNET_SYSERR on error
  */
 int
 GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
                                  enum GNUNET_TESTING_Topology topology,
                                  enum GNUNET_TESTING_TopologyOption options,
-                                 double option_modifier)
+                                 double option_modifier,
+                                 GNUNET_TESTING_NotifyCompletion notify_callback,
+                                 void *notify_cls)
 {
   switch (topology)
       {
@@ -3409,7 +3557,7 @@ GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
       break;
     }
 
-  return connect_topology(pg);
+  return connect_topology(pg, notify_callback, notify_cls);
 }
 
 /**