eb039435da82c0ea6735a2710d20227a5ab22d0b
[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_arm_service.h"
35 #include "gnunet_testing_lib.h"
36
37 #define LOG(kind,...)                                           \
38   GNUNET_log_from (kind, "testing-api", __VA_ARGS__)
39
40
41 /**
42  * We need pipe control only on WINDOWS
43  */
44 #if WINDOWS
45 #define PIPE_CONTROL GNUNET_YES
46 #else
47 #define PIPE_CONTROL GNUNET_NO
48 #endif
49
50
51 /**
52  * Lowest port used for GNUnet testing.  Should be high enough to not
53  * conflict with other applications running on the hosts but be low
54  * enough to not conflict with client-ports (typically starting around
55  * 32k).
56  */
57 #define LOW_PORT 12000
58
59 /**
60  * Highest port used for GNUnet testing.  Should be low enough to not
61  * conflict with the port range for "local" ports (client apps; see
62  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
63  */
64 #define HIGH_PORT 56000
65
66
67 /**
68  * Handle for a system on which GNUnet peers are executed;
69  * a system is used for reserving unique paths and ports.
70  */
71 struct GNUNET_TESTING_System
72 {
73   /**
74    * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
75    * SERVICEHOME.    */
76   char *tmppath;
77
78   /**
79    * The trusted ip. Can either be a single ip address or a network address in
80    * CIDR notation.
81    */
82   char *trusted_ip;
83
84   /**
85    * our hostname
86    */
87   char *hostname;
88
89   /**
90    * Hostkeys data, contains "GNUNET_TESTING_HOSTKEYFILESIZE * total_hostkeys" bytes.
91    */
92   char *hostkeys_data;
93
94   /**
95    * Bitmap where each TCP port that has already been reserved for
96    * some GNUnet peer is recorded.  Note that we additionally need to
97    * test if a port is already in use by non-GNUnet components before
98    * assigning it to a peer/service.  If we detect that a port is
99    * already in use, we also mark it in this bitmap.  So all the bits
100    * that are zero merely indicate ports that MIGHT be available for
101    * peers.
102    */
103   uint32_t reserved_tcp_ports[65536 / 32];
104
105   /**
106    * Bitmap where each UDP port that has already been reserved for
107    * some GNUnet peer is recorded.  Note that we additionally need to
108    * test if a port is already in use by non-GNUnet components before
109    * assigning it to a peer/service.  If we detect that a port is
110    * already in use, we also mark it in this bitmap.  So all the bits
111    * that are zero merely indicate ports that MIGHT be available for
112    * peers.
113    */
114   uint32_t reserved_udp_ports[65536 / 32];
115
116   /**
117    * Counter we use to make service home paths unique on this system;
118    * the full path consists of the tmppath and this number.  Each
119    * UNIXPATH for a peer is also modified to include the respective
120    * path counter to ensure uniqueness.  This field is incremented
121    * by one for each configured peer.  Even if peers are destroyed,
122    * we never re-use path counters.
123    */
124   uint32_t path_counter;  
125
126   /**
127    * The number of hostkeys
128    */
129   uint32_t total_hostkeys;
130
131   /**
132    * Lowest port we are allowed to use.
133    */
134   uint16_t lowport;
135
136   /**
137    * Highest port we are allowed to use.
138    */
139   uint16_t highport;
140 };
141
142
143 /**
144  * Handle for a GNUnet peer controlled by testing.
145  */
146 struct GNUNET_TESTING_Peer
147 {
148   /**
149    * The TESTING system associated with this peer
150    */
151   struct GNUNET_TESTING_System *system;
152
153   /**
154    * Path to the configuration file for this peer.
155    */
156   char *cfgfile;
157
158   /**
159    * Binary to be executed during 'GNUNET_TESTING_peer_start'.
160    * Typically 'gnunet-service-arm' (but can be set to a 
161    * specific service by 'GNUNET_TESTING_service_run' if
162    * necessary).
163    */ 
164   char *main_binary;
165   char *args;
166   
167   /**
168    * Handle to the running binary of the service, NULL if the
169    * peer/service is currently not running.
170    */
171   struct GNUNET_OS_Process *main_process;
172
173   /**
174    * The handle to the peer's ARM service
175    */
176   struct GNUNET_ARM_Handle *ah;
177
178   /**
179    * Handle to ARM monitoring
180    */
181   struct GNUNET_ARM_MonitorHandle *mh;
182
183   /**
184    * The config of the peer
185    */
186   struct GNUNET_CONFIGURATION_Handle *cfg;
187
188   /**
189    * The callback to call asynchronously when a peer is stopped
190    */  
191   GNUNET_TESTING_PeerStopCallback cb;
192   
193   /**
194    * The closure for the above callback
195    */
196   void *cb_cls;
197   
198   /**
199    * The cached identity of this peer.  Will be populated on call to
200    * GNUNET_TESTING_peer_get_identity()
201    */
202   struct GNUNET_PeerIdentity *id;
203
204   /**
205    * Array of ports currently allocated to this peer.  These ports will be
206    * released upon peer destroy and can be used by other peers which are
207    * configured after.
208    */
209   uint16_t *ports;
210
211   /**
212    * The number of ports in the above array
213    */
214   unsigned int nports;
215
216   /**
217    * The keynumber of this peer's hostkey
218    */
219   uint32_t key_number;
220 };
221
222
223 /**
224  * Testing includes a number of pre-created hostkeys for faster peer
225  * startup. This function loads such keys into memory from a file.
226  *
227  * @param system the testing system handle
228  * @return GNUNET_OK on success; GNUNET_SYSERR on error
229  */
230 static int
231 hostkeys_load (struct GNUNET_TESTING_System *system)
232 {
233   uint64_t fs; 
234   char *data_dir;
235   char *filename;
236   
237   GNUNET_assert (NULL == system->hostkeys_data);
238   data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
239   GNUNET_asprintf (&filename, "%s/testing_hostkeys.ecc", data_dir);
240   GNUNET_free (data_dir);  
241
242   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
243   {
244     LOG (GNUNET_ERROR_TYPE_ERROR,
245          _("Hostkeys file not found: %s\n"), filename);
246     GNUNET_free (filename);
247     return GNUNET_SYSERR;
248   }
249   /* Check hostkey file size, read entire thing into memory */
250   if (GNUNET_OK != 
251       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
252     fs = 0;
253   if (0 == fs)
254   {
255     GNUNET_free (filename);
256     return GNUNET_SYSERR;       /* File is empty */
257   }
258   if (0 != (fs % GNUNET_TESTING_HOSTKEYFILESIZE))
259   {
260     LOG (GNUNET_ERROR_TYPE_ERROR,
261          _("Incorrect hostkey file format: %s\n"), filename);
262     GNUNET_free (filename);
263     return GNUNET_SYSERR;
264   }
265   system->hostkeys_data = GNUNET_malloc_large ((size_t) fs);
266   if (GNUNET_DISK_fn_read (filename, system->hostkeys_data, fs) != (ssize_t) fs)
267   {
268     GNUNET_free (filename);
269     GNUNET_free (system->hostkeys_data);
270     system->hostkeys_data = NULL;
271     return GNUNET_SYSERR;
272   }
273   system->total_hostkeys = fs / GNUNET_TESTING_HOSTKEYFILESIZE;
274   GNUNET_free (filename);
275   return GNUNET_OK;
276 }
277
278
279 /**
280  * Function to remove the loaded hostkeys
281  *
282  * @param system the testing system handle
283  */
284 static void
285 hostkeys_unload (struct GNUNET_TESTING_System *system)
286 {
287   GNUNET_break (NULL != system->hostkeys_data);
288   GNUNET_free_non_null (system->hostkeys_data);
289   system->hostkeys_data = NULL;
290   system->total_hostkeys = 0;
291 }
292
293
294 /**
295  * Create a system handle.  There must only be one system
296  * handle per operating system.
297  *
298  * @param testdir only the directory name without any path. This is used for
299  *          all service homes; the directory will be created in a temporary
300  *          location depending on the underlying OS
301  * @param trusted_ip the ip address which will be set as TRUSTED HOST in all
302  *          service configurations generated to allow control connections from
303  *          this ip. This can either be a single ip address or a network address
304  *          in CIDR notation.
305  * @param hostname the hostname of the system we are using for testing; NULL for
306  *          localhost
307  * @param lowport lowest port number this system is allowed to allocate (inclusive)
308  * @param highport highest port number this system is allowed to allocate (exclusive)
309  * @return handle to this system, NULL on error
310  */
311 struct GNUNET_TESTING_System *
312 GNUNET_TESTING_system_create_with_portrange (const char *testdir,
313                                              const char *trusted_ip,
314                                              const char *hostname,
315                                              uint16_t lowport,
316                                              uint16_t highport)
317 {
318   struct GNUNET_TESTING_System *system;
319
320   GNUNET_assert (NULL != testdir);
321   system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System));
322   system->tmppath = GNUNET_DISK_mkdtemp (testdir);
323   system->lowport = lowport;
324   system->highport = highport;
325   if (NULL == system->tmppath)
326   {
327     GNUNET_free (system);
328     return NULL;
329   }
330   if (NULL != trusted_ip)
331     system->trusted_ip = GNUNET_strdup (trusted_ip);
332   if (NULL != hostname)
333     system->hostname = GNUNET_strdup (hostname);
334   if (GNUNET_OK != hostkeys_load (system))
335   {
336     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
337     return NULL;
338   }
339   return system;
340 }
341
342
343 /**
344  * Create a system handle.  There must only be one system handle per operating
345  * system.  Uses a default range for allowed ports.  Ports are still tested for
346  * availability.
347  *
348  * @param testdir only the directory name without any path. This is used for all
349  *          service homes; the directory will be created in a temporary location
350  *          depending on the underlying OS
351  * @param trusted_ip the ip address which will be set as TRUSTED HOST in all
352  *          service configurations generated to allow control connections from
353  *          this ip. This can either be a single ip address or a network address
354  *          in CIDR notation.
355  * @param hostname the hostname of the system we are using for testing; NULL for
356  *          localhost
357  * @return handle to this system, NULL on error
358  */
359 struct GNUNET_TESTING_System *
360 GNUNET_TESTING_system_create (const char *testdir,
361                               const char *trusted_ip,
362                               const char *hostname)
363 {
364   return GNUNET_TESTING_system_create_with_portrange (testdir,
365                                                       trusted_ip,
366                                                       hostname,
367                                                       LOW_PORT,
368                                                       HIGH_PORT);
369 }
370
371
372 /**
373  * Free system resources.
374  *
375  * @param system system to be freed
376  * @param remove_paths should the 'testdir' and all subdirectories
377  *        be removed (clean up on shutdown)?
378  */
379 void
380 GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
381                                int remove_paths)
382 {
383   if (NULL != system->hostkeys_data)
384     hostkeys_unload (system);
385   if (GNUNET_YES == remove_paths)
386     GNUNET_DISK_directory_remove (system->tmppath);
387   GNUNET_free (system->tmppath);
388   GNUNET_free_non_null (system->trusted_ip);
389   GNUNET_free_non_null (system->hostname);
390   GNUNET_free (system);
391 }
392
393
394 /**
395  * Reserve a TCP or UDP port for a peer.
396  *
397  * @param system system to use for reservation tracking
398  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
399  * @return 0 if no free port was available
400  */
401 uint16_t 
402 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system,
403                              int is_tcp)
404 {
405   struct GNUNET_NETWORK_Handle *socket;
406   struct addrinfo hint;
407   struct addrinfo *ret;
408   struct addrinfo *ai;
409   uint32_t *port_buckets;
410   char *open_port_str;
411   int bind_status;
412   uint32_t xor_image;
413   uint16_t index;
414   uint16_t open_port;
415   uint16_t pos;
416
417   /*
418   FIXME: Instead of using getaddrinfo we should try to determine the port
419          status by the following heurestics.
420   
421          On systems which support both IPv4 and IPv6, only ports open on both
422          address families are considered open.
423          On system with either IPv4 or IPv6. A port is considered open if it's
424          open in the respective address family
425   */
426   hint.ai_family = AF_UNSPEC;   /* IPv4 and IPv6 */
427   hint.ai_socktype = (GNUNET_YES == is_tcp)? SOCK_STREAM : SOCK_DGRAM;
428   hint.ai_protocol = 0;
429   hint.ai_addrlen = 0;
430   hint.ai_addr = NULL;
431   hint.ai_canonname = NULL;
432   hint.ai_next = NULL;
433   hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV;  /* Wild card address */
434   port_buckets = (GNUNET_YES == is_tcp) ?
435     system->reserved_tcp_ports : system->reserved_udp_ports;
436   for (index = (system->lowport / 32) + 1; index < (system->highport / 32); index++)
437   {
438     xor_image = (UINT32_MAX ^ port_buckets[index]);
439     if (0 == xor_image)        /* Ports in the bucket are full */
440       continue;
441     pos = system->lowport % 32;
442     while (pos < 32)
443     {
444       if (0 == ((xor_image >> pos) & 1U))
445       {
446         pos++;
447         continue;
448       }
449       open_port = (index * 32) + pos;
450       if (open_port >= system->highport)
451         return 0;
452       GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port);
453       ret = NULL;
454       GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
455       GNUNET_free (open_port_str);
456       bind_status = GNUNET_NO;
457       for (ai = ret; NULL != ai; ai = ai->ai_next)
458       {
459         socket = GNUNET_NETWORK_socket_create (ai->ai_family,
460                                                (GNUNET_YES == is_tcp) ?
461                                                SOCK_STREAM : SOCK_DGRAM,
462                                                0);
463         if (NULL == socket)
464           continue;
465         bind_status = GNUNET_NETWORK_socket_bind (socket,
466                                                   ai->ai_addr,
467                                                   ai->ai_addrlen);
468         GNUNET_NETWORK_socket_close (socket);
469         if (GNUNET_OK != bind_status)
470           break;
471       }
472       port_buckets[index] |= (1U << pos); /* Set the port bit */
473       freeaddrinfo (ret);
474       if (GNUNET_OK == bind_status)
475       {
476         LOG (GNUNET_ERROR_TYPE_DEBUG,
477              "Found a free port %u\n", (unsigned int) open_port);
478         return open_port;
479       }
480       pos++;
481     }
482   }
483   return 0;
484 }
485
486
487 /**
488  * Release reservation of a TCP or UDP port for a peer
489  * (used during GNUNET_TESTING_peer_destroy).
490  *
491  * @param system system to use for reservation tracking
492  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
493  * @param port reserved port to release
494  */
495 void
496 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
497                              int is_tcp,
498                              uint16_t port)
499 {
500   uint32_t *port_buckets;
501   uint16_t bucket;
502   uint16_t pos;
503
504   port_buckets = (GNUNET_YES == is_tcp) ?
505     system->reserved_tcp_ports : system->reserved_udp_ports;
506   bucket = port / 32;
507   pos = port % 32;
508   LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
509   if (0 == (port_buckets[bucket] & (1U << pos)))
510   {
511     GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
512     return;
513   }
514   port_buckets[bucket] &= ~(1U << pos);
515 }
516
517
518 /**
519  * Testing includes a number of pre-created hostkeys for
520  * faster peer startup.  This function can be used to
521  * access the n-th key of those pre-created hostkeys; note
522  * that these keys are ONLY useful for testing and not
523  * secure as the private keys are part of the public 
524  * GNUnet source code.
525  *
526  * This is primarily a helper function used internally
527  * by 'GNUNET_TESTING_peer_configure'.
528  *
529  * @param system the testing system handle
530  * @param key_number desired pre-created hostkey to obtain
531  * @param id set to the peer's identity (hash of the public
532  *        key; if NULL, GNUNET_SYSERR is returned immediately
533  * @return NULL on error (not enough keys)
534  */
535 struct GNUNET_CRYPTO_EccPrivateKey *
536 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
537                             uint32_t key_number,
538                             struct GNUNET_PeerIdentity *id)
539 {  
540   struct GNUNET_CRYPTO_EccPrivateKey *private_key;
541   struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key;
542   
543   if ((NULL == id) || (NULL == system->hostkeys_data))
544     return NULL;
545   if (key_number >= system->total_hostkeys)
546   {
547     LOG (GNUNET_ERROR_TYPE_ERROR,
548          _("Key number %u does not exist\n"), key_number);
549     return NULL;
550   }   
551   private_key = GNUNET_CRYPTO_ecc_decode_key (system->hostkeys_data +
552                                               (key_number *
553                                                GNUNET_TESTING_HOSTKEYFILESIZE),
554                                               GNUNET_TESTING_HOSTKEYFILESIZE,
555                                               GNUNET_NO);
556   if (NULL == private_key)
557   {
558     LOG (GNUNET_ERROR_TYPE_ERROR,
559          _("Error while decoding key %u\n"), key_number);
560     return NULL;
561   }
562   GNUNET_CRYPTO_ecc_key_get_public (private_key, &public_key);
563   GNUNET_CRYPTO_hash (&public_key,
564                       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
565                       &(id->hashPubKey));
566   return private_key;
567 }
568
569
570 /**
571  * Structure for holding data to build new configurations from a configuration
572  * template
573  */
574 struct UpdateContext
575 {
576   /**
577    * The system for which we are building configurations
578    */
579   struct GNUNET_TESTING_System *system;
580   
581   /**
582    * The configuration we are building
583    */
584   struct GNUNET_CONFIGURATION_Handle *cfg;
585
586   /**
587    * The customized service home path for this peer
588    */
589   char *service_home;
590
591   /**
592    * Array of ports currently allocated to this peer.  These ports will be
593    * released upon peer destroy and can be used by other peers which are
594    * configured after.
595    */
596   uint16_t *ports;
597
598   /**
599    * The number of ports in the above array
600    */
601   unsigned int nports;
602
603   /**
604    * build status - to signal error while building a configuration
605    */
606   int status;
607 };
608
609
610 /**
611  * Function to iterate over options.  Copies
612  * the options to the target configuration,
613  * updating PORT values as needed.
614  *
615  * @param cls the UpdateContext
616  * @param section name of the section
617  * @param option name of the option
618  * @param value value of the option
619  */
620 static void
621 update_config (void *cls, const char *section, const char *option,
622                const char *value)
623 {
624   struct UpdateContext *uc = cls;
625   unsigned int ival;
626   char cval[12];
627   char uval[128];
628   char *single_variable;
629   char *per_host_variable;
630   unsigned long long num_per_host;
631   uint16_t new_port;
632
633   if (GNUNET_OK != uc->status)
634     return;
635   if (! ((0 == strcmp (option, "PORT"))
636          || (0 == strcmp (option, "UNIXPATH"))
637          || (0 == strcmp (option, "HOSTNAME"))))
638     return;
639   GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
640   GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
641   if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival)))
642   {
643     if ((ival != 0) &&
644         (GNUNET_YES !=
645          GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
646                                                single_variable)))
647     {
648       /* FIXME: What about UDP? */
649       new_port = GNUNET_TESTING_reserve_port (uc->system, GNUNET_YES);
650       if (0 == new_port)
651       {
652         uc->status = GNUNET_SYSERR;
653         GNUNET_free (single_variable);
654         GNUNET_free (per_host_variable);
655         return;
656       }
657       GNUNET_snprintf (cval, sizeof (cval), "%u", new_port);
658       value = cval;
659       GNUNET_array_append (uc->ports, uc->nports, new_port);
660     }
661     else if ((ival != 0) &&
662              (GNUNET_YES ==
663               GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
664                                                     single_variable)) &&
665              GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
666                                                     per_host_variable,
667                                                     &num_per_host))
668     {
669       /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
670       /*                  ival + ctx->fdnum % num_per_host); */
671       /* value = cval; */
672       GNUNET_break (0);         /* FIXME */
673     }
674   }
675   if (0 == strcmp (option, "UNIXPATH"))
676   {
677     if (GNUNET_YES !=
678         GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
679                                               single_variable))
680     {
681       GNUNET_snprintf (uval, sizeof (uval), "%s/%s.sock",
682                        uc->service_home, section);
683       value = uval;
684     }
685     else if ((GNUNET_YES ==
686               GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
687                                                      per_host_variable,
688                                                      &num_per_host)) &&
689              (num_per_host > 0))
690     {
691       GNUNET_break(0);          /* FIXME */
692     }
693   }
694   if (0 == strcmp (option, "HOSTNAME"))
695   {
696     value = (NULL == uc->system->hostname) ? "localhost" : uc->system->hostname;
697   }
698   GNUNET_free (single_variable);
699   GNUNET_free (per_host_variable);
700   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
701 }
702
703
704 /**
705  * Section iterator to set ACCEPT_FROM/ACCEPT_FROM6 to include the address of
706  * 'trusted_hosts' in all sections
707  *
708  * @param cls the UpdateContext
709  * @param section name of the section
710  */
711 static void
712 update_config_sections (void *cls,
713                         const char *section)
714 {
715   struct UpdateContext *uc = cls;  
716   char **ikeys;
717   char *val;
718   char *ptr;
719   char *orig_allowed_hosts;
720   char *allowed_hosts;
721   char *ACCEPT_FROM_key;
722   uint16_t ikeys_cnt;
723   uint16_t key;
724   
725   ikeys_cnt = 0;
726   val = NULL;
727   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section,
728                                                      "TESTING_IGNORE_KEYS"))
729   {
730     GNUNET_assert 
731       (GNUNET_YES == 
732        GNUNET_CONFIGURATION_get_value_string (uc->cfg, section,
733                                               "TESTING_IGNORE_KEYS", &val));
734     ptr = val;
735     for (ikeys_cnt = 0; NULL != (ptr = strstr (ptr, ";")); ikeys_cnt++)
736       ptr++;
737     if (0 == ikeys_cnt)
738       GNUNET_break (0);
739     else
740     {
741       ikeys = GNUNET_malloc ((sizeof (char *)) * ikeys_cnt);
742       ptr = val;
743       for (key = 0; key < ikeys_cnt; key++)
744       {
745         ikeys[key] = ptr;
746         ptr = strstr (ptr, ";");
747         *ptr = '\0';
748         ptr++;
749       }
750     }
751   }
752   if (0 != ikeys_cnt)
753   {
754     for (key = 0; key < ikeys_cnt; key++)
755     {
756       if (NULL != strstr (ikeys[key], "ADVERTISED_PORT"))
757         break;
758     }
759     if ((key == ikeys_cnt) &&
760         (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section,
761                                                         "ADVERTISED_PORT")))
762     {
763       if (GNUNET_OK == 
764           GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "PORT", &ptr))
765       {
766         GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, 
767                                                "ADVERTISED_PORT", ptr);
768         GNUNET_free (ptr);
769       }
770     }
771     for (key = 0; key < ikeys_cnt; key++)
772     {
773       if (NULL != strstr (ikeys[key], "ACCEPT_FROM"))
774       {
775         GNUNET_free (ikeys);
776         GNUNET_free (val);
777         return;
778       }
779     }
780     GNUNET_free (ikeys);
781   }
782   GNUNET_free_non_null (val);
783   ACCEPT_FROM_key = "ACCEPT_FROM";  
784   if ((NULL != uc->system->trusted_ip) && 
785       (NULL != strstr (uc->system->trusted_ip, ":"))) /* IPv6 in use */
786     ACCEPT_FROM_key = "ACCEPT_FROM6";
787   if (GNUNET_OK != 
788       GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, ACCEPT_FROM_key,
789                                              &orig_allowed_hosts))
790   {
791     orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;");
792   }
793   if (NULL == uc->system->trusted_ip)
794     allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
795   else
796     GNUNET_asprintf (&allowed_hosts, "%s%s;", orig_allowed_hosts,
797                      uc->system->trusted_ip);
798   GNUNET_free (orig_allowed_hosts);
799   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, ACCEPT_FROM_key,
800                                          allowed_hosts);
801   GNUNET_free (allowed_hosts);  
802 }
803
804
805 /**
806  * Create a new configuration using the given configuration as a template;
807  * ports and paths will be modified to select available ports on the local
808  * system. The default configuration will be available in PATHS section under
809  * the option DEFAULTCONFIG after the call. SERVICE_HOME is also set in PATHS
810  * section to the temporary directory specific to this configuration. If we run
811  * out of "*port" numbers, return SYSERR.
812  *
813  * This is primarily a helper function used internally
814  * by 'GNUNET_TESTING_peer_configure'.
815  *
816  * @param system system to use to coordinate resource usage
817  * @param cfg template configuration to update
818  * @return GNUNET_OK on success, GNUNET_SYSERR on error - the configuration will
819  *           be incomplete and should not be used there upon
820  */
821 static int
822 GNUNET_TESTING_configuration_create_ (struct GNUNET_TESTING_System *system,
823                                       struct GNUNET_CONFIGURATION_Handle *cfg,
824                                       uint16_t **ports,
825                                       unsigned int *nports)
826 {
827   struct UpdateContext uc;
828   char *default_config;
829   
830   uc.system = system;
831   uc.cfg = cfg;
832   uc.status = GNUNET_OK;
833   uc.ports = NULL;
834   uc.nports = 0;
835   GNUNET_asprintf (&uc.service_home, "%s/%u", system->tmppath,
836                    system->path_counter++);
837   GNUNET_asprintf (&default_config, "%s/config", uc.service_home);
838   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "DEFAULTCONFIG",
839                                          default_config);
840   GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG",
841                                          default_config);
842   GNUNET_free (default_config);
843   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "SERVICEHOME",
844                                          uc.service_home);
845   /* make PORTs and UNIXPATHs unique */
846   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
847   /* allow connections to services from system trusted_ip host */
848   GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
849   /* enable loopback-based connections between peers */
850   GNUNET_CONFIGURATION_set_value_string (cfg, 
851                                          "nat",
852                                          "USE_LOCALADDR", "YES");
853   GNUNET_free (uc.service_home);
854   if ((NULL != ports) && (NULL != nports))
855   {
856     *ports = uc.ports;
857     *nports = uc.nports;
858   }
859   else
860     GNUNET_free_non_null (uc.ports);
861   return uc.status;
862 }
863
864
865 /**
866  * Create a new configuration using the given configuration as a template;
867  * ports and paths will be modified to select available ports on the local
868  * system. The default configuration will be available in PATHS section under
869  * the option DEFAULTCONFIG after the call. SERVICE_HOME is also set in PATHS
870  * section to the temporary directory specific to this configuration. If we run
871  * out of "*port" numbers, return SYSERR.
872  *
873  * This is primarily a helper function used internally
874  * by 'GNUNET_TESTING_peer_configure'.
875  *
876  * @param system system to use to coordinate resource usage
877  * @param cfg template configuration to update
878  * @return GNUNET_OK on success, GNUNET_SYSERR on error - the configuration will
879  *           be incomplete and should not be used there upon
880  */
881 int
882 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
883                                      struct GNUNET_CONFIGURATION_Handle *cfg)
884 {
885   return GNUNET_TESTING_configuration_create_ (system, cfg, NULL, NULL);
886 }
887
888
889 /**
890  * Configure a GNUnet peer.  GNUnet must be installed on the local
891  * system and available in the PATH. 
892  *
893  * @param system system to use to coordinate resource usage
894  * @param cfg configuration to use; will be UPDATED (to reflect needed
895  *            changes in port numbers and paths)
896  * @param key_number number of the hostkey to use for the peer
897  * @param id identifier for the daemon, will be set, can be NULL
898  * @param emsg set to freshly allocated error message (set to NULL on success), 
899  *          can be NULL
900  * @return handle to the peer, NULL on error
901  */
902 struct GNUNET_TESTING_Peer *
903 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
904                                struct GNUNET_CONFIGURATION_Handle *cfg,
905                                uint32_t key_number,
906                                struct GNUNET_PeerIdentity *id,
907                                char **emsg)
908 {
909   struct GNUNET_TESTING_Peer *peer;
910   struct GNUNET_DISK_FileHandle *fd;
911   char *service_home;  
912   char hostkey_filename[128];
913   char *config_filename;
914   char *libexec_binary;
915   char *emsg_;
916   struct GNUNET_CRYPTO_EccPrivateKey *pk;
917   uint16_t *ports;
918   unsigned int nports;      
919
920   ports = NULL;
921   nports = 0;
922   if (NULL != emsg)
923     *emsg = NULL;
924   if (GNUNET_OK != GNUNET_TESTING_configuration_create_ (system, cfg,
925                                                          &ports, &nports))
926   {
927     GNUNET_asprintf (&emsg_,
928                      _("Failed to create configuration for peer "
929                        "(not enough free ports?)\n"));
930     goto err_ret;
931   }
932   if (key_number >= system->total_hostkeys)
933   {
934     GNUNET_asprintf (&emsg_,
935                      _("You attempted to create a testbed with more than %u hosts.  Please precompute more hostkeys first.\n"),
936                      (unsigned int) system->total_hostkeys);    
937     goto err_ret;
938   }
939   pk = NULL;
940   if ((NULL != id) &&
941       (NULL == (pk = GNUNET_TESTING_hostkey_get (system, key_number, id))))
942   {
943     GNUNET_asprintf (&emsg_,
944                      _("Failed to initialize hostkey for peer %u\n"),
945                      (unsigned int) key_number);
946     goto err_ret;
947   }
948   if (NULL != pk)
949     GNUNET_CRYPTO_ecc_key_free (pk);
950   GNUNET_assert (GNUNET_OK == 
951                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
952                                                         "SERVICEHOME",
953                                                         &service_home));
954   /* FIXME: might be better to evaluate actual configuration option here... */
955   GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/private.ecc",
956                    service_home);
957   GNUNET_free (service_home);
958   fd = GNUNET_DISK_file_open (hostkey_filename,
959                               GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
960                               GNUNET_DISK_PERM_USER_READ 
961                               | GNUNET_DISK_PERM_USER_WRITE);
962   if (NULL == fd)
963   {
964     GNUNET_asprintf (&emsg_, _("Cannot open hostkey file: %s\n"),
965                      STRERROR (errno));
966     goto err_ret;
967   }
968   if (GNUNET_TESTING_HOSTKEYFILESIZE !=
969       GNUNET_DISK_file_write (fd, system->hostkeys_data 
970                               + (key_number * GNUNET_TESTING_HOSTKEYFILESIZE),
971                               GNUNET_TESTING_HOSTKEYFILESIZE))
972   {
973     GNUNET_asprintf (&emsg_,
974                      _("Failed to write hostkey file for peer %u: %s\n"),
975                      (unsigned int) key_number,
976                      STRERROR (errno));
977     GNUNET_DISK_file_close (fd);
978     goto err_ret;
979   }
980   GNUNET_DISK_file_close (fd);
981   GNUNET_assert (GNUNET_OK ==
982                  GNUNET_CONFIGURATION_get_value_string 
983                  (cfg, "PATHS", "DEFAULTCONFIG", &config_filename));  
984   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
985   {
986     GNUNET_asprintf (&emsg_,
987                      _("Failed to write configuration file `%s' for peer %u: %s\n"),
988                      config_filename,
989                      (unsigned int) key_number,
990                      STRERROR (errno));    
991     GNUNET_free (config_filename);
992     goto err_ret;
993   }
994   peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
995   peer->cfgfile = config_filename; /* Free in peer_destroy */
996   peer->cfg = GNUNET_CONFIGURATION_dup (cfg);
997   libexec_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
998   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "arm", "PREFIX", &peer->main_binary))
999   {
1000     /* No prefix */
1001     GNUNET_asprintf(&peer->main_binary, "%s", libexec_binary);
1002     peer->args = strdup ("");
1003   }
1004   else
1005     peer->args = strdup (libexec_binary);
1006   peer->system = system;
1007   peer->key_number = key_number;
1008   GNUNET_free (libexec_binary);
1009   peer->ports = ports;          /* Free in peer_destroy */
1010   peer->nports = nports;
1011   return peer;
1012
1013  err_ret:
1014   GNUNET_free_non_null (ports);
1015   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
1016   if (NULL != emsg)
1017     *emsg = emsg_;
1018   else
1019     GNUNET_free (emsg_);
1020   return NULL;
1021 }
1022
1023
1024 /**
1025  * Obtain the peer identity from a peer handle.
1026  *
1027  * @param peer peer handle for which we want the peer's identity
1028  * @param id identifier for the daemon, will be set
1029  */
1030 void
1031 GNUNET_TESTING_peer_get_identity (struct GNUNET_TESTING_Peer *peer,
1032                                   struct GNUNET_PeerIdentity *id)
1033 {
1034   if (NULL != peer->id)
1035   {
1036     memcpy (id, peer->id, sizeof (struct GNUNET_PeerIdentity));
1037     return;
1038   }
1039   peer->id = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity));
1040   GNUNET_CRYPTO_ecc_key_free (GNUNET_TESTING_hostkey_get (peer->system,
1041                                                           peer->key_number,
1042                                                           peer->id));
1043   memcpy (id, peer->id, sizeof (struct GNUNET_PeerIdentity));
1044 }
1045
1046
1047 /**
1048  * Start the peer. 
1049  *
1050  * @param peer peer to start
1051  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
1052  */
1053 int
1054 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
1055 {
1056   if (NULL != peer->main_process)
1057   {
1058     GNUNET_break (0);
1059     return GNUNET_SYSERR;
1060   }
1061   GNUNET_assert (NULL != peer->cfgfile);
1062   peer->main_process = GNUNET_OS_start_process (PIPE_CONTROL, 
1063                                                 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
1064                                                 NULL, NULL,
1065                                                 peer->main_binary,
1066                                                 peer->main_binary,
1067                                                 peer->args,
1068                                                 "-c",
1069                                                 peer->cfgfile,
1070                                                 NULL);
1071   if (NULL == peer->main_process)
1072   {
1073     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1074                 _("Failed to start `%s': %s\n"),
1075                 peer->main_binary,
1076                 STRERROR (errno));
1077     return GNUNET_SYSERR;
1078   }
1079   return GNUNET_OK;
1080 }
1081
1082
1083 /**
1084  * Start a service at a peer using its ARM service
1085  *
1086  * @param peer the peer whose service has to be started
1087  * @param service_name name of the service to start
1088  * @param timeout how long should the ARM API try to send the request to start
1089  *          the service
1090  * @param cont the callback to call with result and status from ARM API
1091  * @param cont_cls the closure for the above callback
1092  * @return GNUNET_OK upon successfully queuing the service start request;
1093  *           GNUNET_SYSERR upon error
1094  */
1095 int
1096 GNUNET_TESTING_peer_service_start (struct GNUNET_TESTING_Peer *peer,
1097                                    const char *service_name,
1098                                    struct GNUNET_TIME_Relative timeout,
1099                                    GNUNET_ARM_ResultCallback cont,
1100                                    void *cont_cls)
1101 {
1102   if (NULL == peer->ah)
1103     return GNUNET_SYSERR;
1104   GNUNET_ARM_request_service_start (peer->ah,
1105                                     service_name,
1106                                     GNUNET_OS_INHERIT_STD_ALL,
1107                                     timeout,
1108                                     cont, cont_cls);
1109   return GNUNET_OK;
1110 }
1111
1112
1113 /**
1114  * Stop a service at a peer using its ARM service
1115  *
1116  * @param peer the peer whose service has to be stopped
1117  * @param service_name name of the service to stop
1118  * @param timeout how long should the ARM API try to send the request to stop
1119  *          the service
1120  * @param cont the callback to call with result and status from ARM API
1121  * @param cont_cls the closure for the above callback
1122  * @return GNUNET_OK upon successfully queuing the service stop request;
1123  *           GNUNET_SYSERR upon error
1124  */
1125 int 
1126 GNUNET_TESTING_peer_service_stop (struct GNUNET_TESTING_Peer *peer,
1127                                   const char *service_name,
1128                                   struct GNUNET_TIME_Relative timeout,
1129                                   GNUNET_ARM_ResultCallback cont,
1130                                   void *cont_cls)
1131 {
1132   if (NULL == peer->ah)
1133     return GNUNET_SYSERR;
1134   GNUNET_ARM_request_service_stop (peer->ah, 
1135                                    service_name,
1136                                    timeout,
1137                                    cont, cont_cls);
1138   return GNUNET_OK;
1139 }
1140                                        
1141
1142 /**
1143  * Sends SIGTERM to the peer's main process
1144  *
1145  * @param peer the handle to the peer
1146  * @return GNUNET_OK if successful; GNUNET_SYSERR if the main process is NULL
1147  *           or upon any error while sending SIGTERM
1148  */
1149 int
1150 GNUNET_TESTING_peer_kill (struct GNUNET_TESTING_Peer *peer)
1151 {
1152   if (NULL == peer->main_process)
1153   {
1154     GNUNET_break (0);
1155     return GNUNET_SYSERR;
1156   }
1157   return (0 == GNUNET_OS_process_kill (peer->main_process, SIGTERM)) ?
1158       GNUNET_OK : GNUNET_SYSERR;
1159 }
1160
1161
1162 /**
1163  * Waits for a peer to terminate. The peer's main process will also be destroyed.
1164  *
1165  * @param peer the handle to the peer
1166  * @return GNUNET_OK if successful; GNUNET_SYSERR if the main process is NULL
1167  *           or upon any error while waiting
1168  */
1169 int
1170 GNUNET_TESTING_peer_wait (struct GNUNET_TESTING_Peer *peer)
1171 {
1172   int ret;
1173
1174   if (NULL == peer->main_process)
1175   {
1176     GNUNET_break (0);
1177     return GNUNET_SYSERR;
1178   }
1179   ret = GNUNET_OS_process_wait (peer->main_process);
1180   GNUNET_OS_process_destroy (peer->main_process);
1181   peer->main_process = NULL;
1182   return ret;
1183 }
1184
1185
1186 /**
1187  * Stop the peer. 
1188  *
1189  * @param peer peer to stop
1190  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1191  */
1192 int
1193 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
1194 {
1195   if (GNUNET_SYSERR == GNUNET_TESTING_peer_kill (peer))
1196     return GNUNET_SYSERR;
1197   if (GNUNET_SYSERR == GNUNET_TESTING_peer_wait (peer))
1198     return GNUNET_SYSERR;
1199   return GNUNET_OK;
1200 }
1201
1202
1203 /**
1204  * Function called whenever we connect to or disconnect from ARM.
1205  *
1206  * @param cls closure
1207  * @param arm handle to the ARM connection
1208  * @param connected GNUNET_YES if connected, GNUNET_NO if disconnected,
1209  *                  GNUNET_SYSERR on error.
1210  */
1211 static void
1212 disconn_status (void *cls, struct GNUNET_ARM_Handle *arm, 
1213                 int connected)
1214 {
1215   struct GNUNET_TESTING_Peer *peer = cls;
1216
1217   if (GNUNET_SYSERR == connected)
1218   {
1219     peer->cb (peer->cb_cls, peer, connected);
1220     return;
1221   }
1222   if (GNUNET_YES == connected)
1223   {
1224     GNUNET_break (GNUNET_OK == GNUNET_TESTING_peer_kill (peer));
1225     return;
1226   }
1227   GNUNET_break (GNUNET_OK == GNUNET_TESTING_peer_wait (peer));
1228   GNUNET_ARM_disconnect_and_free (peer->ah);
1229   peer->ah = NULL;
1230   peer->cb (peer->cb_cls, peer, GNUNET_YES);
1231 }
1232
1233
1234 /**
1235  * Stop a peer asynchronously using ARM API.  Peer's shutdown is signaled
1236  * through the GNUNET_TESTING_PeerStopCallback().
1237  *
1238  * @param peer the peer to stop
1239  * @param cb the callback to signal peer shutdown
1240  * @param cb_cls closure for the above callback
1241  * @return GNUNET_OK upon successfully giving the request to the ARM API (this
1242  *           does not mean that the peer is successfully stopped); GNUNET_SYSERR
1243  *           upon any error.
1244  */
1245 int
1246 GNUNET_TESTING_peer_stop_async (struct GNUNET_TESTING_Peer *peer,
1247                                 GNUNET_TESTING_PeerStopCallback cb,
1248                                 void *cb_cls)
1249 {
1250   if (NULL == peer->main_process)
1251     return GNUNET_SYSERR;  
1252   peer->ah = GNUNET_ARM_connect (peer->cfg, &disconn_status, peer);
1253   if (NULL == peer->ah)
1254     return GNUNET_SYSERR;
1255   peer->cb = cb;
1256   peer->cb_cls = cb_cls;
1257   return GNUNET_OK;
1258 }
1259
1260
1261 /**
1262  * Destroy the peer.  Releases resources locked during peer configuration.
1263  * If the peer is still running, it will be stopped AND a warning will be
1264  * printed (users of the API should stop the peer explicitly first).
1265  *
1266  * @param peer peer to destroy
1267  */
1268 void
1269 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
1270 {
1271   unsigned int cnt;
1272
1273   if (NULL != peer->main_process)
1274     GNUNET_TESTING_peer_stop (peer);
1275   if (NULL != peer->ah)
1276     GNUNET_ARM_disconnect_and_free (peer->ah);
1277   if (NULL != peer->mh)
1278     GNUNET_ARM_monitor_disconnect_and_free (peer->mh);
1279   GNUNET_free (peer->cfgfile);
1280   if (NULL != peer->cfg)
1281     GNUNET_CONFIGURATION_destroy (peer->cfg);
1282   GNUNET_free (peer->main_binary);
1283   GNUNET_free (peer->args);
1284   GNUNET_free_non_null (peer->id);
1285   if (NULL != peer->ports)
1286   {
1287     for (cnt = 0; cnt < peer->nports; cnt++)
1288       GNUNET_TESTING_release_port (peer->system, 
1289                                    GNUNET_YES,
1290                                    peer->ports[cnt]);
1291     GNUNET_free (peer->ports);
1292   }
1293   GNUNET_free (peer);
1294 }
1295
1296
1297 /**
1298  * Start a single peer and run a test using the testing library.
1299  * Starts a peer using the given configuration and then invokes the
1300  * given callback.  This function ALSO initializes the scheduler loop
1301  * and should thus be called directly from "main".  The testcase
1302  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
1303  *
1304  * @param testdir only the directory name without any path. This is used for
1305  *          all service homes; the directory will be created in a temporary
1306  *          location depending on the underlying OS
1307  * @param cfgfilename name of the configuration file to use;
1308  *         use NULL to only run with defaults
1309  * @param tm main function of the testcase
1310  * @param tm_cls closure for 'tm'
1311  * @return 0 on success, 1 on error
1312  */
1313 int
1314 GNUNET_TESTING_peer_run (const char *testdir,
1315                          const char *cfgfilename,
1316                          GNUNET_TESTING_TestMain tm,
1317                          void *tm_cls)
1318 {
1319   return GNUNET_TESTING_service_run (testdir, "arm",
1320                                      cfgfilename, tm, tm_cls);
1321 }
1322
1323
1324 /**
1325  * Structure for holding service data
1326  */
1327 struct ServiceContext
1328 {
1329   /**
1330    * The configuration of the peer in which the service is run
1331    */
1332   const struct GNUNET_CONFIGURATION_Handle *cfg;
1333
1334   /**
1335    * Callback to signal service startup
1336    */
1337   GNUNET_TESTING_TestMain tm;
1338   
1339   /**
1340    * The peer in which the service is run.
1341    */
1342   struct GNUNET_TESTING_Peer *peer;
1343
1344   /**
1345    * Closure for the above callback
1346    */
1347   void *tm_cls;
1348 };
1349
1350
1351 /**
1352  * Callback to be called when SCHEDULER has been started
1353  *
1354  * @param cls the ServiceContext
1355  * @param tc the TaskContext
1356  */
1357 static void
1358 service_run_main (void *cls,
1359                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1360 {
1361   struct ServiceContext *sc = cls;
1362
1363   sc->tm (sc->tm_cls, sc->cfg, sc->peer);
1364 }
1365
1366
1367 /**
1368  * Start a single service (no ARM, except of course if the given
1369  * service name is 'arm') and run a test using the testing library.
1370  * Starts a service using the given configuration and then invokes the
1371  * given callback.  This function ALSO initializes the scheduler loop
1372  * and should thus be called directly from "main".  The testcase
1373  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
1374  *
1375  * This function is useful if the testcase is for a single service
1376  * and if that service doesn't itself depend on other services.
1377  *
1378  * @param testdir only the directory name without any path. This is used for
1379  *          all service homes; the directory will be created in a temporary
1380  *          location depending on the underlying OS
1381  * @param service_name name of the service to run
1382  * @param cfgfilename name of the configuration file to use;
1383  *         use NULL to only run with defaults
1384  * @param tm main function of the testcase
1385  * @param tm_cls closure for 'tm'
1386  * @return 0 on success, 1 on error
1387  */
1388 int
1389 GNUNET_TESTING_service_run (const char *testdir,
1390                             const char *service_name,
1391                             const char *cfgfilename,
1392                             GNUNET_TESTING_TestMain tm,
1393                             void *tm_cls)
1394 {
1395   struct ServiceContext sc;
1396   struct GNUNET_TESTING_System *system;
1397   struct GNUNET_TESTING_Peer *peer;
1398   struct GNUNET_CONFIGURATION_Handle *cfg;
1399   char *binary;
1400   char *libexec_binary;
1401
1402   GNUNET_log_setup (testdir, "WARNING", NULL);
1403   system = GNUNET_TESTING_system_create (testdir, "127.0.0.1", NULL);
1404   if (NULL == system)
1405     return 1;
1406   cfg = GNUNET_CONFIGURATION_create ();
1407   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
1408   {
1409     LOG (GNUNET_ERROR_TYPE_ERROR,
1410          _("Failed to load configuration from %s\n"), cfgfilename);
1411     GNUNET_CONFIGURATION_destroy (cfg);
1412     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1413     return 1;
1414   }
1415   peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
1416   if (NULL == peer)
1417   {
1418     GNUNET_CONFIGURATION_destroy (cfg);
1419     hostkeys_unload (system);
1420     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1421     return 1;
1422   }
1423   GNUNET_free (peer->main_binary);
1424   GNUNET_free (peer->args);
1425   GNUNET_asprintf (&binary, "gnunet-service-%s", service_name);
1426   libexec_binary = GNUNET_OS_get_libexec_binary_path (binary);
1427   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, service_name, "PREFIX", &peer->main_binary))
1428   {
1429     /* No prefix */
1430     GNUNET_asprintf(&peer->main_binary, "%s", libexec_binary);
1431     peer->args = strdup ("");
1432   }
1433   else
1434     peer->args = strdup (libexec_binary);
1435
1436   GNUNET_free (libexec_binary);
1437   GNUNET_free (binary);
1438   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
1439   {    
1440     GNUNET_TESTING_peer_destroy (peer);
1441     GNUNET_CONFIGURATION_destroy (cfg);
1442     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1443     return 1;
1444   }
1445   sc.cfg = cfg;
1446   sc.tm = tm;
1447   sc.tm_cls = tm_cls;
1448   sc.peer = peer;
1449   GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
1450   if ((NULL != peer->main_process) &&
1451       (GNUNET_OK != GNUNET_TESTING_peer_stop (peer)))
1452   {
1453     GNUNET_TESTING_peer_destroy (peer);
1454     GNUNET_CONFIGURATION_destroy (cfg);
1455     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1456     return 1;
1457   }
1458   GNUNET_TESTING_peer_destroy (peer);
1459   GNUNET_CONFIGURATION_destroy (cfg);
1460   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1461   return 0;
1462 }
1463
1464
1465 /**
1466  * Sometimes we use the binary name to determine which specific
1467  * test to run.  In those cases, the string after the last "_"
1468  * in 'argv[0]' specifies a string that determines the configuration
1469  * file or plugin to use.  
1470  *
1471  * This function returns the respective substring, taking care
1472  * of issues such as binaries ending in '.exe' on W32.
1473  *
1474  * @param argv0 the name of the binary
1475  * @return string between the last '_' and the '.exe' (or the end of the string),
1476  *         NULL if argv0 has no '_' 
1477  */
1478 char *
1479 GNUNET_TESTING_get_testname_from_underscore (const char *argv0)
1480 {
1481   size_t slen = strlen (argv0) + 1;
1482   char sbuf[slen];
1483   char *ret;
1484   char *dot;
1485
1486   memcpy (sbuf, argv0, slen);
1487   ret = strrchr (sbuf, '_');
1488   if (NULL == ret)
1489     return NULL;
1490   ret++; /* skip underscore */
1491   dot = strchr (ret, '.');
1492   if (NULL != dot)
1493     *dot = '\0';
1494   return GNUNET_strdup (ret);
1495 }
1496
1497
1498 /* end of testing.c */