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