remove 'illegal' (non-reentrant) log logic from signal handler
[oweals/gnunet.git] / src / testing / testing.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2008, 2009, 2012 GNUnet e.V.
4
5       GNUnet is free software: you can redistribute it and/or modify it
6       under the terms of the GNU Affero General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your 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       Affero General Public License for more details.
14
15       You should have received a copy of the GNU Affero General Public License
16       along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file testing/testing.c
23  * @brief convenience API for writing testcases for GNUnet
24  *        Many testcases need to start and stop a peer/service
25  *        and this library is supposed to make that easier
26  *        for TESTCASES.  Normal programs should always
27  *        use functions from gnunet_{util,arm}_lib.h.  This API is
28  *        ONLY for writing testcases (or internal use of the testbed).
29  * @author Christian Grothoff
30  *
31  */
32 #include "platform.h"
33 #include "gnunet_util_lib.h"
34 #include "gnunet_arm_service.h"
35 #include "gnunet_testing_lib.h"
36
37 #define LOG(kind, ...) GNUNET_log_from (kind, "testing-api", __VA_ARGS__)
38
39
40 /**
41  * We need pipe control only on WINDOWS
42  */
43 #define PIPE_CONTROL GNUNET_NO
44
45
46 /**
47  * Lowest port used for GNUnet testing.  Should be high enough to not
48  * conflict with other applications running on the hosts but be low
49  * enough to not conflict with client-ports (typically starting around
50  * 32k).
51  */
52 #define LOW_PORT 12000
53
54 /**
55  * Highest port used for GNUnet testing.  Should be low enough to not
56  * conflict with the port range for "local" ports (client apps; see
57  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
58  */
59 #define HIGH_PORT 56000
60
61
62 struct SharedServiceInstance
63 {
64   struct SharedService *ss;
65
66   char *cfg_fn;
67
68   struct GNUNET_OS_Process *proc;
69
70   char *unix_sock;
71
72   char *port_str;
73
74   unsigned int n_refs;
75 };
76
77 struct SharedService
78 {
79   char *sname;
80
81   struct SharedServiceInstance **instances;
82
83   struct GNUNET_CONFIGURATION_Handle *cfg;
84
85   unsigned int n_peers;
86
87   unsigned int share;
88
89   unsigned int n_instances;
90 };
91
92
93 /**
94  * Handle for a system on which GNUnet peers are executed;
95  * a system is used for reserving unique paths and ports.
96  */
97 struct GNUNET_TESTING_System
98 {
99   /**
100    * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
101    * GNUNET_HOME.
102    */
103   char *tmppath;
104
105   /**
106    * The trusted ip. Can either be a single ip address or a network address in
107    * CIDR notation.
108    */
109   char *trusted_ip;
110
111   /**
112    * our hostname
113    */
114   char *hostname;
115
116   /**
117    * Hostkeys data, contains "GNUNET_TESTING_HOSTKEYFILESIZE * total_hostkeys" bytes.
118    */
119   char *hostkeys_data;
120
121   /**
122    * memory map for @e hostkeys_data.
123    */
124   struct GNUNET_DISK_MapHandle *map;
125
126   struct SharedService **shared_services;
127
128   unsigned int n_shared_services;
129
130   /**
131    * Bitmap where each port that has already been reserved for some GNUnet peer
132    * is recorded.  Note that we make no distinction between TCP and UDP ports
133    * and test if a port is already in use before assigning it to a peer/service.
134    * If we detect that a port is already in use, we also mark it in this bitmap.
135    * So all the bits that are zero merely indicate ports that MIGHT be available
136    * for peers.
137    */
138   uint32_t reserved_ports[65536 / 32];
139
140   /**
141    * Counter we use to make service home paths unique on this system;
142    * the full path consists of the tmppath and this number.  Each
143    * UNIXPATH for a peer is also modified to include the respective
144    * path counter to ensure uniqueness.  This field is incremented
145    * by one for each configured peer.  Even if peers are destroyed,
146    * we never re-use path counters.
147    */
148   uint32_t path_counter;
149
150   /**
151    * The number of hostkeys
152    */
153   uint32_t total_hostkeys;
154
155   /**
156    * Lowest port we are allowed to use.
157    */
158   uint16_t lowport;
159
160   /**
161    * Highest port we are allowed to use.
162    */
163   uint16_t highport;
164 };
165
166
167 /**
168  * Handle for a GNUnet peer controlled by testing.
169  */
170 struct GNUNET_TESTING_Peer
171 {
172   /**
173    * The TESTING system associated with this peer
174    */
175   struct GNUNET_TESTING_System *system;
176
177   /**
178    * Path to the configuration file for this peer.
179    */
180   char *cfgfile;
181
182   /**
183    * Binary to be executed during 'GNUNET_TESTING_peer_start'.
184    * Typically 'gnunet-service-arm' (but can be set to a
185    * specific service by 'GNUNET_TESTING_service_run' if
186    * necessary).
187    */
188   char *main_binary;
189   char *args;
190
191   /**
192    * Handle to the running binary of the service, NULL if the
193    * peer/service is currently not running.
194    */
195   struct GNUNET_OS_Process *main_process;
196
197   /**
198    * The handle to the peer's ARM service
199    */
200   struct GNUNET_ARM_Handle *ah;
201
202   /**
203    * The config of the peer
204    */
205   struct GNUNET_CONFIGURATION_Handle *cfg;
206
207   /**
208    * The callback to call asynchronously when a peer is stopped
209    */
210   GNUNET_TESTING_PeerStopCallback cb;
211
212   /**
213    * The closure for the above callback
214    */
215   void *cb_cls;
216
217   /**
218    * The cached identity of this peer.  Will be populated on call to
219    * GNUNET_TESTING_peer_get_identity()
220    */
221   struct GNUNET_PeerIdentity *id;
222
223   struct SharedServiceInstance **ss_instances;
224
225   /**
226    * Array of ports currently allocated to this peer.  These ports will be
227    * released upon peer destroy and can be used by other peers which are
228    * configured after.
229    */
230   uint16_t *ports;
231
232   /**
233    * The number of ports in the above array
234    */
235   unsigned int nports;
236
237   /**
238    * The keynumber of this peer's hostkey
239    */
240   uint32_t key_number;
241 };
242
243
244 /**
245  * Testing includes a number of pre-created hostkeys for faster peer
246  * startup. This function loads such keys into memory from a file.
247  *
248  * @param system the testing system handle
249  * @return #GNUNET_OK on success; #GNUNET_SYSERR on error
250  */
251 static int
252 hostkeys_load (struct GNUNET_TESTING_System *system)
253 {
254   uint64_t fs;
255   char *data_dir;
256   char *filename;
257   struct GNUNET_DISK_FileHandle *fd;
258
259   GNUNET_assert (NULL == system->hostkeys_data);
260   data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
261   GNUNET_asprintf (&filename, "%s/testing_hostkeys.ecc", data_dir);
262   GNUNET_free (data_dir);
263
264   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
265   {
266     LOG (GNUNET_ERROR_TYPE_ERROR,
267          _ ("Hostkeys file not found: %s\n"),
268          filename);
269     GNUNET_free (filename);
270     return GNUNET_SYSERR;
271   }
272   /* Check hostkey file size, read entire thing into memory */
273   if (GNUNET_OK !=
274       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
275     fs = 0;
276   if (0 == fs)
277   {
278     GNUNET_free (filename);
279     return GNUNET_SYSERR;   /* File is empty */
280   }
281   if (0 != (fs % GNUNET_TESTING_HOSTKEYFILESIZE))
282   {
283     LOG (GNUNET_ERROR_TYPE_ERROR,
284          _ ("Incorrect hostkey file format: %s\n"),
285          filename);
286     GNUNET_free (filename);
287     return GNUNET_SYSERR;
288   }
289   fd = GNUNET_DISK_file_open (filename,
290                               GNUNET_DISK_OPEN_READ,
291                               GNUNET_DISK_PERM_NONE);
292   if (NULL == fd)
293   {
294     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename);
295     GNUNET_free (filename);
296     return GNUNET_SYSERR;
297   }
298   GNUNET_free (filename);
299   system->hostkeys_data =
300     GNUNET_DISK_file_map (fd, &system->map, GNUNET_DISK_MAP_TYPE_READ, fs);
301   GNUNET_DISK_file_close (fd);
302   if (NULL == system->hostkeys_data)
303     return GNUNET_SYSERR;
304   system->total_hostkeys = fs / GNUNET_TESTING_HOSTKEYFILESIZE;
305   return GNUNET_OK;
306 }
307
308
309 /**
310  * Function to remove the loaded hostkeys
311  *
312  * @param system the testing system handle
313  */
314 static void
315 hostkeys_unload (struct GNUNET_TESTING_System *system)
316 {
317   GNUNET_break (NULL != system->hostkeys_data);
318   system->hostkeys_data = NULL;
319   GNUNET_DISK_file_unmap (system->map);
320   system->map = NULL;
321   system->hostkeys_data = NULL;
322   system->total_hostkeys = 0;
323 }
324
325
326 /**
327  * Function to iterate over options.
328  *
329  * @param cls closure
330  * @param section name of the section
331  * @param option name of the option
332  * @param value value of the option
333  */
334 static void
335 cfg_copy_iterator (void *cls,
336                    const char *section,
337                    const char *option,
338                    const char *value)
339 {
340   struct GNUNET_CONFIGURATION_Handle *cfg2 = cls;
341
342   GNUNET_CONFIGURATION_set_value_string (cfg2, section, option, value);
343 }
344
345
346 /**
347  * Create a system handle.  There must only be one system
348  * handle per operating system.
349  *
350  * @param testdir only the directory name without any path. This is used for
351  *          all service homes; the directory will be created in a temporary
352  *          location depending on the underlying OS.  This variable will be
353  *          overridden with the value of the environmental variable
354  *          GNUNET_TESTING_PREFIX, if it exists.
355  * @param trusted_ip the ip address which will be set as TRUSTED HOST in all
356  *          service configurations generated to allow control connections from
357  *          this ip. This can either be a single ip address or a network address
358  *          in CIDR notation.
359  * @param hostname the hostname of the system we are using for testing; NULL for
360  *          localhost
361  * @param shared_services NULL terminated array describing services that are to
362  *          be shared among peers
363  * @param lowport lowest port number this system is allowed to allocate (inclusive)
364  * @param highport highest port number this system is allowed to allocate (exclusive)
365  * @return handle to this system, NULL on error
366  */
367 struct GNUNET_TESTING_System *
368 GNUNET_TESTING_system_create_with_portrange (
369   const char *testdir,
370   const char *trusted_ip,
371   const char *hostname,
372   const struct GNUNET_TESTING_SharedService *shared_services,
373   uint16_t lowport,
374   uint16_t highport)
375 {
376   struct GNUNET_TESTING_System *system;
377   struct GNUNET_TESTING_SharedService tss;
378   struct SharedService *ss;
379   unsigned int cnt;
380
381   GNUNET_assert (NULL != testdir);
382   system = GNUNET_new (struct GNUNET_TESTING_System);
383   if (NULL == (system->tmppath = getenv (GNUNET_TESTING_PREFIX)))
384     system->tmppath = GNUNET_DISK_mkdtemp (testdir);
385   else
386     system->tmppath = GNUNET_strdup (system->tmppath);
387   system->lowport = lowport;
388   system->highport = highport;
389   if (NULL == system->tmppath)
390   {
391     GNUNET_free (system);
392     return NULL;
393   }
394   if (NULL != trusted_ip)
395     system->trusted_ip = GNUNET_strdup (trusted_ip);
396   if (NULL != hostname)
397     system->hostname = GNUNET_strdup (hostname);
398   if (GNUNET_OK != hostkeys_load (system))
399   {
400     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
401     return NULL;
402   }
403   if (NULL == shared_services)
404     return system;
405   for (cnt = 0; NULL != shared_services[cnt].service; cnt++)
406   {
407     tss = shared_services[cnt];
408     ss = GNUNET_new (struct SharedService);
409     ss->sname = GNUNET_strdup (tss.service);
410     ss->cfg = GNUNET_CONFIGURATION_create ();
411     GNUNET_CONFIGURATION_iterate_section_values (tss.cfg,
412                                                  ss->sname,
413                                                  &cfg_copy_iterator,
414                                                  ss->cfg);
415     GNUNET_CONFIGURATION_iterate_section_values (tss.cfg,
416                                                  "TESTING",
417                                                  &cfg_copy_iterator,
418                                                  ss->cfg);
419     GNUNET_CONFIGURATION_iterate_section_values (tss.cfg,
420                                                  "PATHS",
421                                                  &cfg_copy_iterator,
422                                                  ss->cfg);
423     ss->share = tss.share;
424     GNUNET_array_append (system->shared_services,
425                          system->n_shared_services,
426                          ss);
427   }
428   return system;
429 }
430
431
432 /**
433  * Create a system handle.  There must only be one system handle per operating
434  * system.  Uses a default range for allowed ports.  Ports are still tested for
435  * availability.
436  *
437  * @param testdir only the directory name without any path. This is used for all
438  *          service homes; the directory will be created in a temporary location
439  *          depending on the underlying OS.  This variable will be
440  *          overridden with the value of the environmental variable
441  *          GNUNET_TESTING_PREFIX, if it exists.
442  * @param trusted_ip the ip address which will be set as TRUSTED HOST in all
443  *          service configurations generated to allow control connections from
444  *          this ip. This can either be a single ip address or a network address
445  *          in CIDR notation.
446  * @param hostname the hostname of the system we are using for testing; NULL for
447  *          localhost
448  * @param shared_services NULL terminated array describing services that are to
449  *          be shared among peers
450  * @return handle to this system, NULL on error
451  */
452 struct GNUNET_TESTING_System *
453 GNUNET_TESTING_system_create (
454   const char *testdir,
455   const char *trusted_ip,
456   const char *hostname,
457   const struct GNUNET_TESTING_SharedService *shared_services)
458 {
459   return GNUNET_TESTING_system_create_with_portrange (testdir,
460                                                       trusted_ip,
461                                                       hostname,
462                                                       shared_services,
463                                                       LOW_PORT,
464                                                       HIGH_PORT);
465 }
466
467
468 static void
469 cleanup_shared_service_instance (struct SharedServiceInstance *i)
470 {
471   if (NULL != i->cfg_fn)
472   {
473     (void) unlink (i->cfg_fn);
474     GNUNET_free (i->cfg_fn);
475   }
476   GNUNET_free_non_null (i->unix_sock);
477   GNUNET_free_non_null (i->port_str);
478   GNUNET_break (NULL == i->proc);
479   GNUNET_break (0 == i->n_refs);
480   GNUNET_free (i);
481 }
482
483
484 static int
485 start_shared_service_instance (struct SharedServiceInstance *i)
486 {
487   char *binary;
488   char *libexec_binary;
489
490   GNUNET_assert (NULL == i->proc);
491   GNUNET_assert (NULL != i->cfg_fn);
492   (void) GNUNET_asprintf (&binary, "gnunet-service-%s", i->ss->sname);
493   libexec_binary = GNUNET_OS_get_libexec_binary_path (binary);
494   GNUNET_free (binary);
495   i->proc = GNUNET_OS_start_process (PIPE_CONTROL,
496                                      GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
497                                      NULL,
498                                      NULL,
499                                      NULL,
500                                      libexec_binary,
501                                      libexec_binary,
502                                      "-c",
503                                      i->cfg_fn,
504                                      NULL);
505   GNUNET_free (libexec_binary);
506   if (NULL == i->proc)
507     return GNUNET_SYSERR;
508   return GNUNET_OK;
509 }
510
511
512 static void
513 stop_shared_service_instance (struct SharedServiceInstance *i)
514 {
515   GNUNET_break (0 == i->n_refs);
516   if (0 != GNUNET_OS_process_kill (i->proc, GNUNET_TERM_SIG))
517     LOG (GNUNET_ERROR_TYPE_WARNING,
518          "Killing shared service instance (%s) failed\n",
519          i->ss->sname);
520   (void) GNUNET_OS_process_wait (i->proc);
521   GNUNET_OS_process_destroy (i->proc);
522   i->proc = NULL;
523 }
524
525
526 /**
527  * Free system resources.
528  *
529  * @param system system to be freed
530  * @param remove_paths should the 'testdir' and all subdirectories
531  *        be removed (clean up on shutdown)?
532  */
533 void
534 GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
535                                int remove_paths)
536 {
537   struct SharedService *ss;
538   struct SharedServiceInstance *i;
539   unsigned int ss_cnt;
540   unsigned int i_cnt;
541
542   if (NULL != system->hostkeys_data)
543     hostkeys_unload (system);
544   for (ss_cnt = 0; ss_cnt < system->n_shared_services; ss_cnt++)
545   {
546     ss = system->shared_services[ss_cnt];
547     for (i_cnt = 0; i_cnt < ss->n_instances; i_cnt++)
548     {
549       i = ss->instances[i_cnt];
550       if (NULL != i->proc)
551         stop_shared_service_instance (i);
552       cleanup_shared_service_instance (i);
553     }
554     GNUNET_free_non_null (ss->instances);
555     GNUNET_CONFIGURATION_destroy (ss->cfg);
556     GNUNET_free (ss->sname);
557     GNUNET_free (ss);
558   }
559   GNUNET_free_non_null (system->shared_services);
560   if (GNUNET_YES == remove_paths)
561     GNUNET_DISK_directory_remove (system->tmppath);
562   GNUNET_free (system->tmppath);
563   GNUNET_free_non_null (system->trusted_ip);
564   GNUNET_free_non_null (system->hostname);
565   GNUNET_free (system);
566 }
567
568
569 /**
570  * Reserve a TCP or UDP port for a peer.
571  *
572  * @param system system to use for reservation tracking
573  * @return 0 if no free port was available
574  */
575 uint16_t
576 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system)
577 {
578   struct GNUNET_NETWORK_Handle *socket;
579   struct addrinfo hint;
580   struct addrinfo *ret;
581   struct addrinfo *ai;
582   uint32_t *port_buckets;
583   char *open_port_str;
584   int bind_status;
585   uint32_t xor_image;
586   uint16_t index;
587   uint16_t open_port;
588   uint16_t pos;
589
590   /*
591      FIXME: Instead of using getaddrinfo we should try to determine the port
592          status by the following heurestics.
593
594          On systems which support both IPv4 and IPv6, only ports open on both
595          address families are considered open.
596          On system with either IPv4 or IPv6. A port is considered open if it's
597          open in the respective address family
598    */hint.ai_family = AF_UNSPEC; /* IPv4 and IPv6 */
599   hint.ai_socktype = 0;
600   hint.ai_protocol = 0;
601   hint.ai_addrlen = 0;
602   hint.ai_addr = NULL;
603   hint.ai_canonname = NULL;
604   hint.ai_next = NULL;
605   hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* Wild card address */
606   port_buckets = system->reserved_ports;
607   for (index = (system->lowport / 32) + 1; index < (system->highport / 32);
608        index++)
609   {
610     xor_image = (UINT32_MAX ^ port_buckets[index]);
611     if (0 == xor_image)   /* Ports in the bucket are full */
612       continue;
613     pos = system->lowport % 32;
614     while (pos < 32)
615     {
616       if (0 == ((xor_image >> pos) & 1U))
617       {
618         pos++;
619         continue;
620       }
621       open_port = (index * 32) + pos;
622       if (open_port >= system->highport)
623         return 0;
624       GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port);
625       ret = NULL;
626       GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
627       GNUNET_free (open_port_str);
628       bind_status = GNUNET_NO;
629       for (ai = ret; NULL != ai; ai = ai->ai_next)
630       {
631         socket = GNUNET_NETWORK_socket_create (ai->ai_family, SOCK_STREAM, 0);
632         if (NULL == socket)
633           continue;
634         bind_status =
635           GNUNET_NETWORK_socket_bind (socket, ai->ai_addr, ai->ai_addrlen);
636         GNUNET_NETWORK_socket_close (socket);
637         if (GNUNET_OK != bind_status)
638           break;
639         socket = GNUNET_NETWORK_socket_create (ai->ai_family, SOCK_DGRAM, 0);
640         if (NULL == socket)
641           continue;
642         bind_status =
643           GNUNET_NETWORK_socket_bind (socket, ai->ai_addr, ai->ai_addrlen);
644         GNUNET_NETWORK_socket_close (socket);
645         if (GNUNET_OK != bind_status)
646           break;
647       }
648       port_buckets[index] |= (1U << pos);     /* Set the port bit */
649       freeaddrinfo (ret);
650       if (GNUNET_OK == bind_status)
651       {
652         LOG (GNUNET_ERROR_TYPE_DEBUG,
653              "Found a free port %u\n",
654              (unsigned int) open_port);
655         return open_port;
656       }
657       pos++;
658     }
659   }
660   return 0;
661 }
662
663
664 /**
665  * Release reservation of a TCP or UDP port for a peer
666  * (used during #GNUNET_TESTING_peer_destroy()).
667  *
668  * @param system system to use for reservation tracking
669  * @param port reserved port to release
670  */
671 void
672 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
673                              uint16_t port)
674 {
675   uint32_t *port_buckets;
676   uint16_t bucket;
677   uint16_t pos;
678
679   port_buckets = system->reserved_ports;
680   bucket = port / 32;
681   pos = port % 32;
682   LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
683   if (0 == (port_buckets[bucket] & (1U << pos)))
684   {
685     GNUNET_break (0);  /* Port was not reserved by us using reserve_port() */
686     return;
687   }
688   port_buckets[bucket] &= ~(1U << pos);
689 }
690
691
692 /**
693  * Testing includes a number of pre-created hostkeys for
694  * faster peer startup.  This function can be used to
695  * access the n-th key of those pre-created hostkeys; note
696  * that these keys are ONLY useful for testing and not
697  * secure as the private keys are part of the public
698  * GNUnet source code.
699  *
700  * This is primarily a helper function used internally
701  * by #GNUNET_TESTING_peer_configure.
702  *
703  * @param system the testing system handle
704  * @param key_number desired pre-created hostkey to obtain
705  * @param id set to the peer's identity (hash of the public
706  *        key; if NULL, NULL is returned immediately
707  * @return NULL on error (not enough keys)
708  */
709 struct GNUNET_CRYPTO_EddsaPrivateKey *
710 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
711                             uint32_t key_number,
712                             struct GNUNET_PeerIdentity *id)
713 {
714   struct GNUNET_CRYPTO_EddsaPrivateKey *private_key;
715
716   if ((NULL == id) || (NULL == system->hostkeys_data))
717     return NULL;
718   if (key_number >= system->total_hostkeys)
719   {
720     LOG (GNUNET_ERROR_TYPE_ERROR,
721          _ ("Key number %u does not exist\n"),
722          key_number);
723     return NULL;
724   }
725   private_key = GNUNET_new (struct GNUNET_CRYPTO_EddsaPrivateKey);
726   GNUNET_memcpy (private_key,
727                  system->hostkeys_data
728                  + (key_number * GNUNET_TESTING_HOSTKEYFILESIZE),
729                  GNUNET_TESTING_HOSTKEYFILESIZE);
730   GNUNET_CRYPTO_eddsa_key_get_public (private_key, &id->public_key);
731   return private_key;
732 }
733
734
735 /**
736  * Structure for holding data to build new configurations from a configuration
737  * template
738  */
739 struct UpdateContext
740 {
741   /**
742    * The system for which we are building configurations
743    */
744   struct GNUNET_TESTING_System *system;
745
746   /**
747    * The configuration we are building
748    */
749   struct GNUNET_CONFIGURATION_Handle *cfg;
750
751   /**
752    * The customized service home path for this peer
753    */
754   char *gnunet_home;
755
756   /**
757    * Array of ports currently allocated to this peer.  These ports will be
758    * released upon peer destroy and can be used by other peers which are
759    * configured after.
760    */
761   uint16_t *ports;
762
763   /**
764    * The number of ports in the above array
765    */
766   unsigned int nports;
767
768   /**
769    * build status - to signal error while building a configuration
770    */
771   int status;
772 };
773
774
775 /**
776  * Function to iterate over options.  Copies
777  * the options to the target configuration,
778  * updating PORT values as needed.
779  *
780  * @param cls the UpdateContext
781  * @param section name of the section
782  * @param option name of the option
783  * @param value value of the option
784  */
785 static void
786 update_config (void *cls,
787                const char *section,
788                const char *option,
789                const char *value)
790 {
791   struct UpdateContext *uc = cls;
792   unsigned int ival;
793   char cval[12];
794   char uval[PATH_MAX];
795   char *single_variable;
796   char *per_host_variable;
797   unsigned long long num_per_host;
798   uint16_t new_port;
799
800   if (GNUNET_OK != uc->status)
801     return;
802   if (! ((0 == strcmp (option, "PORT")) || (0 == strcmp (option, "UNIXPATH")) ||
803          (0 == strcmp (option, "HOSTNAME"))))
804     return;
805   GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
806   GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
807   if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
808   {
809     if ((ival != 0) &&
810         (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (uc->cfg,
811                                                              "testing",
812                                                              single_variable)))
813     {
814       new_port = GNUNET_TESTING_reserve_port (uc->system);
815       if (0 == new_port)
816       {
817         uc->status = GNUNET_SYSERR;
818         GNUNET_free (single_variable);
819         GNUNET_free (per_host_variable);
820         return;
821       }
822       GNUNET_snprintf (cval, sizeof(cval), "%u", new_port);
823       value = cval;
824       GNUNET_array_append (uc->ports, uc->nports, new_port);
825     }
826     else if ((ival != 0) &&
827              (GNUNET_YES ==
828               GNUNET_CONFIGURATION_get_value_yesno (uc->cfg,
829                                                     "testing",
830                                                     single_variable)) &&
831              GNUNET_CONFIGURATION_get_value_number (uc->cfg,
832                                                     "testing",
833                                                     per_host_variable,
834                                                     &num_per_host))
835     {
836       /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
837       /*                  ival + ctx->fdnum % num_per_host); */
838       /* value = cval; */
839       GNUNET_break (0);    /* FIXME */
840     }
841   }
842   if (0 == strcmp (option, "UNIXPATH"))
843   {
844     if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (uc->cfg,
845                                                             "testing",
846                                                             single_variable))
847     {
848       GNUNET_snprintf (uval,
849                        sizeof(uval),
850                        "%s/%s.sock",
851                        uc->gnunet_home,
852                        section);
853       value = uval;
854     }
855     else if ((GNUNET_YES ==
856               GNUNET_CONFIGURATION_get_value_number (uc->cfg,
857                                                      "testing",
858                                                      per_host_variable,
859                                                      &num_per_host)) &&
860              (num_per_host > 0))
861     {
862       GNUNET_break (0);    /* FIXME */
863     }
864   }
865   if (0 == strcmp (option, "HOSTNAME"))
866   {
867     value = (NULL == uc->system->hostname) ? "localhost" : uc->system->hostname;
868   }
869   GNUNET_free (single_variable);
870   GNUNET_free (per_host_variable);
871   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
872 }
873
874
875 /**
876  * Section iterator to set ACCEPT_FROM/ACCEPT_FROM6 to include the address of
877  * 'trusted_hosts' in all sections
878  *
879  * @param cls the UpdateContext
880  * @param section name of the section
881  */
882 static void
883 update_config_sections (void *cls, const char *section)
884 {
885   struct UpdateContext *uc = cls;
886   char **ikeys;
887   char *val;
888   char *ptr;
889   char *orig_allowed_hosts;
890   char *allowed_hosts;
891   char *ACCEPT_FROM_key;
892   uint16_t ikeys_cnt;
893   uint16_t key;
894
895   ikeys_cnt = 0;
896   val = NULL;
897   /* Ignore certain options from sections.  See
898      https://gnunet.org/bugs/view.php?id=2476 */
899   if (GNUNET_YES ==
900       GNUNET_CONFIGURATION_have_value (uc->cfg, section, "TESTING_IGNORE_KEYS"))
901   {
902     GNUNET_assert (GNUNET_YES ==
903                    GNUNET_CONFIGURATION_get_value_string (uc->cfg,
904                                                           section,
905                                                           "TESTING_IGNORE_KEYS",
906                                                           &val));
907     ptr = val;
908     for (ikeys_cnt = 0; NULL != (ptr = strstr (ptr, ";")); ikeys_cnt++)
909       ptr++;
910     if (0 == ikeys_cnt)
911       GNUNET_break (0);
912     else
913     {
914       ikeys = GNUNET_malloc ((sizeof(char *)) * ikeys_cnt);
915       ptr = val;
916       for (key = 0; key < ikeys_cnt; key++)
917       {
918         ikeys[key] = ptr;
919         ptr = strstr (ptr, ";");
920         GNUNET_assert (NULL != ptr);      /* worked just before... */
921         *ptr = '\0';
922         ptr++;
923       }
924     }
925   }
926   if (0 != ikeys_cnt)
927   {
928     for (key = 0; key < ikeys_cnt; key++)
929     {
930       if (NULL != strstr (ikeys[key], "ADVERTISED_PORT"))
931         break;
932     }
933     if ((key == ikeys_cnt) &&
934         (GNUNET_YES ==
935          GNUNET_CONFIGURATION_have_value (uc->cfg, section, "ADVERTISED_PORT")))
936     {
937       if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (uc->cfg,
938                                                               section,
939                                                               "PORT",
940                                                               &ptr))
941       {
942         GNUNET_CONFIGURATION_set_value_string (uc->cfg,
943                                                section,
944                                                "ADVERTISED_PORT",
945                                                ptr);
946         GNUNET_free (ptr);
947       }
948     }
949     for (key = 0; key < ikeys_cnt; key++)
950     {
951       if (NULL != strstr (ikeys[key], "ACCEPT_FROM"))
952       {
953         GNUNET_free (ikeys);
954         GNUNET_free (val);
955         return;
956       }
957     }
958     GNUNET_free (ikeys);
959   }
960   GNUNET_free_non_null (val);
961   ACCEPT_FROM_key = "ACCEPT_FROM";
962   if ((NULL != uc->system->trusted_ip) &&
963       (NULL != strstr (uc->system->trusted_ip, ":"))) /* IPv6 in use */
964     ACCEPT_FROM_key = "ACCEPT_FROM6";
965   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (uc->cfg,
966                                                           section,
967                                                           ACCEPT_FROM_key,
968                                                           &orig_allowed_hosts))
969   {
970     orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;");
971   }
972   if (NULL == uc->system->trusted_ip)
973     allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
974   else
975     GNUNET_asprintf (&allowed_hosts,
976                      "%s%s;",
977                      orig_allowed_hosts,
978                      uc->system->trusted_ip);
979   GNUNET_free (orig_allowed_hosts);
980   GNUNET_CONFIGURATION_set_value_string (uc->cfg,
981                                          section,
982                                          ACCEPT_FROM_key,
983                                          allowed_hosts);
984   GNUNET_free (allowed_hosts);
985 }
986
987
988 static struct SharedServiceInstance *
989 associate_shared_service (struct GNUNET_TESTING_System *system,
990                           struct SharedService *ss,
991                           struct GNUNET_CONFIGURATION_Handle *cfg)
992 {
993   struct SharedServiceInstance *i;
994   struct GNUNET_CONFIGURATION_Handle *temp;
995   char *gnunet_home;
996   uint32_t port;
997
998   ss->n_peers++;
999   if (((0 == ss->share) && (NULL == ss->instances)) ||
1000       ((0 != ss->share) &&
1001        (ss->n_instances < ((ss->n_peers + ss->share - 1) / ss->share))))
1002   {
1003     i = GNUNET_new (struct SharedServiceInstance);
1004     i->ss = ss;
1005     (void) GNUNET_asprintf (&gnunet_home,
1006                             "%s/shared/%s/%u",
1007                             system->tmppath,
1008                             ss->sname,
1009                             ss->n_instances);
1010     (void) GNUNET_asprintf (&i->unix_sock, "%s/sock", gnunet_home);
1011     port = GNUNET_TESTING_reserve_port (system);
1012     if (0 == port)
1013     {
1014       GNUNET_free (gnunet_home);
1015       cleanup_shared_service_instance (i);
1016       return NULL;
1017     }
1018     GNUNET_array_append (ss->instances, ss->n_instances, i);
1019     temp = GNUNET_CONFIGURATION_dup (ss->cfg);
1020     (void) GNUNET_asprintf (&i->port_str, "%u", port);
1021     (void) GNUNET_asprintf (&i->cfg_fn, "%s/config", gnunet_home);
1022     GNUNET_CONFIGURATION_set_value_string (temp,
1023                                            "PATHS",
1024                                            "GNUNET_HOME",
1025                                            gnunet_home);
1026     GNUNET_free (gnunet_home);
1027     GNUNET_CONFIGURATION_set_value_string (temp,
1028                                            ss->sname,
1029                                            "UNIXPATH",
1030                                            i->unix_sock);
1031     GNUNET_CONFIGURATION_set_value_string (temp,
1032                                            ss->sname,
1033                                            "PORT",
1034                                            i->port_str);
1035     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_write (temp, i->cfg_fn))
1036     {
1037       GNUNET_CONFIGURATION_destroy (temp);
1038       cleanup_shared_service_instance (i);
1039       return NULL;
1040     }
1041     GNUNET_CONFIGURATION_destroy (temp);
1042   }
1043   else
1044   {
1045     GNUNET_assert (NULL != ss->instances);
1046     GNUNET_assert (0 < ss->n_instances);
1047     i = ss->instances[ss->n_instances - 1];
1048   }
1049   GNUNET_CONFIGURATION_iterate_section_values (ss->cfg,
1050                                                ss->sname,
1051                                                &cfg_copy_iterator,
1052                                                cfg);
1053   GNUNET_CONFIGURATION_set_value_string (cfg,
1054                                          ss->sname,
1055                                          "UNIXPATH",
1056                                          i->unix_sock);
1057   GNUNET_CONFIGURATION_set_value_string (cfg, ss->sname, "PORT", i->port_str);
1058   return i;
1059 }
1060
1061
1062 /**
1063  * Create a new configuration using the given configuration as a template;
1064  * ports and paths will be modified to select available ports on the local
1065  * system. The default configuration will be available in PATHS section under
1066  * the option DEFAULTCONFIG after the call. GNUNET_HOME is also set in PATHS
1067  * section to the temporary directory specific to this configuration. If we run
1068  * out of "*port" numbers, return #GNUNET_SYSERR.
1069  *
1070  * This is primarily a helper function used internally
1071  * by 'GNUNET_TESTING_peer_configure'.
1072  *
1073  * @param system system to use to coordinate resource usage
1074  * @param cfg template configuration to update
1075  * @param ports array with port numbers used in the created configuration.
1076  *          Will be updated upon successful return.  Can be NULL
1077  * @param nports the size of the `ports' array.  Will be updated.
1078  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - the configuration will
1079  *           be incomplete and should not be used there upon
1080  */
1081 static int
1082 GNUNET_TESTING_configuration_create_ (struct GNUNET_TESTING_System *system,
1083                                       struct GNUNET_CONFIGURATION_Handle *cfg,
1084                                       uint16_t **ports,
1085                                       unsigned int *nports)
1086 {
1087   struct UpdateContext uc;
1088   char *default_config;
1089
1090   uc.system = system;
1091   uc.cfg = cfg;
1092   uc.status = GNUNET_OK;
1093   uc.ports = NULL;
1094   uc.nports = 0;
1095   GNUNET_asprintf (&uc.gnunet_home,
1096                    "%s/%u",
1097                    system->tmppath,
1098                    system->path_counter++);
1099   GNUNET_asprintf (&default_config, "%s/config", uc.gnunet_home);
1100   GNUNET_CONFIGURATION_set_value_string (cfg,
1101                                          "PATHS",
1102                                          "DEFAULTCONFIG",
1103                                          default_config);
1104   GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG", default_config);
1105   GNUNET_free (default_config);
1106   GNUNET_CONFIGURATION_set_value_string (cfg,
1107                                          "PATHS",
1108                                          "GNUNET_HOME",
1109                                          uc.gnunet_home);
1110   /* make PORTs and UNIXPATHs unique */
1111   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
1112   /* allow connections to services from system trusted_ip host */
1113   GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
1114   /* enable loopback-based connections between peers */
1115   GNUNET_CONFIGURATION_set_value_string (cfg, "nat", "USE_LOCALADDR", "YES");
1116   GNUNET_free (uc.gnunet_home);
1117   if ((NULL != ports) && (NULL != nports))
1118   {
1119     *ports = uc.ports;
1120     *nports = uc.nports;
1121   }
1122   else
1123     GNUNET_free_non_null (uc.ports);
1124   return uc.status;
1125 }
1126
1127
1128 /**
1129  * Create a new configuration using the given configuration as a template;
1130  * ports and paths will be modified to select available ports on the local
1131  * system. The default configuration will be available in PATHS section under
1132  * the option DEFAULTCONFIG after the call. GNUNET_HOME is also set in PATHS
1133  * section to the temporary directory specific to this configuration. If we run
1134  * out of "*port" numbers, return #GNUNET_SYSERR.
1135  *
1136  * This is primarily a helper function used internally
1137  * by #GNUNET_TESTING_peer_configure().
1138  *
1139  * @param system system to use to coordinate resource usage
1140  * @param cfg template configuration to update
1141  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - the configuration will
1142  *           be incomplete and should not be used there upon
1143  */
1144 int
1145 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
1146                                      struct GNUNET_CONFIGURATION_Handle *cfg)
1147 {
1148   return GNUNET_TESTING_configuration_create_ (system, cfg, NULL, NULL);
1149 }
1150
1151
1152 /**
1153  * Configure a GNUnet peer.  GNUnet must be installed on the local
1154  * system and available in the PATH.
1155  *
1156  * @param system system to use to coordinate resource usage
1157  * @param cfg configuration to use; will be UPDATED (to reflect needed
1158  *            changes in port numbers and paths)
1159  * @param key_number number of the hostkey to use for the peer
1160  * @param id identifier for the daemon, will be set, can be NULL
1161  * @param emsg set to freshly allocated error message (set to NULL on success),
1162  *          can be NULL
1163  * @return handle to the peer, NULL on error
1164  */
1165 struct GNUNET_TESTING_Peer *
1166 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
1167                                struct GNUNET_CONFIGURATION_Handle *cfg,
1168                                uint32_t key_number,
1169                                struct GNUNET_PeerIdentity *id,
1170                                char **emsg)
1171 {
1172   struct GNUNET_TESTING_Peer *peer;
1173   struct GNUNET_DISK_FileHandle *fd;
1174   char *hostkey_filename;
1175   char *config_filename;
1176   char *libexec_binary;
1177   char *emsg_;
1178   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
1179   uint16_t *ports;
1180   struct SharedService *ss;
1181   struct SharedServiceInstance **ss_instances;
1182   unsigned int cnt;
1183   unsigned int nports;
1184
1185   ports = NULL;
1186   nports = 0;
1187   ss_instances = NULL;
1188   if (NULL != emsg)
1189     *emsg = NULL;
1190   if (key_number >= system->total_hostkeys)
1191   {
1192     GNUNET_asprintf (
1193       &emsg_,
1194       _ (
1195         "You attempted to create a testbed with more than %u hosts.  Please precompute more hostkeys first.\n"),
1196       (unsigned int) system->total_hostkeys);
1197     goto err_ret;
1198   }
1199   pk = NULL;
1200   if ((NULL != id) &&
1201       (NULL == (pk = GNUNET_TESTING_hostkey_get (system, key_number, id))))
1202   {
1203     GNUNET_asprintf (&emsg_,
1204                      _ ("Failed to initialize hostkey for peer %u\n"),
1205                      (unsigned int) key_number);
1206     goto err_ret;
1207   }
1208   if (NULL != pk)
1209     GNUNET_free (pk);
1210   if (GNUNET_NO == GNUNET_CONFIGURATION_have_value (cfg, "PEER", "PRIVATE_KEY"))
1211   {
1212     GNUNET_asprintf (
1213       &emsg_,
1214       _ ("PRIVATE_KEY option in PEER section missing in configuration\n"));
1215     goto err_ret;
1216   }
1217   /* Remove sections for shared services */
1218   for (cnt = 0; cnt < system->n_shared_services; cnt++)
1219   {
1220     ss = system->shared_services[cnt];
1221     GNUNET_CONFIGURATION_remove_section (cfg, ss->sname);
1222   }
1223   if (GNUNET_OK !=
1224       GNUNET_TESTING_configuration_create_ (system, cfg, &ports, &nports))
1225   {
1226     GNUNET_asprintf (&emsg_,
1227                      _ ("Failed to create configuration for peer "
1228                         "(not enough free ports?)\n"));
1229     goto err_ret;
1230   }
1231   GNUNET_assert (GNUNET_OK ==
1232                  GNUNET_CONFIGURATION_get_value_filename (cfg,
1233                                                           "PEER",
1234                                                           "PRIVATE_KEY",
1235                                                           &hostkey_filename));
1236   fd = GNUNET_DISK_file_open (hostkey_filename,
1237                               GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
1238                               GNUNET_DISK_PERM_USER_READ
1239                               | GNUNET_DISK_PERM_USER_WRITE);
1240   if (NULL == fd)
1241   {
1242     GNUNET_asprintf (&emsg_,
1243                      _ ("Cannot open hostkey file `%s': %s\n"),
1244                      hostkey_filename,
1245                      strerror (errno));
1246     GNUNET_free (hostkey_filename);
1247     goto err_ret;
1248   }
1249   GNUNET_free (hostkey_filename);
1250   if (GNUNET_TESTING_HOSTKEYFILESIZE !=
1251       GNUNET_DISK_file_write (fd,
1252                               system->hostkeys_data
1253                               + (key_number * GNUNET_TESTING_HOSTKEYFILESIZE),
1254                               GNUNET_TESTING_HOSTKEYFILESIZE))
1255   {
1256     GNUNET_asprintf (&emsg_,
1257                      _ ("Failed to write hostkey file for peer %u: %s\n"),
1258                      (unsigned int) key_number,
1259                      strerror (errno));
1260     GNUNET_DISK_file_close (fd);
1261     goto err_ret;
1262   }
1263   GNUNET_DISK_file_close (fd);
1264   ss_instances = GNUNET_malloc (sizeof(struct SharedServiceInstance *)
1265                                 * system->n_shared_services);
1266   for (cnt = 0; cnt < system->n_shared_services; cnt++)
1267   {
1268     ss = system->shared_services[cnt];
1269     ss_instances[cnt] = associate_shared_service (system, ss, cfg);
1270     if (NULL == ss_instances[cnt])
1271     {
1272       emsg_ = GNUNET_strdup ("FIXME");
1273       goto err_ret;
1274     }
1275   }
1276   GNUNET_assert (GNUNET_OK ==
1277                  GNUNET_CONFIGURATION_get_value_filename (cfg,
1278                                                           "PATHS",
1279                                                           "DEFAULTCONFIG",
1280                                                           &config_filename));
1281   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
1282   {
1283     GNUNET_asprintf (&emsg_,
1284                      _ (
1285                        "Failed to write configuration file `%s' for peer %u: %s\n"),
1286                      config_filename,
1287                      (unsigned int) key_number,
1288                      strerror (errno));
1289     GNUNET_free (config_filename);
1290     goto err_ret;
1291   }
1292   peer = GNUNET_new (struct GNUNET_TESTING_Peer);
1293   peer->ss_instances = ss_instances;
1294   peer->cfgfile = config_filename; /* Free in peer_destroy */
1295   peer->cfg = GNUNET_CONFIGURATION_dup (cfg);
1296   libexec_binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
1297   if (GNUNET_SYSERR ==
1298       GNUNET_CONFIGURATION_get_value_string (cfg,
1299                                              "arm",
1300                                              "PREFIX",
1301                                              &peer->main_binary))
1302   {
1303     /* No prefix */
1304     GNUNET_asprintf (&peer->main_binary, "%s", libexec_binary);
1305     peer->args = GNUNET_strdup ("");
1306   }
1307   else
1308   {
1309     peer->args = GNUNET_strdup (libexec_binary);
1310   }
1311   peer->system = system;
1312   peer->key_number = key_number;
1313   GNUNET_free (libexec_binary);
1314   peer->ports = ports; /* Free in peer_destroy */
1315   peer->nports = nports;
1316   return peer;
1317
1318 err_ret:
1319   GNUNET_free_non_null (ss_instances);
1320   GNUNET_free_non_null (ports);
1321   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", emsg_);
1322   if (NULL != emsg)
1323     *emsg = emsg_;
1324   else
1325     GNUNET_free (emsg_);
1326   return NULL;
1327 }
1328
1329
1330 /**
1331  * Obtain the peer identity from a peer handle.
1332  *
1333  * @param peer peer handle for which we want the peer's identity
1334  * @param id identifier for the daemon, will be set
1335  */
1336 void
1337 GNUNET_TESTING_peer_get_identity (struct GNUNET_TESTING_Peer *peer,
1338                                   struct GNUNET_PeerIdentity *id)
1339 {
1340   if (NULL != peer->id)
1341   {
1342     GNUNET_memcpy (id, peer->id, sizeof(struct GNUNET_PeerIdentity));
1343     return;
1344   }
1345   peer->id = GNUNET_new (struct GNUNET_PeerIdentity);
1346   GNUNET_free (
1347     GNUNET_TESTING_hostkey_get (peer->system, peer->key_number, peer->id));
1348   GNUNET_memcpy (id, peer->id, sizeof(struct GNUNET_PeerIdentity));
1349 }
1350
1351
1352 /**
1353  * Start the peer.
1354  *
1355  * @param peer peer to start
1356  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (i.e. peer already running)
1357  */
1358 int
1359 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
1360 {
1361   struct SharedServiceInstance *i;
1362   unsigned int cnt;
1363
1364   if (NULL != peer->main_process)
1365   {
1366     GNUNET_break (0);
1367     return GNUNET_SYSERR;
1368   }
1369   GNUNET_assert (NULL != peer->cfgfile);
1370   for (cnt = 0; cnt < peer->system->n_shared_services; cnt++)
1371   {
1372     i = peer->ss_instances[cnt];
1373     if ((0 == i->n_refs) &&
1374         (GNUNET_SYSERR == start_shared_service_instance (i)))
1375       return GNUNET_SYSERR;
1376     i->n_refs++;
1377   }
1378   peer->main_binary =
1379     GNUNET_CONFIGURATION_expand_dollar (peer->cfg, peer->main_binary);
1380   peer->main_process =
1381     GNUNET_OS_start_process_s (PIPE_CONTROL,
1382                                GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
1383                                NULL,
1384                                peer->main_binary,
1385                                peer->args,
1386                                "-c",
1387                                peer->cfgfile,
1388                                NULL);
1389   if (NULL == peer->main_process)
1390   {
1391     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1392                 _ ("Failed to start `%s': %s\n"),
1393                 peer->main_binary,
1394                 strerror (errno));
1395     return GNUNET_SYSERR;
1396   }
1397   return GNUNET_OK;
1398 }
1399
1400
1401 /**
1402  * Sends SIGTERM to the peer's main process
1403  *
1404  * @param peer the handle to the peer
1405  * @return #GNUNET_OK if successful; #GNUNET_SYSERR if the main process is NULL
1406  *           or upon any error while sending SIGTERM
1407  */
1408 int
1409 GNUNET_TESTING_peer_kill (struct GNUNET_TESTING_Peer *peer)
1410 {
1411   struct SharedServiceInstance *i;
1412   unsigned int cnt;
1413
1414   if (NULL == peer->main_process)
1415   {
1416     GNUNET_break (0);
1417     return GNUNET_SYSERR;
1418   }
1419   if (0 != GNUNET_OS_process_kill (peer->main_process, GNUNET_TERM_SIG))
1420     return GNUNET_SYSERR;
1421   for (cnt = 0; cnt < peer->system->n_shared_services; cnt++)
1422   {
1423     i = peer->ss_instances[cnt];
1424     GNUNET_assert (0 != i->n_refs);
1425     i->n_refs--;
1426     if (0 == i->n_refs)
1427       stop_shared_service_instance (i);
1428   }
1429   return GNUNET_OK;
1430 }
1431
1432
1433 /**
1434  * Waits for a peer to terminate. The peer's main process will also be destroyed.
1435  *
1436  * @param peer the handle to the peer
1437  * @return #GNUNET_OK if successful; #GNUNET_SYSERR if the main process is NULL
1438  *           or upon any error while waiting
1439  */
1440 int
1441 GNUNET_TESTING_peer_wait (struct GNUNET_TESTING_Peer *peer)
1442 {
1443   int ret;
1444
1445   if (NULL == peer->main_process)
1446   {
1447     GNUNET_break (0);
1448     return GNUNET_SYSERR;
1449   }
1450   ret = GNUNET_OS_process_wait (peer->main_process);
1451   GNUNET_OS_process_destroy (peer->main_process);
1452   peer->main_process = NULL;
1453   return ret;
1454 }
1455
1456
1457 /**
1458  * Stop the peer.
1459  *
1460  * @param peer peer to stop
1461  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1462  */
1463 int
1464 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
1465 {
1466   if (GNUNET_SYSERR == GNUNET_TESTING_peer_kill (peer))
1467     return GNUNET_SYSERR;
1468   if (GNUNET_SYSERR == GNUNET_TESTING_peer_wait (peer))
1469     return GNUNET_SYSERR;
1470   return GNUNET_OK;
1471 }
1472
1473
1474 /**
1475  * Function called whenever we connect to or disconnect from ARM.
1476  *
1477  * @param cls closure
1478  * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected,
1479  *                  #GNUNET_SYSERR on error.
1480  */
1481 static void
1482 disconn_status (void *cls, int connected)
1483 {
1484   struct GNUNET_TESTING_Peer *peer = cls;
1485
1486   if (GNUNET_SYSERR == connected)
1487   {
1488     peer->cb (peer->cb_cls, peer, connected);
1489     return;
1490   }
1491   if (GNUNET_YES == connected)
1492   {
1493     GNUNET_break (GNUNET_OK == GNUNET_TESTING_peer_kill (peer));
1494     return;
1495   }
1496   GNUNET_break (GNUNET_OK == GNUNET_TESTING_peer_wait (peer));
1497   GNUNET_ARM_disconnect (peer->ah);
1498   peer->ah = NULL;
1499   peer->cb (peer->cb_cls, peer, GNUNET_YES);
1500 }
1501
1502
1503 /**
1504  * Stop a peer asynchronously using ARM API.  Peer's shutdown is signaled
1505  * through the GNUNET_TESTING_PeerStopCallback().
1506  *
1507  * @param peer the peer to stop
1508  * @param cb the callback to signal peer shutdown
1509  * @param cb_cls closure for the above callback
1510  * @return #GNUNET_OK upon successfully giving the request to the ARM API (this
1511  *           does not mean that the peer is successfully stopped); #GNUNET_SYSERR
1512  *           upon any error.
1513  */
1514 int
1515 GNUNET_TESTING_peer_stop_async (struct GNUNET_TESTING_Peer *peer,
1516                                 GNUNET_TESTING_PeerStopCallback cb,
1517                                 void *cb_cls)
1518 {
1519   if (NULL == peer->main_process)
1520     return GNUNET_SYSERR;
1521   peer->ah = GNUNET_ARM_connect (peer->cfg, &disconn_status, peer);
1522   if (NULL == peer->ah)
1523     return GNUNET_SYSERR;
1524   peer->cb = cb;
1525   peer->cb_cls = cb_cls;
1526   return GNUNET_OK;
1527 }
1528
1529
1530 /**
1531  * Cancel a previous asynchronous peer stop request.
1532  * GNUNET_TESTING_peer_stop_async() should have been called before on the given
1533  * peer.  It is an error to call this function if the peer stop callback was
1534  * already called
1535  *
1536  * @param peer the peer on which GNUNET_TESTING_peer_stop_async() was called
1537  *          before.
1538  */
1539 void
1540 GNUNET_TESTING_peer_stop_async_cancel (struct GNUNET_TESTING_Peer *peer)
1541 {
1542   GNUNET_assert (NULL != peer->ah);
1543   GNUNET_ARM_disconnect (peer->ah);
1544   peer->ah = NULL;
1545 }
1546
1547
1548 /**
1549  * Destroy the peer.  Releases resources locked during peer configuration.
1550  * If the peer is still running, it will be stopped AND a warning will be
1551  * printed (users of the API should stop the peer explicitly first).
1552  *
1553  * @param peer peer to destroy
1554  */
1555 void
1556 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
1557 {
1558   unsigned int cnt;
1559
1560   if (NULL != peer->main_process)
1561     GNUNET_TESTING_peer_stop (peer);
1562   if (NULL != peer->ah)
1563     GNUNET_ARM_disconnect (peer->ah);
1564   GNUNET_free (peer->cfgfile);
1565   if (NULL != peer->cfg)
1566     GNUNET_CONFIGURATION_destroy (peer->cfg);
1567   GNUNET_free (peer->main_binary);
1568   GNUNET_free (peer->args);
1569   GNUNET_free_non_null (peer->id);
1570   GNUNET_free_non_null (peer->ss_instances);
1571   if (NULL != peer->ports)
1572   {
1573     for (cnt = 0; cnt < peer->nports; cnt++)
1574       GNUNET_TESTING_release_port (peer->system, peer->ports[cnt]);
1575     GNUNET_free (peer->ports);
1576   }
1577   GNUNET_free (peer);
1578 }
1579
1580
1581 /**
1582  * Start a single peer and run a test using the testing library.
1583  * Starts a peer using the given configuration and then invokes the
1584  * given callback.  This function ALSO initializes the scheduler loop
1585  * and should thus be called directly from "main".  The testcase
1586  * should self-terminate by invoking #GNUNET_SCHEDULER_shutdown().
1587  *
1588  * @param testdir only the directory name without any path. This is used for
1589  *          all service homes; the directory will be created in a temporary
1590  *          location depending on the underlying OS
1591  * @param cfgfilename name of the configuration file to use;
1592  *         use NULL to only run with defaults
1593  * @param tm main function of the testcase
1594  * @param tm_cls closure for @a tm
1595  * @return 0 on success, 1 on error
1596  */
1597 int
1598 GNUNET_TESTING_peer_run (const char *testdir,
1599                          const char *cfgfilename,
1600                          GNUNET_TESTING_TestMain tm,
1601                          void *tm_cls)
1602 {
1603   return GNUNET_TESTING_service_run (testdir, "arm", cfgfilename, tm, tm_cls);
1604 }
1605
1606
1607 /**
1608  * Structure for holding service data
1609  */
1610 struct ServiceContext
1611 {
1612   /**
1613    * The configuration of the peer in which the service is run
1614    */
1615   const struct GNUNET_CONFIGURATION_Handle *cfg;
1616
1617   /**
1618    * Callback to signal service startup
1619    */
1620   GNUNET_TESTING_TestMain tm;
1621
1622   /**
1623    * The peer in which the service is run.
1624    */
1625   struct GNUNET_TESTING_Peer *peer;
1626
1627   /**
1628    * Closure for the above callback
1629    */
1630   void *tm_cls;
1631 };
1632
1633
1634 /**
1635  * Callback to be called when SCHEDULER has been started
1636  *
1637  * @param cls the ServiceContext
1638  */
1639 static void
1640 service_run_main (void *cls)
1641 {
1642   struct ServiceContext *sc = cls;
1643
1644   sc->tm (sc->tm_cls, sc->cfg, sc->peer);
1645 }
1646
1647
1648 /**
1649  * Start a single service (no ARM, except of course if the given
1650  * service name is 'arm') and run a test using the testing library.
1651  * Starts a service using the given configuration and then invokes the
1652  * given callback.  This function ALSO initializes the scheduler loop
1653  * and should thus be called directly from "main".  The testcase
1654  * should self-terminate by invoking #GNUNET_SCHEDULER_shutdown().
1655  *
1656  * This function is useful if the testcase is for a single service
1657  * and if that service doesn't itself depend on other services.
1658  *
1659  * @param testdir only the directory name without any path. This is used for
1660  *          all service homes; the directory will be created in a temporary
1661  *          location depending on the underlying OS
1662  * @param service_name name of the service to run
1663  * @param cfgfilename name of the configuration file to use;
1664  *         use NULL to only run with defaults
1665  * @param tm main function of the testcase
1666  * @param tm_cls closure for @a tm
1667  * @return 0 on success, 1 on error
1668  */
1669 int
1670 GNUNET_TESTING_service_run (const char *testdir,
1671                             const char *service_name,
1672                             const char *cfgfilename,
1673                             GNUNET_TESTING_TestMain tm,
1674                             void *tm_cls)
1675 {
1676   struct ServiceContext sc;
1677   struct GNUNET_TESTING_System *system;
1678   struct GNUNET_TESTING_Peer *peer;
1679   struct GNUNET_CONFIGURATION_Handle *cfg;
1680   char *binary;
1681   char *libexec_binary;
1682
1683   GNUNET_log_setup (testdir, "WARNING", NULL);
1684   system = GNUNET_TESTING_system_create (testdir, "127.0.0.1", NULL, NULL);
1685   if (NULL == system)
1686     return 1;
1687   cfg = GNUNET_CONFIGURATION_create ();
1688   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfgfilename))
1689   {
1690     LOG (GNUNET_ERROR_TYPE_ERROR,
1691          _ ("Failed to load configuration from %s\n"),
1692          cfgfilename);
1693     GNUNET_CONFIGURATION_destroy (cfg);
1694     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1695     return 1;
1696   }
1697   peer = GNUNET_TESTING_peer_configure (system, cfg, 0, NULL, NULL);
1698   if (NULL == peer)
1699   {
1700     GNUNET_CONFIGURATION_destroy (cfg);
1701     hostkeys_unload (system);
1702     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1703     return 1;
1704   }
1705   GNUNET_free (peer->main_binary);
1706   GNUNET_free (peer->args);
1707   GNUNET_asprintf (&binary, "gnunet-service-%s", service_name);
1708   libexec_binary = GNUNET_OS_get_libexec_binary_path (binary);
1709   if (GNUNET_SYSERR ==
1710       GNUNET_CONFIGURATION_get_value_string (cfg,
1711                                              service_name,
1712                                              "PREFIX",
1713                                              &peer->main_binary))
1714   {
1715     /* No prefix */
1716     GNUNET_asprintf (&peer->main_binary, "%s", libexec_binary);
1717     peer->args = GNUNET_strdup ("");
1718   }
1719   else
1720     peer->args = GNUNET_strdup (libexec_binary);
1721
1722   GNUNET_free (libexec_binary);
1723   GNUNET_free (binary);
1724   if (GNUNET_OK != GNUNET_TESTING_peer_start (peer))
1725   {
1726     GNUNET_TESTING_peer_destroy (peer);
1727     GNUNET_CONFIGURATION_destroy (cfg);
1728     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1729     return 1;
1730   }
1731   sc.cfg = cfg;
1732   sc.tm = tm;
1733   sc.tm_cls = tm_cls;
1734   sc.peer = peer;
1735   GNUNET_SCHEDULER_run (&service_run_main, &sc);  /* Scheduler loop */
1736   if ((NULL != peer->main_process) &&
1737       (GNUNET_OK != GNUNET_TESTING_peer_stop (peer)))
1738   {
1739     GNUNET_TESTING_peer_destroy (peer);
1740     GNUNET_CONFIGURATION_destroy (cfg);
1741     GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1742     return 1;
1743   }
1744   GNUNET_TESTING_peer_destroy (peer);
1745   GNUNET_CONFIGURATION_destroy (cfg);
1746   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
1747   return 0;
1748 }
1749
1750
1751 /**
1752  * Sometimes we use the binary name to determine which specific
1753  * test to run.  In those cases, the string after the last "_"
1754  * in 'argv[0]' specifies a string that determines the configuration
1755  * file or plugin to use.
1756  *
1757  * This function returns the respective substring, taking care
1758  * of issues such as binaries ending in '.exe' on W32.
1759  *
1760  * @param argv0 the name of the binary
1761  * @return string between the last '_' and the '.exe' (or the end of the string),
1762  *         NULL if argv0 has no '_'
1763  */
1764 char *
1765 GNUNET_TESTING_get_testname_from_underscore (const char *argv0)
1766 {
1767   size_t slen = strlen (argv0) + 1;
1768   char sbuf[slen];
1769   char *ret;
1770   char *dot;
1771
1772   GNUNET_memcpy (sbuf, argv0, slen);
1773   ret = strrchr (sbuf, '_');
1774   if (NULL == ret)
1775     return NULL;
1776   ret++; /* skip underscore */
1777   dot = strchr (ret, '.');
1778   if (NULL != dot)
1779     *dot = '\0';
1780   return GNUNET_strdup (ret);
1781 }
1782
1783
1784 /* end of testing.c */