c4da3a9737acec0808a3803bace29e794c8dc008
[oweals/gnunet.git] / src / testing / testing.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   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  */
666 struct GNUNET_TESTING_Peer *
667 GNUNET_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  */
783 int
784 GNUNET_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  */
816 int
817 GNUNET_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  */
839 void
840 GNUNET_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  */
867 int
868 GNUNET_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  */
881 struct 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  */
906 static void
907 service_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  */
935 int
936 GNUNET_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 */