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