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 2, 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 Christian Grothoff
27 #include "gnunet_arm_service.h"
28 #include "gnunet_testing_lib.h"
30 #define VERBOSE_TESTING GNUNET_NO
33 * Lowest port used for GNUnet testing. Should be high enough to not
34 * conflict with other applications running on the hosts but be low
35 * enough to not conflict with client-ports (typically starting around
38 #define LOW_PORT 10000
41 * Highest port used for GNUnet testing. Should be low enough to not
42 * conflict with the port range for "local" ports (client apps; see
43 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
45 #define HIGH_PORT 32000
47 #define MAX_OUTSTANDING_CONNECTIONS 50
49 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 160)
51 #define CONNECT_ATTEMPTS 8
58 struct PeerConnection *next;
61 * Pointer to daemon handle
63 struct GNUNET_TESTING_Daemon *daemon;
68 * Data we keep per peer.
73 * (Initial) configuration of the host.
74 * (initial because clients could change
75 * it and we would not know about those
78 struct GNUNET_CONFIGURATION_Handle *cfg;
81 * Handle for controlling the daemon.
83 struct GNUNET_TESTING_Daemon *daemon;
86 * Linked list of peer connections (simply indexes of PeerGroup)
88 struct PeerConnection *connected_peers;
93 * Data we keep per host.
103 * Lowest port that we have not yet used
111 * Handle to a group of GNUnet peers.
113 struct GNUNET_TESTING_PeerGroup
118 struct GNUNET_SCHEDULER_Handle *sched;
121 * Configuration template.
123 const struct GNUNET_CONFIGURATION_Handle *cfg;
126 * Function to call on each started daemon.
128 GNUNET_TESTING_NotifyDaemonRunning cb;
136 * Function to call on each topology connection created
138 GNUNET_TESTING_NotifyConnection notify_connection;
141 * Callback for notify_connection
143 void *notify_connection_cls;
146 * NULL-terminated array of information about
149 struct HostData *hosts;
152 * Array of "total" peers.
154 struct PeerData *peers;
157 * Number of peers in this group.
166 struct GNUNET_CONFIGURATION_Handle *ret;
168 const char *hostname;
172 struct ConnectContext
174 struct GNUNET_TESTING_Daemon *first;
176 struct GNUNET_TESTING_Daemon *second;
178 struct GNUNET_TESTING_PeerGroup *pg;
182 * Number of connects we are waiting on, allows us to rate limit
185 static int outstanding_connects;
189 * Function to iterate over options. Copies
190 * the options to the target configuration,
191 * updating PORT values as needed.
194 * @param section name of the section
195 * @param option name of the option
196 * @param value value of the option
199 update_config (void *cls,
200 const char *section, const char *option, const char *value)
202 struct UpdateContext *ctx = cls;
206 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
208 GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
212 if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
214 value = ctx->hostname;
217 GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
222 * Create a new configuration using the given configuration
223 * as a template; however, each PORT in the existing cfg
224 * must be renumbered by incrementing "*port". If we run
225 * out of "*port" numbers, return NULL.
227 * @param cfg template configuration
228 * @param port port numbers to use, update to reflect
229 * port numbers that were used
230 * @param hostname hostname of the controlling host, to allow control connections from
232 * @return new configuration, NULL on error
234 static struct GNUNET_CONFIGURATION_Handle *
235 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t * port, const char *hostname)
237 struct UpdateContext uc;
244 uc.ret = GNUNET_CONFIGURATION_create ();
245 uc.hostname = hostname;
247 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
248 if (uc.nport >= HIGH_PORT)
251 GNUNET_CONFIGURATION_destroy (uc.ret);
255 if (GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "control_host", &control_host) == GNUNET_OK)
257 GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", control_host);
258 GNUNET_CONFIGURATION_set_value_string(uc.ret, "core", "ACCEPT_FROM", allowed_hosts);
259 GNUNET_free_non_null(control_host);
260 GNUNET_free(allowed_hosts);
264 /* arm needs to know to allow connections from the host on which it is running,
265 * otherwise gnunet-arm is unable to connect to it in some instances */
266 if (hostname != NULL)
268 GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", hostname);
269 GNUNET_CONFIGURATION_set_value_string(uc.ret, "arm", "ACCEPT_FROM", allowed_hosts);
270 GNUNET_free(allowed_hosts);
273 *port = (uint16_t) uc.nport;
278 * Add entries to the peers connected list
280 * @param pg the peer group we are working with
281 * @param first index of the first peer
282 * @param second index of the second peer
284 * @return the number of connections added (can be 0, 1 or 2)
286 * FIXME: add both, or only add one?
287 * - if both are added, then we have to keep track
288 * when connecting so we don't double connect
289 * - if only one is added, we need to iterate over
290 * both lists to find out if connection already exists
291 * - having both allows the whitelisting/friend file
292 * creation to be easier
294 * -- For now, add both, we have to iterate over each to
295 * check for duplicates anyways, so we'll take the performance
296 * hit assuming we don't have __too__ many connections
300 add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
303 struct PeerConnection *first_iter;
304 struct PeerConnection *second_iter;
307 struct PeerConnection *new_first;
308 struct PeerConnection *new_second;
310 first_iter = pg->peers[first].connected_peers;
311 add_first = GNUNET_YES;
312 while (first_iter != NULL)
314 if (first_iter->daemon == pg->peers[second].daemon)
315 add_first = GNUNET_NO;
316 first_iter = first_iter->next;
319 second_iter = pg->peers[second].connected_peers;
320 add_second = GNUNET_YES;
321 while (second_iter != NULL)
323 if (second_iter->daemon == pg->peers[first].daemon)
324 add_second = GNUNET_NO;
325 second_iter = second_iter->next;
331 new_first = GNUNET_malloc(sizeof(struct PeerConnection));
332 new_first->daemon = pg->peers[second].daemon;
333 new_first->next = pg->peers[first].connected_peers;
334 pg->peers[first].connected_peers = new_first;
340 new_second = GNUNET_malloc(sizeof(struct PeerConnection));
341 new_second->daemon = pg->peers[first].daemon;
342 new_second->next = pg->peers[second].connected_peers;
343 pg->peers[second].connected_peers = new_second;
351 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg)
356 unsigned int randomPeer;
357 double random, logNModifier, percentage;
358 unsigned int smallWorldConnections;
364 int connect_attempts;
366 logNModifier = 0.5; /* FIXME: default value? */
367 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
372 if (sscanf(p_string, "%lf", &logNModifier) != 1)
373 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
374 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
378 GNUNET_free (p_string);
380 percentage = 0.5; /* FIXME: default percentage? */
381 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
386 if (sscanf(p_string, "%lf", &percentage) != 1)
387 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
388 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
392 GNUNET_free (p_string);
394 natLog = log (pg->total);
395 connsPerPeer = ceil (natLog * logNModifier);
397 if (connsPerPeer % 2 == 1)
400 smallWorldConnections = 0;
401 connect_attempts = 0;
402 for (i = 0; i < pg->total; i++)
405 max = i + connsPerPeer / 2;
406 min = i - connsPerPeer / 2;
408 if (max > pg->total - 1)
410 max = max - pg->total;
416 min = pg->total - 1 + min;
420 for (j = 0; j < connsPerPeer / 2; j++)
422 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
423 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
424 if (random < percentage)
426 /* Connect to uniformly selected random peer */
428 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
430 while ((((randomPeer < max) && (randomPeer > min))
431 && (useAnd == 0)) || (((randomPeer > min)
432 || (randomPeer < max))
436 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
439 smallWorldConnections +=
440 add_connections (pg, i, randomPeer);
444 nodeToConnect = i + j + 1;
445 if (nodeToConnect > pg->total - 1)
447 nodeToConnect = nodeToConnect - pg->total;
450 add_connections (pg, i, nodeToConnect);
456 connect_attempts += smallWorldConnections;
458 return connect_attempts;
463 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg)
465 unsigned int outer_count, inner_count;
467 int connect_attempts;
468 double nat_percentage;
471 nat_percentage = 0.6; /* FIXME: default percentage? */
472 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
477 if (sscanf(p_string, "%lf", &nat_percentage) != 1)
478 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
479 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
483 GNUNET_free (p_string);
488 cutoff = (unsigned int) (nat_percentage * pg->total);
490 connect_attempts = 0;
492 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
494 for (inner_count = outer_count + 1; inner_count < pg->total;
497 if ((outer_count > cutoff) || (inner_count > cutoff))
500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501 "Connecting peer %d to peer %d\n",
502 outer_count, inner_count);
504 connect_attempts += add_connections(pg, outer_count, inner_count);
509 return connect_attempts;
516 create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
518 unsigned int i, j, k;
522 unsigned int toggle = 1;
523 unsigned int nodeToConnect;
525 unsigned int node1Row;
526 unsigned int node1Col;
527 unsigned int node2Row;
528 unsigned int node2Col;
529 unsigned int distance;
530 double probability, random, percentage;
531 unsigned int smallWorldConnections;
533 int connect_attempts;
534 square = floor (sqrt (pg->total));
538 percentage = 0.5; /* FIXME: default percentage? */
539 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
544 if (sscanf(p_string, "%lf", &percentage) != 1)
545 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
546 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
550 GNUNET_free (p_string);
552 probability = 0.5; /* FIXME: default percentage? */
553 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
558 if (sscanf(p_string, "%lf", &probability) != 1)
559 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
560 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
564 GNUNET_free (p_string);
566 if (square * square != pg->total)
568 while (rows * cols < pg->total)
579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
580 _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
584 connect_attempts = 0;
585 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
586 * to the node to its right and above. Once this is over, we'll have our torus!
587 * Special case for the last node (if the rows and columns are not equal), connect
588 * to the first in the row to maintain topology.
590 for (i = 0; i < pg->total; i++)
592 /* First connect to the node to the right */
593 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
594 nodeToConnect = i + 1;
595 else if (i + 1 == pg->total)
596 nodeToConnect = rows * cols - cols;
598 nodeToConnect = i - cols + 1;
600 connect_attempts += add_connections (pg, i, nodeToConnect);
603 nodeToConnect = (rows * cols) - cols + i;
605 nodeToConnect = i - cols;
607 if (nodeToConnect < pg->total)
608 connect_attempts += add_connections (pg, i, nodeToConnect);
610 natLog = log (pg->total);
611 #if VERBOSE_TESTING > 2
612 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
613 _("natural log of %d is %d, will run %d iterations\n"),
614 pg->total, natLog, (int) (natLog * percentage));
615 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Total connections added thus far: %u!\n"), connect_attempts);
617 smallWorldConnections = 0;
618 for (i = 0; i < (int) (natLog * percentage); i++)
620 for (j = 0; j < pg->total; j++)
622 /* Determine the row and column of node at position j on the 2d torus */
624 node1Col = j - (node1Row * cols);
625 for (k = 0; k < pg->total; k++)
627 /* Determine the row and column of node at position k on the 2d torus */
629 node2Col = k - (node2Row * cols);
630 /* Simple Cartesian distance */
631 distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
634 /* Calculate probability as 1 over the square of the distance */
635 probability = 1.0 / (distance * distance);
636 /* Choose a random value between 0 and 1 */
637 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
638 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
639 /* If random < probability, then connect the two nodes */
640 if (random < probability)
641 smallWorldConnections += add_connections (pg, j, k);
647 connect_attempts += smallWorldConnections;
648 #if VERBOSE_TESTING > 2
649 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
650 _("Total connections added for small world: %d!\n"),
651 smallWorldConnections);
653 return connect_attempts;
659 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg)
662 unsigned int outer_count;
663 unsigned int inner_count;
664 int connect_attempts;
668 probability = 0.5; /* FIXME: default percentage? */
669 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
674 if (sscanf(p_string, "%lf", &probability) != 1)
675 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
676 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
680 GNUNET_free (p_string);
682 connect_attempts = 0;
683 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
685 for (inner_count = outer_count + 1; inner_count < pg->total;
688 temp_rand = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
689 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
691 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
692 _("rand is %f probability is %f\n"), temp_rand,
695 if (temp_rand < probability)
697 connect_attempts += add_connections (pg, outer_count, inner_count);
702 return connect_attempts;
706 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg)
712 unsigned int toggle = 1;
713 unsigned int nodeToConnect;
714 int connect_attempts;
716 connect_attempts = 0;
718 square = floor (sqrt (pg->total));
722 if (square * square != pg->total)
724 while (rows * cols < pg->total)
735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736 _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
739 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
740 * to the node to its right and above. Once this is over, we'll have our torus!
741 * Special case for the last node (if the rows and columns are not equal), connect
742 * to the first in the row to maintain topology.
744 for (i = 0; i < pg->total; i++)
746 /* First connect to the node to the right */
747 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
748 nodeToConnect = i + 1;
749 else if (i + 1 == pg->total)
750 nodeToConnect = rows * cols - cols;
752 nodeToConnect = i - cols + 1;
754 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
755 "Connecting peer %d to peer %d\n",
758 connect_attempts += add_connections(pg, i, nodeToConnect);
760 /* Second connect to the node immediately above */
762 nodeToConnect = (rows * cols) - cols + i;
764 nodeToConnect = i - cols;
766 if (nodeToConnect < pg->total)
769 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
770 "Connecting peer %d to peer %d\n",
773 connect_attempts += add_connections(pg, i, nodeToConnect);
778 return connect_attempts;
784 create_clique (struct GNUNET_TESTING_PeerGroup *pg)
786 unsigned int outer_count;
787 unsigned int inner_count;
788 int connect_attempts;
790 connect_attempts = 0;
792 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
794 for (inner_count = outer_count + 1; inner_count < pg->total;
798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799 "Connecting peer %d to peer %d\n",
800 outer_count, inner_count);
802 connect_attempts += add_connections(pg, outer_count, inner_count);
806 return connect_attempts;
811 create_ring (struct GNUNET_TESTING_PeerGroup *pg)
814 int connect_attempts;
816 connect_attempts = 0;
818 /* Connect each peer to the next highest numbered peer */
819 for (count = 0; count < pg->total - 1; count++)
822 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
823 "Connecting peer %d to peer %d\n",
826 connect_attempts += add_connections(pg, count, count + 1);
829 /* Connect the last peer to the first peer */
830 connect_attempts += add_connections(pg, pg->total - 1, 0);
832 return connect_attempts;
837 * Create the friend files based on the PeerConnection's
838 * of each peer in the peer group, and copy the files
839 * to the appropriate place
841 * @param pg the peer group we are dealing with
844 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
846 FILE *temp_friend_handle;
847 unsigned int pg_iter;
848 struct PeerConnection *connection_iter;
849 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
850 char *temp_service_path;
853 struct GNUNET_PeerIdentity *temppeer;
855 enum GNUNET_OS_ProcessStatusType type;
856 unsigned long return_code;
861 pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
862 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
864 mytemp = GNUNET_DISK_mktemp("friends");
865 temp_friend_handle = fopen (mytemp, "wt");
866 connection_iter = pg->peers[pg_iter].connected_peers;
867 while (connection_iter != NULL)
869 temppeer = &connection_iter->daemon->id;
870 GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
871 fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
872 connection_iter = connection_iter->next;
875 fclose(temp_friend_handle);
878 GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
880 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
881 _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
884 if (UNLINK (mytemp) != 0)
885 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
886 GNUNET_free (mytemp);
890 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
892 GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
893 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
894 "mv", mytemp, arg, NULL);
896 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
897 _("Copying file with command cp %s %s\n"), mytemp, arg);
902 else /* Remote, scp the file to the correct place */
904 if (NULL != pg->peers[pg_iter].daemon->username)
905 GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
907 GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
908 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
909 "scp", mytemp, arg, NULL);
912 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
913 _("Copying file with command scp %s %s\n"), mytemp, arg);
917 GNUNET_free (temp_service_path);
918 GNUNET_free (mytemp);
923 while ((count < max_wait) && (ret != GNUNET_OK))
926 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
929 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
930 _("Checking copy status of file %d\n"), pg_iter);
932 if (pidarr[pg_iter] != 0) /* Check for already completed! */
934 if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
938 else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
946 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
947 _("File %d copied\n"), pg_iter);
953 if (ret == GNUNET_SYSERR)
960 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
961 _("Finished copying all friend files!\n"));
968 * Internal notification of a connection, kept so that we can ensure some connections
969 * happen instead of flooding all testing daemons with requests to connect.
971 static void internal_connect_notify (void *cls,
972 const struct GNUNET_PeerIdentity *first,
973 const struct GNUNET_PeerIdentity *second,
974 const struct GNUNET_CONFIGURATION_Handle *first_cfg,
975 const struct GNUNET_CONFIGURATION_Handle *second_cfg,
976 struct GNUNET_TESTING_Daemon *first_daemon,
977 struct GNUNET_TESTING_Daemon *second_daemon,
980 struct GNUNET_TESTING_PeerGroup *pg = cls;
981 outstanding_connects--;
983 pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
987 static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
989 struct ConnectContext *connect_context = cls;
991 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
994 if (outstanding_connects > MAX_OUTSTANDING_CONNECTIONS)
996 #if VERBOSE_TESTING > 2
997 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
998 _("Delaying connect, we have too many outstanding connections!\n"));
1000 GNUNET_SCHEDULER_add_delayed(connect_context->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 3), &schedule_connect, connect_context);
1004 #if VERBOSE_TESTING > 2
1005 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1006 _("Creating connection, outstanding_connections is %d\n"), outstanding_connects);
1008 outstanding_connects++;
1009 GNUNET_TESTING_daemons_connect (connect_context->first,
1010 connect_context->second,
1013 &internal_connect_notify,
1014 connect_context->pg);
1015 GNUNET_free(connect_context);
1020 * Connect the topology as specified by the PeerConnection's
1021 * of each peer in the peer group
1023 * @param pg the peer group we are dealing with
1026 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
1028 unsigned int pg_iter;
1029 struct PeerConnection *connection_iter;
1030 struct ConnectContext *connect_context;
1032 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1034 connection_iter = pg->peers[pg_iter].connected_peers;
1035 while (connection_iter != NULL)
1037 connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1038 connect_context->pg = pg;
1039 connect_context->first = pg->peers[pg_iter].daemon;
1040 connect_context->second = connection_iter->daemon;
1041 GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
1042 connection_iter = connection_iter->next;
1049 * Takes a peer group and attempts to create a topology based on the
1050 * one specified in the configuration file. Returns the number of connections
1051 * that will attempt to be created, but this will happen asynchronously(?) so
1052 * the caller will have to keep track (via the callback) of whether or not
1053 * the connection actually happened.
1055 * @param pg the peer group struct representing the running peers
1057 * @return the number of connections should be created by the topology, so the
1058 * caller knows how many to wait for (if it so chooses)
1062 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg)
1064 unsigned long long topology_num;
1066 int num_connections;
1068 GNUNET_assert (pg->notify_connection != NULL);
1071 GNUNET_CONFIGURATION_get_value_number (pg->cfg, "testing", "topology",
1074 switch (topology_num)
1076 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
1078 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1079 _("Creating clique topology (may take a bit!)\n"));
1081 num_connections = create_clique (pg);
1083 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
1085 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1086 _("Creating small world (ring) topology (may take a bit!)\n"));
1088 num_connections = create_small_world_ring (pg);
1090 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
1092 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1093 _("Creating small world (2d-torus) topology (may take a bit!)\n"));
1095 num_connections = create_small_world (pg);
1097 case GNUNET_TESTING_TOPOLOGY_RING:
1099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1100 _("Creating ring topology (may take a bit!)\n"));
1102 num_connections = create_ring (pg);
1104 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
1106 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1107 _("Creating 2d torus topology (may take a bit!)\n"));
1109 num_connections = create_2d_torus (pg);
1111 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
1113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1114 _("Creating Erdos-Renyi topology (may take a bit!)\n"));
1116 num_connections = create_erdos_renyi (pg);
1118 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
1120 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1121 _("Creating InterNAT topology (may take a bit!)\n"));
1123 num_connections = create_nated_internet (pg);
1125 case GNUNET_TESTING_TOPOLOGY_NONE:
1126 num_connections = 0;
1129 num_connections = 0;
1132 if (num_connections < 1)
1133 return GNUNET_SYSERR;
1135 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
1136 ret = create_and_copy_friend_files(pg);
1137 if (ret == GNUNET_OK)
1138 connect_topology(pg);
1142 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1143 _("Failed during friend file copying!\n"));
1145 return GNUNET_SYSERR;
1150 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1151 _("No topology specified, was one intended?\n"));
1152 return GNUNET_SYSERR;
1155 return num_connections;
1159 * Start count gnunetd processes with the same set of transports and
1160 * applications. The port numbers (any option called "PORT") will be
1161 * adjusted to ensure that no two peers running on the same system
1162 * have the same port(s) in their respective configurations.
1164 * @param sched scheduler to use
1165 * @param cfg configuration template to use
1166 * @param total number of daemons to start
1167 * @param cb function to call on each daemon that was started
1168 * @param cb_cls closure for cb
1169 * @param connect_callback function to call each time two hosts are connected
1170 * @param connect_callback_cls closure for connect_callback
1171 * @param hostnames space-separated list of hostnames to use; can be NULL (to run
1172 * everything on localhost).
1173 * @return NULL on error, otherwise handle to control peer group
1175 struct GNUNET_TESTING_PeerGroup *
1176 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
1177 const struct GNUNET_CONFIGURATION_Handle *cfg,
1179 GNUNET_TESTING_NotifyDaemonRunning cb,
1181 GNUNET_TESTING_NotifyConnection
1182 connect_callback, void *connect_callback_cls,
1183 const char *hostnames)
1185 struct GNUNET_TESTING_PeerGroup *pg;
1189 const char *hostname;
1190 char *baseservicehome;
1191 char *newservicehome;
1193 struct GNUNET_CONFIGURATION_Handle *pcfg;
1195 unsigned int hostcnt;
1204 pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
1208 pg->cb_cls = cb_cls;
1209 pg->notify_connection = connect_callback;
1210 pg->notify_connection_cls = connect_callback_cls;
1212 pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
1213 if (NULL != hostnames)
1216 /* skip leading spaces */
1217 while ((0 != *hostnames) && (isspace (*hostnames)))
1220 while ('\0' != *rpos)
1222 if (isspace (*rpos))
1226 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
1228 start = GNUNET_strdup (hostnames);
1230 while ('\0' != *pos)
1235 if (strlen (start) > 0)
1237 pg->hosts[off].minport = LOW_PORT;
1238 pg->hosts[off++].hostname = start;
1244 if (strlen (start) > 0)
1246 pg->hosts[off].minport = LOW_PORT;
1247 pg->hosts[off++].hostname = start;
1251 GNUNET_free (start);
1252 GNUNET_free (pg->hosts);
1256 minport = 0; /* make gcc happy */
1263 for (off = 0; off < total; off++)
1267 hostname = pg->hosts[off % hostcnt].hostname;
1268 pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport, hostname);
1273 pcfg = make_config (cfg, &minport, hostname);
1278 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1280 ("Could not create configuration for peer number %u on `%s'!\n"),
1281 off, hostname == NULL ? "localhost" : hostname);
1286 GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
1289 GNUNET_asprintf (&newservicehome,
1290 "%s/%d/", baseservicehome, off);
1291 GNUNET_free (baseservicehome);
1295 tmpdir = getenv ("TMPDIR");
1296 tmpdir = tmpdir ? tmpdir : "/tmp";
1297 GNUNET_asprintf (&newservicehome,
1300 "gnunet-testing-test-test", off);
1302 GNUNET_CONFIGURATION_set_value_string (pcfg,
1304 "SERVICEHOME", newservicehome);
1305 GNUNET_free (newservicehome);
1306 pg->peers[off].cfg = pcfg;
1307 pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
1311 if (NULL == pg->peers[off].daemon)
1312 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1313 _("Could not start peer number %u!\n"), off);
1319 * Get a daemon by number, so callers don't have to do nasty
1320 * offsetting operation.
1322 struct GNUNET_TESTING_Daemon *
1323 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int position)
1325 if (position < pg->total)
1326 return pg->peers[position].daemon;
1332 * Shutdown all peers started in the given group.
1334 * @param pg handle to the peer group
1337 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
1340 struct PeerConnection *pos;
1341 struct PeerConnection *next;
1343 for (off = 0; off < pg->total; off++)
1345 /* FIXME: should we wait for our
1346 continuations to be called here? This
1347 would require us to take a continuation
1350 if (NULL != pg->peers[off].daemon)
1351 GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL);
1352 if (NULL != pg->peers[off].cfg)
1353 GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
1355 pos = pg->peers[off].connected_peers;
1364 GNUNET_free (pg->peers);
1365 if (NULL != pg->hosts)
1367 GNUNET_free (pg->hosts[0].hostname);
1368 GNUNET_free (pg->hosts);
1374 /* end of testing_group.c */