2 This file is part of GNUnet
3 (C) 2008, 2009, 2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file testing/testing_new.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_testing_lib-new.h"
36 #define LOG(kind,...) \
37 GNUNET_log_from (kind, "gnunettestingnew", __VA_ARGS__)
40 * AI_NUMERICSERV not defined in windows. A hack to keep on going.
42 #if !defined (AI_NUMERICSERV)
43 #define AI_NUMERICSERV 0
47 * Size of a hostkey when written to a file
49 #ifndef HOSTKEYFILESIZE
50 #define HOSTKEYFILESIZE 914
54 * Handle for a system on which GNUnet peers are executed;
55 * a system is used for reserving unique paths and ports.
57 struct GNUNET_TESTING_System
60 * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
66 * The hostname of the controller
76 * Bitmap where each TCP port that has already been reserved for
77 * some GNUnet peer is recorded. Note that we additionally need to
78 * test if a port is already in use by non-GNUnet components before
79 * assigning it to a peer/service. If we detect that a port is
80 * already in use, we also mark it in this bitmap. So all the bits
81 * that are zero merely indicate ports that MIGHT be available for
84 uint32_t reserved_tcp_ports[65536 / 32];
87 * Bitmap where each UDP port that has already been reserved for
88 * some GNUnet peer is recorded. Note that we additionally need to
89 * test if a port is already in use by non-GNUnet components before
90 * assigning it to a peer/service. If we detect that a port is
91 * already in use, we also mark it in this bitmap. So all the bits
92 * that are zero merely indicate ports that MIGHT be available for
95 uint32_t reserved_udp_ports[65536 / 32];
98 * Counter we use to make service home paths unique on this system;
99 * the full path consists of the tmppath and this number. Each
100 * UNIXPATH for a peer is also modified to include the respective
101 * path counter to ensure uniqueness. This field is incremented
102 * by one for each configured peer. Even if peers are destroyed,
103 * we never re-use path counters.
105 uint32_t path_counter;
108 * The number of hostkeys
110 uint32_t total_hostkeys;
115 * Handle for a GNUnet peer controlled by testing.
117 struct GNUNET_TESTING_Peer
121 * Path to the configuration file for this peer.
126 * Binary to be executed during 'GNUNET_TESTING_peer_start'.
127 * Typically 'gnunet-service-arm' (but can be set to a
128 * specific service by 'GNUNET_TESTING_service_run' if
134 * Handle to the running binary of the service, NULL if the
135 * peer/service is currently not running.
137 struct GNUNET_OS_Process *main_process;
142 * Lowest port used for GNUnet testing. Should be high enough to not
143 * conflict with other applications running on the hosts but be low
144 * enough to not conflict with client-ports (typically starting around
147 #define LOW_PORT 12000
151 * Highest port used for GNUnet testing. Should be low enough to not
152 * conflict with the port range for "local" ports (client apps; see
153 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
155 #define HIGH_PORT 56000
159 * Create a system handle. There must only be one system
160 * handle per operating system.
162 * @param tmppath prefix path to use for all service homes
163 * @param controller hostname of the controlling host,
164 * service configurations are modified to allow
165 * control connections from this host; can be NULL
166 * @return handle to this system, NULL on error
168 struct GNUNET_TESTING_System *
169 GNUNET_TESTING_system_create (const char *tmppath,
170 const char *controller)
172 struct GNUNET_TESTING_System *system;
176 system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System));
177 system->tmppath = GNUNET_strdup (tmppath);
178 if (NULL != controller)
179 system->controller = GNUNET_strdup (controller);
185 * Free system resources.
187 * @param system system to be freed
188 * @param remove_paths should the 'tmppath' and all subdirectories
189 * be removed (clean up on shutdown)?
192 GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
195 GNUNET_assert (NULL != system);
196 if (NULL != system->hostkeys_data)
198 GNUNET_break (0); /* Use GNUNET_TESTING_hostkeys_unload() */
199 GNUNET_free (system->hostkeys_data);
200 system->hostkeys_data = NULL;
201 system->total_hostkeys = 0;
203 if (GNUNET_YES == remove_paths)
204 GNUNET_DISK_directory_remove (system->tmppath);
205 GNUNET_free (system->tmppath);
206 GNUNET_free_non_null (system->controller);
207 GNUNET_free (system);
212 * Reserve a TCP or UDP port for a peer.
214 * @param system system to use for reservation tracking
215 * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
216 * @return 0 if no free port was available
219 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system,
222 struct GNUNET_NETWORK_Handle *socket;
223 struct addrinfo hint;
224 struct addrinfo *ret;
225 uint32_t *port_buckets;
233 hint.ai_family = AF_UNSPEC; /* IPv4 and IPv6 */
234 hint.ai_socktype = (GNUNET_YES == is_tcp)? SOCK_STREAM : SOCK_DGRAM;
235 hint.ai_protocol = 0;
238 hint.ai_canonname = NULL;
240 hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* Wild card address */
241 port_buckets = (GNUNET_YES == is_tcp) ?
242 system->reserved_tcp_ports : system->reserved_udp_ports;
243 for (index = (LOW_PORT / 32) + 1; index < (HIGH_PORT / 32); index++)
245 xor_image = (UINT32_MAX ^ port_buckets[index]);
246 if (0 == xor_image) /* Ports in the bucket are full */
251 if (0 == ((xor_image >> pos) & 1U))
256 open_port = (index * 32) + pos;
257 GNUNET_asprintf (&open_port_str, "%u", open_port);
259 GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
260 GNUNET_free (open_port_str);
261 socket = GNUNET_NETWORK_socket_create (ret->ai_family,
262 (GNUNET_YES == is_tcp) ?
263 SOCK_STREAM : SOCK_DGRAM,
265 GNUNET_assert (NULL != socket);
266 bind_status = GNUNET_NETWORK_socket_bind (socket,
270 GNUNET_NETWORK_socket_close (socket);
272 port_buckets[index] |= (1U << pos); /* Set the port bit */
273 if (GNUNET_OK == bind_status)
283 * Release reservation of a TCP or UDP port for a peer
284 * (used during GNUNET_TESTING_peer_destroy).
286 * @param system system to use for reservation tracking
287 * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
288 * @param port reserved port to release
291 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
295 uint32_t *port_buckets;
299 GNUNET_assert (NULL != system);
300 port_buckets = (GNUNET_YES == is_tcp) ?
301 system->reserved_tcp_ports : system->reserved_udp_ports;
304 LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
305 if (0 == (port_buckets[bucket] & (1U << pos)))
307 GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
310 port_buckets[bucket] &= ~(1U << pos);
315 * Reserve a SERVICEHOME path for a peer.
317 * @param system system to use for reservation tracking
318 * @return NULL on error, otherwise fresh unique path to use
319 * as the servicehome for the peer; must be freed by the caller
323 reserve_path (struct GNUNET_TESTING_System *system)
327 GNUNET_asprintf (&reserved_path,
328 "%s/%u", system->tmppath, system->path_counter++);
329 return reserved_path;
334 * Testing includes a number of pre-created hostkeys for faster peer
335 * startup. This function loads such keys into memory from a file.
337 * @param system the testing system handle
338 * @param filename the path of the hostkeys file
339 * @return GNUNET_OK on success; GNUNET_SYSERR on error
342 GNUNET_TESTING_hostkeys_load (struct GNUNET_TESTING_System *system,
343 const char *filename)
345 struct GNUNET_DISK_FileHandle *fd;
348 if (GNUNET_YES != GNUNET_DISK_file_test (filename))
350 LOG (GNUNET_ERROR_TYPE_ERROR,
351 "Hostkeys file not found: %s\n", filename);
352 return GNUNET_SYSERR;
354 /* Check hostkey file size, read entire thing into memory */
355 fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
356 GNUNET_DISK_PERM_NONE);
359 LOG (GNUNET_ERROR_TYPE_ERROR,
360 "Could not open hostkeys file: %s\n", filename);
361 return GNUNET_SYSERR;
364 GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
368 GNUNET_DISK_file_close (fd);
369 return GNUNET_SYSERR; /* File is empty */
371 if (0 != (fs % HOSTKEYFILESIZE))
373 GNUNET_DISK_file_close (fd);
374 LOG (GNUNET_ERROR_TYPE_ERROR,
375 "Incorrect hostkey file format: %s\n", filename);
376 return GNUNET_SYSERR;
378 GNUNET_break (NULL == system->hostkeys_data);
379 system->total_hostkeys = fs / HOSTKEYFILESIZE;
380 system->hostkeys_data = GNUNET_malloc_large (fs); /* free in hostkeys_unload */
381 GNUNET_assert (fs == GNUNET_DISK_file_read (fd, system->hostkeys_data, fs));
382 GNUNET_DISK_file_close (fd);
388 * Function to remove the loaded hostkeys
390 * @param system the testing system handle
393 GNUNET_TESTING_hostkeys_unload (struct GNUNET_TESTING_System *system)
395 GNUNET_break (NULL != system->hostkeys_data);
396 GNUNET_free_non_null (system->hostkeys_data);
397 system->hostkeys_data = NULL;
398 system->total_hostkeys = 0;
403 * Testing includes a number of pre-created hostkeys for
404 * faster peer startup. This function can be used to
405 * access the n-th key of those pre-created hostkeys; note
406 * that these keys are ONLY useful for testing and not
407 * secure as the private keys are part of the public
408 * GNUnet source code.
410 * This is primarily a helper function used internally
411 * by 'GNUNET_TESTING_peer_configure'.
413 * @param system the testing system handle
414 * @param key_number desired pre-created hostkey to obtain
415 * @param id set to the peer's identity (hash of the public
416 * key; if NULL, GNUNET_SYSERR is returned immediately
417 * @return GNUNET_SYSERR on error (not enough keys)
420 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
422 struct GNUNET_PeerIdentity *id)
424 struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
425 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
427 if ((NULL == id) || (NULL == system->hostkeys_data))
428 return GNUNET_SYSERR;
429 if (key_number >= system->total_hostkeys)
431 LOG (GNUNET_ERROR_TYPE_DEBUG,
432 "Key number %u doesn't exist\n", key_number);
433 return GNUNET_SYSERR;
435 private_key = GNUNET_CRYPTO_rsa_decode_key (system->hostkeys_data +
436 (key_number * HOSTKEYFILESIZE),
438 if (NULL == private_key)
440 LOG (GNUNET_ERROR_TYPE_DEBUG,
441 "Error while decoding key %u\n", key_number);
442 return GNUNET_SYSERR;
444 GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
445 GNUNET_CRYPTO_hash (&public_key,
446 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
448 GNUNET_CRYPTO_rsa_key_free (private_key);
454 * Structure for holding data to build new configurations from a configuration
460 * The system for which we are building configurations
462 struct GNUNET_TESTING_System *system;
465 * The configuration we are building
467 struct GNUNET_CONFIGURATION_Handle *cfg;
470 * The customized service home path for this peer
475 * build status - to signal error while building a configuration
482 * Function to iterate over options. Copies
483 * the options to the target configuration,
484 * updating PORT values as needed.
486 * @param cls the UpdateContext
487 * @param section name of the section
488 * @param option name of the option
489 * @param value value of the option
492 update_config (void *cls, const char *section, const char *option,
495 struct UpdateContext *uc = cls;
499 char *single_variable;
500 char *per_host_variable;
501 unsigned long long num_per_host;
504 if (GNUNET_OK != uc->status)
506 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
507 GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
508 if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival)))
512 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
515 /* FIXME: What about UDP? */
516 new_port = GNUNET_TESTING_reserve_port (uc->system, GNUNET_YES);
519 uc->status = GNUNET_SYSERR;
522 GNUNET_snprintf (cval, sizeof (cval), "%u", new_port);
525 else if ((ival != 0) &&
527 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
529 GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
533 /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
534 /* ival + ctx->fdnum % num_per_host); */
536 GNUNET_break (0); /* FIXME */
539 if (0 == strcmp (option, "UNIXPATH"))
542 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
545 GNUNET_snprintf (uval, sizeof (uval), "%s\\%s.sock",
546 uc->service_home, section);
549 else if ((GNUNET_YES ==
550 GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
555 GNUNET_break(0); /* FIXME */
558 if ((0 == strcmp (option, "HOSTNAME")) && (NULL != uc->system->controller))
560 value = uc->system->controller;
562 GNUNET_free (single_variable);
563 GNUNET_free (per_host_variable);
564 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
569 * Section iterator to set ACCEPT_FROM in all sections
571 * @param cls the UpdateContext
572 * @param section name of the section
575 update_config_sections (void *cls,
578 struct UpdateContext *uc = cls;
579 char *orig_allowed_hosts;
583 GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "ACCEPT_FROM",
584 &orig_allowed_hosts))
586 orig_allowed_hosts = "127.0.0.1;";
588 if (NULL == uc->system->controller)
589 allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
591 GNUNET_asprintf (&allowed_hosts, "%s %s;", orig_allowed_hosts,
592 uc->system->controller);
593 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, "ACCEPT_FROM",
595 GNUNET_free (allowed_hosts);
600 * Create a new configuration using the given configuration
601 * as a template; ports and paths will be modified to select
602 * available ports on the local system. If we run
603 * out of "*port" numbers, return SYSERR.
605 * This is primarily a helper function used internally
606 * by 'GNUNET_TESTING_peer_configure'.
608 * @param system system to use to coordinate resource usage
609 * @param cfg template configuration to update
610 * @return GNUNET_OK on success, GNUNET_SYSERR on error - the configuration will
611 * be incomplete and should not be used there upon
614 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
615 struct GNUNET_CONFIGURATION_Handle *cfg)
617 struct UpdateContext uc;
621 uc.status = GNUNET_OK;
622 GNUNET_asprintf (&uc.service_home, "%s\\%u", system->tmppath,
623 system->path_counter++);
624 GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "SERVICEHOME",
626 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
627 GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
628 /* FIXME: add other options which enable communication with controller */
629 GNUNET_free (uc.service_home);
635 * Configure a GNUnet peer. GNUnet must be installed on the local
636 * system and available in the PATH.
638 * @param system system to use to coordinate resource usage
639 * @param cfg configuration to use; will be UPDATED (to reflect needed
640 * changes in port numbers and paths)
641 * @param key_number number of the hostkey to use for the peer
642 * @param id identifier for the daemon, will be set, can be NULL
643 * @param emsg set to error message (set to NULL on success), can be NULL
644 * @return handle to the peer, NULL on error
646 struct GNUNET_TESTING_Peer *
647 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
648 struct GNUNET_CONFIGURATION_Handle *cfg,
650 struct GNUNET_PeerIdentity *id,
653 struct GNUNET_TESTING_Peer *peer;
654 struct GNUNET_DISK_FileHandle *fd;
656 char hostkey_filename[128];
657 char *config_filename;
658 size_t bytes_written;
662 if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg))
664 if (key_number >= system->total_hostkeys)
667 (GNUNET_SYSERR == GNUNET_TESTING_hostkey_get (system, key_number, id)))
669 GNUNET_assert (GNUNET_OK ==
670 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
673 GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s\\.hostkey",
675 fd = GNUNET_DISK_file_open (hostkey_filename,
676 GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
677 GNUNET_DISK_PERM_USER_READ
678 | GNUNET_DISK_PERM_USER_WRITE);
681 GNUNET_break (0); return NULL;
686 n = GNUNET_DISK_file_write (fd, system->hostkeys_data
687 + (key_number * HOSTKEYFILESIZE),
688 HOSTKEYFILESIZE - bytes_written);
689 GNUNET_assert (GNUNET_SYSERR != n);
692 while (bytes_written < HOSTKEYFILESIZE);
693 GNUNET_DISK_file_close (fd);
695 GNUNET_asprintf (&config_filename, "%s\\config", service_home);
696 GNUNET_free (service_home);
697 if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
699 peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
700 peer->cfgfile = config_filename; /* Free in peer_destroy */
701 peer->main_binary = GNUNET_strdup ("gnunet-service-arm");
709 * @param peer peer to start
710 * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
713 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
716 return GNUNET_SYSERR;
723 * @param peer peer to stop
724 * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
727 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
730 return GNUNET_SYSERR;
735 * Destroy the peer. Releases resources locked during peer configuration.
736 * If the peer is still running, it will be stopped AND a warning will be
737 * printed (users of the API should stop the peer explicitly first).
739 * @param peer peer to destroy
742 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
750 * Start a single peer and run a test using the testing library.
751 * Starts a peer using the given configuration and then invokes the
752 * given callback. This function ALSO initializes the scheduler loop
753 * and should thus be called directly from "main". The testcase
754 * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
756 * @param tmppath path for storing temporary data for the test
757 * @param cfgfilename name of the configuration file to use;
758 * use NULL to only run with defaults
759 * @param tm main function of the testcase
760 * @param tm_cls closure for 'tm'
761 * @return 0 on success, 1 on error
764 GNUNET_TESTING_peer_run (const char *tmppath,
765 const char *cfgfilename,
766 GNUNET_TESTING_TestMain tm,
769 return GNUNET_TESTING_service_run (tmppath, "arm",
770 cfgfilename, tm, tm_cls);
776 * Start a single service (no ARM, except of course if the given
777 * service name is 'arm') and run a test using the testing library.
778 * Starts a service using the given configuration and then invokes the
779 * given callback. This function ALSO initializes the scheduler loop
780 * and should thus be called directly from "main". The testcase
781 * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
783 * This function is useful if the testcase is for a single service
784 * and if that service doesn't itself depend on other services.
786 * @param tmppath path for storing temporary data for the test
787 * @param service_name name of the service to run
788 * @param cfgfilename name of the configuration file to use;
789 * use NULL to only run with defaults
790 * @param tm main function of the testcase
791 * @param tm_cls closure for 'tm'
792 * @return 0 on success, 1 on error
795 GNUNET_TESTING_service_run (const char *tmppath,
796 const char *service_name,
797 const char *cfgfilename,
798 GNUNET_TESTING_TestMain tm,
807 /* end of testing_new.c */