From b80e650bad570e01b5600aab2a667d177fc17770 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 29 Jul 2009 08:05:52 +0000 Subject: travelhacking --- src/testing/Makefile.am | 29 ++-- src/testing/test_testing.c | 116 ++++++++++++++ src/testing/test_testing_data.conf | 29 ++++ src/testing/testing.c | 186 +++++++++++++++------- src/testing/testing_group.c | 315 ++++++++++++++++++++++++++++++------- 5 files changed, 547 insertions(+), 128 deletions(-) create mode 100644 src/testing/test_testing.c create mode 100644 src/testing/test_testing_data.conf (limited to 'src/testing') diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index 635570482..9ac831a88 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -15,17 +15,20 @@ libgnunettesting_la_SOURCES = \ testing.c \ testing_group.c \ testing_testbed.c -libgnunettesting_la_LIBADD = \ - $(top_builddir)/src/util/libgnunetutil.la $(XLIB) - -#check_PROGRAMS = \ -# test_testing -# -#TESTS = $(check_PROGRAMS) -# -#test_testing_SOURCES = \ -# test_testing.c -#test_testing_LDADD = \ -# $(top_builddir)/src/testing/libgnunettesting.la \ -# $(top_builddir)/src/util/libgnunetutil.la +libgnunettesting_la_LIBADD = $(XLIB) \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la +check_PROGRAMS = \ + test_testing + +TESTS = $(check_PROGRAMS) + +test_testing_SOURCES = \ + test_testing.c +test_testing_LDADD = \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +EXTRA_DIST = test_testing_data.conf diff --git a/src/testing/test_testing.c b/src/testing/test_testing.c new file mode 100644 index 000000000..cbedf60fb --- /dev/null +++ b/src/testing/test_testing.c @@ -0,0 +1,116 @@ +/* + 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.c + * @brief testcase for testing.c + */ +#include "platform.h" +#include "gnunet_testing_lib.h" + +#define VERBOSE GNUNET_YES + +static int ok; + +static void end_cb(void *cls, + const char *emsg) +{ + GNUNET_assert (emsg == NULL); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Daemon terminated, will now exit.\n"); +#endif + ok = 0; +} + +static void my_cb(void *cls, + const struct GNUNET_PeerIdentity *id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Daemon *d, + const char *emsg) +{ + GNUNET_assert (id != NULL); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Daemon started, will now stop it.\n"); +#endif + GNUNET_TESTING_daemon_stop (d, &end_cb, NULL); +} + + +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_TESTING_Daemon *d; + + ok = 1; +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting daemon.\n"); +#endif + d = GNUNET_TESTING_daemon_start (sched, + cfg, + NULL, + &my_cb, + NULL); + GNUNET_assert (d != NULL); +} + +static int +check () +{ + char *const argv[] = { "test-testing", + "-c", + "test_testing_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-tesing", "nohelp", + options, &run, &ok); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-testing", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + return ret; +} + +/* end of test_testing.c */ diff --git a/src/testing/test_testing_data.conf b/src/testing/test_testing_data.conf new file mode 100644 index 000000000..7c46fdf34 --- /dev/null +++ b/src/testing/test_testing_data.conf @@ -0,0 +1,29 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunet-testing/ +DEFAULTCONFIG = test_testing_data.conf + +[resolver] +PORT = 2564 + +[transport] +PORT = 2565 +PLUGINS = tcp + +[arm] +PORT = 2566 +DEFAULTSERVICES = transport core + +[statistics] +PORT = 2567 + +[tcp] +PORT = 2568 + +[peerinfo] +PORT = 2569 + +[core] +PORT = 2570 + +[testing] +WEAKRANDOM = YES diff --git a/src/testing/testing.c b/src/testing/testing.c index 3406355c5..5d465e05f 100644 --- a/src/testing/testing.c +++ b/src/testing/testing.c @@ -85,7 +85,7 @@ struct GNUNET_TESTING_Daemon /** * Our configuration. */ - const struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_CONFIGURATION_Handle *cfg; /** * Host to run GNUnet on. @@ -202,10 +202,11 @@ testing_init (void *cls, d->cb = NULL; if (server == NULL) { - cb (d->cb_cls, NULL, d->cfg, d, - _("Failed to connect to core service\n")); if (GNUNET_YES == d->dead) GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls); + else if (NULL != cb) + cb (d->cb_cls, NULL, d->cfg, d, + _("Failed to connect to core service\n")); return; } #if DEBUG_TESTING @@ -216,7 +217,7 @@ testing_init (void *cls, d->id = *my_identity; if (GNUNET_YES == d->dead) GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls); - else + else if (NULL != cb) cb (d->cb_cls, my_identity, d->cfg, d, NULL); d->server = server; } @@ -240,6 +241,11 @@ start_fsm (void *cls, unsigned long code; char *dst; +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer FSM is in phase %u.\n", + d->phase); +#endif d->task = GNUNET_SCHEDULER_NO_TASK; switch (d->phase) { @@ -255,11 +261,12 @@ start_fsm (void *cls, { cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - _("`scp' does not seem to terminate.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + _("`scp' does not seem to terminate.\n")); return; } /* wait some more */ @@ -278,11 +285,12 @@ start_fsm (void *cls, { cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - _("`scp' did not complete cleanly.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + _("`scp' did not complete cleanly.\n")); return; } #if DEBUG_TESTING @@ -299,7 +307,11 @@ start_fsm (void *cls, "gnunet-service-arm", "-c", d->cfgfile, +#if DEBUG_TESTING + "-L", "DEBUG", +#else "-d", +#endif NULL); } else @@ -328,16 +340,31 @@ start_fsm (void *cls, (NULL == d->hostname) ? "gnunet-service-arm" : "ssh"); cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - (NULL == d->hostname) - ? _("Failed to start `gnunet-service-arm' process.\n") - : _("Failed to start `ssh' process.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + (NULL == d->hostname) + ? _("Failed to start `gnunet-service-arm' process.\n") + : _("Failed to start `ssh' process.\n")); } +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Started `%s', waiting for `%s' to be up.\n", + "gnunet-service-arm", + "gnunet-service-core"); +#endif d->phase = SP_START_ARMING; d->wait_runs = 0; + d->task + = GNUNET_SCHEDULER_add_delayed (d->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_CONSTANTS_EXEC_WAIT, + &start_fsm, + d); break; case SP_START_ARMING: if (GNUNET_OK != @@ -350,13 +377,14 @@ start_fsm (void *cls, { cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - (NULL == d->hostname) - ? _("`gnunet-service-arm' does not seem to terminate.\n") - : _("`ssh' does not seem to terminate.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + (NULL == d->hostname) + ? _("`gnunet-service-arm' does not seem to terminate.\n") + : _("`ssh' does not seem to terminate.\n")); return; } /* wait some more */ @@ -424,18 +452,24 @@ start_fsm (void *cls, if ( (type != GNUNET_OS_PROCESS_EXITED) || (code != 0) ) { - d->dead_cb (d->dead_cb_cls, - _("`sshp' did not complete cleanly.\n")); + if (NULL != d->dead_cb) + d->dead_cb (d->dead_cb_cls, + _("`ssh' did not complete cleanly.\n")); GNUNET_free (d->cfgfile); GNUNET_free_non_null (d->hostname); GNUNET_free_non_null (d->username); GNUNET_free (d); return; } +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer shutdown complete.\n"); +#endif GNUNET_free (d->cfgfile); GNUNET_free_non_null (d->hostname); GNUNET_free_non_null (d->username); - d->dead_cb (d->dead_cb_cls, NULL); + if (NULL != d->dead_cb) + d->dead_cb (d->dead_cb_cls, NULL); GNUNET_free (d); break; case SP_CONFIG_UPDATE: @@ -450,11 +484,12 @@ start_fsm (void *cls, { cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - _("`scp' does not seem to terminate.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + _("`scp' does not seem to terminate.\n")); return; } /* wait some more */ @@ -471,15 +506,17 @@ start_fsm (void *cls, if ( (type != GNUNET_OS_PROCESS_EXITED) || (code != 0) ) { - d->update_cb (d->update_cb_cls, - _("`scp' did not complete cleanly.\n")); + if (NULL != d->update_cb) + d->update_cb (d->update_cb_cls, + _("`scp' did not complete cleanly.\n")); return; } #if DEBUG_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully copied configuration file.\n"); #endif - d->update_cb (d->update_cb_cls, NULL); + if (NULL != d->update_cb) + d->update_cb (d->update_cb_cls, NULL); d->phase = SP_START_DONE; break; } @@ -502,7 +539,7 @@ start_fsm (void *cls, */ struct GNUNET_TESTING_Daemon * GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, - struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_CONFIGURATION_Handle *cfg, const char *hostname, GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls) @@ -513,9 +550,13 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, ret = GNUNET_malloc (sizeof(struct GNUNET_TESTING_Daemon)); ret->sched = sched; - ret->cfg = cfg; ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname); ret->cfgfile = GNUNET_DISK_mktemp ("gnunet-testing-config"); +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setting up peer with configuration file `%s'.\n", + ret->cfgfile); +#endif if (NULL == ret->cfgfile) { GNUNET_free_non_null (ret->hostname); @@ -524,15 +565,21 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, } ret->cb = cb; ret->cb_cls = cb_cls; + ret->cfg = GNUNET_CONFIGURATION_dup (cfg); + GNUNET_CONFIGURATION_set_value_string (ret->cfg, + "PATHS", + "DEFAULTCONFIG", + ret->cfgfile); /* 1) write configuration to temporary file */ if (GNUNET_OK != - GNUNET_CONFIGURATION_write (cfg, + GNUNET_CONFIGURATION_write (ret->cfg, ret->cfgfile)) { if (0 != UNLINK (ret->cfgfile)) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", ret->cfgfile); + GNUNET_CONFIGURATION_destroy (ret->cfg); GNUNET_free_non_null (ret->hostname); GNUNET_free (ret->cfgfile); GNUNET_free (ret); @@ -581,6 +628,7 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", ret->cfgfile); + GNUNET_CONFIGURATION_destroy (ret->cfg); GNUNET_free_non_null (ret->hostname); GNUNET_free_non_null (ret->username); GNUNET_free (ret->cfgfile); @@ -589,7 +637,7 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, } ret->task = GNUNET_SCHEDULER_add_delayed (sched, - GNUNET_NO, + GNUNET_YES, GNUNET_SCHEDULER_PRIORITY_KEEP, GNUNET_SCHEDULER_NO_TASK, GNUNET_CONSTANTS_EXEC_WAIT, @@ -597,6 +645,10 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, ret); return ret; } +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No need to copy configuration file since we are running locally.\n"); +#endif ret->phase = SP_COPIED; GNUNET_SCHEDULER_add_continuation (sched, GNUNET_NO, @@ -687,7 +739,7 @@ void GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d, d->dead_cb_cls = cb_cls; d->task = GNUNET_SCHEDULER_add_delayed (d->sched, - GNUNET_NO, + GNUNET_YES, GNUNET_SCHEDULER_PRIORITY_KEEP, GNUNET_SCHEDULER_NO_TASK, GNUNET_CONSTANTS_EXEC_WAIT, @@ -695,11 +747,13 @@ void GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d, d); return; } + GNUNET_CONFIGURATION_destroy (d->cfg); GNUNET_free (d->cfgfile); GNUNET_free_non_null (d->hostname); GNUNET_free_non_null (d->username); GNUNET_free (d); - cb (cb_cls, NULL); + if (NULL != cb) + cb (cb_cls, NULL); } @@ -720,8 +774,9 @@ void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d, if (d->phase != SP_START_DONE) { - cb (cb_cls, - _("Peer not yet running, can not change configuration at this point.")); + if (NULL != cb) + cb (cb_cls, + _("Peer not yet running, can not change configuration at this point.")); return; } @@ -730,7 +785,8 @@ void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d, GNUNET_CONFIGURATION_write (cfg, d->cfgfile)) { - cb (cb_cls, + if (NULL != cb) + cb (cb_cls, _("Failed to write new configuration to disk.")); return; } @@ -739,7 +795,8 @@ void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d, if (NULL == d->hostname) { /* signal success */ - cb (cb_cls, NULL); + if (NULL != cb) + cb (cb_cls, NULL); return; } d->phase = SP_CONFIG_UPDATE; @@ -765,8 +822,9 @@ void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not start `%s' process to copy configuration file.\n"), "scp"); - cb (cb_cls, - _("Failed to copy new configuration to remote machine.")); + if (NULL != cb) + cb (cb_cls, + _("Failed to copy new configuration to remote machine.")); d->phase = SP_START_DONE; return; } @@ -802,10 +860,14 @@ static size_t transmit_ready (void *cls, size_t size, void *buf) { struct ConnectContext *ctx = cls; - if (buf == NULL) - ctx->cb (ctx->cb_cls, _("Peers failed to connect")); - else - ctx->cb (ctx->cb_cls, NULL); + + if (NULL != ctx->cb) + { + if (buf == NULL) + ctx->cb (ctx->cb_cls, _("Peers failed to connect")); + else + ctx->cb (ctx->cb_cls, NULL); + } GNUNET_free (ctx); return 0; } @@ -831,8 +893,9 @@ process_hello (void *cls, if (peer == NULL) { /* signal error */ - ctx->cb (ctx->cb_cls, - _("Failed to receive `HELLO' from peer\n")); + if (NULL != ctx->cb) + ctx->cb (ctx->cb_cls, + _("Failed to receive `HELLO' from peer\n")); GNUNET_TRANSPORT_disconnect (ctx->d1th); GNUNET_TRANSPORT_disconnect (ctx->d2th); GNUNET_free (ctx); @@ -873,7 +936,8 @@ void GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1, if ( (d1->server == NULL) || (d2->server == NULL) ) { - cb (cb_cls, _("Peers are not fully running yet, can not connect!\n")); + if (NULL != cb) + cb (cb_cls, _("Peers are not fully running yet, can not connect!\n")); return; } ctx = GNUNET_malloc (sizeof(struct ConnectContext)); @@ -886,7 +950,8 @@ void GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1, if (ctx->d1th == NULL) { GNUNET_free (ctx); - cb (cb_cls, _("Failed to connect to transport service!\n")); + if (NULL != cb) + cb (cb_cls, _("Failed to connect to transport service!\n")); return; } ctx->d2th = GNUNET_TRANSPORT_connect (d2->sched, d2->cfg, d2, NULL, NULL, NULL); @@ -894,7 +959,8 @@ void GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1, { GNUNET_TRANSPORT_disconnect (ctx->d1th); GNUNET_free (ctx); - cb (cb_cls, _("Failed to connect to transport service!\n")); + if (NULL != cb) + cb (cb_cls, _("Failed to connect to transport service!\n")); return; } GNUNET_TRANSPORT_get_hello (ctx->d1th, diff --git a/src/testing/testing_group.c b/src/testing/testing_group.c index 917a524de..b87507364 100644 --- a/src/testing/testing_group.c +++ b/src/testing/testing_group.c @@ -27,6 +27,58 @@ #include "gnunet_arm_service.h" #include "gnunet_testing_lib.h" +/** + * Lowest port used for GNUnet testing. Should be high enough to not + * conflict with other applications running on the hosts but be low + * enough to not conflict with client-ports (typically starting around + * 32k). + */ +#define LOW_PORT 10000 + +/** + * Highest port used for GNUnet testing. Should be low enough to not + * conflict with the port range for "local" ports (client apps; see + * /proc/sys/net/ipv4/ip_local_port_range on Linux for example). + */ +#define HIGH_PORT 32000 + +/** + * Data we keep per peer. + */ +struct PeerData +{ + /** + * (Initial) configuration of the host. + * (initial because clients could change + * it and we would not know about those + * updates). + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Handle for controlling the daemon. + */ + struct GNUNET_TESTING_Daemon *daemon; +}; + + +/** + * Data we keep per host. + */ +struct HostData +{ + /** + * Name of the host. + */ + char *hostname; + + /** + * Lowest port that we have not yet used + * for GNUnet. + */ + uint16_t minport; +}; + /** * Handle to a group of GNUnet peers. @@ -41,7 +93,7 @@ struct GNUNET_TESTING_PeerGroup /** * Configuration template. */ - struct GNUNET_CONFIGURATION_Handle *cfg; + const struct GNUNET_CONFIGURATION_Handle *cfg; /** * Function to call on each started daemon. @@ -54,14 +106,15 @@ struct GNUNET_TESTING_PeerGroup void *cb_cls; /** - * NULL-terminated array of hostnames. + * NULL-terminated array of information about + * hosts. */ - char **hostnames; + struct HostData *hosts; /** * Array of "total" peers. */ - struct GNUNET_TESTING_Daemon **peers; + struct PeerData *peers; /** * Number of peers in this group. @@ -71,79 +124,212 @@ struct GNUNET_TESTING_PeerGroup }; +struct UpdateContext +{ + struct GNUNET_CONFIGURATION_Handle *ret; + unsigned int nport; +}; + /** - * Start count gnunetd processes with the same set of transports and - * applications. The port numbers (any option called "PORT") will be - * adjusted to ensure that no two peers running on the same system - * have the same port(s) in their respective configurations. + * Function to iterate over options. Copies + * the options to the target configuration, + * updating PORT values as needed. * - * @param sched scheduler to use - * @param cfg configuration template to use - * @param total number of daemons to start - * @param cb function to call on each daemon that was started - * @param cb_cls closure for cb - * @param hostname where to run the peers; can be NULL (to run - * everything on localhost). - * @param va Additional hosts can be specified using a NULL-terminated list of - * varargs, hosts will then be used round-robin from that - * list; va only contains anything if hostname != NULL. - * @return NULL on error, otherwise handle to control peer group + * @param cls closure + * @param section name of the section + * @param option name of the option + * @param value value of the option */ -struct GNUNET_TESTING_PeerGroup * -GNUNET_TESTING_daemons_start_va (struct GNUNET_SCHEDULER_Handle *sched, - const struct GNUNET_CONFIGURATION_Handle *cfg, - unsigned int total, - GNUNET_TESTING_NotifyDaemonRunning cb, - void *cb_cls, - const char *hostname, - va_list va) +static void +update_config(void *cls, + const char *section, + const char *option, + const char *value) { - struct GNUNET_TESTING_PeerGroup *pg; - - pg = GNUNET_malloc (sizeof(struct GNUNET_TESTING_PeerGroup)); - return pg; + struct UpdateContext *ctx = cls; + unsigned int ival; + char cval[12]; + + if ( (0 == strcmp (option, "PORT")) && + (1 == sscanf (value, "%u", &ival)) ) + { + GNUNET_snprintf (cval, + sizeof(cval), + "%u", + ctx->nport++); + value = cval; + } + GNUNET_CONFIGURATION_set_value_string (ctx->ret, + section, + option, + value); } /** - * Start count gnunetd processes with the same set of - * transports and applications. The port numbers will - * be computed by adding delta each time (zero - * times for the first peer). + * Create a new configuration using the given configuration + * as a template; however, each PORT in the existing cfg + * must be renumbered by incrementing "*port". If we run + * out of "*port" numbers, return NULL. + * + * @param cfg template configuration + * @param port port numbers to use, update to reflect + * port numbers that were used + * @return new configuration, NULL on error + */ +static struct GNUNET_CONFIGURATION_Handle* +make_config (const struct GNUNET_CONFIGURATION_Handle*cfg, + uint16_t *port) +{ + struct UpdateContext uc; + uint16_t orig; + + orig = *port; + uc.nport = *port; + uc.ret = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_iterate (cfg, + &update_config, + &uc); + if (uc.nport >= HIGH_PORT) + { + *port = orig; + GNUNET_CONFIGURATION_destroy (uc.ret); + return NULL; + } + *port = (uint16_t) uc.nport; + return uc.ret; +} + + +/** + * Start count gnunetd processes with the same set of transports and + * applications. The port numbers (any option called "PORT") will be + * adjusted to ensure that no two peers running on the same system + * have the same port(s) in their respective configurations. * * @param sched scheduler to use * @param cfg configuration template to use * @param total number of daemons to start - * @param timeout how long is this allowed to take? * @param cb function to call on each daemon that was started * @param cb_cls closure for cb - * @param hostname where to run the peers; can be NULL (to run - * everything on localhost). Additional - * hosts can be specified using a NULL-terminated list of - * varargs, hosts will then be used round-robin from that - * list. + * @param hostnames space-separated list of hostnames to use; can be NULL (to run + * everything on localhost). * @return NULL on error, otherwise handle to control peer group */ struct GNUNET_TESTING_PeerGroup * GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched, - struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned int total, GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls, - const char *hostname, - ...) + const char *hostnames) { - struct GNUNET_TESTING_PeerGroup * ret; - va_list va; - - va_start (va, hostname); - ret = GNUNET_TESTING_daemons_start_va (sched, cfg, - total, cb, cb_cls, hostname, - va); - va_end (va); - return ret; -} + struct GNUNET_TESTING_PeerGroup *pg; + const char *rpos; + char *pos; + char *start; + const char *hostname; + struct GNUNET_CONFIGURATION_Handle *pcfg; + unsigned int off; + unsigned int hostcnt; + uint16_t minport; + if (0 == total) + { + GNUNET_break (0); + return NULL; + } + pg = GNUNET_malloc (sizeof(struct GNUNET_TESTING_PeerGroup)); + pg->sched = sched; + pg->cfg = cfg; + pg->cb = cb; + pg->cb_cls = cb_cls; + pg->total = total; + pg->peers = GNUNET_malloc (total * sizeof(struct PeerData)); + if (NULL != hostnames) + { + off = 2; + /* skip leading spaces */ + while ( (0 != *hostnames) && + (isspace(*hostnames))) + hostnames++; + rpos = hostnames; + while ('\0' != *rpos) + { + if (isspace (*rpos)) + off++; + rpos++; + } + pg->hosts = GNUNET_malloc (off * sizeof (struct HostData)); + off = 0; + start = GNUNET_strdup (hostnames); + pos = start; + while ('\0' != *pos) + { + if (isspace (*pos)) + { + *pos = '\0'; + if (strlen(start) > 0) + { + pg->hosts[off].minport = LOW_PORT; + pg->hosts[off++].hostname = start; + } + start = pos+1; + } + pos++; + } + if (strlen(start) > 0) + { + pg->hosts[off].minport = LOW_PORT; + pg->hosts[off++].hostname = start; + } + if (off == 0) + { + GNUNET_free (start); + GNUNET_free (pg->hosts); + pg->hosts = NULL; + } + hostcnt = off; + minport = 0; /* make gcc happy */ + } + else + { + hostcnt = 0; + minport = LOW_PORT; + } + for (off = 0; off < total; off++) + { + if (hostcnt > 0) + { + hostname = pg->hosts[off % hostcnt].hostname; + pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport); + } + else + { + hostname = NULL; + pcfg = make_config (cfg, &minport); + } + if (NULL == pcfg) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Could not create configuration for peer number %u on `%s'!\n"), + off, + hostname == NULL ? "localhost" : hostname); + continue; + } + pg->peers[off].cfg = pcfg; + pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched, + pcfg, + hostname, + 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; +} /** @@ -154,7 +340,26 @@ GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched, void GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg) { - + unsigned int off; + + for (off = 0; off < pg->total; off++) + { + /* FIXME: should we wait for our + continuations to be called here? This + would require us to take a continuation + as well... */ + if (NULL != pg->peers[off].daemon) + GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, + NULL, NULL); + if (NULL != pg->peers[off].cfg) + GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg); + } + GNUNET_free (pg->peers); + if (NULL != pg->hosts) + { + GNUNET_free (pg->hosts[0].hostname); + GNUNET_free (pg->hosts); + } GNUNET_free (pg); } -- cgit v1.2.3