- dont run regex test until mesh_new is made default
[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 error message (set to NULL on success), can be NULL
757  * @return handle to the peer, NULL on error
758  */
759 struct GNUNET_TESTING_Peer *
760 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
761                                struct GNUNET_CONFIGURATION_Handle *cfg,
762                                uint32_t key_number,
763                                struct GNUNET_PeerIdentity *id,
764                                char **emsg)
765 {
766   struct GNUNET_TESTING_Peer *peer;
767   struct GNUNET_DISK_FileHandle *fd;
768   char *service_home;  
769   char hostkey_filename[128];
770   char *config_filename;
771   char *emsg_;
772   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
773
774   if (NULL != emsg)
775     *emsg = NULL;
776   if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg))
777   {
778     GNUNET_asprintf (&emsg_,
779                        _("Failed to create configuration for peer (not enough free ports?)\n"));
780     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
781     if (NULL != emsg)
782       *emsg = emsg_;
783     else
784       GNUNET_free (emsg_);
785     return NULL;
786   }
787   if (key_number >= system->total_hostkeys)
788   {
789     GNUNET_asprintf (&emsg_,
790                      _("You attempted to create a testbed with more than %u hosts.  Please precompute more hostkeys first.\n"),
791                      (unsigned int) system->total_hostkeys);    
792     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
793     if (NULL != emsg)
794       *emsg = emsg_;
795     else
796       GNUNET_free (emsg_);
797     return NULL;
798   }
799   pk = NULL;
800   if ((NULL != id) &&
801       (NULL == (pk = GNUNET_TESTING_hostkey_get (system, key_number, id))))
802   {
803     GNUNET_asprintf (&emsg_,
804                      _("Failed to initialize hostkey for peer %u\n"),
805                      (unsigned int) key_number);
806     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
807     if (NULL != emsg)
808       *emsg = emsg_;
809     else
810       GNUNET_free (emsg_);
811     return NULL;
812   }
813   if (NULL != pk)
814     GNUNET_CRYPTO_rsa_key_free (pk);
815   GNUNET_assert (GNUNET_OK == 
816                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
817                                                         "SERVICEHOME",
818                                                         &service_home));
819   GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/.hostkey",
820                    service_home);
821   GNUNET_free (service_home);
822   fd = GNUNET_DISK_file_open (hostkey_filename,
823                               GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
824                               GNUNET_DISK_PERM_USER_READ 
825                               | GNUNET_DISK_PERM_USER_WRITE);
826   if (NULL == fd)
827   {
828     GNUNET_break (0); 
829     return NULL;
830   }
831   if (HOSTKEYFILESIZE !=
832       GNUNET_DISK_file_write (fd, system->hostkeys_data 
833                               + (key_number * HOSTKEYFILESIZE),
834                               HOSTKEYFILESIZE))
835   {
836     GNUNET_asprintf (&emsg_,
837                      _("Failed to write hostkey file for peer %u: %s\n"),
838                      (unsigned int) key_number,
839                      STRERROR (errno));
840     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
841     if (NULL != emsg)
842       *emsg = emsg_;
843     else
844       GNUNET_free (emsg_);
845     GNUNET_DISK_file_close (fd);
846     return NULL;
847   }
848   GNUNET_DISK_file_close (fd);
849   GNUNET_assert (GNUNET_OK ==
850                  GNUNET_CONFIGURATION_get_value_string 
851                  (cfg, "PATHS", "DEFAULTCONFIG", &config_filename));  
852   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
853   {
854     GNUNET_asprintf (&emsg_,
855                      _("Failed to write configuration file `%s' for peer %u: %s\n"),
856                      config_filename,
857                      (unsigned int) key_number,
858                      STRERROR (errno));
859     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", *emsg_);
860     if (NULL != emsg)
861       *emsg = emsg_;
862     else
863       GNUNET_free (emsg_);
864     GNUNET_free (config_filename);
865     return NULL;
866   }
867   peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
868   peer->cfgfile = config_filename; /* Free in peer_destroy */
869   peer->main_binary = GNUNET_strdup ("gnunet-service-arm");
870   peer->system = system;
871   peer->key_number = key_number;
872   return peer;
873 }
874
875
876 /**
877  * Obtain the peer identity from a peer handle.
878  *
879  * @param peer peer handle for which we want the peer's identity
880  * @param id identifier for the daemon, will be set
881  */
882 void
883 GNUNET_TESTING_peer_get_identity (const struct GNUNET_TESTING_Peer *peer,
884                                   struct GNUNET_PeerIdentity *id)
885 {
886   GNUNET_CRYPTO_rsa_key_free (GNUNET_TESTING_hostkey_get (peer->system,
887                                                           peer->key_number,
888                                                           id));
889 }
890
891
892 /**
893  * Start the peer. 
894  *
895  * @param peer peer to start
896  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
897  */
898 int
899 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
900 {
901   if (NULL != peer->main_process)
902   {
903     GNUNET_break (0);
904     return GNUNET_SYSERR;
905   }
906   GNUNET_assert (NULL != peer->cfgfile);
907   peer->main_process = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL,
908                                                 peer->main_binary,
909                                                 peer->main_binary,
910                                                 "-c",
911                                                 peer->cfgfile,
912                                                 NULL);
913   if (NULL == peer->main_process)
914   {
915     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
916                 _("Failed to start `%s': %s\n"),
917                 peer->main_binary,
918                 STRERROR (errno));
919     return GNUNET_SYSERR;
920   }
921   return GNUNET_OK;
922 }
923
924
925 /**
926  * Stop the peer. 
927  *
928  * @param peer peer to stop
929  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
930  */
931 int
932 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
933 {
934   if (NULL == peer->main_process)
935   {
936     GNUNET_break (0);
937     return GNUNET_SYSERR;
938   }
939   (void) GNUNET_OS_process_kill (peer->main_process, SIGTERM);
940   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (peer->main_process));
941   GNUNET_OS_process_destroy (peer->main_process);
942   peer->main_process = NULL;
943   return GNUNET_OK;
944 }
945
946
947 /**
948  * Destroy the peer.  Releases resources locked during peer configuration.
949  * If the peer is still running, it will be stopped AND a warning will be
950  * printed (users of the API should stop the peer explicitly first).
951  *
952  * @param peer peer to destroy
953  */
954 void
955 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
956 {
957   if (NULL != peer->main_process)
958   {
959     GNUNET_break (0);
960     GNUNET_TESTING_peer_stop (peer);
961   }
962   GNUNET_free (peer->cfgfile);
963   GNUNET_free (peer->main_binary);
964   GNUNET_free (peer);
965 }
966
967
968 /**
969  * Start a single peer and run a test using the testing library.
970  * Starts a peer using the given configuration and then invokes the
971  * given callback.  This function ALSO initializes the scheduler loop
972  * and should thus be called directly from "main".  The testcase
973  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
974  *
975  * @param testdir only the directory name without any path. This is used for
976  *          all service homes; the directory will be created in a temporary
977  *          location depending on the underlying OS
978  * @param cfgfilename name of the configuration file to use;
979  *         use NULL to only run with defaults
980  * @param tm main function of the testcase
981  * @param tm_cls closure for 'tm'
982  * @return 0 on success, 1 on error
983  */
984 int
985 GNUNET_TESTING_peer_run (const char *testdir,
986                          const char *cfgfilename,
987                          GNUNET_TESTING_TestMain tm,
988                          void *tm_cls)
989 {
990   return GNUNET_TESTING_service_run (testdir, "arm",
991                                      cfgfilename, tm, tm_cls);
992 }
993
994
995 /**
996  * Structure for holding service data
997  */
998 struct ServiceContext
999 {
1000   /**
1001    * The configuration of the peer in which the service is run
1002    */
1003   const struct GNUNET_CONFIGURATION_Handle *cfg;
1004
1005   /**
1006    * Callback to signal service startup
1007    */
1008   GNUNET_TESTING_TestMain tm;
1009   
1010   /**
1011    * The peer in which the service is run.
1012    */
1013   struct GNUNET_TESTING_Peer *peer;
1014
1015   /**
1016    * Closure for the above callback
1017    */
1018   void *tm_cls;
1019 };
1020
1021
1022 /**
1023  * Callback to be called when SCHEDULER has been started
1024  *
1025  * @param cls the ServiceContext
1026  * @param tc the TaskContext
1027  */
1028 static void
1029 service_run_main (void *cls,
1030                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1031 {
1032   struct ServiceContext *sc = cls;
1033
1034   sc->tm (sc->tm_cls, sc->cfg, sc->peer);
1035 }
1036
1037
1038 /**
1039  * Start a single service (no ARM, except of course if the given
1040  * service name is 'arm') and run a test using the testing library.
1041  * Starts a service using the given configuration and then invokes the
1042  * given callback.  This function ALSO initializes the scheduler loop
1043  * and should thus be called directly from "main".  The testcase
1044  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
1045  *
1046  * This function is useful if the testcase is for a single service
1047  * and if that service doesn't itself depend on other services.
1048  *
1049  * @param testdir only the directory name without any path. This is used for
1050  *          all service homes; the directory will be created in a temporary
1051  *          location depending on the underlying OS
1052  * @param service_name name of the service to run
1053  * @param cfgfilename name of the configuration file to use;
1054  *         use NULL to only run with defaults
1055  * @param tm main function of the testcase
1056  * @param tm_cls closure for 'tm'
1057  * @return 0 on success, 1 on error
1058  */
1059 int
1060 GNUNET_TESTING_service_run (const char *testdir,
1061                             const char *service_name,
1062                             const char *cfgfilename,
1063                             GNUNET_TESTING_TestMain tm,
1064                             void *tm_cls)
1065 {
1066   struct ServiceContext sc;
1067   struct GNUNET_TESTING_System *system;
1068   struct GNUNET_TESTING_Peer *peer;
1069   struct GNUNET_CONFIGURATION_Handle *cfg;
1070
1071   GNUNET_log_setup (testdir, "WARNING", NULL);
1072   system = GNUNET_TESTING_system_create (testdir, "127.0.0.1");
1073   if (NULL == system)
1074     return 1;
1075   cfg = GNUNET_CONFIGURATION_create ();
1076   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
1077   {
1078     LOG (GNUNET_ERROR_TYPE_ERROR,
1079          _("Failed to load configuration from %s\n"), cfgfilename);
1080     GNUNET_CONFIGURATION_destroy (cfg);
1081     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1082     return 1;
1083   }
1084   peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
1085   if (NULL == peer)
1086   {
1087     GNUNET_CONFIGURATION_destroy (cfg);
1088     hostkeys_unload (system);
1089     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1090     return 1;
1091   }
1092   GNUNET_free (peer->main_binary);
1093   GNUNET_asprintf (&peer->main_binary, "gnunet-service-%s", service_name);
1094   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
1095   {    
1096     GNUNET_TESTING_peer_destroy (peer);
1097     GNUNET_CONFIGURATION_destroy (cfg);
1098     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1099     return 1;
1100   }
1101   sc.cfg = cfg;
1102   sc.tm = tm;
1103   sc.tm_cls = tm_cls;
1104   sc.peer = peer;
1105   GNUNET_SCHEDULER_run (&service_run_main, &sc); /* Scheduler loop */
1106   if ((NULL != peer->main_process) &&
1107       (GNUNET_OK != GNUNET_TESTING_peer_stop (peer)))
1108   {
1109     GNUNET_TESTING_peer_destroy (peer);
1110     GNUNET_CONFIGURATION_destroy (cfg);
1111     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1112     return 1;
1113   }
1114   GNUNET_TESTING_peer_destroy (peer);
1115   GNUNET_CONFIGURATION_destroy (cfg);
1116   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1117   return 0;
1118 }
1119
1120
1121 /**
1122  * Sometimes we use the binary name to determine which specific
1123  * test to run.  In those cases, the string after the last "_"
1124  * in 'argv[0]' specifies a string that determines the configuration
1125  * file or plugin to use.  
1126  *
1127  * This function returns the respective substring, taking care
1128  * of issues such as binaries ending in '.exe' on W32.
1129  *
1130  * @param argv0 the name of the binary
1131  * @return string between the last '_' and the '.exe' (or the end of the string),
1132  *         NULL if argv0 has no '_' 
1133  */
1134 char *
1135 GNUNET_TESTING_get_testname_from_underscore (const char *argv0)
1136 {
1137   size_t slen = strlen (argv0) + 1;
1138   char sbuf[slen];
1139   char *ret;
1140   char *dot;
1141
1142   memcpy (sbuf, argv0, slen);
1143   ret = strrchr (sbuf, '_');
1144   if (NULL == ret)
1145     return NULL;
1146   ret++; /* skip underscore */
1147   dot = strchr (ret, '.');
1148   if (NULL != dot)
1149     *dot = '\0';
1150   return GNUNET_strdup (ret);
1151 }
1152
1153
1154 /* end of testing.c */