From f670d73ee633b91ff44c44ee53ab03f450dca7d9 Mon Sep 17 00:00:00 2001 From: "Nathan S. Evans" Date: Tue, 11 May 2010 16:11:56 +0000 Subject: blacklist testcase, testing_group support for blacklisting --- src/testing/test_testing_topology_blacklist.c | 505 ++++++++++++++++++++++++++ src/testing/testing_group.c | 186 ++++++++-- 2 files changed, 669 insertions(+), 22 deletions(-) create mode 100644 src/testing/test_testing_topology_blacklist.c (limited to 'src') diff --git a/src/testing/test_testing_topology_blacklist.c b/src/testing/test_testing_topology_blacklist.c new file mode 100644 index 000000000..08a30a347 --- /dev/null +++ b/src/testing/test_testing_topology_blacklist.c @@ -0,0 +1,505 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file testing/test_testing_topology_blacklist.c + * @brief base testcase for testing transport level blacklisting + */ +#include "platform.h" +#include "gnunet_testing_lib.h" +#include "gnunet_core_service.h" + +#define VERBOSE GNUNET_YES + +/** + * How long until we fail the whole testcase? + */ +#define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + +/** + * How long until we give up on starting the peers? (Must be longer than the connect timeout!) + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300) + +#define DEFAULT_NUM_PEERS 4 + +#define MAX_OUTSTANDING_CONNECTIONS 300 + +static int ok; + +static unsigned long long num_peers; + +static unsigned int total_connections; + +static unsigned int failed_connections; + +static unsigned int expected_connections; + +static unsigned int expected_failed_connections; + +static unsigned long long peers_left; + +static struct GNUNET_TESTING_PeerGroup *pg; + +static struct GNUNET_SCHEDULER_Handle *sched; + +const struct GNUNET_CONFIGURATION_Handle *main_cfg; + +GNUNET_SCHEDULER_TaskIdentifier die_task; + +static char *dotOutFileName; + +static FILE *dotOutFile; + +static char *blacklist_transports; + +static enum GNUNET_TESTING_Topology topology = GNUNET_TESTING_TOPOLOGY_CLIQUE; /* Overlay should allow all connections */ + +static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_RING; /* Blacklist underlay into a ring */ + +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; /* Try to connect all possible OVERLAY connections */ + +static double connect_topology_option_modifier = 0.0; + +static char *test_directory; + +#define MTYPE 12345 + +struct GNUNET_TestMessage +{ + /** + * Header of the message + */ + struct GNUNET_MessageHeader header; + + /** + * Unique identifier for this message. + */ + uint32_t uid; +}; + +static void +finish_testing () +{ + GNUNET_assert (pg != NULL); + +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Called finish testing, stopping daemons.\n"); +#endif + sleep(1); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Calling daemons_stop\n"); +#endif + GNUNET_TESTING_daemons_stop (pg, TIMEOUT); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "daemons_stop finished\n"); +#endif + if (dotOutFile != NULL) + { + fprintf(dotOutFile, "}"); + fclose(dotOutFile); + } + + ok = 0; +} + +static void +end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc) +{ + char *msg = cls; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "End badly was called (%s)... stopping daemons.\n", msg); + + if (pg != NULL) + { + GNUNET_TESTING_daemons_stop (pg, TIMEOUT); + ok = 7331; /* Opposite of leet */ + } + else + ok = 401; /* Never got peers started */ + + if (dotOutFile != NULL) + { + fprintf(dotOutFile, "}"); + fclose(dotOutFile); + } +} + + + +void +topology_callback (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) +{ + if (emsg == NULL) + { + total_connections++; +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s\n", + first_daemon->shortname, + second_daemon->shortname); +#endif + if (dotOutFile != NULL) + fprintf(dotOutFile, "\tn%s -- n%s;\n", first_daemon->shortname, second_daemon->shortname); + } +#if VERBOSE + else + { + failed_connections++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect peer %s to peer %s with error :\n%s\n", + first_daemon->shortname, + second_daemon->shortname, emsg); + } +#endif + + if (total_connections == expected_connections) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created %d total connections, which is our target number (that's bad)!\n", + total_connections); +#endif + + GNUNET_SCHEDULER_cancel (sched, die_task); + die_task = GNUNET_SCHEDULER_NO_TASK; + die_task = GNUNET_SCHEDULER_add_now (sched, + &end_badly, "from topology_callback (too many successful connections)"); + } + else if (total_connections + failed_connections == expected_connections) + { + if ((failed_connections == expected_failed_connections) && (total_connections == expected_connections - expected_failed_connections)) + { + GNUNET_SCHEDULER_cancel (sched, die_task); + die_task = GNUNET_SCHEDULER_NO_TASK; + die_task = GNUNET_SCHEDULER_add_now (sched, + &finish_testing, NULL); + } + else + { + GNUNET_SCHEDULER_cancel (sched, die_task); + die_task = GNUNET_SCHEDULER_add_now (sched, + &end_badly, "from topology_callback (wrong number of failed connections)"); + } + } + else + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Have %d total connections, %d failed connections, Want %d (failed) and %d (successful)\n", + total_connections, failed_connections, expected_failed_connections, expected_connections - expected_failed_connections); +#endif + } +} + +static void +connect_topology () +{ + expected_connections = -1; + if ((pg != NULL) && (peers_left == 0)) + { + 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); +#endif + } + + 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, blacklist_transports) != 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 continue startup (timeout)"); +} + + +static void +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", + (num_peers - peers_left) + 1, num_peers); +#endif + peers_left--; + if (peers_left == 0) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All %d daemons started, 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 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, + struct GNUNET_SCHEDULER_Handle *s, + char *const *args, + 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; + + dotOutFile = fopen (dotOutFileName, "w"); + if (dotOutFile != NULL) + { + fprintf (dotOutFile, "strict graph G {\n"); + } + +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting daemons based on config file %s\n", cfgfile); +#endif + + if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_string(cfg, "paths", "servicehome", &test_directory)) + { + ok = 404; + return; + } + + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "topology", + &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); + } + } + + GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "blacklist_transports", + &blacklist_transports); + + 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)) + num_peers = DEFAULT_NUM_PEERS; + + main_cfg = cfg; + + peers_left = num_peers; + + /* For this specific test we only really want a CLIQUE topology as the + * overlay allowed topology, and a RING topology as the underlying connection + * allowed topology. So we will expect only num_peers * 2 connections to + * work, and (num_peers * (num_peers - 1)) - (num_peers * 2) to fail. + */ + expected_connections = num_peers * (num_peers - 1); + expected_failed_connections = expected_connections - (num_peers * 2); + + + /* Set up a task to end testing if peer start fails */ + die_task = GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 5), + &end_badly, "didn't start all daemons in reasonable amount of time!!!"); + + pg = GNUNET_TESTING_daemons_start (sched, cfg, + peers_left, TIMEOUT, &hostkey_callback, NULL, &peers_started_callback, NULL, + &topology_callback, NULL, NULL); + +} + +static int +check () +{ + int ret; + char *const argv[] = {"test-testing-topology-blacklist", + "-c", + "test_testing_data_topology_blacklist.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + ret = GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-testing-topology-blacklist", "nohelp", + options, &run, &ok); + if (ret != GNUNET_OK) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "`test-testing-topology-blacklist': Failed with error code %d\n", ret); + } + + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test_testing_topology_blacklist", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + /** + * Need to remove base directory, subdirectories taken care + * of by the testing framework. + */ + if (test_directory != NULL) + { + if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to remove testing directory %s\n", test_directory); + } + } + + return ret; +} + +/* end of test_testing_topology_blacklist.c */ diff --git a/src/testing/testing_group.c b/src/testing/testing_group.c index 835b1d979..4b1e771e9 100644 --- a/src/testing/testing_group.c +++ b/src/testing/testing_group.c @@ -57,6 +57,42 @@ typedef int (*GNUNET_TESTING_ConnectionProcessor) (struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second); +/** + * Context for handling churning a peer group + */ +struct ChurnContext +{ + /** + * Callback used to notify of churning finished + */ + GNUNET_TESTING_NotifyCompletion cb; + + /** + * Closure for callback + */ + void *cb_cls; + + /** + * Number of peers that still need to be started + */ + unsigned int num_to_start; + + /** + * Number of peers that still need to be stopped + */ + unsigned int num_to_stop; + + /** + * Number of peers that failed to start + */ + unsigned int num_failed_start; + + /** + * Number of peers that failed to stop + */ + unsigned int num_failed_stop; +}; + struct RestartContext { /** @@ -1208,6 +1244,18 @@ friend_file_iterator (void *cls, return GNUNET_YES; } +struct BlacklistContext +{ + /* + * The (open) file handle to write to + */ + FILE *temp_file_handle; + + /* + * The transport that this peer will be blacklisted on. + */ + char *transport; +}; /** * Iterator for writing blacklist data to appropriate files. @@ -1223,14 +1271,15 @@ blacklist_file_iterator (void *cls, const GNUNET_HashCode * key, void *value) { - FILE *temp_blacklist_handle = cls; + struct BlacklistContext *blacklist_ctx = cls; + //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); + fprintf(blacklist_ctx->temp_file_handle, "%s:%s\n", blacklist_ctx->transport, (char *)&peer_enc); return GNUNET_YES; } @@ -1364,11 +1413,13 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg) * to the appropriate place. * * @param pg the peer group we are dealing with + * @param transports space delimited list of transports to blacklist */ static int -create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg) +create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *transports) { - FILE *temp_friend_handle; + FILE *temp_file_handle; + static struct BlacklistContext blacklist_ctx; unsigned int pg_iter; char *temp_service_path; pid_t *pidarr; @@ -1379,16 +1430,42 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg) int count; int ret; int max_wait = 10; + int transport_len; + unsigned int i; + char *pos; + char *temp_transports; 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); + temp_file_handle = fopen (mytemp, "wt"); + GNUNET_assert(temp_file_handle != NULL); + temp_transports = GNUNET_strdup(transports); + blacklist_ctx.temp_file_handle = temp_file_handle; + transport_len = strlen(temp_transports) + 1; + pos = NULL; + + for (i = 0; i < transport_len; i++) + { + if ((temp_transports[i] == ' ') && (pos == NULL)) + continue; /* At start of string (whitespace) */ + else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */ + { + temp_transports[i] = '\0'; + blacklist_ctx.transport = pos; + GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].blacklisted_peers, &blacklist_file_iterator, &blacklist_ctx); + pos = NULL; + } /* At beginning of actual string */ + else if (pos == NULL) + { + pos = &temp_transports[i]; + } + } + + GNUNET_free_non_null(temp_transports); + fclose(temp_file_handle); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path)) @@ -1669,6 +1746,8 @@ connect_topology (struct GNUNET_TESTING_PeerGroup *pg) * @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 + * @param restrict_transports space delimited list of transports to blacklist + * to create restricted topology * * @return the maximum number of connections were all allowed peers * connected to each other @@ -1676,7 +1755,8 @@ connect_topology (struct GNUNET_TESTING_PeerGroup *pg) int GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET_TESTING_Topology topology, - enum GNUNET_TESTING_Topology restrict_topology) + enum GNUNET_TESTING_Topology restrict_topology, + char *restrict_transports) { int ret; int num_connections; @@ -1847,9 +1927,9 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, break; } - if (unblacklisted_connections > 0) + if ((unblacklisted_connections > 0) && (restrict_transports != NULL)) { - ret = create_and_copy_blacklist_files(pg); + ret = create_and_copy_blacklist_files(pg, restrict_transports); if (ret != GNUNET_OK) { #if VERBOSE_TESTING @@ -2606,12 +2686,55 @@ void restart_callback (void *cls, } +/** + * Callback for informing us about a successful + * or unsuccessful churn stop call. + * + * @param cls a ChurnContext + * @param emsg NULL on success, non-NULL on failure + * + */ void churn_stop_callback (void *cls, const char *emsg) { + struct ChurnContext *churn_ctx = cls; + unsigned int total_left; + char *error_message; + + error_message = NULL; + if (emsg != NULL) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Churn stop callback failed with error `%s'\n", emsg); + churn_ctx->num_failed_stop++; + } + else + { + churn_ctx->num_to_stop--; + } + + total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + (churn_ctx->num_to_start - churn_ctx->num_failed_start); + if (total_left == 0) + { + if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0)) + GNUNET_asprintf(&error_message, "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!", churn_ctx->num_failed_start, churn_ctx->num_failed_stop); + churn_ctx->cb(churn_ctx->cb_cls, error_message); + GNUNET_free_non_null(error_message); + GNUNET_free(churn_ctx); + } } +/** + * Callback for informing us about a successful + * or unsuccessful churn start call. + * + * @param cls a ChurnContext + * @param id the peer identity of the started peer + * @param cfg the handle to the configuration of the peer + * @param d handle to the daemon for the peer + * @param emsg NULL on success, non-NULL on failure + * + */ void churn_start_callback (void *cls, const struct GNUNET_PeerIdentity *id, @@ -2619,16 +2742,33 @@ churn_start_callback (void *cls, struct GNUNET_TESTING_Daemon *d, const char *emsg) { + struct ChurnContext *churn_ctx = cls; + unsigned int total_left; + char *error_message; -} + error_message = NULL; + if (emsg != NULL) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Churn stop callback failed with error `%s'\n", emsg); + churn_ctx->num_failed_start++; + } + else + { + churn_ctx->num_to_start--; + } -/** - * Context for handling churning a peer group - */ -struct ChurnContext -{ + total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + (churn_ctx->num_to_start - churn_ctx->num_failed_start); -}; + if (total_left == 0) + { + if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0)) + GNUNET_asprintf(&error_message, "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!", churn_ctx->num_failed_start, churn_ctx->num_failed_stop); + churn_ctx->cb(churn_ctx->cb_cls, error_message); + GNUNET_free_non_null(error_message); + GNUNET_free(churn_ctx); + } + +} /** * Simulate churn by stopping some peers (and possibly @@ -2638,9 +2778,7 @@ struct ChurnContext * online will be taken offline; then "von" random peers that are then * offline will be put back online. No notifications will be * generated for any of these operations except for the callback upon - * completion. Note that the implementation is at liberty to keep - * the ARM service itself (but none of the other services or daemons) - * running even though the "peer" is being varied offline. + * completion. * * @param pg handle for the peer group * @param voff number of peers that should go offline @@ -2707,6 +2845,11 @@ GNUNET_TESTING_daemons_churn (struct GNUNET_TESTING_PeerGroup *pg, running = 0; stopped = 0; + churn_ctx->num_to_start = von; + churn_ctx->num_to_stop = voff; + churn_ctx->cb = cb; + churn_ctx->cb_cls = cb_cls; + for (i = 0; i < pg->total; i++) { if (pg->peers[i].daemon->running == GNUNET_YES) @@ -2730,7 +2873,6 @@ GNUNET_TESTING_daemons_churn (struct GNUNET_TESTING_PeerGroup *pg, { GNUNET_TESTING_daemon_start_stopped(pg->peers[stopped_arr[stopped_permute[i]]].daemon, timeout, &churn_start_callback, churn_ctx); } - } -- cgit v1.2.3