216dddfeaea690d03ab78c01c85c68d743c27abc
[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_PeerStartCallback 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  * Obtain the peer identity from a peer handle.
1001  *
1002  * @param peer peer handle for which we want the peer's identity
1003  * @param id identifier for the daemon, will be set
1004  */
1005 void
1006 GNUNET_TESTING_peer_get_identity (struct GNUNET_TESTING_Peer *peer,
1007                                   struct GNUNET_PeerIdentity *id)
1008 {
1009   if (NULL != peer->id)
1010   {
1011     memcpy (id, peer->id, sizeof (struct GNUNET_PeerIdentity));
1012     return;
1013   }
1014   peer->id = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity));
1015   GNUNET_CRYPTO_ecc_key_free (GNUNET_TESTING_hostkey_get (peer->system,
1016                                                           peer->key_number,
1017                                                           peer->id));
1018   memcpy (id, peer->id, sizeof (struct GNUNET_PeerIdentity));
1019 }
1020
1021
1022 /**
1023  * Start the peer. 
1024  *
1025  * @param peer peer to start
1026  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
1027  */
1028 int
1029 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
1030 {
1031   if (NULL != peer->main_process)
1032   {
1033     GNUNET_break (0);
1034     return GNUNET_SYSERR;
1035   }
1036   GNUNET_assert (NULL != peer->cfgfile);
1037   peer->main_process = GNUNET_OS_start_process (PIPE_CONTROL, 
1038                                                 GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
1039                                                 NULL, NULL,
1040                                                 peer->main_binary,
1041                                                 peer->main_binary,
1042                                                 peer->args,
1043                                                 "-c",
1044                                                 peer->cfgfile,
1045                                                 NULL);
1046   if (NULL == peer->main_process)
1047   {
1048     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1049                 _("Failed to start `%s': %s\n"),
1050                 peer->main_binary,
1051                 STRERROR (errno));
1052     return GNUNET_SYSERR;
1053   }
1054   return GNUNET_OK;
1055 }
1056
1057
1058 void
1059 GNUNET_TESTING_peer_service_start (struct GNUNET_TESTING_Peer *peer,
1060                                    const char *service_name,
1061                                    GNUNET_ARM_ResultCallback cont,
1062                                    void *cont_cls)
1063 {
1064   GNUNET_ARM_request_service_start (peer->ah,
1065                                     service_name,
1066                                     GNUNET_OS_INHERIT_STD_ALL,
1067                                     GNUNET_TIME_UNIT_MINUTES, 
1068                                     cont,
1069                                     cont_cls);
1070
1071 }
1072
1073
1074 void GNUNET_TESTING_peer_service_stop (struct GNUNET_TESTING_Peer *peer,
1075                                         const char *service_name,
1076                                         GNUNET_ARM_ResultCallback cont,
1077                                        void *cont_cls)
1078 {
1079   GNUNET_ARM_request_service_stop (peer->ah, service_name,
1080                                    GNUNET_TIME_UNIT_MINUTES,
1081                                    cont, cont_cls);
1082 }
1083                                        
1084 static void
1085 arm_start_result_cb (void *cls, 
1086                      struct GNUNET_ARM_Handle *arm, 
1087                      enum GNUNET_ARM_RequestStatus rs,
1088                      const char *service, 
1089                      enum GNUNET_ARM_Result result)
1090 {
1091   struct GNUNET_TESTING_Peer *peer = cls;
1092
1093   if ((GNUNET_ARM_REQUEST_SENT_OK != rs)
1094       || ! ((GNUNET_ARM_RESULT_STARTING == result)
1095             || (GNUNET_ARM_RESULT_IS_STARTING_ALREADY == result)
1096             || (GNUNET_ARM_RESULT_IS_STARTED_ALREADY == result)))
1097   {
1098     peer->cb (peer->cb_cls, peer, GNUNET_NO);
1099     return;
1100   }
1101   peer->cb (peer->cb_cls, peer, GNUNET_OK);
1102 }
1103
1104 /**
1105  * Function called whenever we connect to or disconnect from ARM.
1106  *
1107  * @param cls closure
1108  * @param arm handle to the ARM connection
1109  * @param connected GNUNET_YES if connected, GNUNET_NO if disconnected,
1110  *                  GNUNET_SYSERR on error.
1111  */
1112 static void
1113 conn_status (void *cls, struct GNUNET_ARM_Handle *arm, 
1114              int connected)
1115 {
1116   struct GNUNET_TESTING_Peer *peer = cls;
1117
1118   peer->cb (peer->cb_cls, peer, connected);  
1119 }
1120
1121
1122 int
1123 GNUNET_TESTING_peer_start2 (struct GNUNET_TESTING_Peer *peer,
1124                             GNUNET_TESTING_PeerStartCallback cb,
1125                             void *cb_cls)
1126 {
1127   if (NULL != peer->ah)
1128   {
1129     GNUNET_break (0);
1130     return GNUNET_SYSERR;
1131   }
1132   GNUNET_assert (NULL != (peer->cb = cb));
1133   peer->cb_cls = cb_cls;
1134   peer->ah = GNUNET_ARM_connect (peer->cfg, &conn_status, peer);
1135   if (NULL == peer->ah)
1136     return GNUNET_SYSERR;
1137   //GNUNET_TESTING_peer_service_start (peer, "arm", &arm_start_result_cb, peer);
1138   GNUNET_TESTING_peer_service_start (peer, "arm", NULL, NULL);
1139   return GNUNET_OK;
1140 }
1141
1142
1143
1144 /**
1145  * Sends SIGTERM to the peer's main process
1146  *
1147  * @param peer the handle to the peer
1148  * @return GNUNET_OK if successful; GNUNET_SYSERR if the main process is NULL
1149  *           or upon any error while sending SIGTERM
1150  */
1151 int
1152 GNUNET_TESTING_peer_kill (struct GNUNET_TESTING_Peer *peer)
1153 {
1154   if (NULL == peer->main_process)
1155   {
1156     GNUNET_break (0);
1157     return GNUNET_SYSERR;
1158   }
1159   return (0 == GNUNET_OS_process_kill (peer->main_process, SIGTERM)) ?
1160       GNUNET_OK : GNUNET_SYSERR;
1161 }
1162
1163
1164 /**
1165  * Waits for a peer to terminate. The peer's main process will also be destroyed.
1166  *
1167  * @param peer the handle to the peer
1168  * @return GNUNET_OK if successful; GNUNET_SYSERR if the main process is NULL
1169  *           or upon any error while waiting
1170  */
1171 int
1172 GNUNET_TESTING_peer_wait (struct GNUNET_TESTING_Peer *peer)
1173 {
1174   int ret;
1175
1176   if (NULL == peer->main_process)
1177   {
1178     GNUNET_break (0);
1179     return GNUNET_SYSERR;
1180   }
1181   ret = GNUNET_OS_process_wait (peer->main_process);
1182   GNUNET_OS_process_destroy (peer->main_process);
1183   peer->main_process = NULL;
1184   return ret;
1185 }
1186
1187
1188 /**
1189  * Stop the peer. 
1190  *
1191  * @param peer peer to stop
1192  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1193  */
1194 int
1195 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
1196 {
1197   if (GNUNET_SYSERR == GNUNET_TESTING_peer_kill (peer))
1198     return GNUNET_SYSERR;
1199   if (GNUNET_SYSERR == GNUNET_TESTING_peer_wait (peer))
1200     return GNUNET_SYSERR;
1201   return GNUNET_OK;
1202 }
1203                                        
1204 static void
1205 arm_stop_result_cb (void *cls, 
1206                      struct GNUNET_ARM_Handle *arm, 
1207                      enum GNUNET_ARM_RequestStatus rs,
1208                      const char *service, 
1209                      enum GNUNET_ARM_Result result)
1210 {
1211   struct GNUNET_TESTING_Peer *peer = cls;
1212   
1213   if ((GNUNET_ARM_REQUEST_SENT_OK != rs)
1214       || ! ((GNUNET_ARM_RESULT_STOPPED == result)
1215             || (GNUNET_ARM_RESULT_STOPPING == result)
1216             || (GNUNET_ARM_RESULT_IS_STOPPING_ALREADY == result)
1217             || (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY == result)))
1218   {
1219     peer->cb (peer->cb_cls, peer, GNUNET_NO);
1220     return;
1221   }
1222   peer->cb (peer->cb_cls, peer, GNUNET_OK);
1223 }
1224
1225
1226 static void 
1227 arm_service_monitor (void *cls,
1228                  struct GNUNET_ARM_MonitorHandle *arm,
1229                  const char *service, 
1230                  enum GNUNET_ARM_ServiceStatus status)
1231 {
1232   struct GNUNET_TESTING_Peer *peer = cls;
1233   
1234   peer->mh = arm;
1235   if (GNUNET_ARM_SERVICE_STOPPED != status)
1236     return;
1237   if (0 != strcasecmp (service, "arm"))
1238     return;
1239   peer->cb (peer->cb_cls, peer, GNUNET_OK);
1240 }
1241
1242
1243 int
1244 GNUNET_TESTING_peer_stop2 (struct GNUNET_TESTING_Peer *peer,
1245                            GNUNET_TESTING_PeerStartCallback cb,
1246                            void *cb_cls)
1247 {
1248   if (NULL == peer->ah)
1249     return GNUNET_SYSERR;
1250   GNUNET_assert (NULL != (peer->cb = cb));
1251   peer->cb_cls = cb_cls;
1252   /* if (NULL == peer->mh) */
1253   /*   peer->mh = GNUNET_ARM_monitor (peer->cfg, &arm_service_monitor, peer); */
1254   GNUNET_TESTING_peer_service_stop (peer, "arm", NULL, NULL);
1255   return GNUNET_OK;
1256 }
1257
1258
1259 /**
1260  * Destroy the peer.  Releases resources locked during peer configuration.
1261  * If the peer is still running, it will be stopped AND a warning will be
1262  * printed (users of the API should stop the peer explicitly first).
1263  *
1264  * @param peer peer to destroy
1265  */
1266 void
1267 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
1268 {
1269   if (NULL != peer->main_process)
1270     GNUNET_TESTING_peer_stop (peer);
1271   if (NULL != peer->mh)
1272     GNUNET_ARM_monitor_disconnect_and_free (peer->mh);
1273   if (NULL != peer->ah)
1274     GNUNET_ARM_disconnect_and_free (peer->ah);
1275   GNUNET_free (peer->cfgfile);
1276   GNUNET_CONFIGURATION_destroy (peer->cfg);
1277   GNUNET_free (peer->main_binary);
1278   GNUNET_free (peer->args);
1279   GNUNET_free_non_null (peer->id);
1280   GNUNET_free (peer);
1281 }
1282
1283
1284 /**
1285  * Start a single peer and run a test using the testing library.
1286  * Starts a peer using the given configuration and then invokes the
1287  * given callback.  This function ALSO initializes the scheduler loop
1288  * and should thus be called directly from "main".  The testcase
1289  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
1290  *
1291  * @param testdir only the directory name without any path. This is used for
1292  *          all service homes; the directory will be created in a temporary
1293  *          location depending on the underlying OS
1294  * @param cfgfilename name of the configuration file to use;
1295  *         use NULL to only run with defaults
1296  * @param tm main function of the testcase
1297  * @param tm_cls closure for 'tm'
1298  * @return 0 on success, 1 on error
1299  */
1300 int
1301 GNUNET_TESTING_peer_run (const char *testdir,
1302                          const char *cfgfilename,
1303                          GNUNET_TESTING_TestMain tm,
1304                          void *tm_cls)
1305 {
1306   return GNUNET_TESTING_service_run (testdir, "arm",
1307                                      cfgfilename, tm, tm_cls);
1308 }
1309
1310
1311 /**
1312  * Structure for holding service data
1313  */
1314 struct ServiceContext
1315 {
1316   /**
1317    * The configuration of the peer in which the service is run
1318    */
1319   const struct GNUNET_CONFIGURATION_Handle *cfg;
1320
1321   /**
1322    * Callback to signal service startup
1323    */
1324   GNUNET_TESTING_TestMain tm;
1325   
1326   /**
1327    * The peer in which the service is run.
1328    */
1329   struct GNUNET_TESTING_Peer *peer;
1330
1331   /**
1332    * Closure for the above callback
1333    */
1334   void *tm_cls;
1335 };
1336
1337
1338 /**
1339  * Callback to be called when SCHEDULER has been started
1340  *
1341  * @param cls the ServiceContext
1342  * @param tc the TaskContext
1343  */
1344 static void
1345 service_run_main (void *cls,
1346                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1347 {
1348   struct ServiceContext *sc = cls;
1349
1350   sc->tm (sc->tm_cls, sc->cfg, sc->peer);
1351 }
1352
1353
1354 /**
1355  * Start a single service (no ARM, except of course if the given
1356  * service name is 'arm') and run a test using the testing library.
1357  * Starts a service using the given configuration and then invokes the
1358  * given callback.  This function ALSO initializes the scheduler loop
1359  * and should thus be called directly from "main".  The testcase
1360  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
1361  *
1362  * This function is useful if the testcase is for a single service
1363  * and if that service doesn't itself depend on other services.
1364  *
1365  * @param testdir only the directory name without any path. This is used for
1366  *          all service homes; the directory will be created in a temporary
1367  *          location depending on the underlying OS
1368  * @param service_name name of the service to run
1369  * @param cfgfilename name of the configuration file to use;
1370  *         use NULL to only run with defaults
1371  * @param tm main function of the testcase
1372  * @param tm_cls closure for 'tm'
1373  * @return 0 on success, 1 on error
1374  */
1375 int
1376 GNUNET_TESTING_service_run (const char *testdir,
1377                             const char *service_name,
1378                             const char *cfgfilename,
1379                             GNUNET_TESTING_TestMain tm,
1380                             void *tm_cls)
1381 {
1382   struct ServiceContext sc;
1383   struct GNUNET_TESTING_System *system;
1384   struct GNUNET_TESTING_Peer *peer;
1385   struct GNUNET_CONFIGURATION_Handle *cfg;
1386   char *binary;
1387   char *libexec_binary;
1388
1389   GNUNET_log_setup (testdir, "WARNING", NULL);
1390   system = GNUNET_TESTING_system_create (testdir, "127.0.0.1", NULL);
1391   if (NULL == system)
1392     return 1;
1393   cfg = GNUNET_CONFIGURATION_create ();
1394   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
1395   {
1396     LOG (GNUNET_ERROR_TYPE_ERROR,
1397          _("Failed to load configuration from %s\n"), cfgfilename);
1398     GNUNET_CONFIGURATION_destroy (cfg);
1399     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1400     return 1;
1401   }
1402   peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
1403   if (NULL == peer)
1404   {
1405     GNUNET_CONFIGURATION_destroy (cfg);
1406     hostkeys_unload (system);
1407     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1408     return 1;
1409   }
1410   GNUNET_free (peer->main_binary);
1411   GNUNET_free (peer->args);
1412   GNUNET_asprintf (&binary, "gnunet-service-%s", service_name);
1413   libexec_binary = GNUNET_OS_get_libexec_binary_path (binary);
1414   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, service_name, "PREFIX", &peer->main_binary))
1415   {
1416     /* No prefix */
1417     GNUNET_asprintf(&peer->main_binary, "%s", libexec_binary);
1418     peer->args = strdup ("");
1419   }
1420   else
1421     peer->args = strdup (libexec_binary);
1422
1423   GNUNET_free (libexec_binary);
1424   GNUNET_free (binary);
1425   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
1426   {    
1427     GNUNET_TESTING_peer_destroy (peer);
1428     GNUNET_CONFIGURATION_destroy (cfg);
1429     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1430     return 1;
1431   }
1432   sc.cfg = cfg;
1433   sc.tm = tm;
1434   sc.tm_cls = tm_cls;
1435   sc.peer = peer;
1436   GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
1437   if ((NULL != peer->main_process) &&
1438       (GNUNET_OK != GNUNET_TESTING_peer_stop (peer)))
1439   {
1440     GNUNET_TESTING_peer_destroy (peer);
1441     GNUNET_CONFIGURATION_destroy (cfg);
1442     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1443     return 1;
1444   }
1445   GNUNET_TESTING_peer_destroy (peer);
1446   GNUNET_CONFIGURATION_destroy (cfg);
1447   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1448   return 0;
1449 }
1450
1451
1452 /**
1453  * Sometimes we use the binary name to determine which specific
1454  * test to run.  In those cases, the string after the last "_"
1455  * in 'argv[0]' specifies a string that determines the configuration
1456  * file or plugin to use.  
1457  *
1458  * This function returns the respective substring, taking care
1459  * of issues such as binaries ending in '.exe' on W32.
1460  *
1461  * @param argv0 the name of the binary
1462  * @return string between the last '_' and the '.exe' (or the end of the string),
1463  *         NULL if argv0 has no '_' 
1464  */
1465 char *
1466 GNUNET_TESTING_get_testname_from_underscore (const char *argv0)
1467 {
1468   size_t slen = strlen (argv0) + 1;
1469   char sbuf[slen];
1470   char *ret;
1471   char *dot;
1472
1473   memcpy (sbuf, argv0, slen);
1474   ret = strrchr (sbuf, '_');
1475   if (NULL == ret)
1476     return NULL;
1477   ret++; /* skip underscore */
1478   dot = strchr (ret, '.');
1479   if (NULL != dot)
1480     *dot = '\0';
1481   return GNUNET_strdup (ret);
1482 }
1483
1484
1485 /* end of testing.c */