From 8dceae82b3478d223779cbcc335b4ca497acbba5 Mon Sep 17 00:00:00 2001 From: Sree Harsha Totakura Date: Thu, 18 Apr 2013 14:59:32 +0000 Subject: [PATCH] - service sharing in testing --- src/include/gnunet_testing_lib.h | 34 ++- src/testbed/gnunet-helper-testbed.c | 2 +- src/testbed/gnunet-service-testbed.c | 2 +- src/testing/gnunet-testing.c | 5 +- src/testing/test_testing_peerstartup.c | 2 +- src/testing/test_testing_peerstartup2.c | 2 +- src/testing/test_testing_portreservation.c | 2 +- src/testing/testing.c | 273 ++++++++++++++++++++- src/transport/transport-testing.c | 3 +- 9 files changed, 309 insertions(+), 16 deletions(-) diff --git a/src/include/gnunet_testing_lib.h b/src/include/gnunet_testing_lib.h index d96743615..d4c5b5b37 100644 --- a/src/include/gnunet_testing_lib.h +++ b/src/include/gnunet_testing_lib.h @@ -66,6 +66,29 @@ struct GNUNET_TESTING_System; struct GNUNET_TESTING_Peer; +/** + * Specification of a service that is to be shared among peers + */ +struct GNUNET_TESTING_SharedService +{ + /** + * The name of the service. + */ + const char *service; + + /** + * The configuration template for the service. Cannot be NULL + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * The number of peers which share an instance of the service. 0 for sharing + * among all peers + */ + unsigned int share; +}; + + /** * Create a system handle. There must only be one system handle per operating * system. Uses a default range for allowed ports. Ports are still tested for @@ -80,12 +103,16 @@ struct GNUNET_TESTING_Peer; * in CIDR notation. * @param hostname the hostname of the system we are using for testing; NULL for * localhost + * @param shared_services NULL terminated array describing services that are to + * be shared among peers * @return handle to this system, NULL on error */ struct GNUNET_TESTING_System * GNUNET_TESTING_system_create (const char *testdir, const char *trusted_ip, - const char *hostname); + const char *hostname, + const struct GNUNET_TESTING_SharedService ** + shared_services); /** @@ -104,6 +131,8 @@ GNUNET_TESTING_system_create (const char *testdir, * in CIDR notation. * @param hostname the hostname of the system we are using for testing; NULL for * localhost + * @param shared_services NULL terminated array describing services that are to + * be shared among peers * @param lowport lowest port number this system is allowed to allocate (inclusive) * @param highport highest port number this system is allowed to allocate (exclusive) * @return handle to this system, NULL on error @@ -112,6 +141,9 @@ struct GNUNET_TESTING_System * GNUNET_TESTING_system_create_with_portrange (const char *testdir, const char *trusted_ip, const char *hostname, + const struct + GNUNET_TESTING_SharedService ** + shared_services, uint16_t lowport, uint16_t highport); diff --git a/src/testbed/gnunet-helper-testbed.c b/src/testbed/gnunet-helper-testbed.c index ab0a595d2..bdd8911de 100644 --- a/src/testbed/gnunet-helper-testbed.c +++ b/src/testbed/gnunet-helper-testbed.c @@ -393,7 +393,7 @@ tokenizer_cb (void *cls, void *client, hostname[hostname_size] = '\0'; } test_system = - GNUNET_TESTING_system_create ("testbed-helper", trusted_ip, hostname); + GNUNET_TESTING_system_create ("testbed-helper", trusted_ip, hostname, NULL); GNUNET_free_non_null (hostname); hostname = NULL; GNUNET_assert (NULL != test_system); diff --git a/src/testbed/gnunet-service-testbed.c b/src/testbed/gnunet-service-testbed.c index 3d4a54db3..9a0084efa 100644 --- a/src/testbed/gnunet-service-testbed.c +++ b/src/testbed/gnunet-service-testbed.c @@ -485,7 +485,7 @@ handle_init (void *cls, struct GNUNET_SERVER_Client *client, LOG_DEBUG ("Our IP: %s\n", GST_context->master_ip); GST_context->system = GNUNET_TESTING_system_create ("testbed", GST_context->master_ip, - hostname); + hostname, NULL); host = GNUNET_TESTBED_host_create_with_id (GST_context->host_id, GST_context->master_ip, NULL, diff --git a/src/testing/gnunet-testing.c b/src/testing/gnunet-testing.c index 8c92068ab..574b585fc 100644 --- a/src/testing/gnunet-testing.c +++ b/src/testing/gnunet-testing.c @@ -76,7 +76,8 @@ create_unique_cfgs (const char * template, const unsigned int no) } fail = GNUNET_NO; - system = GNUNET_TESTING_system_create ("testing", NULL /* controller */, NULL); + system = GNUNET_TESTING_system_create ("testing", NULL /* controller */, + NULL, NULL); for (cur = 0; cur < no; cur++) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating configuration no. %u \n", cur); @@ -125,7 +126,7 @@ create_hostkeys (const unsigned int no) struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *pkb; ssize_t ret; - system = GNUNET_TESTING_system_create ("testing", NULL, NULL); + system = GNUNET_TESTING_system_create ("testing", NULL, NULL, NULL); pk = GNUNET_TESTING_hostkey_get (system, create_no, &id); if (NULL == pk) { diff --git a/src/testing/test_testing_peerstartup.c b/src/testing/test_testing_peerstartup.c index fb3967ce0..d32a341b5 100644 --- a/src/testing/test_testing_peerstartup.c +++ b/src/testing/test_testing_peerstartup.c @@ -99,7 +99,7 @@ run (void *cls, char *const *args, const char *cfgfile, test_ctx = GNUNET_malloc (sizeof (struct TestingContext)); test_ctx->system = GNUNET_TESTING_system_create ("test-gnunet-testing", - "127.0.0.1", NULL); + "127.0.0.1", NULL, NULL); emsg = NULL; if (NULL == test_ctx->system) goto end; diff --git a/src/testing/test_testing_peerstartup2.c b/src/testing/test_testing_peerstartup2.c index d290aff16..cd8141b40 100644 --- a/src/testing/test_testing_peerstartup2.c +++ b/src/testing/test_testing_peerstartup2.c @@ -169,7 +169,7 @@ run (void *cls, char *const *args, const char *cfgfile, test_ctx = GNUNET_malloc (sizeof (struct TestingContext)); test_ctx->system = GNUNET_TESTING_system_create ("test-gnunet-testing", - "127.0.0.1", NULL); + "127.0.0.1", NULL, NULL); emsg = NULL; if (NULL == test_ctx->system) goto end; diff --git a/src/testing/test_testing_portreservation.c b/src/testing/test_testing_portreservation.c index 21ef51ffd..81ba67e96 100644 --- a/src/testing/test_testing_portreservation.c +++ b/src/testing/test_testing_portreservation.c @@ -50,7 +50,7 @@ run (void *cls, char *const *args, const char *cfgfile, uint16_t old_port1; system = GNUNET_TESTING_system_create ("/tmp/gnunet-testing-new", - "localhost", NULL); + "localhost", NULL, NULL); GNUNET_assert (NULL != system); new_port1 = GNUNET_TESTING_reserve_port (system, GNUNET_YES); LOG (GNUNET_ERROR_TYPE_DEBUG, diff --git a/src/testing/testing.c b/src/testing/testing.c index a1a2fdc06..23cd7ddc7 100644 --- a/src/testing/testing.c +++ b/src/testing/testing.c @@ -64,6 +64,37 @@ #define HIGH_PORT 56000 +struct SharedServiceInstance +{ + struct SharedService *ss; + + char *cfg_fn; + + struct GNUNET_OS_Process *proc; + + char *unix_sock; + + char *port_str; + + unsigned int n_refs; +}; + +struct SharedService +{ + char *sname; + + struct SharedServiceInstance **instances; + + struct GNUNET_CONFIGURATION_Handle *cfg; + + unsigned int n_peers; + + unsigned int share; + + unsigned int n_instances; +}; + + /** * Handle for a system on which GNUnet peers are executed; * a system is used for reserving unique paths and ports. @@ -96,6 +127,10 @@ struct GNUNET_TESTING_System */ struct GNUNET_DISK_MapHandle *map; + struct SharedService **shared_services; + + unsigned int n_shared_services; + /** * Bitmap where each TCP port that has already been reserved for * some GNUnet peer is recorded. Note that we additionally need to @@ -206,6 +241,8 @@ struct GNUNET_TESTING_Peer */ struct GNUNET_PeerIdentity *id; + struct SharedServiceInstance **ss_instances; + /** * Array of ports currently allocated to this peer. These ports will be * released upon peer destroy and can be used by other peers which are @@ -306,6 +343,24 @@ hostkeys_unload (struct GNUNET_TESTING_System *system) } +/** + * Function to iterate over options. + * + * @param cls closure + * @param section name of the section + * @param option name of the option + * @param value value of the option + */ +static void +cfg_copy_iterator (void *cls, const char *section, + const char *option, const char *value) +{ + struct GNUNET_CONFIGURATION_Handle *cfg2 = cls; + + GNUNET_CONFIGURATION_set_value_string (cfg2, section, option, value); +} + + /** * Create a system handle. There must only be one system * handle per operating system. @@ -319,6 +374,8 @@ hostkeys_unload (struct GNUNET_TESTING_System *system) * in CIDR notation. * @param hostname the hostname of the system we are using for testing; NULL for * localhost + * @param shared_services NULL terminated array describing services that are to + * be shared among peers * @param lowport lowest port number this system is allowed to allocate (inclusive) * @param highport highest port number this system is allowed to allocate (exclusive) * @return handle to this system, NULL on error @@ -327,10 +384,16 @@ struct GNUNET_TESTING_System * GNUNET_TESTING_system_create_with_portrange (const char *testdir, const char *trusted_ip, const char *hostname, + const struct + GNUNET_TESTING_SharedService ** + shared_services, uint16_t lowport, uint16_t highport) { struct GNUNET_TESTING_System *system; + const struct GNUNET_TESTING_SharedService *tss; + struct SharedService *ss; + unsigned int cnt; GNUNET_assert (NULL != testdir); system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System)); @@ -351,6 +414,22 @@ GNUNET_TESTING_system_create_with_portrange (const char *testdir, GNUNET_TESTING_system_destroy (system, GNUNET_YES); return NULL; } + if (NULL == shared_services) + return system; + for (cnt = 0; NULL != (tss = shared_services[cnt]); cnt++) + { + ss = GNUNET_malloc (sizeof (struct SharedService)); + ss->sname = GNUNET_strdup (tss->service); + ss->cfg = GNUNET_CONFIGURATION_dup (tss->cfg); + ss->cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_iterate_section_values (tss->cfg, ss->sname, + &cfg_copy_iterator, ss->cfg); + GNUNET_CONFIGURATION_iterate_section_values (tss->cfg, "TESTING", + &cfg_copy_iterator, ss->cfg); + ss->share = tss->share; + GNUNET_array_append (system->shared_services, system->n_shared_services, + ss); + } return system; } @@ -369,20 +448,72 @@ GNUNET_TESTING_system_create_with_portrange (const char *testdir, * in CIDR notation. * @param hostname the hostname of the system we are using for testing; NULL for * localhost + * @param shared_services NULL terminated array describing services that are to + * be shared among peers * @return handle to this system, NULL on error */ struct GNUNET_TESTING_System * GNUNET_TESTING_system_create (const char *testdir, const char *trusted_ip, - const char *hostname) + const char *hostname, + const struct GNUNET_TESTING_SharedService ** + shared_services) { return GNUNET_TESTING_system_create_with_portrange (testdir, trusted_ip, hostname, + shared_services, LOW_PORT, HIGH_PORT); } +static void +cleanup_shared_service_instance (struct SharedServiceInstance *i) +{ + if (NULL != i->cfg_fn) + { + (void) unlink (i->cfg_fn); + GNUNET_free (i->cfg_fn); + } + GNUNET_free_non_null (i->unix_sock); + GNUNET_free_non_null (i->port_str); + GNUNET_break (NULL == i->proc); + GNUNET_break (0 == i->n_refs); + GNUNET_free (i); +} + +static int +start_shared_service_instance (struct SharedServiceInstance *i) +{ + char *binary; + + GNUNET_assert (NULL == i->proc); + GNUNET_assert (NULL != i->cfg_fn); + (void) GNUNET_asprintf (&binary, "gnunet-service-%s", i->ss->sname); + i->proc = GNUNET_OS_start_process (PIPE_CONTROL, + GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + NULL, NULL, + binary, + binary, + "-c", + i->cfg_fn, + NULL); + if (NULL == i->proc) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +static void +stop_shared_service_instance (struct SharedServiceInstance *i) +{ + GNUNET_break (0 == i->n_refs); + GNUNET_OS_process_kill (i->proc, SIGTERM); + (void) GNUNET_OS_process_wait (i->proc); + GNUNET_OS_process_destroy (i->proc); + i->proc = NULL; +} + /** * Free system resources. @@ -395,8 +526,29 @@ void GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system, int remove_paths) { + struct SharedService *ss; + struct SharedServiceInstance *i; + unsigned int ss_cnt; + unsigned int i_cnt; + if (NULL != system->hostkeys_data) hostkeys_unload (system); + for (ss_cnt = 0; ss_cnt < system->n_shared_services; ss_cnt++) + { + ss = system->shared_services[ss_cnt]; + for (i_cnt = 0; i_cnt < ss->n_instances; i_cnt++) + { + i = ss->instances[i_cnt]; + if (NULL != i->proc) + stop_shared_service_instance (i); + cleanup_shared_service_instance (i); + } + GNUNET_free_non_null (ss->instances); + GNUNET_CONFIGURATION_destroy (ss->cfg); + GNUNET_free (ss->sname); + GNUNET_free (ss); + } + GNUNET_free_non_null (system->shared_services); if (GNUNET_YES == remove_paths) GNUNET_DISK_directory_remove (system->tmppath); GNUNET_free (system->tmppath); @@ -739,6 +891,8 @@ update_config_sections (void *cls, ikeys_cnt = 0; val = NULL; + /* Ignore certain options from sections. See + https://gnunet.org/bugs/view.php?id=2476 */ if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section, "TESTING_IGNORE_KEYS")) { @@ -816,6 +970,66 @@ update_config_sections (void *cls, GNUNET_free (allowed_hosts); } +static struct SharedServiceInstance * +associate_shared_service (struct GNUNET_TESTING_System *system, + struct SharedService *ss, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct SharedServiceInstance *i; + struct GNUNET_CONFIGURATION_Handle *temp; + char *service_home; + uint32_t port; + + ss->n_peers++; + if ( ((0 == ss->share) && (NULL == ss->instances)) + || + ( (0 != ss->share) + && (ss->n_instances < ((ss->n_peers + ss->share - 1) / ss->share)) ) ) + { + i = GNUNET_malloc (sizeof (struct SharedServiceInstance)); + i->ss = ss; + (void) GNUNET_asprintf (&service_home, "%s/shared/%s/%u", + system->tmppath, ss->sname, ss->n_instances); + (void) GNUNET_asprintf (&i->unix_sock, "%s/sock", service_home); + port = GNUNET_TESTING_reserve_port (system, GNUNET_YES); + if (0 == port) + { + GNUNET_free (service_home); + cleanup_shared_service_instance (i); + return NULL; + } + GNUNET_array_append (ss->instances, ss->n_instances, i); + temp = GNUNET_CONFIGURATION_dup (ss->cfg); + (void) GNUNET_asprintf (&i->port_str, "%u", port); + (void) GNUNET_asprintf (&i->cfg_fn, "%s/config", service_home); + GNUNET_CONFIGURATION_set_value_string (temp, "PATHS", "SERVICEHOME", + service_home); + GNUNET_CONFIGURATION_set_value_string (temp, ss->sname, "UNIXPATH", + i->unix_sock); + GNUNET_CONFIGURATION_set_value_string (temp, ss->sname, "PORT", + i->port_str); + if (GNUNET_SYSERR == GNUNET_CONFIGURATION_write (temp, i->cfg_fn)) + { + GNUNET_CONFIGURATION_destroy (temp); + cleanup_shared_service_instance (i); + return NULL; + } + GNUNET_CONFIGURATION_destroy (temp); + } + else + { + GNUNET_assert (NULL != ss->instances); + GNUNET_assert (0 < ss->n_instances); + i = ss->instances[ss->n_instances - 1]; + } + GNUNET_CONFIGURATION_iterate_section_values(ss->cfg, ss->sname, + &cfg_copy_iterator, cfg); + GNUNET_CONFIGURATION_set_value_string (cfg, ss->sname, "UNIXPATH", + i->unix_sock); + GNUNET_CONFIGURATION_set_value_string (cfg, ss->sname, "PORT", i->port_str); + return i; +} + /** * Create a new configuration using the given configuration as a template; @@ -843,8 +1057,8 @@ GNUNET_TESTING_configuration_create_ (struct GNUNET_TESTING_System *system, unsigned int *nports) { struct UpdateContext uc; - char *default_config; - + char *default_config; + uc.system = system; uc.cfg = cfg; uc.status = GNUNET_OK; @@ -933,12 +1147,22 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, char *emsg_; struct GNUNET_CRYPTO_EccPrivateKey *pk; uint16_t *ports; + struct SharedService *ss; + struct SharedServiceInstance **ss_instances; + unsigned int cnt; unsigned int nports; ports = NULL; nports = 0; + ss_instances = NULL; if (NULL != emsg) *emsg = NULL; + /* Remove sections for shared services */ + for (cnt = 0; cnt < system->n_shared_services; cnt++) + { + ss = system->shared_services[cnt]; + GNUNET_CONFIGURATION_remove_section (cfg, ss->sname); + } if (GNUNET_OK != GNUNET_TESTING_configuration_create_ (system, cfg, &ports, &nports)) { @@ -996,6 +1220,15 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, goto err_ret; } GNUNET_DISK_file_close (fd); + ss_instances = GNUNET_malloc (sizeof (struct SharedServiceInstance *) + * system->n_shared_services); + for (cnt=0; cnt < system->n_shared_services; cnt++) + { + ss = system->shared_services[cnt]; + ss_instances[cnt] = associate_shared_service (system, ss, cfg); + if (NULL == ss_instances[cnt]) + goto err_ret; + } GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "DEFAULTCONFIG", &config_filename)); @@ -1010,6 +1243,7 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, goto err_ret; } peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer)); + peer->ss_instances = ss_instances; peer->cfgfile = config_filename; /* Free in peer_destroy */ peer->cfg = GNUNET_CONFIGURATION_dup (cfg); libexec_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm"); @@ -1029,6 +1263,7 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, return peer; err_ret: + GNUNET_free_non_null (ss_instances); GNUNET_free_non_null (ports); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_); if (NULL != emsg) @@ -1071,12 +1306,23 @@ GNUNET_TESTING_peer_get_identity (struct GNUNET_TESTING_Peer *peer, int GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer) { + struct SharedServiceInstance *i; + unsigned int cnt; + if (NULL != peer->main_process) { GNUNET_break (0); return GNUNET_SYSERR; - } + } GNUNET_assert (NULL != peer->cfgfile); + for (cnt = 0; cnt < peer->system->n_shared_services; cnt++) + { + i = peer->ss_instances[cnt]; + if ((0 == i->n_refs) + && (GNUNET_SYSERR == start_shared_service_instance (i)) ) + return GNUNET_SYSERR; + i->n_refs++; + } peer->main_process = GNUNET_OS_start_process (PIPE_CONTROL, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, @@ -1167,13 +1413,25 @@ GNUNET_TESTING_peer_service_stop (struct GNUNET_TESTING_Peer *peer, int GNUNET_TESTING_peer_kill (struct GNUNET_TESTING_Peer *peer) { + struct SharedServiceInstance *i; + unsigned int cnt; + if (NULL == peer->main_process) { GNUNET_break (0); return GNUNET_SYSERR; } - return (0 == GNUNET_OS_process_kill (peer->main_process, SIGTERM)) ? - GNUNET_OK : GNUNET_SYSERR; + if (0 != GNUNET_OS_process_kill (peer->main_process, SIGTERM)) + return GNUNET_SYSERR; + for (cnt = 0; cnt < peer->system->n_shared_services; cnt++) + { + i = peer->ss_instances[cnt]; + GNUNET_assert (0 != i->n_refs); + i->n_refs--; + if (0 == i->n_refs) + stop_shared_service_instance (i); + } + return GNUNET_OK; } @@ -1299,6 +1557,7 @@ GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer) GNUNET_free (peer->main_binary); GNUNET_free (peer->args); GNUNET_free_non_null (peer->id); + GNUNET_free_non_null (peer->ss_instances); if (NULL != peer->ports) { for (cnt = 0; cnt < peer->nports; cnt++) @@ -1417,7 +1676,7 @@ GNUNET_TESTING_service_run (const char *testdir, char *libexec_binary; GNUNET_log_setup (testdir, "WARNING", NULL); - system = GNUNET_TESTING_system_create (testdir, "127.0.0.1", NULL); + system = GNUNET_TESTING_system_create (testdir, "127.0.0.1", NULL, NULL); if (NULL == system) return 1; cfg = GNUNET_CONFIGURATION_create (); diff --git a/src/transport/transport-testing.c b/src/transport/transport-testing.c index 8875afb8e..11344b125 100644 --- a/src/transport/transport-testing.c +++ b/src/transport/transport-testing.c @@ -578,7 +578,8 @@ GNUNET_TRANSPORT_TESTING_init () tth = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_TESTING_handle)); /* Init testing the testing lib */ - tth->tl_system = GNUNET_TESTING_system_create ("transport-testing", NULL, NULL); + tth->tl_system = GNUNET_TESTING_system_create ("transport-testing", NULL, + NULL, NULL); if (NULL == tth->tl_system) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to initialize testing library!\n")); -- 2.25.1