-fixed memory leaks and added windows support for temp dirs
[oweals/gnunet.git] / src / testing / testing_new.c
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  */
50 struct 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  */
110 struct 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  */
161 struct GNUNET_TESTING_System *
162 GNUNET_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  */
187 void
188 GNUNET_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  */
211 uint16_t 
212 GNUNET_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  */
296 void
297 GNUNET_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 
327 char *
328 reserve_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  */
346 int
347 GNUNET_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  */
397 void
398 GNUNET_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  */
424 int
425 GNUNET_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  */
462 struct 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  */
496 static void
497 update_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  */
583 static void
584 update_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  */
623 int
624 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
625                                      struct GNUNET_CONFIGURATION_Handle *cfg)
626 {
627   struct UpdateContext uc;
628   
629   uc.system = system;
630   uc.cfg = cfg;
631   uc.status = GNUNET_OK;
632   GNUNET_asprintf (&uc.service_home, "%s/%u", system->tmppath,
633                    system->path_counter++);
634   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "SERVICEHOME",
635                                          uc.service_home);
636   /* make PORTs and UNIXPATHs unique */
637   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
638   /* allow connections to services from system controller host */
639   GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
640   /* enable loopback-based connections between peers */
641   GNUNET_CONFIGURATION_set_value_string (cfg, 
642                                          "nat",
643                                          "USE_LOCALADDR", "YES");
644   GNUNET_free (uc.service_home);
645   return uc.status;
646 }
647
648
649 /**
650  * Configure a GNUnet peer.  GNUnet must be installed on the local
651  * system and available in the PATH. 
652  *
653  * @param system system to use to coordinate resource usage
654  * @param cfg configuration to use; will be UPDATED (to reflect needed
655  *            changes in port numbers and paths)
656  * @param key_number number of the hostkey to use for the peer
657  * @param id identifier for the daemon, will be set, can be NULL
658  * @param emsg set to error message (set to NULL on success), can be NULL
659  * @return handle to the peer, NULL on error
660  */
661 struct GNUNET_TESTING_Peer *
662 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
663                                struct GNUNET_CONFIGURATION_Handle *cfg,
664                                uint32_t key_number,
665                                struct GNUNET_PeerIdentity *id,
666                                char **emsg)
667 {
668   struct GNUNET_TESTING_Peer *peer;
669   struct GNUNET_DISK_FileHandle *fd;
670   char *service_home;  
671   char hostkey_filename[128];
672   char *config_filename;
673   char *emsg_;
674
675   if (NULL != emsg)
676     *emsg = NULL;
677   if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg))
678   {
679     GNUNET_asprintf (&emsg_,
680                        _("Failed to create configuration for peer (not enough free ports?)\n"));
681     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
682     if (NULL != emsg)
683       *emsg = emsg_;
684     else
685       GNUNET_free (emsg_);
686     return NULL;
687   }
688   if (key_number >= system->total_hostkeys)
689   {
690     GNUNET_asprintf (&emsg_,
691                      _("You attempted to create a testbed with more than %u hosts.  Please precompute more hostkeys first.\n"),
692                      (unsigned int) system->total_hostkeys);    
693     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
694     if (NULL != emsg)
695       *emsg = emsg_;
696     else
697       GNUNET_free (emsg_);
698     return NULL;
699   }
700   if ((NULL != id) &&
701       (GNUNET_SYSERR == GNUNET_TESTING_hostkey_get (system, key_number, id)))
702   {
703     GNUNET_asprintf (&emsg_,
704                      _("Failed to initialize hostkey for peer %u\n"),
705                      (unsigned int) key_number);
706     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
707     if (NULL != emsg)
708       *emsg = emsg_;
709     else
710       GNUNET_free (emsg_);
711     return NULL;
712   }
713   GNUNET_assert (GNUNET_OK == 
714                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
715                                                         "SERVICEHOME",
716                                                         &service_home));
717   GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/.hostkey",
718                    service_home);
719   fd = GNUNET_DISK_file_open (hostkey_filename,
720                               GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
721                               GNUNET_DISK_PERM_USER_READ 
722                               | GNUNET_DISK_PERM_USER_WRITE);
723   if (NULL == fd)
724   {
725     GNUNET_break (0); 
726     return NULL;
727   }
728   if (HOSTKEYFILESIZE !=
729       GNUNET_DISK_file_write (fd, system->hostkeys_data 
730                               + (key_number * HOSTKEYFILESIZE),
731                               HOSTKEYFILESIZE))
732   {
733     GNUNET_asprintf (&emsg_,
734                      _("Failed to write hostkey file for peer %u: %s\n"),
735                      (unsigned int) key_number,
736                      STRERROR (errno));
737     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
738     if (NULL != emsg)
739       *emsg = emsg_;
740     else
741       GNUNET_free (emsg_);
742     GNUNET_DISK_file_close (fd);
743     return NULL;
744   }
745   GNUNET_DISK_file_close (fd);
746   GNUNET_asprintf (&config_filename, "%s/config", service_home);
747   GNUNET_free (service_home);
748   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
749   {
750     GNUNET_asprintf (&emsg_,
751                      _("Failed to write configuration file `%s' for peer %u: %s\n"),
752                      config_filename,
753                      (unsigned int) key_number,
754                      STRERROR (errno));
755     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
756     if (NULL != emsg)
757       *emsg = emsg_;
758     else
759       GNUNET_free (emsg_);
760     return NULL;
761   }
762   peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
763   peer->cfgfile = config_filename; /* Free in peer_destroy */
764   peer->main_binary = GNUNET_strdup ("gnunet-service-arm");
765   return peer;
766 }
767
768
769 /**
770  * Start the peer. 
771  *
772  * @param peer peer to start
773  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
774  */
775 int
776 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
777 {
778   if (NULL != peer->main_process)
779   {
780     GNUNET_break (0);
781     return GNUNET_SYSERR;
782   }
783   GNUNET_assert (NULL != peer->cfgfile);
784   peer->main_process = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL,
785                                                 peer->main_binary, "-c",
786                                                 peer->cfgfile,
787                                                 NULL);
788   if (NULL == peer->main_process)
789   {
790     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
791                 _("Failed to start `%s': %s\n"),
792                 peer->main_binary,
793                 STRERROR (errno));
794     return GNUNET_SYSERR;
795   }
796   return GNUNET_OK;
797 }
798
799
800 /**
801  * Stop the peer. 
802  *
803  * @param peer peer to stop
804  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
805  */
806 int
807 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
808 {
809   if (NULL == peer->main_process)
810   {
811     GNUNET_break (0);
812     return GNUNET_SYSERR;
813   }
814   (void) GNUNET_OS_process_kill (peer->main_process, SIGTERM);
815   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (peer->main_process));
816   GNUNET_OS_process_destroy (peer->main_process);
817   peer->main_process = NULL;
818   return GNUNET_OK;
819 }
820
821
822 /**
823  * Destroy the peer.  Releases resources locked during peer configuration.
824  * If the peer is still running, it will be stopped AND a warning will be
825  * printed (users of the API should stop the peer explicitly first).
826  *
827  * @param peer peer to destroy
828  */
829 void
830 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
831 {
832   if (NULL != peer->main_process)
833   {
834     GNUNET_break (0);
835     GNUNET_TESTING_peer_stop (peer);
836   }
837   GNUNET_free (peer->cfgfile);
838   GNUNET_free (peer->main_binary);
839   GNUNET_free (peer);
840 }
841
842
843 /**
844  * Start a single peer and run a test using the testing library.
845  * Starts a peer using the given configuration and then invokes the
846  * given callback.  This function ALSO initializes the scheduler loop
847  * and should thus be called directly from "main".  The testcase
848  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
849  *
850  * @param tmppath path for storing temporary data for the test
851  * @param cfgfilename name of the configuration file to use;
852  *         use NULL to only run with defaults
853  * @param tm main function of the testcase
854  * @param tm_cls closure for 'tm'
855  * @return 0 on success, 1 on error
856  */
857 int
858 GNUNET_TESTING_peer_run (const char *tmppath,
859                          const char *cfgfilename,
860                          GNUNET_TESTING_TestMain tm,
861                          void *tm_cls)
862 {
863   return GNUNET_TESTING_service_run (tmppath, "arm",
864                                      cfgfilename, tm, tm_cls);
865 }
866
867
868 /**
869  * Structure for holding service data
870  */
871 struct ServiceContext
872 {
873   /**
874    * The configuration of the peer in which the service is run
875    */
876   const struct GNUNET_CONFIGURATION_Handle *cfg;
877
878   /**
879    * Callback to signal service startup
880    */
881   GNUNET_TESTING_TestMain tm;
882
883   /**
884    * Closure for the above callback
885    */
886   void *tm_cls;
887 };
888
889
890 /**
891  * Callback to be called when SCHEDULER has been started
892  *
893  * @param cls the ServiceContext
894  * @param tc the TaskContext
895  */
896 static void
897 service_run_main (void *cls,
898                   const struct GNUNET_SCHEDULER_TaskContext *tc)
899 {
900   struct ServiceContext *sc = cls;
901
902   sc->tm (sc->tm_cls, sc->cfg);
903 }
904
905
906 /**
907  * Start a single service (no ARM, except of course if the given
908  * service name is 'arm') and run a test using the testing library.
909  * Starts a service using the given configuration and then invokes the
910  * given callback.  This function ALSO initializes the scheduler loop
911  * and should thus be called directly from "main".  The testcase
912  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
913  *
914  * This function is useful if the testcase is for a single service
915  * and if that service doesn't itself depend on other services.
916  *
917  * @param tmppath path for storing temporary data for the test
918  * @param service_name name of the service to run
919  * @param cfgfilename name of the configuration file to use;
920  *         use NULL to only run with defaults
921  * @param tm main function of the testcase
922  * @param tm_cls closure for 'tm'
923  * @return 0 on success, 1 on error
924  */
925 int
926 GNUNET_TESTING_service_run (const char *tmppath,
927                             const char *service_name,
928                             const char *cfgfilename,
929                             GNUNET_TESTING_TestMain tm,
930                             void *tm_cls)
931 {
932   struct ServiceContext sc;
933   struct GNUNET_TESTING_System *system;
934   struct GNUNET_TESTING_Peer *peer;
935   struct GNUNET_CONFIGURATION_Handle *cfg;
936   char *data_dir;
937   char *hostkeys_file;
938   
939   data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
940   GNUNET_asprintf (&hostkeys_file, "%s/testing_hostkeys.dat", data_dir);
941   GNUNET_free (data_dir);  
942   system = GNUNET_TESTING_system_create (tmppath, "localhost");
943   if (NULL == system)
944   {
945     GNUNET_free (hostkeys_file);
946     return 1;
947   }
948   if (GNUNET_OK != GNUNET_TESTING_hostkeys_load (system, hostkeys_file))
949   {
950     GNUNET_free (hostkeys_file);
951     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
952     return 1;
953   }
954   GNUNET_free (hostkeys_file);
955   cfg = GNUNET_CONFIGURATION_create ();
956   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
957   {
958     LOG (GNUNET_ERROR_TYPE_ERROR,
959          _("Failed to load configuration from %s\n"), cfgfilename);
960     GNUNET_CONFIGURATION_destroy (cfg);
961     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
962     return 1;
963   }
964   peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
965   if (NULL == peer)
966   {
967     GNUNET_CONFIGURATION_destroy (cfg);
968     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
969     return 1;
970   }  
971   GNUNET_free (peer->main_binary);
972   GNUNET_asprintf (&peer->main_binary, "gnunet-service-%s", service_name);
973   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
974   {    
975     GNUNET_TESTING_peer_destroy (peer);
976     GNUNET_CONFIGURATION_destroy (cfg);
977     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
978     return 1;
979   }
980   sc.cfg = cfg;
981   sc.tm = tm;
982   sc.tm_cls = tm_cls;
983   GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
984   if (GNUNET_OK != GNUNET_TESTING_peer_stop (peer))
985   {
986     GNUNET_TESTING_peer_destroy (peer);
987     GNUNET_CONFIGURATION_destroy (cfg);
988     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
989     return 1;
990   }
991   GNUNET_TESTING_peer_destroy (peer);
992   GNUNET_CONFIGURATION_destroy (cfg);
993   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
994   return 0;
995 }
996
997
998 /* end of testing_new.c */