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