aboutsummaryrefslogtreecommitdiff
path: root/src/testing/testing.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing/testing.c')
-rw-r--r--src/testing/testing.c1010
1 files changed, 1010 insertions, 0 deletions
diff --git a/src/testing/testing.c b/src/testing/testing.c
new file mode 100644
index 000000000..c4da3a973
--- /dev/null
+++ b/src/testing/testing.c
@@ -0,0 +1,1010 @@
1/*
2 This file is part of GNUnet
3 (C) 2008, 2009, 2012 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21/**
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
30 *
31 */
32#include "platform.h"
33#include "gnunet_util_lib.h"
34#include "gnunet_testing_lib-new.h"
35
36#define LOG(kind,...) \
37 GNUNET_log_from (kind, "gnunettestingnew", __VA_ARGS__)
38
39
40/**
41 * Size of a hostkey when written to a file
42 */
43#define HOSTKEYFILESIZE 914
44
45
46/**
47 * Handle for a system on which GNUnet peers are executed;
48 * a system is used for reserving unique paths and ports.
49 */
50struct GNUNET_TESTING_System
51{
52 /**
53 * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
54 * SERVICEHOME.
55 */
56 char *tmppath;
57
58 /**
59 * The hostname of the controller
60 */
61 char *controller;
62
63 /**
64 * Hostkeys data, contains "HOSTKEYFILESIZE * total_hostkeys" bytes.
65 */
66 char *hostkeys_data;
67
68 /**
69 * Bitmap where each TCP port that has already been reserved for
70 * some GNUnet peer is recorded. Note that we additionally need to
71 * test if a port is already in use by non-GNUnet components before
72 * assigning it to a peer/service. If we detect that a port is
73 * already in use, we also mark it in this bitmap. So all the bits
74 * that are zero merely indicate ports that MIGHT be available for
75 * peers.
76 */
77 uint32_t reserved_tcp_ports[65536 / 32];
78
79 /**
80 * Bitmap where each UDP port that has already been reserved for
81 * some GNUnet peer is recorded. Note that we additionally need to
82 * test if a port is already in use by non-GNUnet components before
83 * assigning it to a peer/service. If we detect that a port is
84 * already in use, we also mark it in this bitmap. So all the bits
85 * that are zero merely indicate ports that MIGHT be available for
86 * peers.
87 */
88 uint32_t reserved_udp_ports[65536 / 32];
89
90 /**
91 * Counter we use to make service home paths unique on this system;
92 * the full path consists of the tmppath and this number. Each
93 * UNIXPATH for a peer is also modified to include the respective
94 * path counter to ensure uniqueness. This field is incremented
95 * by one for each configured peer. Even if peers are destroyed,
96 * we never re-use path counters.
97 */
98 uint32_t path_counter;
99
100 /**
101 * The number of hostkeys
102 */
103 uint32_t total_hostkeys;
104};
105
106
107/**
108 * Handle for a GNUnet peer controlled by testing.
109 */
110struct GNUNET_TESTING_Peer
111{
112
113 /**
114 * Path to the configuration file for this peer.
115 */
116 char *cfgfile;
117
118 /**
119 * Binary to be executed during 'GNUNET_TESTING_peer_start'.
120 * Typically 'gnunet-service-arm' (but can be set to a
121 * specific service by 'GNUNET_TESTING_service_run' if
122 * necessary).
123 */
124 char *main_binary;
125
126 /**
127 * Handle to the running binary of the service, NULL if the
128 * peer/service is currently not running.
129 */
130 struct GNUNET_OS_Process *main_process;
131};
132
133
134/**
135 * Lowest port used for GNUnet testing. Should be high enough to not
136 * conflict with other applications running on the hosts but be low
137 * enough to not conflict with client-ports (typically starting around
138 * 32k).
139 */
140#define LOW_PORT 12000
141
142
143/**
144 * Highest port used for GNUnet testing. Should be low enough to not
145 * conflict with the port range for "local" ports (client apps; see
146 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
147 */
148#define HIGH_PORT 56000
149
150
151/**
152 * Create a system handle. There must only be one system
153 * handle per operating system.
154 *
155 * @param tmppath prefix path to use for all service homes
156 * @param controller hostname of the controlling host,
157 * service configurations are modified to allow
158 * control connections from this host; can be NULL
159 * @return handle to this system, NULL on error
160 */
161struct GNUNET_TESTING_System *
162GNUNET_TESTING_system_create (const char *tmppath,
163 const char *controller)
164{
165 struct GNUNET_TESTING_System *system;
166
167 if (NULL == tmppath)
168 {
169 LOG (GNUNET_ERROR_TYPE_ERROR, _("tmppath cannot be NULL\n"));
170 return NULL;
171 }
172 system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System));
173 system->tmppath = GNUNET_strdup (tmppath);
174 if (NULL != controller)
175 system->controller = GNUNET_strdup (controller);
176 return system;
177}
178
179
180/**
181 * Free system resources.
182 *
183 * @param system system to be freed
184 * @param remove_paths should the 'tmppath' and all subdirectories
185 * be removed (clean up on shutdown)?
186 */
187void
188GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
189 int remove_paths)
190{
191 if (NULL != system->hostkeys_data)
192 {
193 GNUNET_break (0); /* Use GNUNET_TESTING_hostkeys_unload() */
194 GNUNET_TESTING_hostkeys_unload (system);
195 }
196 if (GNUNET_YES == remove_paths)
197 GNUNET_DISK_directory_remove (system->tmppath);
198 GNUNET_free (system->tmppath);
199 GNUNET_free_non_null (system->controller);
200 GNUNET_free (system);
201}
202
203
204/**
205 * Reserve a TCP or UDP port for a peer.
206 *
207 * @param system system to use for reservation tracking
208 * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
209 * @return 0 if no free port was available
210 */
211uint16_t
212GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system,
213 int is_tcp)
214{
215 struct GNUNET_NETWORK_Handle *socket;
216 struct addrinfo hint;
217 struct addrinfo *ret;
218 uint32_t *port_buckets;
219 char *open_port_str;
220 int bind_status;
221 uint32_t xor_image;
222 uint16_t index;
223 uint16_t open_port;
224 uint16_t pos;
225
226 /*
227 FIXME: Instead of using getaddrinfo we should try to determine the port
228 status by the following heurestics.
229
230 On systems which support both IPv4 and IPv6, only ports open on both
231 address families are considered open.
232 On system with either IPv4 or IPv6. A port is considered open if it's
233 open in the respective address family
234 */
235 hint.ai_family = AF_UNSPEC; /* IPv4 and IPv6 */
236 hint.ai_socktype = (GNUNET_YES == is_tcp)? SOCK_STREAM : SOCK_DGRAM;
237 hint.ai_protocol = 0;
238 hint.ai_addrlen = 0;
239 hint.ai_addr = NULL;
240 hint.ai_canonname = NULL;
241 hint.ai_next = NULL;
242 hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* Wild card address */
243 port_buckets = (GNUNET_YES == is_tcp) ?
244 system->reserved_tcp_ports : system->reserved_udp_ports;
245 for (index = (LOW_PORT / 32) + 1; index < (HIGH_PORT / 32); index++)
246 {
247 xor_image = (UINT32_MAX ^ port_buckets[index]);
248 if (0 == xor_image) /* Ports in the bucket are full */
249 continue;
250 pos = 0;
251 while (pos < 32)
252 {
253 if (0 == ((xor_image >> pos) & 1U))
254 {
255 pos++;
256 continue;
257 }
258 open_port = (index * 32) + pos;
259 GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port);
260 ret = NULL;
261 GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
262 GNUNET_free (open_port_str);
263 socket = GNUNET_NETWORK_socket_create (ret->ai_family,
264 (GNUNET_YES == is_tcp) ?
265 SOCK_STREAM : SOCK_DGRAM,
266 0);
267 GNUNET_assert (NULL != socket);
268 bind_status = GNUNET_NETWORK_socket_bind (socket,
269 ret->ai_addr,
270 ret->ai_addrlen);
271 freeaddrinfo (ret);
272 GNUNET_NETWORK_socket_close (socket);
273 socket = NULL;
274 port_buckets[index] |= (1U << pos); /* Set the port bit */
275 if (GNUNET_OK == bind_status)
276 {
277 LOG (GNUNET_ERROR_TYPE_DEBUG,
278 "Found a free port %u\n", (unsigned int) open_port);
279 return open_port;
280 }
281 pos++;
282 }
283 }
284 return 0;
285}
286
287
288/**
289 * Release reservation of a TCP or UDP port for a peer
290 * (used during GNUNET_TESTING_peer_destroy).
291 *
292 * @param system system to use for reservation tracking
293 * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
294 * @param port reserved port to release
295 */
296void
297GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
298 int is_tcp,
299 uint16_t port)
300{
301 uint32_t *port_buckets;
302 uint16_t bucket;
303 uint16_t pos;
304
305 port_buckets = (GNUNET_YES == is_tcp) ?
306 system->reserved_tcp_ports : system->reserved_udp_ports;
307 bucket = port / 32;
308 pos = port % 32;
309 LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
310 if (0 == (port_buckets[bucket] & (1U << pos)))
311 {
312 GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
313 return;
314 }
315 port_buckets[bucket] &= ~(1U << pos);
316}
317
318
319/**
320 * Reserve a SERVICEHOME path for a peer.
321 *
322 * @param system system to use for reservation tracking
323 * @return NULL on error, otherwise fresh unique path to use
324 * as the servicehome for the peer; must be freed by the caller
325 */
326// static
327char *
328reserve_path (struct GNUNET_TESTING_System *system)
329{
330 char *reserved_path;
331
332 GNUNET_asprintf (&reserved_path,
333 "%s/%u", system->tmppath, system->path_counter++);
334 return reserved_path;
335}
336
337
338/**
339 * Testing includes a number of pre-created hostkeys for faster peer
340 * startup. This function loads such keys into memory from a file.
341 *
342 * @param system the testing system handle
343 * @param filename the path of the hostkeys file
344 * @return GNUNET_OK on success; GNUNET_SYSERR on error
345 */
346int
347GNUNET_TESTING_hostkeys_load (struct GNUNET_TESTING_System *system,
348 const char *filename)
349{
350 struct GNUNET_DISK_FileHandle *fd;
351 uint64_t fs;
352
353 if (GNUNET_YES != GNUNET_DISK_file_test (filename))
354 {
355 LOG (GNUNET_ERROR_TYPE_ERROR,
356 _("Hostkeys file not found: %s\n"), filename);
357 return GNUNET_SYSERR;
358 }
359 /* Check hostkey file size, read entire thing into memory */
360 fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
361 GNUNET_DISK_PERM_NONE);
362 if (NULL == fd)
363 {
364 LOG (GNUNET_ERROR_TYPE_ERROR,
365 _("Could not open hostkeys file: %s\n"), filename);
366 return GNUNET_SYSERR;
367 }
368 if (GNUNET_OK !=
369 GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
370 fs = 0;
371 if (0 == fs)
372 {
373 GNUNET_DISK_file_close (fd);
374 return GNUNET_SYSERR; /* File is empty */
375 }
376 if (0 != (fs % HOSTKEYFILESIZE))
377 {
378 GNUNET_DISK_file_close (fd);
379 LOG (GNUNET_ERROR_TYPE_ERROR,
380 _("Incorrect hostkey file format: %s\n"), filename);
381 return GNUNET_SYSERR;
382 }
383 GNUNET_break (NULL == system->hostkeys_data);
384 system->total_hostkeys = fs / HOSTKEYFILESIZE;
385 system->hostkeys_data = GNUNET_malloc_large (fs); /* free in hostkeys_unload */
386 GNUNET_assert (fs == GNUNET_DISK_file_read (fd, system->hostkeys_data, fs));
387 GNUNET_DISK_file_close (fd);
388 return GNUNET_OK;
389}
390
391
392/**
393 * Function to remove the loaded hostkeys
394 *
395 * @param system the testing system handle
396 */
397void
398GNUNET_TESTING_hostkeys_unload (struct GNUNET_TESTING_System *system)
399{
400 GNUNET_break (NULL != system->hostkeys_data);
401 GNUNET_free_non_null (system->hostkeys_data);
402 system->hostkeys_data = NULL;
403 system->total_hostkeys = 0;
404}
405
406
407/**
408 * Testing includes a number of pre-created hostkeys for
409 * faster peer startup. This function can be used to
410 * access the n-th key of those pre-created hostkeys; note
411 * that these keys are ONLY useful for testing and not
412 * secure as the private keys are part of the public
413 * GNUnet source code.
414 *
415 * This is primarily a helper function used internally
416 * by 'GNUNET_TESTING_peer_configure'.
417 *
418 * @param system the testing system handle
419 * @param key_number desired pre-created hostkey to obtain
420 * @param id set to the peer's identity (hash of the public
421 * key; if NULL, GNUNET_SYSERR is returned immediately
422 * @return GNUNET_SYSERR on error (not enough keys)
423 */
424int
425GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
426 uint32_t key_number,
427 struct GNUNET_PeerIdentity *id)
428{
429 struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
430 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
431
432 if ((NULL == id) || (NULL == system->hostkeys_data))
433 return GNUNET_SYSERR;
434 if (key_number >= system->total_hostkeys)
435 {
436 LOG (GNUNET_ERROR_TYPE_ERROR,
437 _("Key number %u does not exist\n"), key_number);
438 return GNUNET_SYSERR;
439 }
440 private_key = GNUNET_CRYPTO_rsa_decode_key (system->hostkeys_data +
441 (key_number * HOSTKEYFILESIZE),
442 HOSTKEYFILESIZE);
443 if (NULL == private_key)
444 {
445 LOG (GNUNET_ERROR_TYPE_ERROR,
446 _("Error while decoding key %u\n"), key_number);
447 return GNUNET_SYSERR;
448 }
449 GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
450 GNUNET_CRYPTO_hash (&public_key,
451 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
452 &(id->hashPubKey));
453 GNUNET_CRYPTO_rsa_key_free (private_key);
454 return GNUNET_OK;
455}
456
457
458/**
459 * Structure for holding data to build new configurations from a configuration
460 * template
461 */
462struct UpdateContext
463{
464 /**
465 * The system for which we are building configurations
466 */
467 struct GNUNET_TESTING_System *system;
468
469 /**
470 * The configuration we are building
471 */
472 struct GNUNET_CONFIGURATION_Handle *cfg;
473
474 /**
475 * The customized service home path for this peer
476 */
477 char *service_home;
478
479 /**
480 * build status - to signal error while building a configuration
481 */
482 int status;
483};
484
485
486/**
487 * Function to iterate over options. Copies
488 * the options to the target configuration,
489 * updating PORT values as needed.
490 *
491 * @param cls the UpdateContext
492 * @param section name of the section
493 * @param option name of the option
494 * @param value value of the option
495 */
496static void
497update_config (void *cls, const char *section, const char *option,
498 const char *value)
499{
500 struct UpdateContext *uc = cls;
501 unsigned int ival;
502 char cval[12];
503 char uval[128];
504 char *single_variable;
505 char *per_host_variable;
506 unsigned long long num_per_host;
507 uint16_t new_port;
508
509 if (GNUNET_OK != uc->status)
510 return;
511 if (! ((0 == strcmp (option, "PORT"))
512 || (0 == strcmp (option, "UNIXPATH"))
513 || (0 == strcmp (option, "HOSTNAME"))))
514 return;
515 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
516 GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
517 if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival)))
518 {
519 if ((ival != 0) &&
520 (GNUNET_YES !=
521 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
522 single_variable)))
523 {
524 /* FIXME: What about UDP? */
525 new_port = GNUNET_TESTING_reserve_port (uc->system, GNUNET_YES);
526 if (0 == new_port)
527 {
528 uc->status = GNUNET_SYSERR;
529 return;
530 }
531 GNUNET_snprintf (cval, sizeof (cval), "%u", new_port);
532 value = cval;
533 }
534 else if ((ival != 0) &&
535 (GNUNET_YES ==
536 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
537 single_variable)) &&
538 GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
539 per_host_variable,
540 &num_per_host))
541 {
542 /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
543 /* ival + ctx->fdnum % num_per_host); */
544 /* value = cval; */
545 GNUNET_break (0); /* FIXME */
546 }
547 }
548 if (0 == strcmp (option, "UNIXPATH"))
549 {
550 if (GNUNET_YES !=
551 GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
552 single_variable))
553 {
554 GNUNET_snprintf (uval, sizeof (uval), "%s/%s.sock",
555 uc->service_home, section);
556 value = uval;
557 }
558 else if ((GNUNET_YES ==
559 GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
560 per_host_variable,
561 &num_per_host)) &&
562 (num_per_host > 0))
563 {
564 GNUNET_break(0); /* FIXME */
565 }
566 }
567 if ((0 == strcmp (option, "HOSTNAME")) && (NULL != uc->system->controller))
568 {
569 value = uc->system->controller;
570 }
571 GNUNET_free (single_variable);
572 GNUNET_free (per_host_variable);
573 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
574}
575
576
577/**
578 * Section iterator to set ACCEPT_FROM in all sections
579 *
580 * @param cls the UpdateContext
581 * @param section name of the section
582 */
583static void
584update_config_sections (void *cls,
585 const char *section)
586{
587 struct UpdateContext *uc = cls;
588 char *orig_allowed_hosts;
589 char *allowed_hosts;
590
591 if (GNUNET_OK !=
592 GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "ACCEPT_FROM",
593 &orig_allowed_hosts))
594 {
595 orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;");
596 }
597 if (NULL == uc->system->controller)
598 allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
599 else
600 GNUNET_asprintf (&allowed_hosts, "%s%s;", orig_allowed_hosts,
601 uc->system->controller);
602 GNUNET_free (orig_allowed_hosts);
603 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, "ACCEPT_FROM",
604 allowed_hosts);
605 GNUNET_free (allowed_hosts);
606}
607
608
609/**
610 * Create a new configuration using the given configuration
611 * as a template; ports and paths will be modified to select
612 * available ports on the local system. If we run
613 * out of "*port" numbers, return SYSERR.
614 *
615 * This is primarily a helper function used internally
616 * by 'GNUNET_TESTING_peer_configure'.
617 *
618 * @param system system to use to coordinate resource usage
619 * @param cfg template configuration to update
620 * @return GNUNET_OK on success, GNUNET_SYSERR on error - the configuration will
621 * be incomplete and should not be used there upon
622 */
623int
624GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
625 struct GNUNET_CONFIGURATION_Handle *cfg)
626{
627 struct UpdateContext uc;
628 char *default_config;
629
630 uc.system = system;
631 uc.cfg = cfg;
632 uc.status = GNUNET_OK;
633 GNUNET_asprintf (&uc.service_home, "%s/%u", system->tmppath,
634 system->path_counter++);
635 GNUNET_asprintf (&default_config, "%s/config", uc.service_home);
636 GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "DEFAULTCONFIG",
637 default_config);
638 GNUNET_free (default_config);
639 GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "SERVICEHOME",
640 uc.service_home);
641 /* make PORTs and UNIXPATHs unique */
642 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
643 /* allow connections to services from system controller host */
644 GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
645 /* enable loopback-based connections between peers */
646 GNUNET_CONFIGURATION_set_value_string (cfg,
647 "nat",
648 "USE_LOCALADDR", "YES");
649 GNUNET_free (uc.service_home);
650 return uc.status;
651}
652
653
654/**
655 * Configure a GNUnet peer. GNUnet must be installed on the local
656 * system and available in the PATH.
657 *
658 * @param system system to use to coordinate resource usage
659 * @param cfg configuration to use; will be UPDATED (to reflect needed
660 * changes in port numbers and paths)
661 * @param key_number number of the hostkey to use for the peer
662 * @param id identifier for the daemon, will be set, can be NULL
663 * @param emsg set to error message (set to NULL on success), can be NULL
664 * @return handle to the peer, NULL on error
665 */
666struct GNUNET_TESTING_Peer *
667GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
668 struct GNUNET_CONFIGURATION_Handle *cfg,
669 uint32_t key_number,
670 struct GNUNET_PeerIdentity *id,
671 char **emsg)
672{
673 struct GNUNET_TESTING_Peer *peer;
674 struct GNUNET_DISK_FileHandle *fd;
675 char *service_home;
676 char hostkey_filename[128];
677 char *config_filename;
678 char *emsg_;
679
680 if (NULL != emsg)
681 *emsg = NULL;
682 if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg))
683 {
684 GNUNET_asprintf (&emsg_,
685 _("Failed to create configuration for peer (not enough free ports?)\n"));
686 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
687 if (NULL != emsg)
688 *emsg = emsg_;
689 else
690 GNUNET_free (emsg_);
691 return NULL;
692 }
693 if (key_number >= system->total_hostkeys)
694 {
695 GNUNET_asprintf (&emsg_,
696 _("You attempted to create a testbed with more than %u hosts. Please precompute more hostkeys first.\n"),
697 (unsigned int) system->total_hostkeys);
698 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
699 if (NULL != emsg)
700 *emsg = emsg_;
701 else
702 GNUNET_free (emsg_);
703 return NULL;
704 }
705 if ((NULL != id) &&
706 (GNUNET_SYSERR == GNUNET_TESTING_hostkey_get (system, key_number, id)))
707 {
708 GNUNET_asprintf (&emsg_,
709 _("Failed to initialize hostkey for peer %u\n"),
710 (unsigned int) key_number);
711 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
712 if (NULL != emsg)
713 *emsg = emsg_;
714 else
715 GNUNET_free (emsg_);
716 return NULL;
717 }
718 GNUNET_assert (GNUNET_OK ==
719 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
720 "SERVICEHOME",
721 &service_home));
722 GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/.hostkey",
723 service_home);
724 GNUNET_free (service_home);
725 fd = GNUNET_DISK_file_open (hostkey_filename,
726 GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
727 GNUNET_DISK_PERM_USER_READ
728 | GNUNET_DISK_PERM_USER_WRITE);
729 if (NULL == fd)
730 {
731 GNUNET_break (0);
732 return NULL;
733 }
734 if (HOSTKEYFILESIZE !=
735 GNUNET_DISK_file_write (fd, system->hostkeys_data
736 + (key_number * HOSTKEYFILESIZE),
737 HOSTKEYFILESIZE))
738 {
739 GNUNET_asprintf (&emsg_,
740 _("Failed to write hostkey file for peer %u: %s\n"),
741 (unsigned int) key_number,
742 STRERROR (errno));
743 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
744 if (NULL != emsg)
745 *emsg = emsg_;
746 else
747 GNUNET_free (emsg_);
748 GNUNET_DISK_file_close (fd);
749 return NULL;
750 }
751 GNUNET_DISK_file_close (fd);
752 GNUNET_assert (GNUNET_OK ==
753 GNUNET_CONFIGURATION_get_value_string
754 (cfg, "PATHS", "DEFAULTCONFIG", &config_filename));
755 if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
756 {
757 GNUNET_asprintf (&emsg_,
758 _("Failed to write configuration file `%s' for peer %u: %s\n"),
759 config_filename,
760 (unsigned int) key_number,
761 STRERROR (errno));
762 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
763 if (NULL != emsg)
764 *emsg = emsg_;
765 else
766 GNUNET_free (emsg_);
767 GNUNET_free (config_filename);
768 return NULL;
769 }
770 peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
771 peer->cfgfile = config_filename; /* Free in peer_destroy */
772 peer->main_binary = GNUNET_strdup ("gnunet-service-arm");
773 return peer;
774}
775
776
777/**
778 * Start the peer.
779 *
780 * @param peer peer to start
781 * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
782 */
783int
784GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
785{
786 if (NULL != peer->main_process)
787 {
788 GNUNET_break (0);
789 return GNUNET_SYSERR;
790 }
791 GNUNET_assert (NULL != peer->cfgfile);
792 peer->main_process = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL,
793 peer->main_binary,
794 peer->main_binary,
795 "-c",
796 peer->cfgfile,
797 NULL);
798 if (NULL == peer->main_process)
799 {
800 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
801 _("Failed to start `%s': %s\n"),
802 peer->main_binary,
803 STRERROR (errno));
804 return GNUNET_SYSERR;
805 }
806 return GNUNET_OK;
807}
808
809
810/**
811 * Stop the peer.
812 *
813 * @param peer peer to stop
814 * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
815 */
816int
817GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
818{
819 if (NULL == peer->main_process)
820 {
821 GNUNET_break (0);
822 return GNUNET_SYSERR;
823 }
824 (void) GNUNET_OS_process_kill (peer->main_process, SIGTERM);
825 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (peer->main_process));
826 GNUNET_OS_process_destroy (peer->main_process);
827 peer->main_process = NULL;
828 return GNUNET_OK;
829}
830
831
832/**
833 * Destroy the peer. Releases resources locked during peer configuration.
834 * If the peer is still running, it will be stopped AND a warning will be
835 * printed (users of the API should stop the peer explicitly first).
836 *
837 * @param peer peer to destroy
838 */
839void
840GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
841{
842 if (NULL != peer->main_process)
843 {
844 GNUNET_break (0);
845 GNUNET_TESTING_peer_stop (peer);
846 }
847 GNUNET_free (peer->cfgfile);
848 GNUNET_free (peer->main_binary);
849 GNUNET_free (peer);
850}
851
852
853/**
854 * Start a single peer and run a test using the testing library.
855 * Starts a peer using the given configuration and then invokes the
856 * given callback. This function ALSO initializes the scheduler loop
857 * and should thus be called directly from "main". The testcase
858 * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
859 *
860 * @param tmppath path for storing temporary data for the test
861 * @param cfgfilename name of the configuration file to use;
862 * use NULL to only run with defaults
863 * @param tm main function of the testcase
864 * @param tm_cls closure for 'tm'
865 * @return 0 on success, 1 on error
866 */
867int
868GNUNET_TESTING_peer_run (const char *tmppath,
869 const char *cfgfilename,
870 GNUNET_TESTING_TestMain tm,
871 void *tm_cls)
872{
873 return GNUNET_TESTING_service_run (tmppath, "arm",
874 cfgfilename, tm, tm_cls);
875}
876
877
878/**
879 * Structure for holding service data
880 */
881struct ServiceContext
882{
883 /**
884 * The configuration of the peer in which the service is run
885 */
886 const struct GNUNET_CONFIGURATION_Handle *cfg;
887
888 /**
889 * Callback to signal service startup
890 */
891 GNUNET_TESTING_TestMain tm;
892
893 /**
894 * Closure for the above callback
895 */
896 void *tm_cls;
897};
898
899
900/**
901 * Callback to be called when SCHEDULER has been started
902 *
903 * @param cls the ServiceContext
904 * @param tc the TaskContext
905 */
906static void
907service_run_main (void *cls,
908 const struct GNUNET_SCHEDULER_TaskContext *tc)
909{
910 struct ServiceContext *sc = cls;
911
912 sc->tm (sc->tm_cls, sc->cfg);
913}
914
915
916/**
917 * Start a single service (no ARM, except of course if the given
918 * service name is 'arm') and run a test using the testing library.
919 * Starts a service using the given configuration and then invokes the
920 * given callback. This function ALSO initializes the scheduler loop
921 * and should thus be called directly from "main". The testcase
922 * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
923 *
924 * This function is useful if the testcase is for a single service
925 * and if that service doesn't itself depend on other services.
926 *
927 * @param tmppath path for storing temporary data for the test
928 * @param service_name name of the service to run
929 * @param cfgfilename name of the configuration file to use;
930 * use NULL to only run with defaults
931 * @param tm main function of the testcase
932 * @param tm_cls closure for 'tm'
933 * @return 0 on success, 1 on error
934 */
935int
936GNUNET_TESTING_service_run (const char *tmppath,
937 const char *service_name,
938 const char *cfgfilename,
939 GNUNET_TESTING_TestMain tm,
940 void *tm_cls)
941{
942 struct ServiceContext sc;
943 struct GNUNET_TESTING_System *system;
944 struct GNUNET_TESTING_Peer *peer;
945 struct GNUNET_CONFIGURATION_Handle *cfg;
946 char *data_dir;
947 char *hostkeys_file;
948
949 data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
950 GNUNET_asprintf (&hostkeys_file, "%s/testing_hostkeys.dat", data_dir);
951 GNUNET_free (data_dir);
952 system = GNUNET_TESTING_system_create (tmppath, "127.0.0.1");
953 if (NULL == system)
954 {
955 GNUNET_free (hostkeys_file);
956 return 1;
957 }
958 if (GNUNET_OK != GNUNET_TESTING_hostkeys_load (system, hostkeys_file))
959 {
960 GNUNET_free (hostkeys_file);
961 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
962 return 1;
963 }
964 GNUNET_free (hostkeys_file);
965 cfg = GNUNET_CONFIGURATION_create ();
966 if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
967 {
968 LOG (GNUNET_ERROR_TYPE_ERROR,
969 _("Failed to load configuration from %s\n"), cfgfilename);
970 GNUNET_CONFIGURATION_destroy (cfg);
971 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
972 return 1;
973 }
974 peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
975 if (NULL == peer)
976 {
977 GNUNET_CONFIGURATION_destroy (cfg);
978 GNUNET_TESTING_hostkeys_unload (system);
979 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
980 return 1;
981 }
982 GNUNET_TESTING_hostkeys_unload (system);
983 GNUNET_free (peer->main_binary);
984 GNUNET_asprintf (&peer->main_binary, "gnunet-service-%s", service_name);
985 if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
986 {
987 GNUNET_TESTING_peer_destroy (peer);
988 GNUNET_CONFIGURATION_destroy (cfg);
989 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
990 return 1;
991 }
992 sc.cfg = cfg;
993 sc.tm = tm;
994 sc.tm_cls = tm_cls;
995 GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
996 if (GNUNET_OK != GNUNET_TESTING_peer_stop (peer))
997 {
998 GNUNET_TESTING_peer_destroy (peer);
999 GNUNET_CONFIGURATION_destroy (cfg);
1000 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1001 return 1;
1002 }
1003 GNUNET_TESTING_peer_destroy (peer);
1004 GNUNET_CONFIGURATION_destroy (cfg);
1005 GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1006 return 0;
1007}
1008
1009
1010/* end of testing_new.c */