X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftesting%2Ftesting.c;h=f4df6d48c45376ee82691f38db44c150502bf63a;hb=f4d040c0f0dd2fef3d73b1f4532c76219f760f75;hp=c4da3a9737acec0808a3803bace29e794c8dc008;hpb=e96eef7d4ed698a008e66e9a24cfff360361ef74;p=oweals%2Fgnunet.git diff --git a/src/testing/testing.c b/src/testing/testing.c index c4da3a973..f4df6d48c 100644 --- a/src/testing/testing.c +++ b/src/testing/testing.c @@ -19,7 +19,7 @@ */ /** - * @file testing/testing_new.c + * @file testing/testing.c * @brief convenience API for writing testcases for GNUnet * Many testcases need to start and stop a peer/service * and this library is supposed to make that easier @@ -34,13 +34,22 @@ #include "gnunet_testing_lib-new.h" #define LOG(kind,...) \ - GNUNET_log_from (kind, "gnunettestingnew", __VA_ARGS__) + GNUNET_log_from (kind, "testing-api", __VA_ARGS__) +/** + * 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 12000 /** - * Size of a hostkey when written to a file + * 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 HOSTKEYFILESIZE 914 +#define HIGH_PORT 56000 /** @@ -51,8 +60,7 @@ struct GNUNET_TESTING_System { /** * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each - * SERVICEHOME. - */ + * SERVICEHOME. */ char *tmppath; /** @@ -61,10 +69,25 @@ struct GNUNET_TESTING_System char *controller; /** - * Hostkeys data, contains "HOSTKEYFILESIZE * total_hostkeys" bytes. + * our hostname + */ + char *hostname; + + /** + * Hostkeys data, contains "GNUNET_TESTING_HOSTKEYFILESIZE * total_hostkeys" bytes. */ char *hostkeys_data; + /** + * memory map for 'hostkeys_data'. + */ + struct GNUNET_DISK_MapHandle *map; + + /** + * File descriptor for the map. + */ + struct GNUNET_DISK_FileHandle *map_fd; + /** * Bitmap where each TCP port that has already been reserved for * some GNUnet peer is recorded. Note that we additionally need to @@ -101,6 +124,16 @@ struct GNUNET_TESTING_System * The number of hostkeys */ uint32_t total_hostkeys; + + /** + * Lowest port we are allowed to use. + */ + uint16_t lowport; + + /** + * Highest port we are allowed to use. + */ + uint16_t highport; }; @@ -109,6 +142,10 @@ struct GNUNET_TESTING_System */ struct GNUNET_TESTING_Peer { + /** + * The TESTING system associated with this peer + */ + struct GNUNET_TESTING_System *system; /** * Path to the configuration file for this peer. @@ -128,60 +165,173 @@ struct GNUNET_TESTING_Peer * peer/service is currently not running. */ struct GNUNET_OS_Process *main_process; + + /** + * The keynumber of this peer's hostkey + */ + uint32_t key_number; }; /** - * 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). + * Testing includes a number of pre-created hostkeys for faster peer + * startup. This function loads such keys into memory from a file. + * + * @param system the testing system handle + * @return GNUNET_OK on success; GNUNET_SYSERR on error */ -#define LOW_PORT 12000 +static int +hostkeys_load (struct GNUNET_TESTING_System *system) +{ + uint64_t fs; + char *data_dir; + char *filename; + + GNUNET_assert (NULL == system->hostkeys_data); + data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); + GNUNET_asprintf (&filename, "%s/testing_hostkeys.dat", data_dir); + GNUNET_free (data_dir); + + if (GNUNET_YES != GNUNET_DISK_file_test (filename)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Hostkeys file not found: %s\n"), filename); + GNUNET_free (filename); + return GNUNET_SYSERR; + } + /* Check hostkey file size, read entire thing into memory */ + if (GNUNET_OK != + GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES)) + fs = 0; + if (0 == fs) + { + GNUNET_free (filename); + return GNUNET_SYSERR; /* File is empty */ + } + if (0 != (fs % GNUNET_TESTING_HOSTKEYFILESIZE)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Incorrect hostkey file format: %s\n"), filename); + GNUNET_free (filename); + return GNUNET_SYSERR; + } + system->map_fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, + GNUNET_DISK_PERM_NONE); + if (NULL == system->map_fd) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename); + GNUNET_free (filename); + return GNUNET_SYSERR; + } + system->total_hostkeys = fs / GNUNET_TESTING_HOSTKEYFILESIZE; + system->hostkeys_data = GNUNET_DISK_file_map (system->map_fd, + &system->map, + GNUNET_DISK_MAP_TYPE_READ, + fs); + GNUNET_free (filename); + return GNUNET_OK; +} /** - * 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). + * Function to remove the loaded hostkeys + * + * @param system the testing system handle */ -#define HIGH_PORT 56000 +static void +hostkeys_unload (struct GNUNET_TESTING_System *system) +{ + GNUNET_break (NULL != system->hostkeys_data); + system->hostkeys_data = NULL; + GNUNET_DISK_file_unmap (system->map); + system->map = NULL; + GNUNET_DISK_file_close (system->map_fd); + system->map_fd = NULL; + system->hostkeys_data = NULL; + system->total_hostkeys = 0; +} /** * Create a system handle. There must only be one system * handle per operating system. * - * @param tmppath prefix path to use for all service homes + * @param testdir only the directory name without any path. This is used for + * all service homes; the directory will be created in a temporary + * location depending on the underlying OS * @param controller hostname of the controlling host, * service configurations are modified to allow * control connections from this host; can be NULL + * @param hostname the hostname of the system we are using for testing; NULL for + * localhost + * @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 */ struct GNUNET_TESTING_System * -GNUNET_TESTING_system_create (const char *tmppath, - const char *controller) +GNUNET_TESTING_system_create_with_portrange (const char *testdir, + const char *controller, + const char *hostname, + uint16_t lowport, + uint16_t highport) { struct GNUNET_TESTING_System *system; - if (NULL == tmppath) + GNUNET_assert (NULL != testdir); + system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System)); + system->tmppath = GNUNET_DISK_mkdtemp (testdir); + system->lowport = lowport; + system->highport = highport; + if (NULL == system->tmppath) { - LOG (GNUNET_ERROR_TYPE_ERROR, _("tmppath cannot be NULL\n")); + GNUNET_free (system); return NULL; } - system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System)); - system->tmppath = GNUNET_strdup (tmppath); if (NULL != controller) system->controller = GNUNET_strdup (controller); + if (NULL != hostname) + system->hostname = GNUNET_strdup (hostname); + if (GNUNET_OK != hostkeys_load (system)) + { + GNUNET_TESTING_system_destroy (system, GNUNET_YES); + return NULL; + } return system; } +/** + * 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 + * availability. + * + * @param testdir only the directory name without any path. This is used for all + * service homes; the directory will be created in a temporary location + * depending on the underlying OS + * @param controller hostname of the controlling host, service configurations + * are modified to allow control connections from this host; can be NULL + * @param hostname the hostname of the system we are using for testing; NULL for + * localhost + * @return handle to this system, NULL on error + */ +struct GNUNET_TESTING_System * +GNUNET_TESTING_system_create (const char *testdir, + const char *controller, + const char *hostname) +{ + return GNUNET_TESTING_system_create_with_portrange (testdir, + controller, + hostname, + LOW_PORT, + HIGH_PORT); +} + + /** * Free system resources. * * @param system system to be freed - * @param remove_paths should the 'tmppath' and all subdirectories + * @param remove_paths should the 'testdir' and all subdirectories * be removed (clean up on shutdown)? */ void @@ -189,14 +339,12 @@ GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system, int remove_paths) { if (NULL != system->hostkeys_data) - { - GNUNET_break (0); /* Use GNUNET_TESTING_hostkeys_unload() */ - GNUNET_TESTING_hostkeys_unload (system); - } + hostkeys_unload (system); if (GNUNET_YES == remove_paths) GNUNET_DISK_directory_remove (system->tmppath); GNUNET_free (system->tmppath); GNUNET_free_non_null (system->controller); + GNUNET_free_non_null (system->hostname); GNUNET_free (system); } @@ -215,6 +363,7 @@ GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system, struct GNUNET_NETWORK_Handle *socket; struct addrinfo hint; struct addrinfo *ret; + struct addrinfo *ai; uint32_t *port_buckets; char *open_port_str; int bind_status; @@ -242,12 +391,12 @@ GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system, hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* Wild card address */ port_buckets = (GNUNET_YES == is_tcp) ? system->reserved_tcp_ports : system->reserved_udp_ports; - for (index = (LOW_PORT / 32) + 1; index < (HIGH_PORT / 32); index++) + for (index = (system->lowport / 32) + 1; index < (system->highport / 32); index++) { xor_image = (UINT32_MAX ^ port_buckets[index]); if (0 == xor_image) /* Ports in the bucket are full */ continue; - pos = 0; + pos = system->lowport % 32; while (pos < 32) { if (0 == ((xor_image >> pos) & 1U)) @@ -256,22 +405,30 @@ GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system, continue; } open_port = (index * 32) + pos; + if (open_port >= system->highport) + return 0; GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port); ret = NULL; GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret)); - GNUNET_free (open_port_str); - socket = GNUNET_NETWORK_socket_create (ret->ai_family, - (GNUNET_YES == is_tcp) ? - SOCK_STREAM : SOCK_DGRAM, - 0); - GNUNET_assert (NULL != socket); - bind_status = GNUNET_NETWORK_socket_bind (socket, - ret->ai_addr, - ret->ai_addrlen); - freeaddrinfo (ret); - GNUNET_NETWORK_socket_close (socket); - socket = NULL; + GNUNET_free (open_port_str); + bind_status = GNUNET_NO; + for (ai = ret; NULL != ai; ai = ai->ai_next) + { + socket = GNUNET_NETWORK_socket_create (ai->ai_family, + (GNUNET_YES == is_tcp) ? + SOCK_STREAM : SOCK_DGRAM, + 0); + if (NULL == socket) + continue; + bind_status = GNUNET_NETWORK_socket_bind (socket, + ai->ai_addr, + ai->ai_addrlen); + GNUNET_NETWORK_socket_close (socket); + if (GNUNET_OK != bind_status) + break; + } port_buckets[index] |= (1U << pos); /* Set the port bit */ + freeaddrinfo (ret); if (GNUNET_OK == bind_status) { LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -335,75 +492,6 @@ reserve_path (struct GNUNET_TESTING_System *system) } -/** - * Testing includes a number of pre-created hostkeys for faster peer - * startup. This function loads such keys into memory from a file. - * - * @param system the testing system handle - * @param filename the path of the hostkeys file - * @return GNUNET_OK on success; GNUNET_SYSERR on error - */ -int -GNUNET_TESTING_hostkeys_load (struct GNUNET_TESTING_System *system, - const char *filename) -{ - struct GNUNET_DISK_FileHandle *fd; - uint64_t fs; - - if (GNUNET_YES != GNUNET_DISK_file_test (filename)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Hostkeys file not found: %s\n"), filename); - return GNUNET_SYSERR; - } - /* Check hostkey file size, read entire thing into memory */ - fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, - GNUNET_DISK_PERM_NONE); - if (NULL == fd) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Could not open hostkeys file: %s\n"), filename); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES)) - fs = 0; - if (0 == fs) - { - GNUNET_DISK_file_close (fd); - return GNUNET_SYSERR; /* File is empty */ - } - if (0 != (fs % HOSTKEYFILESIZE)) - { - GNUNET_DISK_file_close (fd); - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Incorrect hostkey file format: %s\n"), filename); - return GNUNET_SYSERR; - } - GNUNET_break (NULL == system->hostkeys_data); - system->total_hostkeys = fs / HOSTKEYFILESIZE; - system->hostkeys_data = GNUNET_malloc_large (fs); /* free in hostkeys_unload */ - GNUNET_assert (fs == GNUNET_DISK_file_read (fd, system->hostkeys_data, fs)); - GNUNET_DISK_file_close (fd); - return GNUNET_OK; -} - - -/** - * Function to remove the loaded hostkeys - * - * @param system the testing system handle - */ -void -GNUNET_TESTING_hostkeys_unload (struct GNUNET_TESTING_System *system) -{ - GNUNET_break (NULL != system->hostkeys_data); - GNUNET_free_non_null (system->hostkeys_data); - system->hostkeys_data = NULL; - system->total_hostkeys = 0; -} - - /** * Testing includes a number of pre-created hostkeys for * faster peer startup. This function can be used to @@ -419,9 +507,9 @@ GNUNET_TESTING_hostkeys_unload (struct GNUNET_TESTING_System *system) * @param key_number desired pre-created hostkey to obtain * @param id set to the peer's identity (hash of the public * key; if NULL, GNUNET_SYSERR is returned immediately - * @return GNUNET_SYSERR on error (not enough keys) + * @return NULL on error (not enough keys) */ -int +struct GNUNET_CRYPTO_RsaPrivateKey * GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system, uint32_t key_number, struct GNUNET_PeerIdentity *id) @@ -430,28 +518,28 @@ GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system, struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key; if ((NULL == id) || (NULL == system->hostkeys_data)) - return GNUNET_SYSERR; + return NULL; if (key_number >= system->total_hostkeys) { LOG (GNUNET_ERROR_TYPE_ERROR, _("Key number %u does not exist\n"), key_number); - return GNUNET_SYSERR; + return NULL; } private_key = GNUNET_CRYPTO_rsa_decode_key (system->hostkeys_data + - (key_number * HOSTKEYFILESIZE), - HOSTKEYFILESIZE); + (key_number * + GNUNET_TESTING_HOSTKEYFILESIZE), + GNUNET_TESTING_HOSTKEYFILESIZE); if (NULL == private_key) { LOG (GNUNET_ERROR_TYPE_ERROR, _("Error while decoding key %u\n"), key_number); - return GNUNET_SYSERR; + return NULL; } GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key); GNUNET_CRYPTO_hash (&public_key, sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), &(id->hashPubKey)); - GNUNET_CRYPTO_rsa_key_free (private_key); - return GNUNET_OK; + return private_key; } @@ -564,9 +652,9 @@ update_config (void *cls, const char *section, const char *option, GNUNET_break(0); /* FIXME */ } } - if ((0 == strcmp (option, "HOSTNAME")) && (NULL != uc->system->controller)) + if (0 == strcmp (option, "HOSTNAME")) { - value = uc->system->controller; + value = (NULL == uc->system->hostname) ? "localhost" : uc->system->hostname; } GNUNET_free (single_variable); GNUNET_free (per_host_variable); @@ -575,7 +663,8 @@ update_config (void *cls, const char *section, const char *option, /** - * Section iterator to set ACCEPT_FROM in all sections + * Section iterator to set ACCEPT_FROM/ACCEPT_FROM6 depending on the ip of the + * controller in all sections * * @param cls the UpdateContext * @param section name of the section @@ -584,12 +673,80 @@ static void update_config_sections (void *cls, const char *section) { - struct UpdateContext *uc = cls; + struct UpdateContext *uc = cls; + char **ikeys; + char *val; + char *ptr; char *orig_allowed_hosts; char *allowed_hosts; - + char *ACCEPT_FROM_key; + uint16_t ikeys_cnt; + uint16_t key; + + ikeys_cnt = 0; + val = NULL; + if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section, + "TESTING_IGNORE_KEYS")) + { + GNUNET_assert + (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, + "TESTING_IGNORE_KEYS", &val)); + ptr = val; + for (ikeys_cnt = 0; NULL != (ptr = strstr (ptr, ";")); ikeys_cnt++) + ptr++; + if (0 == ikeys_cnt) + GNUNET_break (0); + else + { + ikeys = GNUNET_malloc ((sizeof (char *)) * ikeys_cnt); + ptr = val; + for (key = 0; key < ikeys_cnt; key++) + { + ikeys[key] = ptr; + ptr = strstr (ptr, ";"); + *ptr = '\0'; + ptr++; + } + } + } + if (0 != ikeys_cnt) + { + for (key = 0; key < ikeys_cnt; key++) + { + if (NULL != strstr (ikeys[key], "ADVERTISED_PORT")) + break; + } + if ((key == ikeys_cnt) && + (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section, + "ADVERTISED_PORT"))) + { + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "PORT", &ptr)) + { + GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, + "ADVERTISED_PORT", ptr); + GNUNET_free (ptr); + } + } + for (key = 0; key < ikeys_cnt; key++) + { + if (NULL != strstr (ikeys[key], "ACCEPT_FROM")) + { + GNUNET_free (ikeys); + GNUNET_free (val); + return; + } + } + GNUNET_free (ikeys); + } + GNUNET_free_non_null (val); + ACCEPT_FROM_key = "ACCEPT_FROM"; + if ((NULL != uc->system->controller) && + (NULL != strstr (uc->system->controller, ":"))) /* IPv6 in use */ + ACCEPT_FROM_key = "ACCEPT_FROM6"; if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "ACCEPT_FROM", + GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, ACCEPT_FROM_key, &orig_allowed_hosts)) { orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;"); @@ -600,16 +757,18 @@ update_config_sections (void *cls, GNUNET_asprintf (&allowed_hosts, "%s%s;", orig_allowed_hosts, uc->system->controller); GNUNET_free (orig_allowed_hosts); - GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, "ACCEPT_FROM", + GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, ACCEPT_FROM_key, allowed_hosts); GNUNET_free (allowed_hosts); } /** - * Create a new configuration using the given configuration - * as a template; ports and paths will be modified to select - * available ports on the local system. If we run + * Create a new configuration using the given configuration as a template; + * ports and paths will be modified to select available ports on the local + * system. The default configuration will be available in PATHS section under + * the option DEFAULTCONFIG after the call. SERVICE_HOME is also set in PATHS + * section to the temporary directory specific to this configuration. If we run * out of "*port" numbers, return SYSERR. * * This is primarily a helper function used internally @@ -660,7 +819,8 @@ GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system, * changes in port numbers and paths) * @param key_number number of the hostkey to use for the peer * @param id identifier for the daemon, will be set, can be NULL - * @param emsg set to error message (set to NULL on success), can be NULL + * @param emsg set to freshly allocated error message (set to NULL on success), + * can be NULL * @return handle to the peer, NULL on error */ struct GNUNET_TESTING_Peer * @@ -676,6 +836,7 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, char hostkey_filename[128]; char *config_filename; char *emsg_; + struct GNUNET_CRYPTO_RsaPrivateKey *pk; if (NULL != emsg) *emsg = NULL; @@ -683,7 +844,7 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, { GNUNET_asprintf (&emsg_, _("Failed to create configuration for peer (not enough free ports?)\n")); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_); if (NULL != emsg) *emsg = emsg_; else @@ -695,26 +856,29 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, GNUNET_asprintf (&emsg_, _("You attempted to create a testbed with more than %u hosts. Please precompute more hostkeys first.\n"), (unsigned int) system->total_hostkeys); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_); if (NULL != emsg) *emsg = emsg_; else GNUNET_free (emsg_); return NULL; } + pk = NULL; if ((NULL != id) && - (GNUNET_SYSERR == GNUNET_TESTING_hostkey_get (system, key_number, id))) + (NULL == (pk = GNUNET_TESTING_hostkey_get (system, key_number, id)))) { GNUNET_asprintf (&emsg_, _("Failed to initialize hostkey for peer %u\n"), (unsigned int) key_number); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_); if (NULL != emsg) *emsg = emsg_; else GNUNET_free (emsg_); return NULL; } + if (NULL != pk) + GNUNET_CRYPTO_rsa_key_free (pk); GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME", @@ -731,16 +895,16 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, GNUNET_break (0); return NULL; } - if (HOSTKEYFILESIZE != + if (GNUNET_TESTING_HOSTKEYFILESIZE != GNUNET_DISK_file_write (fd, system->hostkeys_data - + (key_number * HOSTKEYFILESIZE), - HOSTKEYFILESIZE)) + + (key_number * GNUNET_TESTING_HOSTKEYFILESIZE), + GNUNET_TESTING_HOSTKEYFILESIZE)) { GNUNET_asprintf (&emsg_, _("Failed to write hostkey file for peer %u: %s\n"), (unsigned int) key_number, STRERROR (errno)); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_); if (NULL != emsg) *emsg = emsg_; else @@ -759,7 +923,7 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, config_filename, (unsigned int) key_number, STRERROR (errno)); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_); if (NULL != emsg) *emsg = emsg_; else @@ -769,11 +933,29 @@ GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system, } peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer)); peer->cfgfile = config_filename; /* Free in peer_destroy */ - peer->main_binary = GNUNET_strdup ("gnunet-service-arm"); + peer->main_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm"); + peer->system = system; + peer->key_number = key_number; return peer; } +/** + * Obtain the peer identity from a peer handle. + * + * @param peer peer handle for which we want the peer's identity + * @param id identifier for the daemon, will be set + */ +void +GNUNET_TESTING_peer_get_identity (const struct GNUNET_TESTING_Peer *peer, + struct GNUNET_PeerIdentity *id) +{ + GNUNET_CRYPTO_rsa_key_free (GNUNET_TESTING_hostkey_get (peer->system, + peer->key_number, + id)); +} + + /** * Start the peer. * @@ -789,7 +971,7 @@ GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer) return GNUNET_SYSERR; } GNUNET_assert (NULL != peer->cfgfile); - peer->main_process = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, + peer->main_process = GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, peer->main_binary, peer->main_binary, "-c", @@ -857,7 +1039,9 @@ GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer) * and should thus be called directly from "main". The testcase * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'. * - * @param tmppath path for storing temporary data for the test + * @param testdir only the directory name without any path. This is used for + * all service homes; the directory will be created in a temporary + * location depending on the underlying OS * @param cfgfilename name of the configuration file to use; * use NULL to only run with defaults * @param tm main function of the testcase @@ -865,12 +1049,12 @@ GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer) * @return 0 on success, 1 on error */ int -GNUNET_TESTING_peer_run (const char *tmppath, +GNUNET_TESTING_peer_run (const char *testdir, const char *cfgfilename, GNUNET_TESTING_TestMain tm, void *tm_cls) { - return GNUNET_TESTING_service_run (tmppath, "arm", + return GNUNET_TESTING_service_run (testdir, "arm", cfgfilename, tm, tm_cls); } @@ -889,6 +1073,11 @@ struct ServiceContext * Callback to signal service startup */ GNUNET_TESTING_TestMain tm; + + /** + * The peer in which the service is run. + */ + struct GNUNET_TESTING_Peer *peer; /** * Closure for the above callback @@ -909,7 +1098,7 @@ service_run_main (void *cls, { struct ServiceContext *sc = cls; - sc->tm (sc->tm_cls, sc->cfg); + sc->tm (sc->tm_cls, sc->cfg, sc->peer); } @@ -924,7 +1113,9 @@ service_run_main (void *cls, * This function is useful if the testcase is for a single service * and if that service doesn't itself depend on other services. * - * @param tmppath path for storing temporary data for the test + * @param testdir only the directory name without any path. This is used for + * all service homes; the directory will be created in a temporary + * location depending on the underlying OS * @param service_name name of the service to run * @param cfgfilename name of the configuration file to use; * use NULL to only run with defaults @@ -933,7 +1124,7 @@ service_run_main (void *cls, * @return 0 on success, 1 on error */ int -GNUNET_TESTING_service_run (const char *tmppath, +GNUNET_TESTING_service_run (const char *testdir, const char *service_name, const char *cfgfilename, GNUNET_TESTING_TestMain tm, @@ -943,25 +1134,12 @@ GNUNET_TESTING_service_run (const char *tmppath, struct GNUNET_TESTING_System *system; struct GNUNET_TESTING_Peer *peer; struct GNUNET_CONFIGURATION_Handle *cfg; - char *data_dir; - char *hostkeys_file; - - data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); - GNUNET_asprintf (&hostkeys_file, "%s/testing_hostkeys.dat", data_dir); - GNUNET_free (data_dir); - system = GNUNET_TESTING_system_create (tmppath, "127.0.0.1"); + char *binary; + + GNUNET_log_setup (testdir, "WARNING", NULL); + system = GNUNET_TESTING_system_create (testdir, "127.0.0.1", NULL); if (NULL == system) - { - GNUNET_free (hostkeys_file); return 1; - } - if (GNUNET_OK != GNUNET_TESTING_hostkeys_load (system, hostkeys_file)) - { - GNUNET_free (hostkeys_file); - GNUNET_TESTING_system_destroy (system, GNUNET_YES); - return 1; - } - GNUNET_free (hostkeys_file); cfg = GNUNET_CONFIGURATION_create (); if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename)) { @@ -975,13 +1153,14 @@ GNUNET_TESTING_service_run (const char *tmppath, if (NULL == peer) { GNUNET_CONFIGURATION_destroy (cfg); - GNUNET_TESTING_hostkeys_unload (system); + hostkeys_unload (system); GNUNET_TESTING_system_destroy (system, GNUNET_YES); return 1; } - GNUNET_TESTING_hostkeys_unload (system); GNUNET_free (peer->main_binary); - GNUNET_asprintf (&peer->main_binary, "gnunet-service-%s", service_name); + GNUNET_asprintf (&binary, "gnunet-service-%s", service_name); + peer->main_binary = GNUNET_OS_get_libexec_binary_path (binary); + GNUNET_free (binary); if (GNUNET_OK != GNUNET_TESTING_peer_start (peer)) { GNUNET_TESTING_peer_destroy (peer); @@ -992,8 +1171,10 @@ GNUNET_TESTING_service_run (const char *tmppath, sc.cfg = cfg; sc.tm = tm; sc.tm_cls = tm_cls; + sc.peer = peer; GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */ - if (GNUNET_OK != GNUNET_TESTING_peer_stop (peer)) + if ((NULL != peer->main_process) && + (GNUNET_OK != GNUNET_TESTING_peer_stop (peer))) { GNUNET_TESTING_peer_destroy (peer); GNUNET_CONFIGURATION_destroy (cfg); @@ -1007,4 +1188,37 @@ GNUNET_TESTING_service_run (const char *tmppath, } -/* end of testing_new.c */ +/** + * Sometimes we use the binary name to determine which specific + * test to run. In those cases, the string after the last "_" + * in 'argv[0]' specifies a string that determines the configuration + * file or plugin to use. + * + * This function returns the respective substring, taking care + * of issues such as binaries ending in '.exe' on W32. + * + * @param argv0 the name of the binary + * @return string between the last '_' and the '.exe' (or the end of the string), + * NULL if argv0 has no '_' + */ +char * +GNUNET_TESTING_get_testname_from_underscore (const char *argv0) +{ + size_t slen = strlen (argv0) + 1; + char sbuf[slen]; + char *ret; + char *dot; + + memcpy (sbuf, argv0, slen); + ret = strrchr (sbuf, '_'); + if (NULL == ret) + return NULL; + ret++; /* skip underscore */ + dot = strchr (ret, '.'); + if (NULL != dot) + *dot = '\0'; + return GNUNET_strdup (ret); +} + + +/* end of testing.c */