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