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