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