2 This file is part of GNUnet
3 Copyright (C) 2008, 2009, 2012 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file testing/testing.c
23 * @brief convenience API for writing testcases for GNUnet
24 * Many testcases need to start and stop a peer/service
25 * and this library is supposed to make that easier
26 * for TESTCASES. Normal programs should always
27 * use functions from gnunet_{util,arm}_lib.h. This API is
28 * ONLY for writing testcases (or internal use of the testbed).
29 * @author Christian Grothoff
33 #include "gnunet_util_lib.h"
34 #include "gnunet_arm_service.h"
35 #include "gnunet_testing_lib.h"
37 #define LOG(kind,...) \
38 GNUNET_log_from (kind, "testing-api", __VA_ARGS__)
42 * We need pipe control only on WINDOWS
45 #define PIPE_CONTROL GNUNET_YES
47 #define PIPE_CONTROL GNUNET_NO
52 * Lowest port used for GNUnet testing. Should be high enough to not
53 * conflict with other applications running on the hosts but be low
54 * enough to not conflict with client-ports (typically starting around
57 #define LOW_PORT 12000
60 * Highest port used for GNUnet testing. Should be low enough to not
61 * conflict with the port range for "local" ports (client apps; see
62 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
64 #define HIGH_PORT 56000
67 struct SharedServiceInstance
69 struct SharedService *ss;
73 struct GNUNET_OS_Process *proc;
86 struct SharedServiceInstance **instances;
88 struct GNUNET_CONFIGURATION_Handle *cfg;
94 unsigned int n_instances;
99 * Handle for a system on which GNUnet peers are executed;
100 * a system is used for reserving unique paths and ports.
102 struct GNUNET_TESTING_System
105 * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
111 * The trusted ip. Can either be a single ip address or a network address in
122 * Hostkeys data, contains "GNUNET_TESTING_HOSTKEYFILESIZE * total_hostkeys" bytes.
127 * memory map for @e hostkeys_data.
129 struct GNUNET_DISK_MapHandle *map;
131 struct SharedService **shared_services;
133 unsigned int n_shared_services;
136 * Bitmap where each port that has already been reserved for some GNUnet peer
137 * is recorded. Note that we make no distinction between TCP and UDP ports
138 * and test if a port is already in use before assigning it to a peer/service.
139 * If we detect that a port is already in use, we also mark it in this bitmap.
140 * So all the bits that are zero merely indicate ports that MIGHT be available
143 uint32_t reserved_ports[65536 / 32];
146 * Counter we use to make service home paths unique on this system;
147 * the full path consists of the tmppath and this number. Each
148 * UNIXPATH for a peer is also modified to include the respective
149 * path counter to ensure uniqueness. This field is incremented
150 * by one for each configured peer. Even if peers are destroyed,
151 * we never re-use path counters.
153 uint32_t path_counter;
156 * The number of hostkeys
158 uint32_t total_hostkeys;
161 * Lowest port we are allowed to use.
166 * Highest port we are allowed to use.
173 * Handle for a GNUnet peer controlled by testing.
175 struct GNUNET_TESTING_Peer
178 * The TESTING system associated with this peer
180 struct GNUNET_TESTING_System *system;
183 * Path to the configuration file for this peer.
188 * Binary to be executed during 'GNUNET_TESTING_peer_start'.
189 * Typically 'gnunet-service-arm' (but can be set to a
190 * specific service by 'GNUNET_TESTING_service_run' if
197 * Handle to the running binary of the service, NULL if the
198 * peer/service is currently not running.
200 struct GNUNET_OS_Process *main_process;
203 * The handle to the peer's ARM service
205 struct GNUNET_ARM_Handle *ah;
208 * The config of the peer
210 struct GNUNET_CONFIGURATION_Handle *cfg;
213 * The callback to call asynchronously when a peer is stopped
215 GNUNET_TESTING_PeerStopCallback cb;
218 * The closure for the above callback
223 * The cached identity of this peer. Will be populated on call to
224 * GNUNET_TESTING_peer_get_identity()
226 struct GNUNET_PeerIdentity *id;
228 struct SharedServiceInstance **ss_instances;
231 * Array of ports currently allocated to this peer. These ports will be
232 * released upon peer destroy and can be used by other peers which are
238 * The number of ports in the above array
243 * The keynumber of this peer's hostkey
250 * Testing includes a number of pre-created hostkeys for faster peer
251 * startup. This function loads such keys into memory from a file.
253 * @param system the testing system handle
254 * @return #GNUNET_OK on success; #GNUNET_SYSERR on error
257 hostkeys_load (struct GNUNET_TESTING_System *system)
262 struct GNUNET_DISK_FileHandle *fd;
264 GNUNET_assert (NULL == system->hostkeys_data);
265 data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
266 GNUNET_asprintf (&filename, "%s/testing_hostkeys.ecc", data_dir);
267 GNUNET_free (data_dir);
269 if (GNUNET_YES != GNUNET_DISK_file_test (filename))
271 LOG (GNUNET_ERROR_TYPE_ERROR,
272 _("Hostkeys file not found: %s\n"), filename);
273 GNUNET_free (filename);
274 return GNUNET_SYSERR;
276 /* Check hostkey file size, read entire thing into memory */
278 GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
282 GNUNET_free (filename);
283 return GNUNET_SYSERR; /* File is empty */
285 if (0 != (fs % GNUNET_TESTING_HOSTKEYFILESIZE))
287 LOG (GNUNET_ERROR_TYPE_ERROR,
288 _("Incorrect hostkey file format: %s\n"), filename);
289 GNUNET_free (filename);
290 return GNUNET_SYSERR;
292 fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
293 GNUNET_DISK_PERM_NONE);
296 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename);
297 GNUNET_free (filename);
298 return GNUNET_SYSERR;
300 GNUNET_free (filename);
301 system->hostkeys_data = GNUNET_DISK_file_map (fd,
303 GNUNET_DISK_MAP_TYPE_READ,
305 GNUNET_DISK_file_close (fd);
306 if (NULL == system->hostkeys_data)
307 return GNUNET_SYSERR;
308 system->total_hostkeys = fs / GNUNET_TESTING_HOSTKEYFILESIZE;
314 * Function to remove the loaded hostkeys
316 * @param system the testing system handle
319 hostkeys_unload (struct GNUNET_TESTING_System *system)
321 GNUNET_break (NULL != system->hostkeys_data);
322 system->hostkeys_data = NULL;
323 GNUNET_DISK_file_unmap (system->map);
325 system->hostkeys_data = NULL;
326 system->total_hostkeys = 0;
331 * Function to iterate over options.
334 * @param section name of the section
335 * @param option name of the option
336 * @param value value of the option
339 cfg_copy_iterator (void *cls, const char *section,
340 const char *option, const char *value)
342 struct GNUNET_CONFIGURATION_Handle *cfg2 = cls;
344 GNUNET_CONFIGURATION_set_value_string (cfg2, section, option, value);
349 * Create a system handle. There must only be one system
350 * handle per operating system.
352 * @param testdir only the directory name without any path. This is used for
353 * all service homes; the directory will be created in a temporary
354 * location depending on the underlying OS. This variable will be
355 * overridden with the value of the environmental variable
356 * GNUNET_TESTING_PREFIX, if it exists.
357 * @param trusted_ip the ip address which will be set as TRUSTED HOST in all
358 * service configurations generated to allow control connections from
359 * this ip. This can either be a single ip address or a network address
361 * @param hostname the hostname of the system we are using for testing; NULL for
363 * @param shared_services NULL terminated array describing services that are to
364 * be shared among peers
365 * @param lowport lowest port number this system is allowed to allocate (inclusive)
366 * @param highport highest port number this system is allowed to allocate (exclusive)
367 * @return handle to this system, NULL on error
369 struct GNUNET_TESTING_System *
370 GNUNET_TESTING_system_create_with_portrange (const char *testdir,
371 const char *trusted_ip,
372 const char *hostname,
374 GNUNET_TESTING_SharedService *
379 struct GNUNET_TESTING_System *system;
380 struct GNUNET_TESTING_SharedService tss;
381 struct SharedService *ss;
384 GNUNET_assert (NULL != testdir);
385 system = GNUNET_new (struct GNUNET_TESTING_System);
386 if (NULL == (system->tmppath = getenv (GNUNET_TESTING_PREFIX)))
387 system->tmppath = GNUNET_DISK_mkdtemp (testdir);
389 system->tmppath = GNUNET_strdup (system->tmppath);
390 system->lowport = lowport;
391 system->highport = highport;
392 if (NULL == system->tmppath)
394 GNUNET_free (system);
397 if (NULL != trusted_ip)
398 system->trusted_ip = GNUNET_strdup (trusted_ip);
399 if (NULL != hostname)
400 system->hostname = GNUNET_strdup (hostname);
401 if (GNUNET_OK != hostkeys_load (system))
403 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
406 if (NULL == shared_services)
408 for (cnt = 0; NULL != shared_services[cnt].service; cnt++)
410 tss = shared_services[cnt];
411 ss = GNUNET_new (struct SharedService);
412 ss->sname = GNUNET_strdup (tss.service);
413 ss->cfg = GNUNET_CONFIGURATION_create ();
414 GNUNET_CONFIGURATION_iterate_section_values (tss.cfg, ss->sname,
415 &cfg_copy_iterator, ss->cfg);
416 GNUNET_CONFIGURATION_iterate_section_values (tss.cfg, "TESTING",
417 &cfg_copy_iterator, ss->cfg);
418 GNUNET_CONFIGURATION_iterate_section_values (tss.cfg, "PATHS",
419 &cfg_copy_iterator, ss->cfg);
420 ss->share = tss.share;
421 GNUNET_array_append (system->shared_services, system->n_shared_services,
429 * Create a system handle. There must only be one system handle per operating
430 * system. Uses a default range for allowed ports. Ports are still tested for
433 * @param testdir only the directory name without any path. This is used for all
434 * service homes; the directory will be created in a temporary location
435 * depending on the underlying OS. This variable will be
436 * overridden with the value of the environmental variable
437 * GNUNET_TESTING_PREFIX, if it exists.
438 * @param trusted_ip the ip address which will be set as TRUSTED HOST in all
439 * service configurations generated to allow control connections from
440 * this ip. This can either be a single ip address or a network address
442 * @param hostname the hostname of the system we are using for testing; NULL for
444 * @param shared_services NULL terminated array describing services that are to
445 * be shared among peers
446 * @return handle to this system, NULL on error
448 struct GNUNET_TESTING_System *
449 GNUNET_TESTING_system_create (const char *testdir,
450 const char *trusted_ip,
451 const char *hostname,
452 const struct GNUNET_TESTING_SharedService *
455 return GNUNET_TESTING_system_create_with_portrange (testdir,
465 cleanup_shared_service_instance (struct SharedServiceInstance *i)
467 if (NULL != i->cfg_fn)
469 (void) unlink (i->cfg_fn);
470 GNUNET_free (i->cfg_fn);
472 GNUNET_free_non_null (i->unix_sock);
473 GNUNET_free_non_null (i->port_str);
474 GNUNET_break (NULL == i->proc);
475 GNUNET_break (0 == i->n_refs);
481 start_shared_service_instance (struct SharedServiceInstance *i)
484 char *libexec_binary;
486 GNUNET_assert (NULL == i->proc);
487 GNUNET_assert (NULL != i->cfg_fn);
488 (void) GNUNET_asprintf (&binary, "gnunet-service-%s", i->ss->sname);
489 libexec_binary = GNUNET_OS_get_libexec_binary_path (binary);
490 GNUNET_free (binary);
491 i->proc = GNUNET_OS_start_process (PIPE_CONTROL,
492 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
499 GNUNET_free (libexec_binary);
501 return GNUNET_SYSERR;
507 stop_shared_service_instance (struct SharedServiceInstance *i)
509 GNUNET_break (0 == i->n_refs);
510 if (0 != GNUNET_OS_process_kill (i->proc, GNUNET_TERM_SIG))
511 LOG (GNUNET_ERROR_TYPE_WARNING,
512 "Killing shared service instance (%s) failed\n", i->ss->sname);
513 (void) GNUNET_OS_process_wait (i->proc);
514 GNUNET_OS_process_destroy (i->proc);
520 * Free system resources.
522 * @param system system to be freed
523 * @param remove_paths should the 'testdir' and all subdirectories
524 * be removed (clean up on shutdown)?
527 GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
530 struct SharedService *ss;
531 struct SharedServiceInstance *i;
535 if (NULL != system->hostkeys_data)
536 hostkeys_unload (system);
537 for (ss_cnt = 0; ss_cnt < system->n_shared_services; ss_cnt++)
539 ss = system->shared_services[ss_cnt];
540 for (i_cnt = 0; i_cnt < ss->n_instances; i_cnt++)
542 i = ss->instances[i_cnt];
544 stop_shared_service_instance (i);
545 cleanup_shared_service_instance (i);
547 GNUNET_free_non_null (ss->instances);
548 GNUNET_CONFIGURATION_destroy (ss->cfg);
549 GNUNET_free (ss->sname);
552 GNUNET_free_non_null (system->shared_services);
553 if (GNUNET_YES == remove_paths)
554 GNUNET_DISK_directory_remove (system->tmppath);
555 GNUNET_free (system->tmppath);
556 GNUNET_free_non_null (system->trusted_ip);
557 GNUNET_free_non_null (system->hostname);
558 GNUNET_free (system);
563 * Reserve a TCP or UDP port for a peer.
565 * @param system system to use for reservation tracking
566 * @return 0 if no free port was available
569 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system)
571 struct GNUNET_NETWORK_Handle *socket;
572 struct addrinfo hint;
573 struct addrinfo *ret;
575 uint32_t *port_buckets;
584 FIXME: Instead of using getaddrinfo we should try to determine the port
585 status by the following heurestics.
587 On systems which support both IPv4 and IPv6, only ports open on both
588 address families are considered open.
589 On system with either IPv4 or IPv6. A port is considered open if it's
590 open in the respective address family
592 hint.ai_family = AF_UNSPEC; /* IPv4 and IPv6 */
593 hint.ai_socktype = 0;
594 hint.ai_protocol = 0;
597 hint.ai_canonname = NULL;
599 hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* Wild card address */
600 port_buckets = system->reserved_ports;
601 for (index = (system->lowport / 32) + 1; index < (system->highport / 32); index++)
603 xor_image = (UINT32_MAX ^ port_buckets[index]);
604 if (0 == xor_image) /* Ports in the bucket are full */
606 pos = system->lowport % 32;
609 if (0 == ((xor_image >> pos) & 1U))
614 open_port = (index * 32) + pos;
615 if (open_port >= system->highport)
617 GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port);
619 GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
620 GNUNET_free (open_port_str);
621 bind_status = GNUNET_NO;
622 for (ai = ret; NULL != ai; ai = ai->ai_next)
624 socket = GNUNET_NETWORK_socket_create (ai->ai_family, SOCK_STREAM, 0);
627 bind_status = GNUNET_NETWORK_socket_bind (socket,
630 GNUNET_NETWORK_socket_close (socket);
631 if (GNUNET_OK != bind_status)
633 socket = GNUNET_NETWORK_socket_create (ai->ai_family, SOCK_DGRAM, 0);
636 bind_status = GNUNET_NETWORK_socket_bind (socket,
639 GNUNET_NETWORK_socket_close (socket);
640 if (GNUNET_OK != bind_status)
643 port_buckets[index] |= (1U << pos); /* Set the port bit */
645 if (GNUNET_OK == bind_status)
647 LOG (GNUNET_ERROR_TYPE_DEBUG,
648 "Found a free port %u\n", (unsigned int) open_port);
659 * Release reservation of a TCP or UDP port for a peer
660 * (used during #GNUNET_TESTING_peer_destroy()).
662 * @param system system to use for reservation tracking
663 * @param port reserved port to release
666 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
669 uint32_t *port_buckets;
673 port_buckets = system->reserved_ports;
676 LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
677 if (0 == (port_buckets[bucket] & (1U << pos)))
679 GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
682 port_buckets[bucket] &= ~(1U << pos);
687 * Testing includes a number of pre-created hostkeys for
688 * faster peer startup. This function can be used to
689 * access the n-th key of those pre-created hostkeys; note
690 * that these keys are ONLY useful for testing and not
691 * secure as the private keys are part of the public
692 * GNUnet source code.
694 * This is primarily a helper function used internally
695 * by #GNUNET_TESTING_peer_configure.
697 * @param system the testing system handle
698 * @param key_number desired pre-created hostkey to obtain
699 * @param id set to the peer's identity (hash of the public
700 * key; if NULL, NULL is returned immediately
701 * @return NULL on error (not enough keys)
703 struct GNUNET_CRYPTO_EddsaPrivateKey *
704 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
706 struct GNUNET_PeerIdentity *id)
708 struct GNUNET_CRYPTO_EddsaPrivateKey *private_key;
710 if ((NULL == id) || (NULL == system->hostkeys_data))
712 if (key_number >= system->total_hostkeys)
714 LOG (GNUNET_ERROR_TYPE_ERROR,
715 _("Key number %u does not exist\n"),
719 private_key = GNUNET_new (struct GNUNET_CRYPTO_EddsaPrivateKey);
720 GNUNET_memcpy (private_key,
721 system->hostkeys_data +
722 (key_number * GNUNET_TESTING_HOSTKEYFILESIZE),
723 GNUNET_TESTING_HOSTKEYFILESIZE);
724 GNUNET_CRYPTO_eddsa_key_get_public (private_key,
731 * Structure for holding data to build new configurations from a configuration
737 * The system for which we are building configurations
739 struct GNUNET_TESTING_System *system;
742 * The configuration we are building
744 struct GNUNET_CONFIGURATION_Handle *cfg;
747 * The customized service home path for this peer
752 * Array of ports currently allocated to this peer. These ports will be
753 * released upon peer destroy and can be used by other peers which are
759 * The number of ports in the above array
764 * build status - to signal error while building a configuration
771 * Function to iterate over options. Copies
772 * the options to the target configuration,
773 * updating PORT values as needed.
775 * @param cls the UpdateContext
776 * @param section name of the section
777 * @param option name of the option
778 * @param value value of the option
781 update_config (void *cls,
786 struct UpdateContext *uc = cls;
790 char *single_variable;
791 char *per_host_variable;
792 unsigned long long num_per_host;
795 if (GNUNET_OK != uc->status)
797 if (! ((0 == strcmp (option, "PORT"))
798 || (0 == strcmp (option, "UNIXPATH"))
799 || (0 == strcmp (option, "HOSTNAME"))))
801 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
802 GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
803 if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival)))
807 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
810 new_port = GNUNET_TESTING_reserve_port (uc->system);
813 uc->status = GNUNET_SYSERR;
814 GNUNET_free (single_variable);
815 GNUNET_free (per_host_variable);
818 GNUNET_snprintf (cval, sizeof (cval), "%u", new_port);
820 GNUNET_array_append (uc->ports, uc->nports, new_port);
822 else if ((ival != 0) &&
824 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
826 GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
830 /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
831 /* ival + ctx->fdnum % num_per_host); */
833 GNUNET_break (0); /* FIXME */
836 if (0 == strcmp (option, "UNIXPATH"))
839 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
842 GNUNET_snprintf (uval, sizeof (uval), "%s/%s.sock",
843 uc->gnunet_home, section);
846 else if ((GNUNET_YES ==
847 GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
852 GNUNET_break(0); /* FIXME */
855 if (0 == strcmp (option, "HOSTNAME"))
857 value = (NULL == uc->system->hostname) ? "localhost" : uc->system->hostname;
859 GNUNET_free (single_variable);
860 GNUNET_free (per_host_variable);
861 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
866 * Section iterator to set ACCEPT_FROM/ACCEPT_FROM6 to include the address of
867 * 'trusted_hosts' in all sections
869 * @param cls the UpdateContext
870 * @param section name of the section
873 update_config_sections (void *cls,
876 struct UpdateContext *uc = cls;
880 char *orig_allowed_hosts;
882 char *ACCEPT_FROM_key;
888 /* Ignore certain options from sections. See
889 https://gnunet.org/bugs/view.php?id=2476 */
890 if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section,
891 "TESTING_IGNORE_KEYS"))
895 GNUNET_CONFIGURATION_get_value_string (uc->cfg, section,
896 "TESTING_IGNORE_KEYS", &val));
898 for (ikeys_cnt = 0; NULL != (ptr = strstr (ptr, ";")); ikeys_cnt++)
904 ikeys = GNUNET_malloc ((sizeof (char *)) * ikeys_cnt);
906 for (key = 0; key < ikeys_cnt; key++)
909 ptr = strstr (ptr, ";");
910 GNUNET_assert (NULL != ptr); /* worked just before... */
918 for (key = 0; key < ikeys_cnt; key++)
920 if (NULL != strstr (ikeys[key], "ADVERTISED_PORT"))
923 if ((key == ikeys_cnt) &&
924 (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section,
928 GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "PORT", &ptr))
930 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section,
931 "ADVERTISED_PORT", ptr);
935 for (key = 0; key < ikeys_cnt; key++)
937 if (NULL != strstr (ikeys[key], "ACCEPT_FROM"))
946 GNUNET_free_non_null (val);
947 ACCEPT_FROM_key = "ACCEPT_FROM";
948 if ((NULL != uc->system->trusted_ip) &&
949 (NULL != strstr (uc->system->trusted_ip, ":"))) /* IPv6 in use */
950 ACCEPT_FROM_key = "ACCEPT_FROM6";
952 GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, ACCEPT_FROM_key,
953 &orig_allowed_hosts))
955 orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;");
957 if (NULL == uc->system->trusted_ip)
958 allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
960 GNUNET_asprintf (&allowed_hosts, "%s%s;", orig_allowed_hosts,
961 uc->system->trusted_ip);
962 GNUNET_free (orig_allowed_hosts);
963 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, ACCEPT_FROM_key,
965 GNUNET_free (allowed_hosts);
969 static struct SharedServiceInstance *
970 associate_shared_service (struct GNUNET_TESTING_System *system,
971 struct SharedService *ss,
972 struct GNUNET_CONFIGURATION_Handle *cfg)
974 struct SharedServiceInstance *i;
975 struct GNUNET_CONFIGURATION_Handle *temp;
980 if ( ((0 == ss->share) && (NULL == ss->instances))
983 && (ss->n_instances < ((ss->n_peers + ss->share - 1) / ss->share)) ) )
985 i = GNUNET_new (struct SharedServiceInstance);
987 (void) GNUNET_asprintf (&gnunet_home, "%s/shared/%s/%u",
988 system->tmppath, ss->sname, ss->n_instances);
989 (void) GNUNET_asprintf (&i->unix_sock, "%s/sock", gnunet_home);
990 port = GNUNET_TESTING_reserve_port (system);
993 GNUNET_free (gnunet_home);
994 cleanup_shared_service_instance (i);
997 GNUNET_array_append (ss->instances, ss->n_instances, i);
998 temp = GNUNET_CONFIGURATION_dup (ss->cfg);
999 (void) GNUNET_asprintf (&i->port_str, "%u", port);
1000 (void) GNUNET_asprintf (&i->cfg_fn, "%s/config", gnunet_home);
1001 GNUNET_CONFIGURATION_set_value_string (temp, "PATHS", "GNUNET_HOME",
1003 GNUNET_free (gnunet_home);
1004 GNUNET_CONFIGURATION_set_value_string (temp, ss->sname, "UNIXPATH",
1006 GNUNET_CONFIGURATION_set_value_string (temp, ss->sname, "PORT",
1008 if (GNUNET_SYSERR == GNUNET_CONFIGURATION_write (temp, i->cfg_fn))
1010 GNUNET_CONFIGURATION_destroy (temp);
1011 cleanup_shared_service_instance (i);
1014 GNUNET_CONFIGURATION_destroy (temp);
1018 GNUNET_assert (NULL != ss->instances);
1019 GNUNET_assert (0 < ss->n_instances);
1020 i = ss->instances[ss->n_instances - 1];
1022 GNUNET_CONFIGURATION_iterate_section_values(ss->cfg, ss->sname,
1023 &cfg_copy_iterator, cfg);
1024 GNUNET_CONFIGURATION_set_value_string (cfg, ss->sname, "UNIXPATH",
1026 GNUNET_CONFIGURATION_set_value_string (cfg, ss->sname, "PORT", i->port_str);
1032 * Create a new configuration using the given configuration as a template;
1033 * ports and paths will be modified to select available ports on the local
1034 * system. The default configuration will be available in PATHS section under
1035 * the option DEFAULTCONFIG after the call. GNUNET_HOME is also set in PATHS
1036 * section to the temporary directory specific to this configuration. If we run
1037 * out of "*port" numbers, return #GNUNET_SYSERR.
1039 * This is primarily a helper function used internally
1040 * by 'GNUNET_TESTING_peer_configure'.
1042 * @param system system to use to coordinate resource usage
1043 * @param cfg template configuration to update
1044 * @param ports array with port numbers used in the created configuration.
1045 * Will be updated upon successful return. Can be NULL
1046 * @param nports the size of the `ports' array. Will be updated.
1047 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - the configuration will
1048 * be incomplete and should not be used there upon
1051 GNUNET_TESTING_configuration_create_ (struct GNUNET_TESTING_System *system,
1052 struct GNUNET_CONFIGURATION_Handle *cfg,
1054 unsigned int *nports)
1056 struct UpdateContext uc;
1057 char *default_config;
1061 uc.status = GNUNET_OK;
1064 GNUNET_asprintf (&uc.gnunet_home, "%s/%u", system->tmppath,
1065 system->path_counter++);
1066 GNUNET_asprintf (&default_config, "%s/config", uc.gnunet_home);
1067 GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "DEFAULTCONFIG",
1069 GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG",
1071 GNUNET_free (default_config);
1072 GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "GNUNET_HOME",
1074 /* make PORTs and UNIXPATHs unique */
1075 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
1076 /* allow connections to services from system trusted_ip host */
1077 GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
1078 /* enable loopback-based connections between peers */
1079 GNUNET_CONFIGURATION_set_value_string (cfg,
1081 "USE_LOCALADDR", "YES");
1082 GNUNET_free (uc.gnunet_home);
1083 if ((NULL != ports) && (NULL != nports))
1086 *nports = uc.nports;
1089 GNUNET_free_non_null (uc.ports);
1095 * Create a new configuration using the given configuration as a template;
1096 * ports and paths will be modified to select available ports on the local
1097 * system. The default configuration will be available in PATHS section under
1098 * the option DEFAULTCONFIG after the call. GNUNET_HOME is also set in PATHS
1099 * section to the temporary directory specific to this configuration. If we run
1100 * out of "*port" numbers, return #GNUNET_SYSERR.
1102 * This is primarily a helper function used internally
1103 * by #GNUNET_TESTING_peer_configure().
1105 * @param system system to use to coordinate resource usage
1106 * @param cfg template configuration to update
1107 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - the configuration will
1108 * be incomplete and should not be used there upon
1111 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
1112 struct GNUNET_CONFIGURATION_Handle *cfg)
1114 return GNUNET_TESTING_configuration_create_ (system, cfg, NULL, NULL);
1119 * Configure a GNUnet peer. GNUnet must be installed on the local
1120 * system and available in the PATH.
1122 * @param system system to use to coordinate resource usage
1123 * @param cfg configuration to use; will be UPDATED (to reflect needed
1124 * changes in port numbers and paths)
1125 * @param key_number number of the hostkey to use for the peer
1126 * @param id identifier for the daemon, will be set, can be NULL
1127 * @param emsg set to freshly allocated error message (set to NULL on success),
1129 * @return handle to the peer, NULL on error
1131 struct GNUNET_TESTING_Peer *
1132 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
1133 struct GNUNET_CONFIGURATION_Handle *cfg,
1134 uint32_t key_number,
1135 struct GNUNET_PeerIdentity *id,
1138 struct GNUNET_TESTING_Peer *peer;
1139 struct GNUNET_DISK_FileHandle *fd;
1140 char *hostkey_filename;
1141 char *config_filename;
1142 char *libexec_binary;
1144 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1146 struct SharedService *ss;
1147 struct SharedServiceInstance **ss_instances;
1149 unsigned int nports;
1153 ss_instances = NULL;
1156 if (key_number >= system->total_hostkeys)
1158 GNUNET_asprintf (&emsg_,
1159 _("You attempted to create a testbed with more than %u hosts. Please precompute more hostkeys first.\n"),
1160 (unsigned int) system->total_hostkeys);
1165 (NULL == (pk = GNUNET_TESTING_hostkey_get (system, key_number, id))))
1167 GNUNET_asprintf (&emsg_,
1168 _("Failed to initialize hostkey for peer %u\n"),
1169 (unsigned int) key_number);
1175 GNUNET_CONFIGURATION_have_value (cfg, "PEER", "PRIVATE_KEY"))
1177 GNUNET_asprintf (&emsg_,
1178 _("PRIVATE_KEY option in PEER section missing in configuration\n"));
1181 /* Remove sections for shared services */
1182 for (cnt = 0; cnt < system->n_shared_services; cnt++)
1184 ss = system->shared_services[cnt];
1185 GNUNET_CONFIGURATION_remove_section (cfg, ss->sname);
1187 if (GNUNET_OK != GNUNET_TESTING_configuration_create_ (system, cfg,
1190 GNUNET_asprintf (&emsg_,
1191 _("Failed to create configuration for peer "
1192 "(not enough free ports?)\n"));
1195 GNUNET_assert (GNUNET_OK ==
1196 GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER",
1198 &hostkey_filename));
1199 fd = GNUNET_DISK_file_open (hostkey_filename,
1200 GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
1201 GNUNET_DISK_PERM_USER_READ
1202 | GNUNET_DISK_PERM_USER_WRITE);
1205 GNUNET_asprintf (&emsg_, _("Cannot open hostkey file `%s': %s\n"),
1206 hostkey_filename, STRERROR (errno));
1207 GNUNET_free (hostkey_filename);
1210 GNUNET_free (hostkey_filename);
1211 if (GNUNET_TESTING_HOSTKEYFILESIZE !=
1212 GNUNET_DISK_file_write (fd, system->hostkeys_data
1213 + (key_number * GNUNET_TESTING_HOSTKEYFILESIZE),
1214 GNUNET_TESTING_HOSTKEYFILESIZE))
1216 GNUNET_asprintf (&emsg_,
1217 _("Failed to write hostkey file for peer %u: %s\n"),
1218 (unsigned int) key_number,
1220 GNUNET_DISK_file_close (fd);
1223 GNUNET_DISK_file_close (fd);
1224 ss_instances = GNUNET_malloc (sizeof (struct SharedServiceInstance *)
1225 * system->n_shared_services);
1226 for (cnt=0; cnt < system->n_shared_services; cnt++)
1228 ss = system->shared_services[cnt];
1229 ss_instances[cnt] = associate_shared_service (system, ss, cfg);
1230 if (NULL == ss_instances[cnt])
1232 emsg_ = GNUNET_strdup ("FIXME");
1236 GNUNET_assert (GNUNET_OK ==
1237 GNUNET_CONFIGURATION_get_value_filename
1238 (cfg, "PATHS", "DEFAULTCONFIG", &config_filename));
1239 if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
1241 GNUNET_asprintf (&emsg_,
1242 _("Failed to write configuration file `%s' for peer %u: %s\n"),
1244 (unsigned int) key_number,
1246 GNUNET_free (config_filename);
1249 peer = GNUNET_new (struct GNUNET_TESTING_Peer);
1250 peer->ss_instances = ss_instances;
1251 peer->cfgfile = config_filename; /* Free in peer_destroy */
1252 peer->cfg = GNUNET_CONFIGURATION_dup (cfg);
1253 libexec_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
1254 if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "arm", "PREFIX", &peer->main_binary))
1257 GNUNET_asprintf(&peer->main_binary, "%s", libexec_binary);
1258 peer->args = GNUNET_strdup ("");
1262 peer->args = GNUNET_strdup (libexec_binary);
1264 peer->system = system;
1265 peer->key_number = key_number;
1266 GNUNET_free (libexec_binary);
1267 peer->ports = ports; /* Free in peer_destroy */
1268 peer->nports = nports;
1272 GNUNET_free_non_null (ss_instances);
1273 GNUNET_free_non_null (ports);
1274 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
1278 GNUNET_free (emsg_);
1284 * Obtain the peer identity from a peer handle.
1286 * @param peer peer handle for which we want the peer's identity
1287 * @param id identifier for the daemon, will be set
1290 GNUNET_TESTING_peer_get_identity (struct GNUNET_TESTING_Peer *peer,
1291 struct GNUNET_PeerIdentity *id)
1293 if (NULL != peer->id)
1295 GNUNET_memcpy (id, peer->id, sizeof (struct GNUNET_PeerIdentity));
1298 peer->id = GNUNET_new (struct GNUNET_PeerIdentity);
1299 GNUNET_free (GNUNET_TESTING_hostkey_get (peer->system,
1302 GNUNET_memcpy (id, peer->id, sizeof (struct GNUNET_PeerIdentity));
1309 * @param peer peer to start
1310 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (i.e. peer already running)
1313 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
1315 struct SharedServiceInstance *i;
1318 if (NULL != peer->main_process)
1321 return GNUNET_SYSERR;
1323 GNUNET_assert (NULL != peer->cfgfile);
1324 for (cnt = 0; cnt < peer->system->n_shared_services; cnt++)
1326 i = peer->ss_instances[cnt];
1327 if ((0 == i->n_refs)
1328 && (GNUNET_SYSERR == start_shared_service_instance (i)) )
1329 return GNUNET_SYSERR;
1332 peer->main_binary = GNUNET_CONFIGURATION_expand_dollar (peer->cfg, peer->main_binary);
1333 peer->main_process = GNUNET_OS_start_process_s (PIPE_CONTROL,
1334 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
1341 if (NULL == peer->main_process)
1343 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1344 _("Failed to start `%s': %s\n"),
1347 return GNUNET_SYSERR;
1354 * Sends SIGTERM to the peer's main process
1356 * @param peer the handle to the peer
1357 * @return #GNUNET_OK if successful; #GNUNET_SYSERR if the main process is NULL
1358 * or upon any error while sending SIGTERM
1361 GNUNET_TESTING_peer_kill (struct GNUNET_TESTING_Peer *peer)
1363 struct SharedServiceInstance *i;
1366 if (NULL == peer->main_process)
1369 return GNUNET_SYSERR;
1371 if (0 != GNUNET_OS_process_kill (peer->main_process, GNUNET_TERM_SIG))
1372 return GNUNET_SYSERR;
1373 for (cnt = 0; cnt < peer->system->n_shared_services; cnt++)
1375 i = peer->ss_instances[cnt];
1376 GNUNET_assert (0 != i->n_refs);
1379 stop_shared_service_instance (i);
1386 * Waits for a peer to terminate. The peer's main process will also be destroyed.
1388 * @param peer the handle to the peer
1389 * @return #GNUNET_OK if successful; #GNUNET_SYSERR if the main process is NULL
1390 * or upon any error while waiting
1393 GNUNET_TESTING_peer_wait (struct GNUNET_TESTING_Peer *peer)
1397 if (NULL == peer->main_process)
1400 return GNUNET_SYSERR;
1402 ret = GNUNET_OS_process_wait (peer->main_process);
1403 GNUNET_OS_process_destroy (peer->main_process);
1404 peer->main_process = NULL;
1412 * @param peer peer to stop
1413 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1416 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
1418 if (GNUNET_SYSERR == GNUNET_TESTING_peer_kill (peer))
1419 return GNUNET_SYSERR;
1420 if (GNUNET_SYSERR == GNUNET_TESTING_peer_wait (peer))
1421 return GNUNET_SYSERR;
1427 * Function called whenever we connect to or disconnect from ARM.
1429 * @param cls closure
1430 * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected,
1431 * #GNUNET_SYSERR on error.
1434 disconn_status (void *cls,
1437 struct GNUNET_TESTING_Peer *peer = cls;
1439 if (GNUNET_SYSERR == connected)
1441 peer->cb (peer->cb_cls, peer, connected);
1444 if (GNUNET_YES == connected)
1446 GNUNET_break (GNUNET_OK == GNUNET_TESTING_peer_kill (peer));
1449 GNUNET_break (GNUNET_OK == GNUNET_TESTING_peer_wait (peer));
1450 GNUNET_ARM_disconnect (peer->ah);
1452 peer->cb (peer->cb_cls, peer, GNUNET_YES);
1457 * Stop a peer asynchronously using ARM API. Peer's shutdown is signaled
1458 * through the GNUNET_TESTING_PeerStopCallback().
1460 * @param peer the peer to stop
1461 * @param cb the callback to signal peer shutdown
1462 * @param cb_cls closure for the above callback
1463 * @return #GNUNET_OK upon successfully giving the request to the ARM API (this
1464 * does not mean that the peer is successfully stopped); #GNUNET_SYSERR
1468 GNUNET_TESTING_peer_stop_async (struct GNUNET_TESTING_Peer *peer,
1469 GNUNET_TESTING_PeerStopCallback cb,
1472 if (NULL == peer->main_process)
1473 return GNUNET_SYSERR;
1474 peer->ah = GNUNET_ARM_connect (peer->cfg, &disconn_status, peer);
1475 if (NULL == peer->ah)
1476 return GNUNET_SYSERR;
1478 peer->cb_cls = cb_cls;
1484 * Cancel a previous asynchronous peer stop request.
1485 * GNUNET_TESTING_peer_stop_async() should have been called before on the given
1486 * peer. It is an error to call this function if the peer stop callback was
1489 * @param peer the peer on which GNUNET_TESTING_peer_stop_async() was called
1493 GNUNET_TESTING_peer_stop_async_cancel (struct GNUNET_TESTING_Peer *peer)
1495 GNUNET_assert (NULL != peer->ah);
1496 GNUNET_ARM_disconnect (peer->ah);
1502 * Destroy the peer. Releases resources locked during peer configuration.
1503 * If the peer is still running, it will be stopped AND a warning will be
1504 * printed (users of the API should stop the peer explicitly first).
1506 * @param peer peer to destroy
1509 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
1513 if (NULL != peer->main_process)
1514 GNUNET_TESTING_peer_stop (peer);
1515 if (NULL != peer->ah)
1516 GNUNET_ARM_disconnect (peer->ah);
1517 GNUNET_free (peer->cfgfile);
1518 if (NULL != peer->cfg)
1519 GNUNET_CONFIGURATION_destroy (peer->cfg);
1520 GNUNET_free (peer->main_binary);
1521 GNUNET_free (peer->args);
1522 GNUNET_free_non_null (peer->id);
1523 GNUNET_free_non_null (peer->ss_instances);
1524 if (NULL != peer->ports)
1526 for (cnt = 0; cnt < peer->nports; cnt++)
1527 GNUNET_TESTING_release_port (peer->system, peer->ports[cnt]);
1528 GNUNET_free (peer->ports);
1535 * Start a single peer and run a test using the testing library.
1536 * Starts a peer using the given configuration and then invokes the
1537 * given callback. This function ALSO initializes the scheduler loop
1538 * and should thus be called directly from "main". The testcase
1539 * should self-terminate by invoking #GNUNET_SCHEDULER_shutdown().
1541 * @param testdir only the directory name without any path. This is used for
1542 * all service homes; the directory will be created in a temporary
1543 * location depending on the underlying OS
1544 * @param cfgfilename name of the configuration file to use;
1545 * use NULL to only run with defaults
1546 * @param tm main function of the testcase
1547 * @param tm_cls closure for @a tm
1548 * @return 0 on success, 1 on error
1551 GNUNET_TESTING_peer_run (const char *testdir,
1552 const char *cfgfilename,
1553 GNUNET_TESTING_TestMain tm,
1556 return GNUNET_TESTING_service_run (testdir, "arm",
1557 cfgfilename, tm, tm_cls);
1562 * Structure for holding service data
1564 struct ServiceContext
1567 * The configuration of the peer in which the service is run
1569 const struct GNUNET_CONFIGURATION_Handle *cfg;
1572 * Callback to signal service startup
1574 GNUNET_TESTING_TestMain tm;
1577 * The peer in which the service is run.
1579 struct GNUNET_TESTING_Peer *peer;
1582 * Closure for the above callback
1589 * Callback to be called when SCHEDULER has been started
1591 * @param cls the ServiceContext
1594 service_run_main (void *cls)
1596 struct ServiceContext *sc = cls;
1598 sc->tm (sc->tm_cls, sc->cfg, sc->peer);
1603 * Start a single service (no ARM, except of course if the given
1604 * service name is 'arm') and run a test using the testing library.
1605 * Starts a service using the given configuration and then invokes the
1606 * given callback. This function ALSO initializes the scheduler loop
1607 * and should thus be called directly from "main". The testcase
1608 * should self-terminate by invoking #GNUNET_SCHEDULER_shutdown().
1610 * This function is useful if the testcase is for a single service
1611 * and if that service doesn't itself depend on other services.
1613 * @param testdir only the directory name without any path. This is used for
1614 * all service homes; the directory will be created in a temporary
1615 * location depending on the underlying OS
1616 * @param service_name name of the service to run
1617 * @param cfgfilename name of the configuration file to use;
1618 * use NULL to only run with defaults
1619 * @param tm main function of the testcase
1620 * @param tm_cls closure for @a tm
1621 * @return 0 on success, 1 on error
1624 GNUNET_TESTING_service_run (const char *testdir,
1625 const char *service_name,
1626 const char *cfgfilename,
1627 GNUNET_TESTING_TestMain tm,
1630 struct ServiceContext sc;
1631 struct GNUNET_TESTING_System *system;
1632 struct GNUNET_TESTING_Peer *peer;
1633 struct GNUNET_CONFIGURATION_Handle *cfg;
1635 char *libexec_binary;
1637 GNUNET_log_setup (testdir,
1640 system = GNUNET_TESTING_system_create (testdir, "127.0.0.1", NULL, NULL);
1643 cfg = GNUNET_CONFIGURATION_create ();
1644 if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
1646 LOG (GNUNET_ERROR_TYPE_ERROR,
1647 _("Failed to load configuration from %s\n"), cfgfilename);
1648 GNUNET_CONFIGURATION_destroy (cfg);
1649 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1652 peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
1655 GNUNET_CONFIGURATION_destroy (cfg);
1656 hostkeys_unload (system);
1657 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1660 GNUNET_free (peer->main_binary);
1661 GNUNET_free (peer->args);
1662 GNUNET_asprintf (&binary, "gnunet-service-%s", service_name);
1663 libexec_binary = GNUNET_OS_get_libexec_binary_path (binary);
1664 if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, service_name, "PREFIX", &peer->main_binary))
1667 GNUNET_asprintf(&peer->main_binary, "%s", libexec_binary);
1668 peer->args = GNUNET_strdup ("");
1671 peer->args = GNUNET_strdup (libexec_binary);
1673 GNUNET_free (libexec_binary);
1674 GNUNET_free (binary);
1675 if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
1677 GNUNET_TESTING_peer_destroy (peer);
1678 GNUNET_CONFIGURATION_destroy (cfg);
1679 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1686 GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
1687 if ((NULL != peer->main_process) &&
1688 (GNUNET_OK != GNUNET_TESTING_peer_stop (peer)))
1690 GNUNET_TESTING_peer_destroy (peer);
1691 GNUNET_CONFIGURATION_destroy (cfg);
1692 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1695 GNUNET_TESTING_peer_destroy (peer);
1696 GNUNET_CONFIGURATION_destroy (cfg);
1697 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1703 * Sometimes we use the binary name to determine which specific
1704 * test to run. In those cases, the string after the last "_"
1705 * in 'argv[0]' specifies a string that determines the configuration
1706 * file or plugin to use.
1708 * This function returns the respective substring, taking care
1709 * of issues such as binaries ending in '.exe' on W32.
1711 * @param argv0 the name of the binary
1712 * @return string between the last '_' and the '.exe' (or the end of the string),
1713 * NULL if argv0 has no '_'
1716 GNUNET_TESTING_get_testname_from_underscore (const char *argv0)
1718 size_t slen = strlen (argv0) + 1;
1723 GNUNET_memcpy (sbuf, argv0, slen);
1724 ret = strrchr (sbuf, '_');
1727 ret++; /* skip underscore */
1728 dot = strchr (ret, '.');
1731 return GNUNET_strdup (ret);
1735 /* end of testing.c */