From b94fa4e20a07cddbd1a4fbff283c0d7f6f4a88f9 Mon Sep 17 00:00:00 2001 From: "Nathan S. Evans" Date: Sun, 9 May 2010 15:55:32 +0000 Subject: [PATCH] changes to testing... create hostkey using peerinfo before starting peers, better topology creation and more options therein, changes to order of starting peers, connecting peers... new order is hostkey create, topology create, blacklist create, start peers, connect peers --- src/testing/test_testing.c | 16 +- src/testing/test_testing_connect.c | 24 +- .../test_testing_data_topology_clique.conf | 2 +- .../test_testing_data_topology_ring.conf | 1 + src/testing/test_testing_group.c | 1 + src/testing/test_testing_topology.c | 138 +- src/testing/testing.c | 355 ++++- src/testing/testing_group.c | 1231 +++++++++++++++-- 8 files changed, 1616 insertions(+), 152 deletions(-) diff --git a/src/testing/test_testing.c b/src/testing/test_testing.c index 925579a71..b33f3ee1b 100644 --- a/src/testing/test_testing.c +++ b/src/testing/test_testing.c @@ -33,14 +33,16 @@ end_cb (void *cls, const char *emsg) { if (emsg != NULL) { - fprintf (stderr, "Error terminaing daemon: `%s'\n", - emsg); - return; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Ending with error: %s\n", emsg); + ok = 1; } + else + { #if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Daemon terminated, will now exit.\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Daemon terminated, will now exit.\n"); #endif - ok = 0; + ok = 0; + } } static void @@ -54,7 +56,7 @@ my_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Daemon `%s' started, will now stop it.\n", GNUNET_i2s (id)); #endif - GNUNET_TESTING_daemon_stop (d, &end_cb, NULL); + GNUNET_TESTING_daemon_stop (d, &end_cb, NULL, GNUNET_YES); } @@ -70,7 +72,7 @@ run (void *cls, #if VERBOSE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting daemon.\n"); #endif - d = GNUNET_TESTING_daemon_start (sched, cfg, NULL, &my_cb, NULL); + d = GNUNET_TESTING_daemon_start (sched, cfg, NULL, NULL, NULL, &my_cb, NULL); GNUNET_assert (d != NULL); } diff --git a/src/testing/test_testing_connect.c b/src/testing/test_testing_connect.c index 60721298e..a55207cec 100644 --- a/src/testing/test_testing_connect.c +++ b/src/testing/test_testing_connect.c @@ -49,26 +49,34 @@ static struct GNUNET_SCHEDULER_Handle *sched; static void end2_cb (void *cls, const char *emsg) { - GNUNET_assert (emsg == NULL); + + if (emsg != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Ending with error: %s\n", emsg); + ok = 1; + } + else + { #if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Both daemons terminated, will now exit.\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Both daemons terminated, will now exit.\n"); #endif - ok = 0; + ok = 0; + } } static void end1_cb (void *cls, const char *emsg) { GNUNET_assert (emsg == NULL); - GNUNET_TESTING_daemon_stop (d2, &end2_cb, NULL); + GNUNET_TESTING_daemon_stop (d2, &end2_cb, NULL, GNUNET_YES); d2 = NULL; } static void finish_testing(void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc) { - GNUNET_TESTING_daemon_stop (d1, &end1_cb, NULL); + GNUNET_TESTING_daemon_stop (d1, &end1_cb, NULL, GNUNET_YES); d1 = NULL; } @@ -113,7 +121,7 @@ my_cb1 (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Daemon `%s' started.\n", GNUNET_i2s (id)); #endif - d2 = GNUNET_TESTING_daemon_start (sched, c2, NULL, &my_cb2, NULL); + d2 = GNUNET_TESTING_daemon_start (sched, c2, NULL, NULL, NULL, &my_cb2, NULL); GNUNET_assert (d2 != NULL); } @@ -134,7 +142,7 @@ run (void *cls, GNUNET_CONFIGURATION_parse (c1, "test_testing_connect_peer1.conf"); c2 = GNUNET_CONFIGURATION_create (); GNUNET_CONFIGURATION_parse (c2, "test_testing_connect_peer2.conf"); - d1 = GNUNET_TESTING_daemon_start (sched, c1, NULL, &my_cb1, NULL); + d1 = GNUNET_TESTING_daemon_start (sched, c1, NULL, NULL, NULL, &my_cb1, NULL); GNUNET_assert (d1 != NULL); } diff --git a/src/testing/test_testing_data_topology_clique.conf b/src/testing/test_testing_data_topology_clique.conf index 11a017c87..689a45d74 100644 --- a/src/testing/test_testing_data_topology_clique.conf +++ b/src/testing/test_testing_data_topology_clique.conf @@ -40,7 +40,7 @@ PORT = 2570 #DEBUG = YES [testing] -NUM_PEERS = 3 +NUM_PEERS = 4 WEAKRANDOM = YES TOPOLOGY = 0 F2F = YES diff --git a/src/testing/test_testing_data_topology_ring.conf b/src/testing/test_testing_data_topology_ring.conf index 41493887c..480eb6392 100644 --- a/src/testing/test_testing_data_topology_ring.conf +++ b/src/testing/test_testing_data_topology_ring.conf @@ -34,3 +34,4 @@ NUM_PEERS = 5 WEAKRANDOM = YES TOPOLOGY = 3 F2F = YES +BLACKLISTING = YES diff --git a/src/testing/test_testing_group.c b/src/testing/test_testing_group.c index d9d22dd02..8458bfbd6 100644 --- a/src/testing/test_testing_group.c +++ b/src/testing/test_testing_group.c @@ -80,6 +80,7 @@ run (void *cls, peers_left = NUM_PEERS; pg = GNUNET_TESTING_daemons_start (sched, cfg, peers_left, + NULL, NULL, &my_cb, NULL, NULL, NULL, NULL); GNUNET_assert (pg != NULL); } diff --git a/src/testing/test_testing_topology.c b/src/testing/test_testing_topology.c index 784c850f3..62ffb9a76 100644 --- a/src/testing/test_testing_topology.c +++ b/src/testing/test_testing_topology.c @@ -25,7 +25,7 @@ #include "gnunet_testing_lib.h" #include "gnunet_core_service.h" -#define VERBOSE GNUNET_YES +#define VERBOSE GNUNET_NO /** * How long until we fail the whole testcase? @@ -83,6 +83,14 @@ static int transmit_ready_called; static enum GNUNET_TESTING_Topology topology; +static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* Don't do any blacklisting */ + +static enum GNUNET_TESTING_Topology connection_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */ + +static enum GNUNET_TESTING_TopologyOption connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; + +static double connect_topology_option_modifier = 0.0; + static char *test_directory; #define MTYPE 12345 @@ -509,20 +517,13 @@ topology_callback (void *cls, } } - static void -create_topology () +connect_topology () { expected_connections = -1; if ((pg != NULL) && (peers_left == 0)) { - /* create_topology will read the topology information from - the config already contained in the peer group, so should - we have create_topology called from start peers? I think - maybe this way is best so that the client can know both - when peers are started, and when they are connected. - */ - expected_connections = GNUNET_TESTING_create_topology (pg, topology); + expected_connections = GNUNET_TESTING_connect_topology (pg, connection_topology, connect_topology_option, connect_topology_option_modifier); #if VERBOSE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Have %d expected connections\n", expected_connections); @@ -532,21 +533,52 @@ create_topology () GNUNET_SCHEDULER_cancel (sched, die_task); if (expected_connections == GNUNET_SYSERR) { + die_task = GNUNET_SCHEDULER_add_now (sched, + &end_badly, "from connect topology (bad return)"); + } + + die_task = GNUNET_SCHEDULER_add_delayed (sched, + TEST_TIMEOUT, + &end_badly, "from connect topology (timeout)"); +} + +static void +create_topology () +{ + peers_left = num_peers; /* Reset counter */ + if (GNUNET_TESTING_create_topology (pg, topology, blacklist_topology) != GNUNET_SYSERR) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Topology set up, now starting peers!\n"); +#endif + GNUNET_TESTING_daemons_continue_startup(pg); + } + else + { + GNUNET_SCHEDULER_cancel (sched, die_task); die_task = GNUNET_SCHEDULER_add_now (sched, &end_badly, "from create topology (bad return)"); } + GNUNET_SCHEDULER_cancel (sched, die_task); die_task = GNUNET_SCHEDULER_add_delayed (sched, TEST_TIMEOUT, - &end_badly, "from create topology (timeout)"); + &end_badly, "from continue startup (timeout)"); } static void -my_cb (void *cls, +peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id, const struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_TESTING_Daemon *d, const char *emsg) { + if (emsg != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start daemon with error: `%s'\n", + emsg); + return; + } GNUNET_assert (id != NULL); #if VERBOSE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n", @@ -566,12 +598,54 @@ my_cb (void *cls, die_task = GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5), - &end_badly, "from my_cb"); - create_topology (); + &end_badly, "from peers_started_callback"); + connect_topology (); ok = 0; } } +/** + * Callback indicating that the hostkey was created for a peer. + * + * @param cls NULL + * @param id the peer identity + * @param d the daemon handle (pretty useless at this point, remove?) + * @param emsg non-null on failure + */ +void hostkey_callback (void *cls, + const struct GNUNET_PeerIdentity *id, + struct GNUNET_TESTING_Daemon *d, + const char *emsg) +{ + if (emsg != NULL) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Hostkey callback received error: %s\n", emsg); + } + +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Hostkey created for peer `%s'\n", + GNUNET_i2s(id)); +#endif + peers_left--; + if (peers_left == 0) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All %d hostkeys created, now creating topology!\n", + num_peers); +#endif + GNUNET_SCHEDULER_cancel (sched, die_task); + /* Set up task in case topology creation doesn't finish + * within a reasonable amount of time */ + die_task = GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 5), + &end_badly, "from hostkey_callback"); + GNUNET_SCHEDULER_add_now(sched, &create_topology, NULL); + ok = 0; + } +} static void run (void *cls, @@ -580,6 +654,10 @@ run (void *cls, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { unsigned long long topology_num; + unsigned long long connect_topology_num; + unsigned long long blacklist_topology_num; + unsigned long long connect_topology_option_num; + char *connect_topology_option_modifier_string; sched = s; ok = 1; @@ -605,6 +683,36 @@ run (void *cls, &topology_num)) topology = topology_num; + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology", + &connect_topology_num)) + connection_topology = connect_topology_num; + + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology_option", + &connect_topology_option_num)) + connect_topology_option = connect_topology_option_num; + + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "connect_topology_option_modifier", + &connect_topology_option_modifier_string)) + { + if (sscanf(connect_topology_option_modifier_string, "%lf", &connect_topology_option_modifier) != 1) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Invalid value `%s' for option `%s' in section `%s': expected float\n"), + connect_topology_option_modifier_string, + "connect_topology_option_modifier", + "TESTING"); + GNUNET_free (connect_topology_option_modifier_string); + } + } + + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "blacklist_topology", + &blacklist_topology_num)) + blacklist_topology = blacklist_topology_num; + if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers", &num_peers)) @@ -621,7 +729,7 @@ run (void *cls, &end_badly, "didn't start all daemons in reasonable amount of time!!!"); pg = GNUNET_TESTING_daemons_start (sched, cfg, - peers_left, &my_cb, NULL, + peers_left, &hostkey_callback, NULL, &peers_started_callback, NULL, &topology_callback, NULL, NULL); } diff --git a/src/testing/testing.c b/src/testing/testing.c index fcc8c9767..65441c188 100644 --- a/src/testing/testing.c +++ b/src/testing/testing.c @@ -37,7 +37,7 @@ #include "gnunet_transport_service.h" #include "gnunet_hello_lib.h" -#define DEBUG_TESTING GNUNET_NO +#define DEBUG_TESTING GNUNET_YES #define DEBUG_TESTING_RECONNECT GNUNET_YES /** @@ -50,7 +50,7 @@ * How many times are we willing to try to wait for "scp" or * "gnunet-service-arm" to complete (waitpid) before giving up? */ -#define MAX_EXEC_WAIT_RUNS 50 +#define MAX_EXEC_WAIT_RUNS 250 static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} }; @@ -126,7 +126,7 @@ testing_init (void *cls, { d->server = NULL; if (GNUNET_YES == d->dead) - GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls); + GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls, GNUNET_YES); else if (NULL != cb) cb (d->cb_cls, NULL, d->cfg, d, _("Failed to connect to core service\n")); @@ -141,7 +141,7 @@ testing_init (void *cls, d->server = server; d->running = GNUNET_YES; if (GNUNET_YES == d->dead) - GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls); + GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls, GNUNET_YES); else if (NULL != cb) cb (d->cb_cls, my_identity, d->cfg, d, NULL); #if DEBUG_TESTING @@ -155,7 +155,7 @@ testing_init (void *cls, if (d->th == NULL) { if (GNUNET_YES == d->dead) - GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls); + GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls, GNUNET_YES); else if (NULL != d->cb) d->cb (d->cb_cls, &d->id, d->cfg, d, _("Failed to connect to transport service!\n")); @@ -180,6 +180,9 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) enum GNUNET_OS_ProcessStatusType type; unsigned long code; char *dst; + int bytes_read; + static char hostkeybuf[105]; + static const char temphostkey[104]; #if DEBUG_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -227,6 +230,187 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) d->phase = SP_COPIED; /* fall-through */ case SP_COPIED: + /* Start create hostkey process */ + d->pipe_stdout = GNUNET_DISK_pipe(GNUNET_NO); + if (d->pipe_stdout == NULL) + { + cb = d->cb; + d->cb = NULL; + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + (NULL == d->hostname) + ? _("Failed to create pipe for `gnunet-peerinfo' process.\n") + : _("Failed to create pipe for `ssh' process.\n")); + return; + } + if (NULL == d->hostname) + { +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting `%s', with command `%s %s %s %s'.\n", + "gnunet-peerinfo", "gnunet-peerinfo", "-c", d->cfgfile, + "-sq"); +#endif + d->pid = GNUNET_OS_start_process (NULL, d->pipe_stdout, "gnunet-peerinfo", + "gnunet-peerinfo", + "-c", d->cfgfile, + "-sq", NULL); + GNUNET_DISK_pipe_close_end(d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE); + } + else + { + if (d->username != NULL) + GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname); + else + dst = GNUNET_strdup (d->hostname); + +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting `%s', with command `%s %s %s %s %s %s'.\n", + "gnunet-peerinfo", "ssh", dst, "gnunet-peerinfo", "-c", d->cfgfile, + "-sq"); +#endif + d->pid = GNUNET_OS_start_process (NULL, d->pipe_stdout, "ssh", + "ssh", + dst, + "gnunet-peerinfo", + "-c", d->cfgfile, "-sq", NULL); + GNUNET_DISK_pipe_close_end(d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE); + GNUNET_free (dst); + } + if (-1 == d->pid) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Could not start `%s' process to create hostkey.\n"), + (NULL == d->hostname) ? "gnunet-peerinfo" : "ssh"); + cb = d->cb; + d->cb = NULL; + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + (NULL == d->hostname) + ? _("Failed to start `gnunet-peerinfo' process.\n") + : _("Failed to start `ssh' process.\n")); + GNUNET_DISK_pipe_close(d->pipe_stdout); + return; + } +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Started `%s', waiting for hostkey.\n", + "gnunet-peerinfo"); +#endif + d->phase = SP_HOSTKEY_CREATE; + d->wait_runs = 0; + d->task + = GNUNET_SCHEDULER_add_delayed (d->sched, + GNUNET_CONSTANTS_EXEC_WAIT, + &start_fsm, d); + break; + case SP_HOSTKEY_CREATE: + + bytes_read = GNUNET_DISK_file_read(GNUNET_DISK_pipe_handle(d->pipe_stdout, GNUNET_DISK_PIPE_END_READ), &hostkeybuf, sizeof(hostkeybuf)); + if (bytes_read == 104) /* Success, we have read in the hostkey */ + { + if (hostkeybuf[103] == '\n') + hostkeybuf[103] = '\0'; + else + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Malformed output from gnunet-peerinfo!\n"); + memcpy(&temphostkey, &hostkeybuf, bytes_read); + + if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (&temphostkey[0], + &d->id.hashPubKey)) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to convert string to peer identity!\n"); + } + else + { + GNUNET_DISK_pipe_close(d->pipe_stdout); + d->pipe_stdout = NULL; + } + } + + if (GNUNET_OK != GNUNET_OS_process_status (d->pid, &type, &code)) + { + d->wait_runs++; + if (d->wait_runs > MAX_EXEC_WAIT_RUNS) + { + cb = d->cb; + d->cb = NULL; + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + (NULL == d->hostname) + ? _("`gnunet-peerinfo' does not seem to terminate.\n") + : _("`ssh' does not seem to terminate.\n")); + + GNUNET_DISK_pipe_close(d->pipe_stdout); + return; + } + /* wait some more */ + d->task + = GNUNET_SCHEDULER_add_delayed (d->sched, + GNUNET_CONSTANTS_EXEC_WAIT, + &start_fsm, d); + return; + } +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Successfully got hostkey!\n"); +#endif + if (d->pipe_stdout != NULL) + { + cb = d->cb; + d->cb = NULL; + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + _("`Failed to get hostkey!\n")); + GNUNET_DISK_pipe_close(d->pipe_stdout); + return; + } + + if (d->hostkey_callback != NULL) + { + d->hostkey_callback(d->hostkey_cls, &d->id, d, NULL); + d->phase = SP_HOSTKEY_CREATED; + } + else + { + d->phase = SP_TOPOLOGY_SETUP; + } + + /* Fall through */ + case SP_HOSTKEY_CREATED: + /* wait for topology finished */ + d->wait_runs++; + if ((GNUNET_YES == d->dead) || (d->wait_runs > MAX_EXEC_WAIT_RUNS)) + { + cb = d->cb; + d->cb = NULL; + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + _("`Failed while waiting for topology setup!\n")); + return; + } + + d->task + = GNUNET_SCHEDULER_add_delayed (d->sched, + GNUNET_CONSTANTS_EXEC_WAIT, + &start_fsm, d); + break; + case SP_TOPOLOGY_SETUP: /* start GNUnet on remote host */ if (NULL == d->hostname) { @@ -283,6 +467,7 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) (NULL == d->hostname) ? _("Failed to start `gnunet-arm' process.\n") : _("Failed to start `ssh' process.\n")); + return; } #if DEBUG_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -452,6 +637,19 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } } +/** + * Continues GNUnet daemon startup when user wanted to be notified + * once a hostkey was generated (for creating friends files, blacklists, + * etc.). + * + * @param daemon the daemon to finish starting + */ +void +GNUNET_TESTING_daemon_continue_startup(struct GNUNET_TESTING_Daemon *daemon) +{ + GNUNET_assert(daemon->phase == SP_HOSTKEY_CREATED); + daemon->phase = SP_TOPOLOGY_SETUP; +} /** * Starts a GNUnet daemon. GNUnet must be installed on the target @@ -463,6 +661,10 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * @param cfg configuration to use * @param hostname name of the machine where to run GNUnet * (use NULL for localhost). + * @param hostkey_callback function to call once the hostkey has been + * generated for this peer, but it hasn't yet been started + * (NULL to start immediately, otherwise waits on GNUNET_TESTING_daemon_continue_start) + * @param hostkey_cls closure for hostkey callback * @param cb function to call with the result * @param cb_cls closure for cb * @return handle to the daemon (actual start will be completed asynchronously) @@ -471,6 +673,8 @@ struct GNUNET_TESTING_Daemon * GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, const struct GNUNET_CONFIGURATION_Handle *cfg, const char *hostname, + GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback, + void *hostkey_cls, GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls) { @@ -493,6 +697,8 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, GNUNET_free (ret); return NULL; } + ret->hostkey_callback = hostkey_callback; + ret->hostkey_cls = hostkey_cls; ret->cb = cb; ret->cb_cls = cb_cls; ret->cfg = GNUNET_CONFIGURATION_dup (cfg); @@ -573,27 +779,140 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, } +/** + * Restart (stop and start) a GNUnet daemon. + * + * @param d the daemon that should be restarted + * @param cb function called once the daemon is (re)started + * @param cb_cls closure for cb + */ +void +GNUNET_TESTING_daemon_restart (struct GNUNET_TESTING_Daemon *d, + GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls) +{ + char *arg; + char *del_arg; + + del_arg = NULL; + if (NULL != d->cb) + { + d->dead = GNUNET_YES; + return; + } + + d->cb = cb; + d->cb_cls = cb_cls; + + if (d->phase == SP_CONFIG_UPDATE) + { + GNUNET_SCHEDULER_cancel (d->sched, d->task); + d->phase = SP_START_DONE; + } + if (d->server != NULL) + { + GNUNET_CORE_disconnect (d->server); + d->server = NULL; + } + + if (d->th != NULL) + { + GNUNET_TRANSPORT_get_hello_cancel(d->th, &process_hello, d); + GNUNET_TRANSPORT_disconnect(d->th); + d->th = NULL; + } + /* state clean up and notifications */ + GNUNET_free_non_null(d->hello); + +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Terminating peer `%4s'\n"), GNUNET_i2s (&d->id)); +#endif + + d->phase = SP_START_ARMING; + + /* Check if this is a local or remote process */ + if (NULL != d->hostname) + { +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stopping gnunet-arm with config `%s' on host `%s'.\n", d->cfgfile, d->hostname); +#endif + + if (d->username != NULL) + GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname); + else + arg = GNUNET_strdup (d->hostname); + + d->pid = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh", + arg, "gnunet-arm", +#if DEBUG_TESTING + "-L", "DEBUG", +#endif + "-c", d->cfgfile, "-e", "-r", NULL); + /* Use -r to restart arm and all services */ + + GNUNET_free (arg); + } + else + { +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile); +#endif + d->pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm", + "gnunet-arm", +#if DEBUG_TESTING + "-L", "DEBUG", +#endif + "-c", d->cfgfile, "-e", "-r", NULL); + } + + GNUNET_free_non_null(del_arg); + d->wait_runs = 0; + d->task + = GNUNET_SCHEDULER_add_delayed (d->sched, + GNUNET_CONSTANTS_EXEC_WAIT, + &start_fsm, d); + +} + + /** * Stops a GNUnet daemon. * * @param d the daemon that should be stopped * @param cb function called once the daemon was stopped * @param cb_cls closure for cb + * @param delete_files GNUNET_YES to remove files, GNUNET_NO + * to leave them (i.e. for restarting at a later time, + * or logfile inspection once finished) */ void GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d, - GNUNET_TESTING_NotifyCompletion cb, void *cb_cls) + GNUNET_TESTING_NotifyCompletion cb, void *cb_cls, + int delete_files) { char *arg; - + char *del_arg; d->dead_cb = cb; d->dead_cb_cls = cb_cls; if (NULL != d->cb) { +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Setting d->dead on peer `%4s'\n"), GNUNET_i2s (&d->id)); +#endif d->dead = GNUNET_YES; return; } + + del_arg = NULL; + if (delete_files == GNUNET_YES) + { + GNUNET_asprintf(&del_arg, "-d"); + } + if (d->phase == SP_CONFIG_UPDATE) { GNUNET_SCHEDULER_cancel (d->sched, d->task); @@ -608,10 +927,9 @@ GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d, #if DEBUG_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Terminating peer `%4s'\n"), GNUNET_i2s (&d->id)); - /* sleep(15); Manual check for running */ #endif - d->phase = SP_SHUTDOWN_START; + d->phase = SP_SHUTDOWN_START; /* Check if this is a local or remote process */ if (NULL != d->hostname) @@ -627,29 +945,30 @@ GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d, arg = GNUNET_strdup (d->hostname); d->pid = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh", - arg, "gnunet-arm", + arg, "gnunet-arm", #if DEBUG_TESTING - "-L", "DEBUG", + "-L", "DEBUG", #endif - "-c", d->cfgfile, "-e", "-d", "-q", NULL); + "-c", d->cfgfile, "-e", "-q", del_arg, NULL); /* Use -e to end arm, and -d to remove temp files */ GNUNET_free (arg); } else - { + { #if DEBUG_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile); #endif - d->pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm", - "gnunet-arm", + d->pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm", + "gnunet-arm", #if DEBUG_TESTING - "-L", "DEBUG", + "-L", "DEBUG", #endif - "-c", d->cfgfile, "-e", "-d", "-q", NULL); - } + "-c", d->cfgfile, "-e", "-q", del_arg, NULL); + } + GNUNET_free_non_null(del_arg); d->wait_runs = 0; d->task = GNUNET_SCHEDULER_add_delayed (d->sched, diff --git a/src/testing/testing_group.c b/src/testing/testing_group.c index e617bc113..e9bc05a74 100644 --- a/src/testing/testing_group.c +++ b/src/testing/testing_group.c @@ -50,6 +50,57 @@ #define CONNECT_ATTEMPTS 8 +/** + * 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, unsigned int first, unsigned int second); + +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 CreateTopologyContext +{ + + /** + * Function to call with number of connections + */ + GNUNET_TESTING_NotifyConnections cont; + + /** + * Closure for connection notification + */ + void *cls; +}; + +#if OLD struct PeerConnection { /* @@ -63,6 +114,7 @@ struct PeerConnection struct GNUNET_TESTING_Daemon *daemon; }; +#endif /** * Data we keep per peer. @@ -83,9 +135,33 @@ struct PeerData struct GNUNET_TESTING_Daemon *daemon; /** - * Linked list of peer connections (simply indexes of PeerGroup) + * The peergroup this peer belongs to. + */ + struct GNUNET_TESTING_PeerGroup *pg; + + /** + * Linked list of peer connections (pointers) + */ + //struct PeerConnection *connected_peers; + /** + * Hash map of allowed peer connections (F2F created topology) + */ + struct GNUNET_CONTAINER_MultiHashMap *allowed_peers; + + /** + * Hash map of blacklisted peers */ - struct PeerConnection *connected_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; /** * Total number of connections this peer has @@ -165,6 +241,31 @@ struct GNUNET_TESTING_PeerGroup }; +/** + * 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)); +} struct UpdateContext { @@ -279,41 +380,107 @@ make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t * port, con return uc.ret; } + /* - * Add entries to the peers connected list + * Add entries to the peers connect list * * @param pg the peer group we are working with * @param first index of the first peer * @param second index of the second peer * * @return the number of connections added (can be 0, 1 or 2) + * technically should only be 0 or 2, but the small price + * of iterating over the lists (hashmaps in the future) + * for being sure doesn't bother me! + * + */ +static int +add_actual_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second) +{ + int added; + int add_first; + int add_second; + + GNUNET_HashCode hash_first; + GNUNET_HashCode hash_second; + + hash_from_uid(first, &hash_first); + hash_from_uid(second, &hash_second); + + add_first = GNUNET_NO; + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].connect_peers, &hash_second)) + { + add_first = GNUNET_YES; + } + + add_second = GNUNET_NO; + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].connect_peers, &hash_first)) + { + add_second = GNUNET_YES; + } + + added = 0; + if (add_first) + { + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].connect_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + added++; + } + + if (add_second) + { + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].connect_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + added++; + } + + return added; +} + + +/* + * Add entries to the peers allowed connections list * - * FIXME: add both, or only add one? - * - if both are added, then we have to keep track - * when connecting so we don't double connect - * - if only one is added, we need to iterate over - * both lists to find out if connection already exists - * - having both allows the whitelisting/friend file - * creation to be easier + * @param pg the peer group we are working with + * @param first index of the first peer + * @param second index of the second peer * - * -- For now, add both, we have to iterate over each to - * check for duplicates anyways, so we'll take the performance - * hit assuming we don't have __too__ many connections + * @return the number of connections added (can be 0, 1 or 2) + * technically should only be 0 or 2, but the small price + * of iterating over the lists (hashmaps in the future) + * for being sure doesn't bother me! * */ static int -add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second) +add_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second) { int added; +#if OLD struct PeerConnection *first_iter; struct PeerConnection *second_iter; - int add_first; - int add_second; struct PeerConnection *new_first; struct PeerConnection *new_second; +#endif + int add_first; + int add_second; + + GNUNET_HashCode hash_first; + GNUNET_HashCode hash_second; + + hash_from_uid(first, &hash_first); + hash_from_uid(second, &hash_second); + + add_first = GNUNET_NO; + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].allowed_peers, &hash_second)) + { + add_first = GNUNET_YES; + } + add_second = GNUNET_NO; + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].allowed_peers, &hash_first)) + { + add_second = GNUNET_YES; + } +#if OLD first_iter = pg->peers[first].connected_peers; - add_first = GNUNET_YES; while (first_iter != NULL) { if (first_iter->daemon == pg->peers[second].daemon) @@ -329,31 +496,129 @@ add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigne add_second = GNUNET_NO; second_iter = second_iter->next; } +#endif added = 0; if (add_first) { + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].allowed_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +#if OLD new_first = GNUNET_malloc(sizeof(struct PeerConnection)); new_first->daemon = pg->peers[second].daemon; new_first->next = pg->peers[first].connected_peers; pg->peers[first].connected_peers = new_first; pg->peers[first].num_connections++; +#endif added++; } if (add_second) { + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].allowed_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +#if OLD new_second = GNUNET_malloc(sizeof(struct PeerConnection)); new_second->daemon = pg->peers[first].daemon; new_second->next = pg->peers[second].connected_peers; pg->peers[second].connected_peers = new_second; pg->peers[first].num_connections++; +#endif + added++; + } + + return added; +} + +/* + * Add entries to the peers blacklisted list + * + * @param pg the peer group we are working with + * @param first index of the first peer + * @param second index of the second peer + * + * @return the number of connections added (can be 0, 1 or 2) + * + */ +static int +blacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second) +{ + int added; + int add_first; + int add_second; + GNUNET_HashCode hash_first; + GNUNET_HashCode hash_second; + + hash_from_uid(first, &hash_first); + hash_from_uid(second, &hash_second); + + add_first = GNUNET_NO; + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second)) + { + add_first = GNUNET_YES; + } + + add_second = GNUNET_NO; + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first)) + { + add_second = GNUNET_YES; + } + + added = 0; + if (add_first) + { + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + added++; + } + + if (add_second) + { + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); added++; } return added; } +/* + * Remove entries from the peers blacklisted list + * + * @param pg the peer group we are working with + * @param first index of the first peer + * @param second index of the second peer + * + * @return the number of connections removed (can be 0, 1 or 2) + * + */ +static int +unblacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second) +{ + int removed; + int remove_first; + int remove_second; + GNUNET_HashCode hash_first; + GNUNET_HashCode hash_second; + + hash_from_uid(first, &hash_first); + hash_from_uid(second, &hash_second); + + remove_first = GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second); + remove_second = GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first); + + removed = 0; + if (remove_first) + { + GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon)); + removed++; + } + + if (remove_second) + { + GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon)); + removed++; + } + + return removed; +} + /** * Scale free network construction as described in: * @@ -369,7 +634,7 @@ add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigne * @return the number of connections created */ static int -create_scale_free (struct GNUNET_TESTING_PeerGroup *pg) +create_scale_free (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc) { unsigned int total_connections; @@ -382,7 +647,7 @@ create_scale_free (struct GNUNET_TESTING_PeerGroup *pg) GNUNET_assert(pg->total > 1); /* Add a connection between the first two nodes */ - total_connections = add_connections(pg, 0, 1); + total_connections = proc(pg, 0, 1); for (outer_count = 1; outer_count < pg->total; outer_count++) { @@ -404,7 +669,7 @@ create_scale_free (struct GNUNET_TESTING_PeerGroup *pg) "Connecting peer %d to peer %d\n", outer_count, i); #endif - total_connections += add_connections(pg, outer_count, i); + total_connections += proc(pg, outer_count, i); } } } @@ -413,7 +678,7 @@ create_scale_free (struct GNUNET_TESTING_PeerGroup *pg) } int -create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg) +create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc) { unsigned int i, j; int nodeToConnect; @@ -502,7 +767,7 @@ create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg) pg->total); } smallWorldConnections += - add_connections (pg, i, randomPeer); + proc (pg, i, randomPeer); } else { @@ -512,7 +777,7 @@ create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg) nodeToConnect = nodeToConnect - pg->total; } connect_attempts += - add_connections (pg, i, nodeToConnect); + proc (pg, i, nodeToConnect); } } @@ -525,7 +790,7 @@ create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg) static int -create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg) +create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc) { unsigned int outer_count, inner_count; unsigned int cutoff; @@ -566,7 +831,7 @@ create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg) "Connecting peer %d to peer %d\n", outer_count, inner_count); #endif - connect_attempts += add_connections(pg, outer_count, inner_count); + connect_attempts += proc(pg, outer_count, inner_count); } } } @@ -578,7 +843,7 @@ create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg) static int -create_small_world (struct GNUNET_TESTING_PeerGroup *pg) +create_small_world (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc) { unsigned int i, j, k; unsigned int square; @@ -662,7 +927,7 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg) else nodeToConnect = i - cols + 1; - connect_attempts += add_connections (pg, i, nodeToConnect); + connect_attempts += proc (pg, i, nodeToConnect); if (i < cols) nodeToConnect = (rows * cols) - cols + i; @@ -670,7 +935,7 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg) nodeToConnect = i - cols; if (nodeToConnect < pg->total) - connect_attempts += add_connections (pg, i, nodeToConnect); + connect_attempts += proc (pg, i, nodeToConnect); } natLog = log (pg->total); #if VERBOSE_TESTING > 2 @@ -703,7 +968,7 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg) (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL); /* If random < probability, then connect the two nodes */ if (random < probability) - smallWorldConnections += add_connections (pg, j, k); + smallWorldConnections += proc (pg, j, k); } } @@ -721,7 +986,7 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg) static int -create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg) +create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc) { double temp_rand; unsigned int outer_count; @@ -759,7 +1024,7 @@ create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg) #endif if (temp_rand < probability) { - connect_attempts += add_connections (pg, outer_count, inner_count); + connect_attempts += proc (pg, outer_count, inner_count); } } } @@ -768,7 +1033,7 @@ create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg) } static int -create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg) +create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc) { unsigned int i; unsigned int square; @@ -820,7 +1085,7 @@ create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg) "Connecting peer %d to peer %d\n", i, nodeToConnect); #endif - connect_attempts += add_connections(pg, i, nodeToConnect); + connect_attempts += proc(pg, i, nodeToConnect); /* Second connect to the node immediately above */ if (i < cols) @@ -835,7 +1100,7 @@ create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg) "Connecting peer %d to peer %d\n", i, nodeToConnect); #endif - connect_attempts += add_connections(pg, i, nodeToConnect); + connect_attempts += proc(pg, i, nodeToConnect); } } @@ -846,7 +1111,7 @@ create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg) static int -create_clique (struct GNUNET_TESTING_PeerGroup *pg) +create_clique (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc) { unsigned int outer_count; unsigned int inner_count; @@ -864,7 +1129,7 @@ create_clique (struct GNUNET_TESTING_PeerGroup *pg) "Connecting peer %d to peer %d\n", outer_count, inner_count); #endif - connect_attempts += add_connections(pg, outer_count, inner_count); + connect_attempts += proc(pg, outer_count, inner_count); } } @@ -873,7 +1138,7 @@ create_clique (struct GNUNET_TESTING_PeerGroup *pg) static int -create_ring (struct GNUNET_TESTING_PeerGroup *pg) +create_ring (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc) { unsigned int count; int connect_attempts; @@ -888,16 +1153,77 @@ create_ring (struct GNUNET_TESTING_PeerGroup *pg) "Connecting peer %d to peer %d\n", count, count + 1); #endif - connect_attempts += add_connections(pg, count, count + 1); + connect_attempts += proc(pg, count, count + 1); } /* Connect the last peer to the first peer */ - connect_attempts += add_connections(pg, pg->total - 1, 0); + connect_attempts += proc(pg, pg->total - 1, 0); return connect_attempts; } +/** + * Iterator for writing friends of a peer to a file. + * + * @param cls closure, an open writable file handle + * @param key the key the daemon was stored under + * @param value the GNUNET_TESTING_Daemon that needs to be written. + * + * @return GNUNET_YES to continue iteration + * + * TODO: Could replace friend_file_iterator and blacklist_file_iterator + * with a single file_iterator that takes a closure which contains + * the prefix to write before the peer. Then this could be used + * for blacklisting multiple transports and writing the friend + * file. I'm sure *someone* will complain loudly about other + * things that negate these functions even existing so no point in + * "fixing" now. + */ +static int +friend_file_iterator (void *cls, + const GNUNET_HashCode * key, + void *value) +{ + FILE *temp_friend_handle = cls; + struct GNUNET_TESTING_Daemon *peer = value; + struct GNUNET_PeerIdentity *temppeer; + struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc; + + temppeer = &peer->id; + GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc); + fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc); + + return GNUNET_YES; +} + + +/** + * Iterator for writing blacklist data to appropriate files. + * + * @param cls closure, an open writable file handle + * @param key the key the daemon was stored under + * @param value the GNUNET_TESTING_Daemon that needs to be written. + * + * @return GNUNET_YES to continue iteration + */ +static int +blacklist_file_iterator (void *cls, + const GNUNET_HashCode * key, + void *value) +{ + FILE *temp_blacklist_handle = cls; + struct GNUNET_TESTING_Daemon *peer = value; + struct GNUNET_PeerIdentity *temppeer; + struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc; + + temppeer = &peer->id; + GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc); + fprintf(temp_blacklist_handle, "tcp:%s\n", (char *)&peer_enc); + + return GNUNET_YES; +} + /* * Create the friend files based on the PeerConnection's * of each peer in the peer group, and copy the files @@ -910,12 +1236,9 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg) { FILE *temp_friend_handle; unsigned int pg_iter; - struct PeerConnection *connection_iter; - struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc; char *temp_service_path; pid_t *pidarr; char *arg; - struct GNUNET_PeerIdentity *temppeer; char * mytemp; enum GNUNET_OS_ProcessStatusType type; unsigned long return_code; @@ -927,16 +1250,10 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg) for (pg_iter = 0; pg_iter < pg->total; pg_iter++) { mytemp = GNUNET_DISK_mktemp("friends"); + GNUNET_assert(mytemp != NULL); temp_friend_handle = fopen (mytemp, "wt"); - connection_iter = pg->peers[pg_iter].connected_peers; - while (connection_iter != NULL) - { - temppeer = &connection_iter->daemon->id; - GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc); - fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc); - connection_iter = connection_iter->next; - } - + GNUNET_assert(temp_friend_handle != NULL); + GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, &friend_file_iterator, temp_friend_handle); fclose(temp_friend_handle); if (GNUNET_OK != @@ -1029,29 +1346,153 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg) return ret; } -/** - * Internal notification of a connection, kept so that we can ensure some connections - * happen instead of flooding all testing daemons with requests to connect. + +/* + * Create the blacklist files based on the PeerConnection's + * of each peer in the peer group, and copy the files + * to the appropriate place. + * + * @param pg the peer group we are dealing with */ -static void internal_connect_notify (void *cls, - const struct GNUNET_PeerIdentity *first, - const struct GNUNET_PeerIdentity *second, - const struct GNUNET_CONFIGURATION_Handle *first_cfg, - const struct GNUNET_CONFIGURATION_Handle *second_cfg, - struct GNUNET_TESTING_Daemon *first_daemon, - struct GNUNET_TESTING_Daemon *second_daemon, - const char *emsg) +static int +create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg) { - struct GNUNET_TESTING_PeerGroup *pg = cls; - outstanding_connects--; + FILE *temp_friend_handle; + unsigned int pg_iter; + char *temp_service_path; + pid_t *pidarr; + char *arg; + char *mytemp; + enum GNUNET_OS_ProcessStatusType type; + unsigned long return_code; + int count; + int ret; + int max_wait = 10; - pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg); + pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total); + for (pg_iter = 0; pg_iter < pg->total; pg_iter++) + { + mytemp = GNUNET_DISK_mktemp("blacklist"); + GNUNET_assert(mytemp != NULL); + temp_friend_handle = fopen (mytemp, "wt"); + GNUNET_assert(temp_friend_handle != NULL); + GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].blacklisted_peers, &blacklist_file_iterator, temp_friend_handle); + fclose(temp_friend_handle); -} + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"), + "SERVICEHOME", + "PATHS"); + if (UNLINK (mytemp) != 0) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp); + GNUNET_free (mytemp); + break; + } -static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct ConnectContext *connect_context = cls; + 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", + "mv", mytemp, arg, NULL); +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Copying file with command cp %s %s\n"), mytemp, arg); +#endif + + GNUNET_free(arg); + } + else /* Remote, scp the file to the correct place */ + { + if (NULL != pg->peers[pg_iter].daemon->username) + 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", + "scp", mytemp, arg, NULL); + +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Copying file with command scp %s %s\n"), mytemp, arg); +#endif + GNUNET_free(arg); + } + GNUNET_free (temp_service_path); + GNUNET_free (mytemp); + } + + count = 0; + ret = GNUNET_SYSERR; + while ((count < max_wait) && (ret != GNUNET_OK)) + { + ret = GNUNET_OK; + for (pg_iter = 0; pg_iter < pg->total; pg_iter++) + { +#if VERBOSE_TESTING + 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 (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK) + { + ret = GNUNET_SYSERR; + } + else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0)) + { + ret = GNUNET_SYSERR; + } + else + { + pidarr[pg_iter] = 0; +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("File %d copied\n"), pg_iter); +#endif + } + } + } + count++; + if (ret == GNUNET_SYSERR) + { + sleep(1); + } + } + +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Finished copying all blacklist files!\n")); +#endif + GNUNET_free(pidarr); + return ret; +} + + +/** + * Internal notification of a connection, kept so that we can ensure some connections + * happen instead of flooding all testing daemons with requests to connect. + */ +static void internal_connect_notify (void *cls, + const struct GNUNET_PeerIdentity *first, + const struct GNUNET_PeerIdentity *second, + const struct GNUNET_CONFIGURATION_Handle *first_cfg, + const struct GNUNET_CONFIGURATION_Handle *second_cfg, + struct GNUNET_TESTING_Daemon *first_daemon, + struct GNUNET_TESTING_Daemon *second_daemon, + const char *emsg) +{ + struct GNUNET_TESTING_PeerGroup *pg = cls; + outstanding_connects--; + + pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg); + +} + +static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ConnectContext *connect_context = cls; if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN) return; @@ -1081,54 +1522,154 @@ static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContex } } +/** + * Iterator for actually scheduling connections to be created + * between two peers. + * + * @param cls closure, a GNUNET_TESTING_Daemon + * @param key the key the second Daemon was stored under + * @param value the GNUNET_TESTING_Daemon that the first is to connect to + * + * @return GNUNET_YES to continue iteration + */ +static int +connect_iterator (void *cls, + const GNUNET_HashCode * key, + void *value) +{ + struct PeerData *first = cls; + 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; + GNUNET_SCHEDULER_add_now(first->pg->sched, &schedule_connect, connect_context); + + return GNUNET_YES; +} + +/** + * Iterator for copying all entries in the allowed hashmap to the + * connect hashmap. + * + * @param cls closure, a GNUNET_TESTING_Daemon + * @param key the key the second Daemon was stored under + * @param value the GNUNET_TESTING_Daemon that the first is to connect to + * + * @return GNUNET_YES to continue iteration + */ +static int +copy_topology_iterator (void *cls, + const GNUNET_HashCode * key, + void *value) +{ + struct PeerData *first = cls; + + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(first->connect_peers, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + + return GNUNET_YES; +} + +/** + * Make the peers to connect the same as those that are allowed to be + * connected. + * + * @param pg the peer group + */ +static int +copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg) +{ + unsigned int pg_iter; + int ret; + int total; + + total = 0; + for (pg_iter = 0; pg_iter < pg->total; pg_iter++) + { + ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, ©_topology_iterator, &pg->peers[pg_iter]); + if (GNUNET_SYSERR == ret) + return GNUNET_SYSERR; + + total = total + ret; + } + + return total; +} + + /* * Connect the topology as specified by the PeerConnection's * of each peer in the peer group * * @param pg the peer group we are dealing with + * + * @return the number of connections that will be attempted */ -static void +static int connect_topology (struct GNUNET_TESTING_PeerGroup *pg) { unsigned int pg_iter; + int ret; + int total; +#if OLD struct PeerConnection *connection_iter; struct ConnectContext *connect_context; +#endif + total = 0; for (pg_iter = 0; pg_iter < pg->total; pg_iter++) { - connection_iter = pg->peers[pg_iter].connected_peers; + 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 = total + ret; + +#if OLD + connection_iter = ; while (connection_iter != NULL) { connect_context = GNUNET_malloc(sizeof(struct ConnectContext)); connect_context->pg = pg; - connect_context->first = pg->peers[pg_iter].daemon; + connect_context->first = ; connect_context->second = connection_iter->daemon; GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context); connection_iter = connection_iter->next; } +#endif } + return total; } /* - * Takes a peer group and attempts to create a topology based on the - * one specified in the configuration file. Returns the number of connections - * that will attempt to be created, but this will happen asynchronously(?) so - * the caller will have to keep track (via the callback) of whether or not - * the connection actually happened. + * Takes a peer group and creates a topology based on the + * one specified. Creates a topology means generates friend + * files for the peers so they can only connect to those allowed + * by the topology. This will only have an effect once peers + * are started if the FRIENDS_ONLY option is set in the base + * config. Also takes an optional restrict topology which + * disallows direct TCP connections UNLESS they are specified in + * the restricted topology. * * @param pg the peer group struct representing the running peers * @param topology which topology to connect the peers in + * @param restrict_topology allow only direct TCP connections in this topology + * use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions * - * @return the number of connections should be created by the topology, so the - * caller knows how many to wait for (if it so chooses) - * + * @return the maximum number of connections were all allowed peers + * connected to each other */ int -GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET_TESTING_Topology topology) +GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, + enum GNUNET_TESTING_Topology topology, + enum GNUNET_TESTING_Topology restrict_topology) { int ret; int num_connections; + int unblacklisted_connections; GNUNET_assert (pg->notify_connection != NULL); ret = GNUNET_OK; @@ -1140,56 +1681,56 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating clique topology\n")); #endif - num_connections = create_clique (pg); + num_connections = create_clique (pg, &add_allowed_connections); break; case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING: #if VERBOSE_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating small world (ring) topology\n")); #endif - num_connections = create_small_world_ring (pg); + num_connections = create_small_world_ring (pg, &add_allowed_connections); break; case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD: #if VERBOSE_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating small world (2d-torus) topology\n")); #endif - num_connections = create_small_world (pg); + num_connections = create_small_world (pg, &add_allowed_connections); break; case GNUNET_TESTING_TOPOLOGY_RING: #if VERBOSE_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating ring topology\n")); #endif - num_connections = create_ring (pg); + num_connections = create_ring (pg, &add_allowed_connections); break; case GNUNET_TESTING_TOPOLOGY_2D_TORUS: #if VERBOSE_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating 2d torus topology\n")); #endif - num_connections = create_2d_torus (pg); + num_connections = create_2d_torus (pg, &add_allowed_connections); break; case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI: #if VERBOSE_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating Erdos-Renyi topology\n")); #endif - num_connections = create_erdos_renyi (pg); + num_connections = create_erdos_renyi (pg, &add_allowed_connections); break; case GNUNET_TESTING_TOPOLOGY_INTERNAT: #if VERBOSE_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating InterNAT topology\n")); #endif - num_connections = create_nated_internet (pg); + num_connections = create_nated_internet (pg, &add_allowed_connections); break; case GNUNET_TESTING_TOPOLOGY_SCALE_FREE: #if VERBOSE_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating Scale Free topology\n")); #endif - num_connections = create_scale_free (pg); + num_connections = create_scale_free (pg, &add_allowed_connections); break; case GNUNET_TESTING_TOPOLOGY_NONE: num_connections = 0; @@ -1202,10 +1743,11 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET return GNUNET_SYSERR; if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F")) - ret = create_and_copy_friend_files(pg); - if (ret == GNUNET_OK) - connect_topology(pg); - else + { + ret = create_and_copy_friend_files(pg); + } + + if (ret != GNUNET_OK) { #if VERBOSE_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -1213,10 +1755,419 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET #endif return GNUNET_SYSERR; } + else + { +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Friend files created/copied successfully!\n")); +#endif + } + + /** + * Use the create clique method to initially set all connections + * as blacklisted. + */ + create_clique (pg, &blacklist_connections); + unblacklisted_connections = 0; + /** + * Un-blacklist connections as per the topology specified + */ + switch (restrict_topology) + { + case GNUNET_TESTING_TOPOLOGY_CLIQUE: +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklisting all but clique topology\n")); +#endif + unblacklisted_connections = create_clique (pg, &unblacklist_connections); + break; + case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING: +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklisting all but small world (ring) topology\n")); +#endif + unblacklisted_connections = create_small_world_ring (pg, &unblacklist_connections); + break; + case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD: +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklisting all but small world (2d-torus) topology\n")); +#endif + unblacklisted_connections = create_small_world (pg, &unblacklist_connections); + break; + case GNUNET_TESTING_TOPOLOGY_RING: +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklisting all but ring topology\n")); +#endif + unblacklisted_connections = create_ring (pg, &unblacklist_connections); + break; + case GNUNET_TESTING_TOPOLOGY_2D_TORUS: +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklisting all but 2d torus topology\n")); +#endif + unblacklisted_connections = create_2d_torus (pg, &unblacklist_connections); + break; + case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI: +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklisting all but Erdos-Renyi topology\n")); +#endif + unblacklisted_connections = create_erdos_renyi (pg, &unblacklist_connections); + break; + case GNUNET_TESTING_TOPOLOGY_INTERNAT: +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklisting all but InterNAT topology\n")); +#endif + unblacklisted_connections = create_nated_internet (pg, &unblacklist_connections); + break; + case GNUNET_TESTING_TOPOLOGY_SCALE_FREE: +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklisting all but Scale Free topology\n")); +#endif + unblacklisted_connections = create_scale_free (pg, &unblacklist_connections); + break; + case GNUNET_TESTING_TOPOLOGY_NONE: + /* Fall through */ + default: + break; + } + + if (unblacklisted_connections > 0) + { + ret = create_and_copy_blacklist_files(pg); + if (ret != GNUNET_OK) + { +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Failed during blacklist file copying!\n")); +#endif + return GNUNET_SYSERR; + } + else + { +#if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklist files created/copied successfully!\n")); +#endif + } + } + return num_connections; } +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; +}; + +/** + * Iterator for choosing random peers to connect. + * + * @param cls closure, a RandomContext + * @param key the key the second Daemon was stored under + * @param value the GNUNET_TESTING_Daemon that the first is to connect to + * + * @return GNUNET_YES to continue iteration + */ +static int +random_connect_iterator (void *cls, + const GNUNET_HashCode * key, + void *value) +{ + struct RandomContext *random_ctx = cls; + double random_number; + uint32_t second_pos; + GNUNET_HashCode first_hash; + random_number = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK, + (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL); + if (random_number < random_ctx->percentage) + { + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(random_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */ + uid_from_hash(key, &second_pos); + hash_from_uid(random_ctx->first_uid, &first_hash); + GNUNET_assert(random_ctx->pg->total > second_pos); + GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(random_ctx->pg->peers[second_pos].connect_peers, &first_hash, random_ctx->first->daemon)); + + return GNUNET_YES; +} + +/** + * Iterator for adding at least X peers to a peers connection set. + * + * @param cls closure, MinimumContext + * @param key the key the second Daemon was stored under + * @param value the GNUNET_TESTING_Daemon that the first is to connect to + * + * @return GNUNET_YES to continue iteration + */ +static int +minimum_connect_iterator (void *cls, + const GNUNET_HashCode * key, + void *value) +{ + struct MinimumContext *min_ctx = cls; + uint32_t second_pos; + GNUNET_HashCode first_hash; + + if (GNUNET_CONTAINER_multihashmap_size(min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add) + { + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */ + uid_from_hash(key, &second_pos); + hash_from_uid(min_ctx->first_uid, &first_hash); + GNUNET_assert(min_ctx->pg->total > second_pos); + GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(min_ctx->pg->peers[second_pos].connect_peers, &first_hash, min_ctx->first->daemon)); + return GNUNET_YES; + } + else + return GNUNET_NO; /* We can stop iterating, we have enough peers! */ + + +} + +/** + * From the set of connections possible, choose percentage percent of connections + * to actually connect. + * + * @param pg the peergroup we are dealing with + * @param percentage what percent of total connections to make + */ +void +choose_random_connections(struct GNUNET_TESTING_PeerGroup *pg, double percentage) +{ + struct RandomContext random_ctx; + uint32_t pg_iter; + + for (pg_iter = 0; pg_iter < pg->total; pg_iter++) + { + random_ctx.first_uid = pg_iter; + random_ctx.first = &pg->peers[pg_iter]; + random_ctx.percentage = percentage; + pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total); + GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &random_connect_iterator, &random_ctx); + /* Now remove the old connections */ + GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers); + /* And replace with the random set */ + pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set; + } +} + +/** + * From the set of connections possible, choose at least num connections per + * peer. + * + * @param pg the peergroup we are dealing with + * @param num how many connections at least should each peer have (if possible)? + */ +void +choose_minimum(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num) +{ + struct MinimumContext minimum_ctx; + uint32_t pg_iter; + + for (pg_iter = 0; pg_iter < pg->total; pg_iter++) + { + pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(num); + } + + for (pg_iter = 0; pg_iter < pg->total; pg_iter++) + { + minimum_ctx.first_uid = pg_iter; + minimum_ctx.first = &pg->peers[pg_iter]; + minimum_ctx.num_to_add = num; + pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total); + GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &minimum_connect_iterator, &minimum_ctx); + } + + for (pg_iter = 0; pg_iter < pg->total; pg_iter++) + { + /* Remove the "old" connections */ + GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers); + /* And replace with the working set */ + pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set; + } + +} + + +/* + * @param pg the peer group struct representing the running peers + * @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 + * + * There are many ways to connect peers that are supported by this function. + * To connect peers in the same topology that was created via the + * GNUNET_TESTING_create_topology, the topology variable must be set to + * GNUNET_TESTING_TOPOLOGY_NONE. If the topology variable is specified, + * a new instance of that topology will be generated and attempted to be + * connected. This could result in some connections being impossible, + * because some topologies are non-deterministic. + * + */ +int +GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg, + enum GNUNET_TESTING_Topology topology, + enum GNUNET_TESTING_TopologyOption options, + double option_modifier) +{ + int num_connections; + + switch (topology) + { + case GNUNET_TESTING_TOPOLOGY_CLIQUE: + #if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Creating clique topology\n")); + #endif + num_connections = create_clique (pg, &add_actual_connections); + break; + case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING: + #if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Creating small world (ring) topology\n")); + #endif + num_connections = create_small_world_ring (pg, &add_actual_connections); + break; + case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD: + #if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Creating small world (2d-torus) topology\n")); + #endif + num_connections = create_small_world (pg, &add_actual_connections); + break; + case GNUNET_TESTING_TOPOLOGY_RING: + #if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Creating ring topology\n")); + #endif + num_connections = create_ring (pg, &add_actual_connections); + break; + case GNUNET_TESTING_TOPOLOGY_2D_TORUS: + #if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Creating 2d torus topology\n")); + #endif + num_connections = create_2d_torus (pg, &add_actual_connections); + break; + case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI: + #if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Creating Erdos-Renyi topology\n")); + #endif + num_connections = create_erdos_renyi (pg, &add_actual_connections); + break; + case GNUNET_TESTING_TOPOLOGY_INTERNAT: + #if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Creating InterNAT topology\n")); + #endif + num_connections = create_nated_internet (pg, &add_actual_connections); + break; + case GNUNET_TESTING_TOPOLOGY_SCALE_FREE: + #if VERBOSE_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Creating Scale Free topology\n")); + #endif + num_connections = create_scale_free (pg, &add_actual_connections); + break; + case GNUNET_TESTING_TOPOLOGY_NONE: + num_connections = copy_allowed_topology(pg); + break; + default: + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Unknown topology specification, can't connect peers!\n"); + return GNUNET_SYSERR; + } + + switch (options) + { + case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM: /* Create a random subset of total connections based on parameter */ + choose_random_connections(pg, option_modifier); + break; + case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM: /* Create at least X connections per peer (if possible!) */ + choose_minimum(pg, (unsigned int)option_modifier); + break; + case GNUNET_TESTING_TOPOLOGY_OPTION_DFS: /* Choose a random starting point, randomly walk graph, try to get each peer X connections */ + //choose_dfs(pg, (int)option_modifier); + break; + case GNUNET_TESTING_TOPOLOGY_OPTION_NONE: + /* Fall through */ + case GNUNET_TESTING_TOPOLOGY_OPTION_ALL: + /* Fall through */ + default: + break; + } + + return connect_topology(pg); +} + +/** + * Function which continues a peer group starting up + * after successfully generating hostkeys for each peer. + * + * @param pg the peer group to continue starting + * + */ +void +GNUNET_TESTING_daemons_continue_startup(struct GNUNET_TESTING_PeerGroup *pg) +{ + unsigned int i; + + for (i = 0; i < pg->total; i++) + { + GNUNET_TESTING_daemon_continue_startup(pg->peers[i].daemon); + } +} + /** * Start count gnunetd processes with the same set of transports and * applications. The port numbers (any option called "PORT") will be @@ -1226,6 +2177,11 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET * @param sched scheduler to use * @param cfg configuration template to use * @param total number of daemons to start + * @param hostkey_callback function to call on each peers hostkey generation + * if NULL, peers will be started by this call, if non-null, + * GNUNET_TESTING_daemons_continue_startup must be called after + * successful hostkey generation + * @param hostkey_cls closure for hostkey callback * @param cb function to call on each daemon that was started * @param cb_cls closure for cb * @param connect_callback function to call each time two hosts are connected @@ -1238,6 +2194,8 @@ struct GNUNET_TESTING_PeerGroup * GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched, const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned int total, + GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback, + void *hostkey_cls, GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls, GNUNET_TESTING_NotifyConnection @@ -1366,13 +2324,20 @@ GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched, "SERVICEHOME", newservicehome); GNUNET_free (newservicehome); pg->peers[off].cfg = pcfg; + pg->peers[off].allowed_peers = GNUNET_CONTAINER_multihashmap_create(total); + pg->peers[off].connect_peers = GNUNET_CONTAINER_multihashmap_create(total); + pg->peers[off].blacklisted_peers = GNUNET_CONTAINER_multihashmap_create(total); + pg->peers[off].pg = pg; pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched, pcfg, hostname, + hostkey_callback, + hostkey_cls, cb, cb_cls); if (NULL == pg->peers[off].daemon) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Could not start peer number %u!\n"), off); + } return pg; } @@ -1390,6 +2355,72 @@ GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int pos return NULL; } +/** + * Prototype of a function that will be called when a + * particular operation was completed the testing library. + * + * @param cls closure + * @param emsg NULL on success + */ +void restart_callback (void *cls, + const struct GNUNET_PeerIdentity *id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Daemon *d, + const char *emsg) +{ + struct RestartContext *restart_context = cls; + + if (emsg == NULL) + { + restart_context->peers_restarted++; + } + else + { + restart_context->peers_restart_failed++; + } + + if (restart_context->peers_restarted == restart_context->peer_group->total) + { + restart_context->callback(restart_context->callback_cls, NULL); + GNUNET_free(restart_context); + } + else if (restart_context->peers_restart_failed + restart_context->peers_restarted == restart_context->peer_group->total) + { + restart_context->callback(restart_context->callback_cls, "Failed to restart peers!"); + GNUNET_free(restart_context); + } + +} + +/** + * Restart all peers in the given group. + * + * @param pg the handle to the peer group + * @param timeout how long to wait on failure + * @param callback function to call on completion (or failure) + * @param callback_cls closure for the callback function + */ +void +GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_NotifyCompletion callback, void *callback_cls) +{ + struct RestartContext *restart_context; + unsigned int off; + + if (pg->total > 0) + { + restart_context = GNUNET_malloc(sizeof(struct RestartContext)); + restart_context->peer_group = pg; + restart_context->peers_restarted = 0; + restart_context->callback = callback; + restart_context->callback_cls = callback_cls; + + for (off = 0; off < pg->total; off++) + { + GNUNET_TESTING_daemon_restart (pg->peers[off].daemon, &restart_callback, restart_context); + } + } +} + /** * Shutdown all peers started in the given group. * @@ -1399,8 +2430,6 @@ void GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg) { unsigned int off; - struct PeerConnection *pos; - struct PeerConnection *next; for (off = 0; off < pg->total; off++) { @@ -1410,17 +2439,13 @@ GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg) as well... */ if (NULL != pg->peers[off].daemon) - GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL); + GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL, GNUNET_YES); if (NULL != pg->peers[off].cfg) GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg); - pos = pg->peers[off].connected_peers; - while (pos != NULL) - { - next = pos->next; - GNUNET_free(pos); - pos = next; - } + GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].allowed_peers); + GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].connect_peers); + GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].blacklisted_peers); } GNUNET_free (pg->peers); -- 2.25.1