39d7b717dd525a9f564624270ae1e160f0a7f509
[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 tmppath prefix path to use for all service homes
241  * @param controller hostname of the controlling host, 
242  *        service configurations are modified to allow 
243  *        control connections from this host; can be NULL
244  * @return handle to this system, NULL on error
245  */
246 struct GNUNET_TESTING_System *
247 GNUNET_TESTING_system_create (const char *tmppath,
248                               const char *controller)
249 {
250   struct GNUNET_TESTING_System *system;
251
252   GNUNET_assert (NULL != tmppath);
253   system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System));
254   system->tmppath = GNUNET_DISK_mkdtemp (tmppath);
255   if (NULL == system->tmppath)
256   {
257     GNUNET_free (system);
258     return NULL;
259   }
260   if (NULL != controller)
261     system->controller = GNUNET_strdup (controller);
262   if (GNUNET_OK != hostkeys_load (system))
263   {
264     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
265     return NULL;
266   }
267   return system;
268 }
269
270
271 /**
272  * Free system resources.
273  *
274  * @param system system to be freed
275  * @param remove_paths should the 'tmppath' and all subdirectories
276  *        be removed (clean up on shutdown)?
277  */
278 void
279 GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
280                                int remove_paths)
281 {
282   if (NULL != system->hostkeys_data)
283     hostkeys_unload (system);
284   if (GNUNET_YES == remove_paths)
285     GNUNET_DISK_directory_remove (system->tmppath);
286   GNUNET_free (system->tmppath);
287   GNUNET_free_non_null (system->controller);
288   GNUNET_free (system);
289 }
290
291
292 /**
293  * Reserve a TCP or UDP port for a peer.
294  *
295  * @param system system to use for reservation tracking
296  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
297  * @return 0 if no free port was available
298  */
299 uint16_t 
300 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system,
301                              int is_tcp)
302 {
303   struct GNUNET_NETWORK_Handle *socket;
304   struct addrinfo hint;
305   struct addrinfo *ret;
306   uint32_t *port_buckets;
307   char *open_port_str;
308   int bind_status;
309   uint32_t xor_image;
310   uint16_t index;
311   uint16_t open_port;
312   uint16_t pos;
313
314   /*
315   FIXME: Instead of using getaddrinfo we should try to determine the port
316          status by the following heurestics.
317   
318          On systems which support both IPv4 and IPv6, only ports open on both
319          address families are considered open.
320          On system with either IPv4 or IPv6. A port is considered open if it's
321          open in the respective address family
322   */
323   hint.ai_family = AF_UNSPEC;   /* IPv4 and IPv6 */
324   hint.ai_socktype = (GNUNET_YES == is_tcp)? SOCK_STREAM : SOCK_DGRAM;
325   hint.ai_protocol = 0;
326   hint.ai_addrlen = 0;
327   hint.ai_addr = NULL;
328   hint.ai_canonname = NULL;
329   hint.ai_next = NULL;
330   hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV;  /* Wild card address */
331   port_buckets = (GNUNET_YES == is_tcp) ?
332     system->reserved_tcp_ports : system->reserved_udp_ports;
333   for (index = (LOW_PORT / 32) + 1; index < (HIGH_PORT / 32); index++)
334   {
335     xor_image = (UINT32_MAX ^ port_buckets[index]);
336     if (0 == xor_image)        /* Ports in the bucket are full */
337       continue;
338     pos = 0;
339     while (pos < 32)
340     {
341       if (0 == ((xor_image >> pos) & 1U))
342       {
343         pos++;
344         continue;
345       }
346       open_port = (index * 32) + pos;
347       GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port);
348       ret = NULL;
349       GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
350       GNUNET_free (open_port_str);  
351       socket = GNUNET_NETWORK_socket_create (ret->ai_family,
352                                              (GNUNET_YES == is_tcp) ?
353                                              SOCK_STREAM : SOCK_DGRAM,
354                                              0);
355       GNUNET_assert (NULL != socket);
356       bind_status = GNUNET_NETWORK_socket_bind (socket,
357                                                 ret->ai_addr,
358                                                 ret->ai_addrlen);
359       freeaddrinfo (ret);
360       GNUNET_NETWORK_socket_close (socket);
361       socket = NULL;
362       port_buckets[index] |= (1U << pos); /* Set the port bit */
363       if (GNUNET_OK == bind_status)
364       {
365         LOG (GNUNET_ERROR_TYPE_DEBUG,
366              "Found a free port %u\n", (unsigned int) open_port);
367         return open_port;
368       }
369       pos++;
370     }
371   }
372   return 0;
373 }
374
375
376 /**
377  * Release reservation of a TCP or UDP port for a peer
378  * (used during GNUNET_TESTING_peer_destroy).
379  *
380  * @param system system to use for reservation tracking
381  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
382  * @param port reserved port to release
383  */
384 void
385 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
386                              int is_tcp,
387                              uint16_t port)
388 {
389   uint32_t *port_buckets;
390   uint16_t bucket;
391   uint16_t pos;
392
393   port_buckets = (GNUNET_YES == is_tcp) ?
394     system->reserved_tcp_ports : system->reserved_udp_ports;
395   bucket = port / 32;
396   pos = port % 32;
397   LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
398   if (0 == (port_buckets[bucket] & (1U << pos)))
399   {
400     GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
401     return;
402   }
403   port_buckets[bucket] &= ~(1U << pos);
404 }
405
406
407 /**
408  * Reserve a SERVICEHOME path for a peer.
409  *
410  * @param system system to use for reservation tracking
411  * @return NULL on error, otherwise fresh unique path to use
412  *         as the servicehome for the peer; must be freed by the caller
413  */
414 // static 
415 char *
416 reserve_path (struct GNUNET_TESTING_System *system)
417 {
418   char *reserved_path;
419
420   GNUNET_asprintf (&reserved_path,
421                    "%s/%u", system->tmppath, system->path_counter++);
422   return reserved_path;
423 }             
424
425
426 /**
427  * Testing includes a number of pre-created hostkeys for
428  * faster peer startup.  This function can be used to
429  * access the n-th key of those pre-created hostkeys; note
430  * that these keys are ONLY useful for testing and not
431  * secure as the private keys are part of the public 
432  * GNUnet source code.
433  *
434  * This is primarily a helper function used internally
435  * by 'GNUNET_TESTING_peer_configure'.
436  *
437  * @param system the testing system handle
438  * @param key_number desired pre-created hostkey to obtain
439  * @param id set to the peer's identity (hash of the public
440  *        key; if NULL, GNUNET_SYSERR is returned immediately
441  * @return GNUNET_SYSERR on error (not enough keys)
442  */
443 int
444 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
445                             uint32_t key_number,
446                             struct GNUNET_PeerIdentity *id)
447 {  
448   struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
449   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
450   
451   if ((NULL == id) || (NULL == system->hostkeys_data))
452     return GNUNET_SYSERR;
453   if (key_number >= system->total_hostkeys)
454   {
455     LOG (GNUNET_ERROR_TYPE_ERROR,
456          _("Key number %u does not exist\n"), key_number);
457     return GNUNET_SYSERR;
458   }   
459   private_key = GNUNET_CRYPTO_rsa_decode_key (system->hostkeys_data +
460                                               (key_number * HOSTKEYFILESIZE),
461                                               HOSTKEYFILESIZE);
462   if (NULL == private_key)
463   {
464     LOG (GNUNET_ERROR_TYPE_ERROR,
465          _("Error while decoding key %u\n"), key_number);
466     return GNUNET_SYSERR;
467   }
468   GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
469   GNUNET_CRYPTO_hash (&public_key,
470                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
471                       &(id->hashPubKey));
472   GNUNET_CRYPTO_rsa_key_free (private_key);
473   return GNUNET_OK;
474 }
475
476
477 /**
478  * Structure for holding data to build new configurations from a configuration
479  * template
480  */
481 struct UpdateContext
482 {
483   /**
484    * The system for which we are building configurations
485    */
486   struct GNUNET_TESTING_System *system;
487   
488   /**
489    * The configuration we are building
490    */
491   struct GNUNET_CONFIGURATION_Handle *cfg;
492
493   /**
494    * The customized service home path for this peer
495    */
496   char *service_home;
497
498   /**
499    * build status - to signal error while building a configuration
500    */
501   int status;
502 };
503
504
505 /**
506  * Function to iterate over options.  Copies
507  * the options to the target configuration,
508  * updating PORT values as needed.
509  *
510  * @param cls the UpdateContext
511  * @param section name of the section
512  * @param option name of the option
513  * @param value value of the option
514  */
515 static void
516 update_config (void *cls, const char *section, const char *option,
517                const char *value)
518 {
519   struct UpdateContext *uc = cls;
520   unsigned int ival;
521   char cval[12];
522   char uval[128];
523   char *single_variable;
524   char *per_host_variable;
525   unsigned long long num_per_host;
526   uint16_t new_port;
527
528   if (GNUNET_OK != uc->status)
529     return;
530   if (! ((0 == strcmp (option, "PORT"))
531          || (0 == strcmp (option, "UNIXPATH"))
532          || (0 == strcmp (option, "HOSTNAME"))))
533     return;
534   GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
535   GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
536   if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival)))
537   {
538     if ((ival != 0) &&
539         (GNUNET_YES !=
540          GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
541                                                single_variable)))
542     {
543       /* FIXME: What about UDP? */
544       new_port = GNUNET_TESTING_reserve_port (uc->system, GNUNET_YES);
545       if (0 == new_port)
546       {
547         uc->status = GNUNET_SYSERR;
548         return;
549       }
550       GNUNET_snprintf (cval, sizeof (cval), "%u", new_port);
551       value = cval;
552     }
553     else if ((ival != 0) &&
554              (GNUNET_YES ==
555               GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
556                                                     single_variable)) &&
557              GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
558                                                     per_host_variable,
559                                                     &num_per_host))
560     {
561       /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
562       /*                  ival + ctx->fdnum % num_per_host); */
563       /* value = cval; */
564       GNUNET_break (0);         /* FIXME */
565     }
566   }
567   if (0 == strcmp (option, "UNIXPATH"))
568   {
569     if (GNUNET_YES !=
570         GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
571                                               single_variable))
572     {
573       GNUNET_snprintf (uval, sizeof (uval), "%s/%s.sock",
574                        uc->service_home, section);
575       value = uval;
576     }
577     else if ((GNUNET_YES ==
578               GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
579                                                      per_host_variable,
580                                                      &num_per_host)) &&
581              (num_per_host > 0))
582     {
583       GNUNET_break(0);          /* FIXME */
584     }
585   }
586   if ((0 == strcmp (option, "HOSTNAME")) && (NULL != uc->system->controller))
587   {
588     value = uc->system->controller;
589   }
590   GNUNET_free (single_variable);
591   GNUNET_free (per_host_variable);
592   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
593 }
594
595
596 /**
597  * Section iterator to set ACCEPT_FROM in all sections
598  *
599  * @param cls the UpdateContext
600  * @param section name of the section
601  */
602 static void
603 update_config_sections (void *cls,
604                         const char *section)
605 {
606   struct UpdateContext *uc = cls;
607   char *orig_allowed_hosts;
608   char *allowed_hosts;
609
610   if (GNUNET_OK != 
611       GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "ACCEPT_FROM",
612                                              &orig_allowed_hosts))
613   {
614     orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;");
615   }
616   if (NULL == uc->system->controller)
617     allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
618   else
619     GNUNET_asprintf (&allowed_hosts, "%s%s;", orig_allowed_hosts,
620                      uc->system->controller);
621   GNUNET_free (orig_allowed_hosts);
622   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, "ACCEPT_FROM",
623                                          allowed_hosts);
624   GNUNET_free (allowed_hosts);  
625 }
626
627
628 /**
629  * Create a new configuration using the given configuration
630  * as a template; ports and paths will be modified to select
631  * available ports on the local system.  If we run
632  * out of "*port" numbers, return SYSERR.
633  *
634  * This is primarily a helper function used internally
635  * by 'GNUNET_TESTING_peer_configure'.
636  *
637  * @param system system to use to coordinate resource usage
638  * @param cfg template configuration to update
639  * @return GNUNET_OK on success, GNUNET_SYSERR on error - the configuration will
640  *           be incomplete and should not be used there upon
641  */
642 int
643 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
644                                      struct GNUNET_CONFIGURATION_Handle *cfg)
645 {
646   struct UpdateContext uc;
647   char *default_config;
648   
649   uc.system = system;
650   uc.cfg = cfg;
651   uc.status = GNUNET_OK;
652   GNUNET_asprintf (&uc.service_home, "%s/%u", system->tmppath,
653                    system->path_counter++);
654   GNUNET_asprintf (&default_config, "%s/config", uc.service_home);
655   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "DEFAULTCONFIG",
656                                          default_config);
657   GNUNET_free (default_config);
658   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "SERVICEHOME",
659                                          uc.service_home);
660   /* make PORTs and UNIXPATHs unique */
661   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
662   /* allow connections to services from system controller host */
663   GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
664   /* enable loopback-based connections between peers */
665   GNUNET_CONFIGURATION_set_value_string (cfg, 
666                                          "nat",
667                                          "USE_LOCALADDR", "YES");
668   GNUNET_free (uc.service_home);
669   return uc.status;
670 }
671
672
673 /**
674  * Configure a GNUnet peer.  GNUnet must be installed on the local
675  * system and available in the PATH. 
676  *
677  * @param system system to use to coordinate resource usage
678  * @param cfg configuration to use; will be UPDATED (to reflect needed
679  *            changes in port numbers and paths)
680  * @param key_number number of the hostkey to use for the peer
681  * @param id identifier for the daemon, will be set, can be NULL
682  * @param emsg set to error message (set to NULL on success), can be NULL
683  * @return handle to the peer, NULL on error
684  */
685 struct GNUNET_TESTING_Peer *
686 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
687                                struct GNUNET_CONFIGURATION_Handle *cfg,
688                                uint32_t key_number,
689                                struct GNUNET_PeerIdentity *id,
690                                char **emsg)
691 {
692   struct GNUNET_TESTING_Peer *peer;
693   struct GNUNET_DISK_FileHandle *fd;
694   char *service_home;  
695   char hostkey_filename[128];
696   char *config_filename;
697   char *emsg_;
698
699   if (NULL != emsg)
700     *emsg = NULL;
701   if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg))
702   {
703     GNUNET_asprintf (&emsg_,
704                        _("Failed to create configuration for peer (not enough free ports?)\n"));
705     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
706     if (NULL != emsg)
707       *emsg = emsg_;
708     else
709       GNUNET_free (emsg_);
710     return NULL;
711   }
712   if (key_number >= system->total_hostkeys)
713   {
714     GNUNET_asprintf (&emsg_,
715                      _("You attempted to create a testbed with more than %u hosts.  Please precompute more hostkeys first.\n"),
716                      (unsigned int) system->total_hostkeys);    
717     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
718     if (NULL != emsg)
719       *emsg = emsg_;
720     else
721       GNUNET_free (emsg_);
722     return NULL;
723   }
724   if ((NULL != id) &&
725       (GNUNET_SYSERR == GNUNET_TESTING_hostkey_get (system, key_number, id)))
726   {
727     GNUNET_asprintf (&emsg_,
728                      _("Failed to initialize hostkey for peer %u\n"),
729                      (unsigned int) key_number);
730     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
731     if (NULL != emsg)
732       *emsg = emsg_;
733     else
734       GNUNET_free (emsg_);
735     return NULL;
736   }
737   GNUNET_assert (GNUNET_OK == 
738                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
739                                                         "SERVICEHOME",
740                                                         &service_home));
741   GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/.hostkey",
742                    service_home);
743   GNUNET_free (service_home);
744   fd = GNUNET_DISK_file_open (hostkey_filename,
745                               GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
746                               GNUNET_DISK_PERM_USER_READ 
747                               | GNUNET_DISK_PERM_USER_WRITE);
748   if (NULL == fd)
749   {
750     GNUNET_break (0); 
751     return NULL;
752   }
753   if (HOSTKEYFILESIZE !=
754       GNUNET_DISK_file_write (fd, system->hostkeys_data 
755                               + (key_number * HOSTKEYFILESIZE),
756                               HOSTKEYFILESIZE))
757   {
758     GNUNET_asprintf (&emsg_,
759                      _("Failed to write hostkey file for peer %u: %s\n"),
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_DISK_file_close (fd);
768     return NULL;
769   }
770   GNUNET_DISK_file_close (fd);
771   GNUNET_assert (GNUNET_OK ==
772                  GNUNET_CONFIGURATION_get_value_string 
773                  (cfg, "PATHS", "DEFAULTCONFIG", &config_filename));  
774   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
775   {
776     GNUNET_asprintf (&emsg_,
777                      _("Failed to write configuration file `%s' for peer %u: %s\n"),
778                      config_filename,
779                      (unsigned int) key_number,
780                      STRERROR (errno));
781     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
782     if (NULL != emsg)
783       *emsg = emsg_;
784     else
785       GNUNET_free (emsg_);
786     GNUNET_free (config_filename);
787     return NULL;
788   }
789   peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
790   peer->cfgfile = config_filename; /* Free in peer_destroy */
791   peer->main_binary = GNUNET_strdup ("gnunet-service-arm");
792   return peer;
793 }
794
795
796 /**
797  * Start the peer. 
798  *
799  * @param peer peer to start
800  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
801  */
802 int
803 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
804 {
805   if (NULL != peer->main_process)
806   {
807     GNUNET_break (0);
808     return GNUNET_SYSERR;
809   }
810   GNUNET_assert (NULL != peer->cfgfile);
811   peer->main_process = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL,
812                                                 peer->main_binary,
813                                                 peer->main_binary,
814                                                 "-c",
815                                                 peer->cfgfile,
816                                                 NULL);
817   if (NULL == peer->main_process)
818   {
819     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
820                 _("Failed to start `%s': %s\n"),
821                 peer->main_binary,
822                 STRERROR (errno));
823     return GNUNET_SYSERR;
824   }
825   return GNUNET_OK;
826 }
827
828
829 /**
830  * Stop the peer. 
831  *
832  * @param peer peer to stop
833  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
834  */
835 int
836 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
837 {
838   if (NULL == peer->main_process)
839   {
840     GNUNET_break (0);
841     return GNUNET_SYSERR;
842   }
843   (void) GNUNET_OS_process_kill (peer->main_process, SIGTERM);
844   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (peer->main_process));
845   GNUNET_OS_process_destroy (peer->main_process);
846   peer->main_process = NULL;
847   return GNUNET_OK;
848 }
849
850
851 /**
852  * Destroy the peer.  Releases resources locked during peer configuration.
853  * If the peer is still running, it will be stopped AND a warning will be
854  * printed (users of the API should stop the peer explicitly first).
855  *
856  * @param peer peer to destroy
857  */
858 void
859 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
860 {
861   if (NULL != peer->main_process)
862   {
863     GNUNET_break (0);
864     GNUNET_TESTING_peer_stop (peer);
865   }
866   GNUNET_free (peer->cfgfile);
867   GNUNET_free (peer->main_binary);
868   GNUNET_free (peer);
869 }
870
871
872 /**
873  * Start a single peer and run a test using the testing library.
874  * Starts a peer using the given configuration and then invokes the
875  * given callback.  This function ALSO initializes the scheduler loop
876  * and should thus be called directly from "main".  The testcase
877  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
878  *
879  * @param tmppath path for storing temporary data for the test
880  *        also used to setup the program name for logging
881  * @param cfgfilename name of the configuration file to use;
882  *         use NULL to only run with defaults
883  * @param tm main function of the testcase
884  * @param tm_cls closure for 'tm'
885  * @return 0 on success, 1 on error
886  */
887 int
888 GNUNET_TESTING_peer_run (const char *tmppath,
889                          const char *cfgfilename,
890                          GNUNET_TESTING_TestMain tm,
891                          void *tm_cls)
892 {
893   return GNUNET_TESTING_service_run (tmppath, "arm",
894                                      cfgfilename, tm, tm_cls);
895 }
896
897
898 /**
899  * Structure for holding service data
900  */
901 struct ServiceContext
902 {
903   /**
904    * The configuration of the peer in which the service is run
905    */
906   const struct GNUNET_CONFIGURATION_Handle *cfg;
907
908   /**
909    * Callback to signal service startup
910    */
911   GNUNET_TESTING_TestMain tm;
912
913   /**
914    * Closure for the above callback
915    */
916   void *tm_cls;
917 };
918
919
920 /**
921  * Callback to be called when SCHEDULER has been started
922  *
923  * @param cls the ServiceContext
924  * @param tc the TaskContext
925  */
926 static void
927 service_run_main (void *cls,
928                   const struct GNUNET_SCHEDULER_TaskContext *tc)
929 {
930   struct ServiceContext *sc = cls;
931
932   sc->tm (sc->tm_cls, sc->cfg);
933 }
934
935
936 /**
937  * Start a single service (no ARM, except of course if the given
938  * service name is 'arm') and run a test using the testing library.
939  * Starts a service using the given configuration and then invokes the
940  * given callback.  This function ALSO initializes the scheduler loop
941  * and should thus be called directly from "main".  The testcase
942  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
943  *
944  * This function is useful if the testcase is for a single service
945  * and if that service doesn't itself depend on other services.
946  *
947  * @param tmppath path for storing temporary data for the test,
948  *        also used to setup the program name for logging
949  * @param service_name name of the service to run
950  * @param cfgfilename name of the configuration file to use;
951  *         use NULL to only run with defaults
952  * @param tm main function of the testcase
953  * @param tm_cls closure for 'tm'
954  * @return 0 on success, 1 on error
955  */
956 int
957 GNUNET_TESTING_service_run (const char *tmppath,
958                             const char *service_name,
959                             const char *cfgfilename,
960                             GNUNET_TESTING_TestMain tm,
961                             void *tm_cls)
962 {
963   struct ServiceContext sc;
964   struct GNUNET_TESTING_System *system;
965   struct GNUNET_TESTING_Peer *peer;
966   struct GNUNET_CONFIGURATION_Handle *cfg;
967
968   GNUNET_log_setup (tmppath,
969                     "WARNING",
970                     NULL);
971   system = GNUNET_TESTING_system_create (tmppath, "127.0.0.1");
972   if (NULL == system)
973     return 1;
974   cfg = GNUNET_CONFIGURATION_create ();
975   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
976   {
977     LOG (GNUNET_ERROR_TYPE_ERROR,
978          _("Failed to load configuration from %s\n"), cfgfilename);
979     GNUNET_CONFIGURATION_destroy (cfg);
980     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
981     return 1;
982   }
983   peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
984   if (NULL == peer)
985   {
986     GNUNET_CONFIGURATION_destroy (cfg);
987     hostkeys_unload (system);
988     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
989     return 1;
990   }
991   GNUNET_free (peer->main_binary);
992   GNUNET_asprintf (&peer->main_binary, "gnunet-service-%s", service_name);
993   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
994   {    
995     GNUNET_TESTING_peer_destroy (peer);
996     GNUNET_CONFIGURATION_destroy (cfg);
997     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
998     return 1;
999   }
1000   sc.cfg = cfg;
1001   sc.tm = tm;
1002   sc.tm_cls = tm_cls;
1003   GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
1004   if (GNUNET_OK != GNUNET_TESTING_peer_stop (peer))
1005   {
1006     GNUNET_TESTING_peer_destroy (peer);
1007     GNUNET_CONFIGURATION_destroy (cfg);
1008     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1009     return 1;
1010   }
1011   GNUNET_TESTING_peer_destroy (peer);
1012   GNUNET_CONFIGURATION_destroy (cfg);
1013   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1014   return 0;
1015 }
1016
1017
1018 /**
1019  * Sometimes we use the binary name to determine which specific
1020  * test to run.  In those cases, the string after the last "_"
1021  * in 'argv[0]' specifies a string that determines the configuration
1022  * file or plugin to use.  
1023  *
1024  * This function returns the respective substring, taking care
1025  * of issues such as binaries ending in '.exe' on W32.
1026  *
1027  * @param argv0 the name of the binary
1028  * @return string between the last '_' and the '.exe' (or the end of the string),
1029  *         NULL if argv0 has no '_' 
1030  */
1031 char *
1032 GNUNET_TESTING_get_testname_from_underscore (const char *argv0)
1033 {
1034   size_t slen = strlen (argv0) + 1;
1035   char sbuf[slen];
1036   char *ret;
1037   char *dot;
1038
1039   memcpy (sbuf, argv0, slen);
1040   ret = strrchr (sbuf, '_');
1041   if (NULL == ret)
1042     return NULL;
1043   ret++; /* skip underscore */
1044   dot = strchr (ret, '.');
1045   if (NULL != dot)
1046     *dot = '\0';
1047   return GNUNET_strdup (ret);
1048 }
1049
1050
1051 /* end of testing.c */