049074cd5344a19c80376885af3c14a7cfb2acc4
[oweals/gnunet.git] / src / testing / testing.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 2009, 2012 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file testing/testing.c
23  * @brief convenience API for writing testcases for GNUnet
24  *        Many testcases need to start and stop a peer/service
25  *        and this library is supposed to make that easier
26  *        for TESTCASES.  Normal programs should always
27  *        use functions from gnunet_{util,arm}_lib.h.  This API is
28  *        ONLY for writing testcases (or internal use of the testbed).
29  * @author Christian Grothoff
30  *
31  */
32 #include "platform.h"
33 #include "gnunet_util_lib.h"
34 #include "gnunet_testing_lib-new.h"
35
36 #define LOG(kind,...)                                           \
37   GNUNET_log_from (kind, "testing-api", __VA_ARGS__)
38
39 /**
40  * Lowest port used for GNUnet testing.  Should be high enough to not
41  * conflict with other applications running on the hosts but be low
42  * enough to not conflict with client-ports (typically starting around
43  * 32k).
44  */
45 #define LOW_PORT 12000
46
47 /**
48  * Highest port used for GNUnet testing.  Should be low enough to not
49  * conflict with the port range for "local" ports (client apps; see
50  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
51  */
52 #define HIGH_PORT 56000
53
54
55 /**
56  * Handle for a system on which GNUnet peers are executed;
57  * a system is used for reserving unique paths and ports.
58  */
59 struct GNUNET_TESTING_System
60 {
61   /**
62    * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
63    * SERVICEHOME.    */
64   char *tmppath;
65
66   /**
67    * The hostname of the controller
68    */
69   char *controller;
70
71   /**
72    * our hostname
73    */
74   char *hostname;
75
76   /**
77    * Hostkeys data, contains "GNUNET_TESTING_HOSTKEYFILESIZE * total_hostkeys" bytes.
78    */
79   char *hostkeys_data;
80
81   /**
82    * memory map for 'hostkeys_data'.
83    */
84   struct GNUNET_DISK_MapHandle *map;
85
86   /**
87    * File descriptor for the map.
88    */
89   struct GNUNET_DISK_FileHandle *map_fd;
90
91   /**
92    * Bitmap where each TCP port that has already been reserved for
93    * some GNUnet peer is recorded.  Note that we additionally need to
94    * test if a port is already in use by non-GNUnet components before
95    * assigning it to a peer/service.  If we detect that a port is
96    * already in use, we also mark it in this bitmap.  So all the bits
97    * that are zero merely indicate ports that MIGHT be available for
98    * peers.
99    */
100   uint32_t reserved_tcp_ports[65536 / 32];
101
102   /**
103    * Bitmap where each UDP port that has already been reserved for
104    * some GNUnet peer is recorded.  Note that we additionally need to
105    * test if a port is already in use by non-GNUnet components before
106    * assigning it to a peer/service.  If we detect that a port is
107    * already in use, we also mark it in this bitmap.  So all the bits
108    * that are zero merely indicate ports that MIGHT be available for
109    * peers.
110    */
111   uint32_t reserved_udp_ports[65536 / 32];
112
113   /**
114    * Counter we use to make service home paths unique on this system;
115    * the full path consists of the tmppath and this number.  Each
116    * UNIXPATH for a peer is also modified to include the respective
117    * path counter to ensure uniqueness.  This field is incremented
118    * by one for each configured peer.  Even if peers are destroyed,
119    * we never re-use path counters.
120    */
121   uint32_t path_counter;  
122
123   /**
124    * The number of hostkeys
125    */
126   uint32_t total_hostkeys;
127
128   /**
129    * Lowest port we are allowed to use.
130    */
131   uint16_t lowport;
132
133   /**
134    * Highest port we are allowed to use.
135    */
136   uint16_t highport;
137 };
138
139
140 /**
141  * Handle for a GNUnet peer controlled by testing.
142  */
143 struct GNUNET_TESTING_Peer
144 {
145   /**
146    * The TESTING system associated with this peer
147    */
148   struct GNUNET_TESTING_System *system;
149
150   /**
151    * Path to the configuration file for this peer.
152    */
153   char *cfgfile;
154
155   /**
156    * Binary to be executed during 'GNUNET_TESTING_peer_start'.
157    * Typically 'gnunet-service-arm' (but can be set to a 
158    * specific service by 'GNUNET_TESTING_service_run' if
159    * necessary).
160    */ 
161   char *main_binary;
162   
163   /**
164    * Handle to the running binary of the service, NULL if the
165    * peer/service is currently not running.
166    */
167   struct GNUNET_OS_Process *main_process;
168
169   /**
170    * The keynumber of this peer's hostkey
171    */
172   uint32_t key_number;
173 };
174
175
176 /**
177  * Testing includes a number of pre-created hostkeys for faster peer
178  * startup. This function loads such keys into memory from a file.
179  *
180  * @param system the testing system handle
181  * @return GNUNET_OK on success; GNUNET_SYSERR on error
182  */
183 static int
184 hostkeys_load (struct GNUNET_TESTING_System *system)
185 {
186   uint64_t fs; 
187   char *data_dir;
188   char *filename;
189   
190   GNUNET_assert (NULL == system->hostkeys_data);
191   data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
192   GNUNET_asprintf (&filename, "%s/testing_hostkeys.dat", data_dir);
193   GNUNET_free (data_dir);  
194
195   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
196   {
197     LOG (GNUNET_ERROR_TYPE_ERROR,
198          _("Hostkeys file not found: %s\n"), filename);
199     GNUNET_free (filename);
200     return GNUNET_SYSERR;
201   }
202   /* Check hostkey file size, read entire thing into memory */
203   if (GNUNET_OK != 
204       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
205     fs = 0;
206   if (0 == fs)
207   {
208     GNUNET_free (filename);
209     return GNUNET_SYSERR;       /* File is empty */
210   }
211   if (0 != (fs % GNUNET_TESTING_HOSTKEYFILESIZE))
212   {
213     LOG (GNUNET_ERROR_TYPE_ERROR,
214          _("Incorrect hostkey file format: %s\n"), filename);
215     GNUNET_free (filename);
216     return GNUNET_SYSERR;
217   }
218   system->map_fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
219                                          GNUNET_DISK_PERM_NONE);
220   if (NULL == system->map_fd)
221   {
222     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename);
223     GNUNET_free (filename);
224     return GNUNET_SYSERR;
225   }
226   system->total_hostkeys = fs / GNUNET_TESTING_HOSTKEYFILESIZE;
227   system->hostkeys_data = GNUNET_DISK_file_map (system->map_fd,
228                                                 &system->map,
229                                                 GNUNET_DISK_MAP_TYPE_READ,
230                                                 fs);
231   GNUNET_free (filename);
232   return GNUNET_OK;
233 }
234
235
236 /**
237  * Function to remove the loaded hostkeys
238  *
239  * @param system the testing system handle
240  */
241 static void
242 hostkeys_unload (struct GNUNET_TESTING_System *system)
243 {
244   GNUNET_break (NULL != system->hostkeys_data);
245   system->hostkeys_data = NULL;
246   GNUNET_DISK_file_unmap (system->map);
247   system->map = NULL;
248   GNUNET_DISK_file_close (system->map_fd);
249   system->map_fd = NULL;
250   system->hostkeys_data = NULL;
251   system->total_hostkeys = 0;
252 }
253
254
255 /**
256  * Create a system handle.  There must only be one system
257  * handle per operating system.
258  *
259  * @param testdir only the directory name without any path. This is used for
260  *          all service homes; the directory will be created in a temporary
261  *          location depending on the underlying OS
262  * @param controller hostname of the controlling host, 
263  *        service configurations are modified to allow 
264  *        control connections from this host; can be NULL
265  * @param hostname the hostname of the system we are using for testing; NULL for
266  *          localhost
267  * @param lowport lowest port number this system is allowed to allocate (inclusive)
268  * @param highport highest port number this system is allowed to allocate (exclusive)
269  * @return handle to this system, NULL on error
270  */
271 struct GNUNET_TESTING_System *
272 GNUNET_TESTING_system_create_with_portrange (const char *testdir,
273                                              const char *controller,
274                                              const char *hostname,
275                                              uint16_t lowport,
276                                              uint16_t highport)
277 {
278   struct GNUNET_TESTING_System *system;
279
280   GNUNET_assert (NULL != testdir);
281   system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System));
282   system->tmppath = GNUNET_DISK_mkdtemp (testdir);
283   system->lowport = lowport;
284   system->highport = highport;
285   if (NULL == system->tmppath)
286   {
287     GNUNET_free (system);
288     return NULL;
289   }
290   if (NULL != controller)
291     system->controller = GNUNET_strdup (controller);
292   if (NULL != hostname)
293     system->hostname = GNUNET_strdup (hostname);
294   if (GNUNET_OK != hostkeys_load (system))
295   {
296     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
297     return NULL;
298   }
299   return system;
300 }
301
302
303 /**
304  * Create a system handle.  There must only be one system handle per operating
305  * system.  Uses a default range for allowed ports.  Ports are still tested for
306  * availability.
307  *
308  * @param testdir only the directory name without any path. This is used for all
309  *          service homes; the directory will be created in a temporary location
310  *          depending on the underlying OS
311  * @param controller hostname of the controlling host, service configurations
312  *        are modified to allow control connections from this host; can be NULL
313  * @param hostname the hostname of the system we are using for testing; NULL for
314  *          localhost
315  * @return handle to this system, NULL on error
316  */
317 struct GNUNET_TESTING_System *
318 GNUNET_TESTING_system_create (const char *testdir,
319                               const char *controller,
320                               const char *hostname)
321 {
322   return GNUNET_TESTING_system_create_with_portrange (testdir,
323                                                       controller,
324                                                       hostname,
325                                                       LOW_PORT,
326                                                       HIGH_PORT);
327 }
328
329
330 /**
331  * Free system resources.
332  *
333  * @param system system to be freed
334  * @param remove_paths should the 'testdir' and all subdirectories
335  *        be removed (clean up on shutdown)?
336  */
337 void
338 GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
339                                int remove_paths)
340 {
341   if (NULL != system->hostkeys_data)
342     hostkeys_unload (system);
343   if (GNUNET_YES == remove_paths)
344     GNUNET_DISK_directory_remove (system->tmppath);
345   GNUNET_free (system->tmppath);
346   GNUNET_free_non_null (system->controller);
347   GNUNET_free_non_null (system->hostname);
348   GNUNET_free (system);
349 }
350
351
352 /**
353  * Reserve a TCP or UDP port for a peer.
354  *
355  * @param system system to use for reservation tracking
356  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
357  * @return 0 if no free port was available
358  */
359 uint16_t 
360 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system,
361                              int is_tcp)
362 {
363   struct GNUNET_NETWORK_Handle *socket;
364   struct addrinfo hint;
365   struct addrinfo *ret;
366   struct addrinfo *ai;
367   uint32_t *port_buckets;
368   char *open_port_str;
369   int bind_status;
370   uint32_t xor_image;
371   uint16_t index;
372   uint16_t open_port;
373   uint16_t pos;
374
375   /*
376   FIXME: Instead of using getaddrinfo we should try to determine the port
377          status by the following heurestics.
378   
379          On systems which support both IPv4 and IPv6, only ports open on both
380          address families are considered open.
381          On system with either IPv4 or IPv6. A port is considered open if it's
382          open in the respective address family
383   */
384   hint.ai_family = AF_UNSPEC;   /* IPv4 and IPv6 */
385   hint.ai_socktype = (GNUNET_YES == is_tcp)? SOCK_STREAM : SOCK_DGRAM;
386   hint.ai_protocol = 0;
387   hint.ai_addrlen = 0;
388   hint.ai_addr = NULL;
389   hint.ai_canonname = NULL;
390   hint.ai_next = NULL;
391   hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV;  /* Wild card address */
392   port_buckets = (GNUNET_YES == is_tcp) ?
393     system->reserved_tcp_ports : system->reserved_udp_ports;
394   for (index = (system->lowport / 32) + 1; index < (system->highport / 32); index++)
395   {
396     xor_image = (UINT32_MAX ^ port_buckets[index]);
397     if (0 == xor_image)        /* Ports in the bucket are full */
398       continue;
399     pos = system->lowport % 32;
400     while (pos < 32)
401     {
402       if (0 == ((xor_image >> pos) & 1U))
403       {
404         pos++;
405         continue;
406       }
407       open_port = (index * 32) + pos;
408       if (open_port >= system->highport)
409         return 0;
410       GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port);
411       ret = NULL;
412       GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
413       GNUNET_free (open_port_str);
414       for (ai = ret; NULL != ai; ai = ai->ai_next)
415       {
416         socket = GNUNET_NETWORK_socket_create (ai->ai_family,
417                                                (GNUNET_YES == is_tcp) ?
418                                                SOCK_STREAM : SOCK_DGRAM,
419                                                0);
420         if (NULL == socket)
421         {
422           bind_status = GNUNET_NO;
423           continue;
424         }
425         bind_status = GNUNET_NETWORK_socket_bind (socket,
426                                                   ai->ai_addr,
427                                                   ai->ai_addrlen);
428         GNUNET_NETWORK_socket_close (socket);
429         if (GNUNET_OK != bind_status)
430           break;
431       }
432       port_buckets[index] |= (1U << pos); /* Set the port bit */
433       freeaddrinfo (ret);
434       if (GNUNET_OK == bind_status)
435       {
436         LOG (GNUNET_ERROR_TYPE_DEBUG,
437              "Found a free port %u\n", (unsigned int) open_port);
438         return open_port;
439       }
440       pos++;
441     }
442   }
443   return 0;
444 }
445
446
447 /**
448  * Release reservation of a TCP or UDP port for a peer
449  * (used during GNUNET_TESTING_peer_destroy).
450  *
451  * @param system system to use for reservation tracking
452  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
453  * @param port reserved port to release
454  */
455 void
456 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
457                              int is_tcp,
458                              uint16_t port)
459 {
460   uint32_t *port_buckets;
461   uint16_t bucket;
462   uint16_t pos;
463
464   port_buckets = (GNUNET_YES == is_tcp) ?
465     system->reserved_tcp_ports : system->reserved_udp_ports;
466   bucket = port / 32;
467   pos = port % 32;
468   LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
469   if (0 == (port_buckets[bucket] & (1U << pos)))
470   {
471     GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
472     return;
473   }
474   port_buckets[bucket] &= ~(1U << pos);
475 }
476
477
478 /**
479  * Reserve a SERVICEHOME path for a peer.
480  *
481  * @param system system to use for reservation tracking
482  * @return NULL on error, otherwise fresh unique path to use
483  *         as the servicehome for the peer; must be freed by the caller
484  */
485 // static 
486 char *
487 reserve_path (struct GNUNET_TESTING_System *system)
488 {
489   char *reserved_path;
490
491   GNUNET_asprintf (&reserved_path,
492                    "%s/%u", system->tmppath, system->path_counter++);
493   return reserved_path;
494 }             
495
496
497 /**
498  * Testing includes a number of pre-created hostkeys for
499  * faster peer startup.  This function can be used to
500  * access the n-th key of those pre-created hostkeys; note
501  * that these keys are ONLY useful for testing and not
502  * secure as the private keys are part of the public 
503  * GNUnet source code.
504  *
505  * This is primarily a helper function used internally
506  * by 'GNUNET_TESTING_peer_configure'.
507  *
508  * @param system the testing system handle
509  * @param key_number desired pre-created hostkey to obtain
510  * @param id set to the peer's identity (hash of the public
511  *        key; if NULL, GNUNET_SYSERR is returned immediately
512  * @return NULL on error (not enough keys)
513  */
514 struct GNUNET_CRYPTO_RsaPrivateKey *
515 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
516                             uint32_t key_number,
517                             struct GNUNET_PeerIdentity *id)
518 {  
519   struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
520   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
521   
522   if ((NULL == id) || (NULL == system->hostkeys_data))
523     return NULL;
524   if (key_number >= system->total_hostkeys)
525   {
526     LOG (GNUNET_ERROR_TYPE_ERROR,
527          _("Key number %u does not exist\n"), key_number);
528     return NULL;
529   }   
530   private_key = GNUNET_CRYPTO_rsa_decode_key (system->hostkeys_data +
531                                               (key_number *
532                                                GNUNET_TESTING_HOSTKEYFILESIZE),
533                                               GNUNET_TESTING_HOSTKEYFILESIZE);
534   if (NULL == private_key)
535   {
536     LOG (GNUNET_ERROR_TYPE_ERROR,
537          _("Error while decoding key %u\n"), key_number);
538     return NULL;
539   }
540   GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
541   GNUNET_CRYPTO_hash (&public_key,
542                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
543                       &(id->hashPubKey));
544   return private_key;
545 }
546
547
548 /**
549  * Structure for holding data to build new configurations from a configuration
550  * template
551  */
552 struct UpdateContext
553 {
554   /**
555    * The system for which we are building configurations
556    */
557   struct GNUNET_TESTING_System *system;
558   
559   /**
560    * The configuration we are building
561    */
562   struct GNUNET_CONFIGURATION_Handle *cfg;
563
564   /**
565    * The customized service home path for this peer
566    */
567   char *service_home;
568
569   /**
570    * build status - to signal error while building a configuration
571    */
572   int status;
573 };
574
575
576 /**
577  * Function to iterate over options.  Copies
578  * the options to the target configuration,
579  * updating PORT values as needed.
580  *
581  * @param cls the UpdateContext
582  * @param section name of the section
583  * @param option name of the option
584  * @param value value of the option
585  */
586 static void
587 update_config (void *cls, const char *section, const char *option,
588                const char *value)
589 {
590   struct UpdateContext *uc = cls;
591   unsigned int ival;
592   char cval[12];
593   char uval[128];
594   char *single_variable;
595   char *per_host_variable;
596   unsigned long long num_per_host;
597   uint16_t new_port;
598
599   if (GNUNET_OK != uc->status)
600     return;
601   if (! ((0 == strcmp (option, "PORT"))
602          || (0 == strcmp (option, "UNIXPATH"))
603          || (0 == strcmp (option, "HOSTNAME"))))
604     return;
605   GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
606   GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
607   if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival)))
608   {
609     if ((ival != 0) &&
610         (GNUNET_YES !=
611          GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
612                                                single_variable)))
613     {
614       /* FIXME: What about UDP? */
615       new_port = GNUNET_TESTING_reserve_port (uc->system, GNUNET_YES);
616       if (0 == new_port)
617       {
618         uc->status = GNUNET_SYSERR;
619         return;
620       }
621       GNUNET_snprintf (cval, sizeof (cval), "%u", new_port);
622       value = cval;
623     }
624     else if ((ival != 0) &&
625              (GNUNET_YES ==
626               GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
627                                                     single_variable)) &&
628              GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
629                                                     per_host_variable,
630                                                     &num_per_host))
631     {
632       /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
633       /*                  ival + ctx->fdnum % num_per_host); */
634       /* value = cval; */
635       GNUNET_break (0);         /* FIXME */
636     }
637   }
638   if (0 == strcmp (option, "UNIXPATH"))
639   {
640     if (GNUNET_YES !=
641         GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
642                                               single_variable))
643     {
644       GNUNET_snprintf (uval, sizeof (uval), "%s/%s.sock",
645                        uc->service_home, section);
646       value = uval;
647     }
648     else if ((GNUNET_YES ==
649               GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
650                                                      per_host_variable,
651                                                      &num_per_host)) &&
652              (num_per_host > 0))
653     {
654       GNUNET_break(0);          /* FIXME */
655     }
656   }
657   if (0 == strcmp (option, "HOSTNAME"))
658   {
659     value = (NULL == uc->system->hostname) ? "localhost" : uc->system->hostname;
660   }
661   GNUNET_free (single_variable);
662   GNUNET_free (per_host_variable);
663   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
664 }
665
666
667 /**
668  * Section iterator to set ACCEPT_FROM/ACCEPT_FROM6 depending on the ip of the
669  * controller in all sections
670  *
671  * @param cls the UpdateContext
672  * @param section name of the section
673  */
674 static void
675 update_config_sections (void *cls,
676                         const char *section)
677 {
678   struct UpdateContext *uc = cls;  
679   char **ikeys;
680   char *val;
681   char *ptr;
682   char *orig_allowed_hosts;
683   char *allowed_hosts;
684   char *ACCEPT_FROM_key;
685   uint16_t ikeys_cnt;
686   uint16_t key;
687   
688   ikeys_cnt = 0;
689   val = NULL;
690   if (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section,
691                                                      "TESTING_IGNORE_KEYS"))
692   {
693     GNUNET_assert 
694       (GNUNET_YES == 
695        GNUNET_CONFIGURATION_get_value_string (uc->cfg, section,
696                                               "TESTING_IGNORE_KEYS", &val));
697     ptr = val;
698     for (ikeys_cnt = 0; NULL != (ptr = strstr (ptr, ";")); ikeys_cnt++)
699       ptr++;
700     if (0 == ikeys_cnt)
701       GNUNET_break (0);
702     else
703     {
704       ikeys = GNUNET_malloc ((sizeof (char *)) * ikeys_cnt);
705       ptr = val;
706       for (key = 0; key < ikeys_cnt; key++)
707       {
708         ikeys[key] = ptr;
709         ptr = strstr (ptr, ";");
710         *ptr = '\0';
711         ptr++;
712       }
713     }
714   }
715   if (0 != ikeys_cnt)
716   {
717     for (key = 0; key < ikeys_cnt; key++)
718     {
719       if (NULL != strstr (ikeys[key], "ADVERTISED_PORT"))
720         break;
721     }
722     if ((key == ikeys_cnt) &&
723         (GNUNET_YES == GNUNET_CONFIGURATION_have_value (uc->cfg, section,
724                                                         "ADVERTISED_PORT")))
725     {
726       if (GNUNET_OK == 
727           GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "PORT", &ptr))
728       {
729         GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, 
730                                                "ADVERTISED_PORT", ptr);
731         GNUNET_free (ptr);
732       }
733     }
734     for (key = 0; key < ikeys_cnt; key++)
735     {
736       if (NULL != strstr (ikeys[key], "ACCEPT_FROM"))
737       {
738         GNUNET_free (ikeys);
739         GNUNET_free (val);
740         return;
741       }
742     }
743     GNUNET_free (ikeys);
744   }
745   GNUNET_free_non_null (val);
746   ACCEPT_FROM_key = "ACCEPT_FROM";  
747   if ((NULL != uc->system->controller) && 
748       (NULL != strstr (uc->system->controller, ":"))) /* IPv6 in use */
749     ACCEPT_FROM_key = "ACCEPT_FROM6";
750   if (GNUNET_OK != 
751       GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, ACCEPT_FROM_key,
752                                              &orig_allowed_hosts))
753   {
754     orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;");
755   }
756   if (NULL == uc->system->controller)
757     allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
758   else
759     GNUNET_asprintf (&allowed_hosts, "%s%s;", orig_allowed_hosts,
760                      uc->system->controller);
761   GNUNET_free (orig_allowed_hosts);
762   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, ACCEPT_FROM_key,
763                                          allowed_hosts);
764   GNUNET_free (allowed_hosts);  
765 }
766
767
768 /**
769  * Create a new configuration using the given configuration as a template;
770  * ports and paths will be modified to select available ports on the local
771  * system. The default configuration will be available in PATHS section under
772  * the option DEFAULTCONFIG after the call. SERVICE_HOME is also set in PATHS
773  * section to the temporary directory specific to this configuration. If we run
774  * out of "*port" numbers, return SYSERR.
775  *
776  * This is primarily a helper function used internally
777  * by 'GNUNET_TESTING_peer_configure'.
778  *
779  * @param system system to use to coordinate resource usage
780  * @param cfg template configuration to update
781  * @return GNUNET_OK on success, GNUNET_SYSERR on error - the configuration will
782  *           be incomplete and should not be used there upon
783  */
784 int
785 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
786                                      struct GNUNET_CONFIGURATION_Handle *cfg)
787 {
788   struct UpdateContext uc;
789   char *default_config;
790   
791   uc.system = system;
792   uc.cfg = cfg;
793   uc.status = GNUNET_OK;
794   GNUNET_asprintf (&uc.service_home, "%s/%u", system->tmppath,
795                    system->path_counter++);
796   GNUNET_asprintf (&default_config, "%s/config", uc.service_home);
797   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "DEFAULTCONFIG",
798                                          default_config);
799   GNUNET_free (default_config);
800   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "SERVICEHOME",
801                                          uc.service_home);
802   /* make PORTs and UNIXPATHs unique */
803   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
804   /* allow connections to services from system controller host */
805   GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
806   /* enable loopback-based connections between peers */
807   GNUNET_CONFIGURATION_set_value_string (cfg, 
808                                          "nat",
809                                          "USE_LOCALADDR", "YES");
810   GNUNET_free (uc.service_home);
811   return uc.status;
812 }
813
814
815 /**
816  * Configure a GNUnet peer.  GNUnet must be installed on the local
817  * system and available in the PATH. 
818  *
819  * @param system system to use to coordinate resource usage
820  * @param cfg configuration to use; will be UPDATED (to reflect needed
821  *            changes in port numbers and paths)
822  * @param key_number number of the hostkey to use for the peer
823  * @param id identifier for the daemon, will be set, can be NULL
824  * @param emsg set to freshly allocated error message (set to NULL on success), 
825  *          can be NULL
826  * @return handle to the peer, NULL on error
827  */
828 struct GNUNET_TESTING_Peer *
829 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
830                                struct GNUNET_CONFIGURATION_Handle *cfg,
831                                uint32_t key_number,
832                                struct GNUNET_PeerIdentity *id,
833                                char **emsg)
834 {
835   struct GNUNET_TESTING_Peer *peer;
836   struct GNUNET_DISK_FileHandle *fd;
837   char *service_home;  
838   char hostkey_filename[128];
839   char *config_filename;
840   char *emsg_;
841   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
842
843   if (NULL != emsg)
844     *emsg = NULL;
845   if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg))
846   {
847     GNUNET_asprintf (&emsg_,
848                        _("Failed to create configuration for peer (not enough free ports?)\n"));
849     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
850     if (NULL != emsg)
851       *emsg = emsg_;
852     else
853       GNUNET_free (emsg_);
854     return NULL;
855   }
856   if (key_number >= system->total_hostkeys)
857   {
858     GNUNET_asprintf (&emsg_,
859                      _("You attempted to create a testbed with more than %u hosts.  Please precompute more hostkeys first.\n"),
860                      (unsigned int) system->total_hostkeys);    
861     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
862     if (NULL != emsg)
863       *emsg = emsg_;
864     else
865       GNUNET_free (emsg_);
866     return NULL;
867   }
868   pk = NULL;
869   if ((NULL != id) &&
870       (NULL == (pk = GNUNET_TESTING_hostkey_get (system, key_number, id))))
871   {
872     GNUNET_asprintf (&emsg_,
873                      _("Failed to initialize hostkey for peer %u\n"),
874                      (unsigned int) key_number);
875     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
876     if (NULL != emsg)
877       *emsg = emsg_;
878     else
879       GNUNET_free (emsg_);
880     return NULL;
881   }
882   if (NULL != pk)
883     GNUNET_CRYPTO_rsa_key_free (pk);
884   GNUNET_assert (GNUNET_OK == 
885                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
886                                                         "SERVICEHOME",
887                                                         &service_home));
888   GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/.hostkey",
889                    service_home);
890   GNUNET_free (service_home);
891   fd = GNUNET_DISK_file_open (hostkey_filename,
892                               GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
893                               GNUNET_DISK_PERM_USER_READ 
894                               | GNUNET_DISK_PERM_USER_WRITE);
895   if (NULL == fd)
896   {
897     GNUNET_break (0); 
898     return NULL;
899   }
900   if (GNUNET_TESTING_HOSTKEYFILESIZE !=
901       GNUNET_DISK_file_write (fd, system->hostkeys_data 
902                               + (key_number * GNUNET_TESTING_HOSTKEYFILESIZE),
903                               GNUNET_TESTING_HOSTKEYFILESIZE))
904   {
905     GNUNET_asprintf (&emsg_,
906                      _("Failed to write hostkey file for peer %u: %s\n"),
907                      (unsigned int) key_number,
908                      STRERROR (errno));
909     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
910     if (NULL != emsg)
911       *emsg = emsg_;
912     else
913       GNUNET_free (emsg_);
914     GNUNET_DISK_file_close (fd);
915     return NULL;
916   }
917   GNUNET_DISK_file_close (fd);
918   GNUNET_assert (GNUNET_OK ==
919                  GNUNET_CONFIGURATION_get_value_string 
920                  (cfg, "PATHS", "DEFAULTCONFIG", &config_filename));  
921   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
922   {
923     GNUNET_asprintf (&emsg_,
924                      _("Failed to write configuration file `%s' for peer %u: %s\n"),
925                      config_filename,
926                      (unsigned int) key_number,
927                      STRERROR (errno));
928     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
929     if (NULL != emsg)
930       *emsg = emsg_;
931     else
932       GNUNET_free (emsg_);
933     GNUNET_free (config_filename);
934     return NULL;
935   }
936   peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
937   peer->cfgfile = config_filename; /* Free in peer_destroy */
938   peer->main_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
939   peer->system = system;
940   peer->key_number = key_number;
941   return peer;
942 }
943
944
945 /**
946  * Obtain the peer identity from a peer handle.
947  *
948  * @param peer peer handle for which we want the peer's identity
949  * @param id identifier for the daemon, will be set
950  */
951 void
952 GNUNET_TESTING_peer_get_identity (const struct GNUNET_TESTING_Peer *peer,
953                                   struct GNUNET_PeerIdentity *id)
954 {
955   GNUNET_CRYPTO_rsa_key_free (GNUNET_TESTING_hostkey_get (peer->system,
956                                                           peer->key_number,
957                                                           id));
958 }
959
960
961 /**
962  * Start the peer. 
963  *
964  * @param peer peer to start
965  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
966  */
967 int
968 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
969 {
970   if (NULL != peer->main_process)
971   {
972     GNUNET_break (0);
973     return GNUNET_SYSERR;
974   }
975   GNUNET_assert (NULL != peer->cfgfile);
976   peer->main_process = GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL,
977                                                 peer->main_binary,
978                                                 peer->main_binary,
979                                                 "-c",
980                                                 peer->cfgfile,
981                                                 NULL);
982   if (NULL == peer->main_process)
983   {
984     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
985                 _("Failed to start `%s': %s\n"),
986                 peer->main_binary,
987                 STRERROR (errno));
988     return GNUNET_SYSERR;
989   }
990   return GNUNET_OS_set_process_priority (peer->main_process, GNUNET_SCHEDULER_PRIORITY_BACKGROUND);
991 }
992
993
994 /**
995  * Stop the peer. 
996  *
997  * @param peer peer to stop
998  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
999  */
1000 int
1001 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
1002 {
1003   if (NULL == peer->main_process)
1004   {
1005     GNUNET_break (0);
1006     return GNUNET_SYSERR;
1007   }
1008   (void) GNUNET_OS_process_kill (peer->main_process, SIGTERM);
1009   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (peer->main_process));
1010   GNUNET_OS_process_destroy (peer->main_process);
1011   peer->main_process = NULL;
1012   return GNUNET_OK;
1013 }
1014
1015
1016 /**
1017  * Destroy the peer.  Releases resources locked during peer configuration.
1018  * If the peer is still running, it will be stopped AND a warning will be
1019  * printed (users of the API should stop the peer explicitly first).
1020  *
1021  * @param peer peer to destroy
1022  */
1023 void
1024 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
1025 {
1026   if (NULL != peer->main_process)
1027   {
1028     GNUNET_break (0);
1029     GNUNET_TESTING_peer_stop (peer);
1030   }
1031   GNUNET_free (peer->cfgfile);
1032   GNUNET_free (peer->main_binary);
1033   GNUNET_free (peer);
1034 }
1035
1036
1037 /**
1038  * Start a single peer and run a test using the testing library.
1039  * Starts a peer using the given configuration and then invokes the
1040  * given callback.  This function ALSO initializes the scheduler loop
1041  * and should thus be called directly from "main".  The testcase
1042  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
1043  *
1044  * @param testdir only the directory name without any path. This is used for
1045  *          all service homes; the directory will be created in a temporary
1046  *          location depending on the underlying OS
1047  * @param cfgfilename name of the configuration file to use;
1048  *         use NULL to only run with defaults
1049  * @param tm main function of the testcase
1050  * @param tm_cls closure for 'tm'
1051  * @return 0 on success, 1 on error
1052  */
1053 int
1054 GNUNET_TESTING_peer_run (const char *testdir,
1055                          const char *cfgfilename,
1056                          GNUNET_TESTING_TestMain tm,
1057                          void *tm_cls)
1058 {
1059   return GNUNET_TESTING_service_run (testdir, "arm",
1060                                      cfgfilename, tm, tm_cls);
1061 }
1062
1063
1064 /**
1065  * Structure for holding service data
1066  */
1067 struct ServiceContext
1068 {
1069   /**
1070    * The configuration of the peer in which the service is run
1071    */
1072   const struct GNUNET_CONFIGURATION_Handle *cfg;
1073
1074   /**
1075    * Callback to signal service startup
1076    */
1077   GNUNET_TESTING_TestMain tm;
1078   
1079   /**
1080    * The peer in which the service is run.
1081    */
1082   struct GNUNET_TESTING_Peer *peer;
1083
1084   /**
1085    * Closure for the above callback
1086    */
1087   void *tm_cls;
1088 };
1089
1090
1091 /**
1092  * Callback to be called when SCHEDULER has been started
1093  *
1094  * @param cls the ServiceContext
1095  * @param tc the TaskContext
1096  */
1097 static void
1098 service_run_main (void *cls,
1099                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1100 {
1101   struct ServiceContext *sc = cls;
1102
1103   sc->tm (sc->tm_cls, sc->cfg, sc->peer);
1104 }
1105
1106
1107 /**
1108  * Start a single service (no ARM, except of course if the given
1109  * service name is 'arm') and run a test using the testing library.
1110  * Starts a service using the given configuration and then invokes the
1111  * given callback.  This function ALSO initializes the scheduler loop
1112  * and should thus be called directly from "main".  The testcase
1113  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
1114  *
1115  * This function is useful if the testcase is for a single service
1116  * and if that service doesn't itself depend on other services.
1117  *
1118  * @param testdir only the directory name without any path. This is used for
1119  *          all service homes; the directory will be created in a temporary
1120  *          location depending on the underlying OS
1121  * @param service_name name of the service to run
1122  * @param cfgfilename name of the configuration file to use;
1123  *         use NULL to only run with defaults
1124  * @param tm main function of the testcase
1125  * @param tm_cls closure for 'tm'
1126  * @return 0 on success, 1 on error
1127  */
1128 int
1129 GNUNET_TESTING_service_run (const char *testdir,
1130                             const char *service_name,
1131                             const char *cfgfilename,
1132                             GNUNET_TESTING_TestMain tm,
1133                             void *tm_cls)
1134 {
1135   struct ServiceContext sc;
1136   struct GNUNET_TESTING_System *system;
1137   struct GNUNET_TESTING_Peer *peer;
1138   struct GNUNET_CONFIGURATION_Handle *cfg;
1139   char *binary;
1140
1141   GNUNET_log_setup (testdir, "WARNING", NULL);
1142   system = GNUNET_TESTING_system_create (testdir, "127.0.0.1", NULL);
1143   if (NULL == system)
1144     return 1;
1145   cfg = GNUNET_CONFIGURATION_create ();
1146   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
1147   {
1148     LOG (GNUNET_ERROR_TYPE_ERROR,
1149          _("Failed to load configuration from %s\n"), cfgfilename);
1150     GNUNET_CONFIGURATION_destroy (cfg);
1151     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1152     return 1;
1153   }
1154   peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
1155   if (NULL == peer)
1156   {
1157     GNUNET_CONFIGURATION_destroy (cfg);
1158     hostkeys_unload (system);
1159     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1160     return 1;
1161   }
1162   GNUNET_free (peer->main_binary);
1163   GNUNET_asprintf (&binary, "gnunet-service-%s", service_name);
1164   peer->main_binary = GNUNET_OS_get_libexec_binary_path (binary);
1165   GNUNET_free (binary);
1166   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
1167   {    
1168     GNUNET_TESTING_peer_destroy (peer);
1169     GNUNET_CONFIGURATION_destroy (cfg);
1170     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1171     return 1;
1172   }
1173   sc.cfg = cfg;
1174   sc.tm = tm;
1175   sc.tm_cls = tm_cls;
1176   sc.peer = peer;
1177   GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
1178   if ((NULL != peer->main_process) &&
1179       (GNUNET_OK != GNUNET_TESTING_peer_stop (peer)))
1180   {
1181     GNUNET_TESTING_peer_destroy (peer);
1182     GNUNET_CONFIGURATION_destroy (cfg);
1183     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1184     return 1;
1185   }
1186   GNUNET_TESTING_peer_destroy (peer);
1187   GNUNET_CONFIGURATION_destroy (cfg);
1188   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1189   return 0;
1190 }
1191
1192
1193 /**
1194  * Sometimes we use the binary name to determine which specific
1195  * test to run.  In those cases, the string after the last "_"
1196  * in 'argv[0]' specifies a string that determines the configuration
1197  * file or plugin to use.  
1198  *
1199  * This function returns the respective substring, taking care
1200  * of issues such as binaries ending in '.exe' on W32.
1201  *
1202  * @param argv0 the name of the binary
1203  * @return string between the last '_' and the '.exe' (or the end of the string),
1204  *         NULL if argv0 has no '_' 
1205  */
1206 char *
1207 GNUNET_TESTING_get_testname_from_underscore (const char *argv0)
1208 {
1209   size_t slen = strlen (argv0) + 1;
1210   char sbuf[slen];
1211   char *ret;
1212   char *dot;
1213
1214   memcpy (sbuf, argv0, slen);
1215   ret = strrchr (sbuf, '_');
1216   if (NULL == ret)
1217     return NULL;
1218   ret++; /* skip underscore */
1219   dot = strchr (ret, '.');
1220   if (NULL != dot)
1221     *dot = '\0';
1222   return GNUNET_strdup (ret);
1223 }
1224
1225
1226 /* end of testing.c */