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