-minor cleanups in testing
[oweals/gnunet.git] / src / testing / testing_new.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_new.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 #define TIME_REL_SEC(sec)                                       \
40   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, sec)
41
42
43 /**
44  * Size of a hostkey when written to a file
45  */
46 #define HOSTKEYFILESIZE 914
47
48
49 /**
50  * Handle for a system on which GNUnet peers are executed;
51  * a system is used for reserving unique paths and ports.
52  */
53 struct GNUNET_TESTING_System
54 {
55   /**
56    * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
57    * SERVICEHOME. 
58    */
59   char *tmppath;
60
61   /**
62    * The hostname of the controller
63    */
64   char *controller;
65
66   /**
67    * Hostkeys data, contains "HOSTKEYFILESIZE * total_hostkeys" bytes.
68    */
69   char *hostkeys_data;
70
71   /**
72    * Bitmap where each TCP port that has already been reserved for
73    * some GNUnet peer is recorded.  Note that we additionally need to
74    * test if a port is already in use by non-GNUnet components before
75    * assigning it to a peer/service.  If we detect that a port is
76    * already in use, we also mark it in this bitmap.  So all the bits
77    * that are zero merely indicate ports that MIGHT be available for
78    * peers.
79    */
80   uint32_t reserved_tcp_ports[65536 / 32];
81
82   /**
83    * Bitmap where each UDP port that has already been reserved for
84    * some GNUnet peer is recorded.  Note that we additionally need to
85    * test if a port is already in use by non-GNUnet components before
86    * assigning it to a peer/service.  If we detect that a port is
87    * already in use, we also mark it in this bitmap.  So all the bits
88    * that are zero merely indicate ports that MIGHT be available for
89    * peers.
90    */
91   uint32_t reserved_udp_ports[65536 / 32];
92
93   /**
94    * Counter we use to make service home paths unique on this system;
95    * the full path consists of the tmppath and this number.  Each
96    * UNIXPATH for a peer is also modified to include the respective
97    * path counter to ensure uniqueness.  This field is incremented
98    * by one for each configured peer.  Even if peers are destroyed,
99    * we never re-use path counters.
100    */
101   uint32_t path_counter;  
102
103   /**
104    * The number of hostkeys
105    */
106   uint32_t total_hostkeys;
107 };
108
109
110 /**
111  * Handle for a GNUnet peer controlled by testing.
112  */
113 struct GNUNET_TESTING_Peer
114 {
115
116   /**
117    * Path to the configuration file for this peer.
118    */
119   char *cfgfile;
120
121   /**
122    * Binary to be executed during 'GNUNET_TESTING_peer_start'.
123    * Typically 'gnunet-service-arm' (but can be set to a 
124    * specific service by 'GNUNET_TESTING_service_run' if
125    * necessary).
126    */ 
127   char *main_binary;
128   
129   /**
130    * Handle to the running binary of the service, NULL if the
131    * peer/service is currently not running.
132    */
133   struct GNUNET_OS_Process *main_process;
134 };
135
136
137 /**
138  * Lowest port used for GNUnet testing.  Should be high enough to not
139  * conflict with other applications running on the hosts but be low
140  * enough to not conflict with client-ports (typically starting around
141  * 32k).
142  */
143 #define LOW_PORT 12000
144
145
146 /**
147  * Highest port used for GNUnet testing.  Should be low enough to not
148  * conflict with the port range for "local" ports (client apps; see
149  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
150  */
151 #define HIGH_PORT 56000
152
153
154 /**
155  * Create a system handle.  There must only be one system
156  * handle per operating system.
157  *
158  * @param tmppath prefix path to use for all service homes
159  * @param controller hostname of the controlling host, 
160  *        service configurations are modified to allow 
161  *        control connections from this host; can be NULL
162  * @return handle to this system, NULL on error
163  */
164 struct GNUNET_TESTING_System *
165 GNUNET_TESTING_system_create (const char *tmppath,
166                               const char *controller)
167 {
168   struct GNUNET_TESTING_System *system;
169
170   if (NULL == tmppath)
171     return NULL;
172   system = GNUNET_malloc (sizeof (struct GNUNET_TESTING_System));
173   system->tmppath = GNUNET_strdup (tmppath);
174   if (NULL != controller)
175     system->controller = GNUNET_strdup (controller);
176   return system;
177 }
178
179
180 /**
181  * Free system resources.
182  *
183  * @param system system to be freed
184  * @param remove_paths should the 'tmppath' and all subdirectories
185  *        be removed (clean up on shutdown)?
186  */
187 void
188 GNUNET_TESTING_system_destroy (struct GNUNET_TESTING_System *system,
189                                int remove_paths)
190 {
191   if (NULL != system->hostkeys_data)
192   {
193     GNUNET_break (0);           /* Use GNUNET_TESTING_hostkeys_unload() */
194     GNUNET_free (system->hostkeys_data);
195     system->hostkeys_data = NULL;
196     system->total_hostkeys = 0;
197   }
198   if (GNUNET_YES == remove_paths)
199     GNUNET_DISK_directory_remove (system->tmppath);
200   GNUNET_free (system->tmppath);
201   GNUNET_free_non_null (system->controller);
202   GNUNET_free (system);
203 }
204
205
206 /**
207  * Reserve a TCP or UDP port for a peer.
208  *
209  * @param system system to use for reservation tracking
210  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
211  * @return 0 if no free port was available
212  */
213 uint16_t 
214 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system,
215                              int is_tcp)
216 {
217   struct GNUNET_NETWORK_Handle *socket;
218   struct addrinfo hint;
219   struct addrinfo *ret;
220   uint32_t *port_buckets;
221   char *open_port_str;
222   int bind_status;
223   uint32_t xor_image;
224   uint16_t index;
225   uint16_t open_port;
226   uint16_t pos;
227
228   hint.ai_family = AF_UNSPEC;   /* IPv4 and IPv6 */
229   hint.ai_socktype = (GNUNET_YES == is_tcp)? SOCK_STREAM : SOCK_DGRAM;
230   hint.ai_protocol = 0;
231   hint.ai_addrlen = 0;
232   hint.ai_addr = NULL;
233   hint.ai_canonname = NULL;
234   hint.ai_next = NULL;
235   hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV;  /* Wild card address */
236   port_buckets = (GNUNET_YES == is_tcp) ?
237     system->reserved_tcp_ports : system->reserved_udp_ports;
238   for (index = (LOW_PORT / 32) + 1; index < (HIGH_PORT / 32); index++)
239   {
240     xor_image = (UINT32_MAX ^ port_buckets[index]);
241     if (0 == xor_image)        /* Ports in the bucket are full */
242       continue;
243     pos = 0;
244     while (pos < 32)
245     {
246       if (0 == ((xor_image >> pos) & 1U))
247       {
248         pos++;
249         continue;
250       }
251       open_port = (index * 32) + pos;
252       GNUNET_asprintf (&open_port_str, "%u", open_port);
253       ret = NULL;
254       GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
255       GNUNET_free (open_port_str);  
256       socket = GNUNET_NETWORK_socket_create (ret->ai_family,
257                                              (GNUNET_YES == is_tcp) ?
258                                              SOCK_STREAM : SOCK_DGRAM,
259                                              0);
260       GNUNET_assert (NULL != socket);
261       bind_status = GNUNET_NETWORK_socket_bind (socket,
262                                                 ret->ai_addr,
263                                                 ret->ai_addrlen);
264       freeaddrinfo (ret);
265       GNUNET_NETWORK_socket_close (socket);
266       socket = NULL;
267       port_buckets[index] |= (1U << pos); /* Set the port bit */
268       if (GNUNET_OK == bind_status)
269         return open_port;
270       pos++;
271     }
272   }
273   return 0;
274 }
275
276
277 /**
278  * Release reservation of a TCP or UDP port for a peer
279  * (used during GNUNET_TESTING_peer_destroy).
280  *
281  * @param system system to use for reservation tracking
282  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
283  * @param port reserved port to release
284  */
285 void
286 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
287                              int is_tcp,
288                              uint16_t port)
289 {
290   uint32_t *port_buckets;
291   uint16_t bucket;
292   uint16_t pos;
293
294   port_buckets = (GNUNET_YES == is_tcp) ?
295     system->reserved_tcp_ports : system->reserved_udp_ports;
296   bucket = port / 32;
297   pos = port % 32;
298   LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
299   if (0 == (port_buckets[bucket] & (1U << pos)))
300   {
301     GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
302     return;
303   }
304   port_buckets[bucket] &= ~(1U << pos);
305 }
306
307
308 /**
309  * Reserve a SERVICEHOME path for a peer.
310  *
311  * @param system system to use for reservation tracking
312  * @return NULL on error, otherwise fresh unique path to use
313  *         as the servicehome for the peer; must be freed by the caller
314  */
315 // static 
316 char *
317 reserve_path (struct GNUNET_TESTING_System *system)
318 {
319   char *reserved_path;
320
321   GNUNET_asprintf (&reserved_path,
322                    "%s/%u", system->tmppath, system->path_counter++);
323   return reserved_path;
324 }             
325
326
327 /**
328  * Testing includes a number of pre-created hostkeys for faster peer
329  * startup. This function loads such keys into memory from a file.
330  *
331  * @param system the testing system handle
332  * @param filename the path of the hostkeys file
333  * @return GNUNET_OK on success; GNUNET_SYSERR on error
334  */
335 int
336 GNUNET_TESTING_hostkeys_load (struct GNUNET_TESTING_System *system,
337                               const char *filename)
338 {
339  struct GNUNET_DISK_FileHandle *fd;
340  uint64_t fs;
341  
342  if (GNUNET_YES != GNUNET_DISK_file_test (filename))
343   {
344     LOG (GNUNET_ERROR_TYPE_ERROR,
345          "Hostkeys file not found: %s\n", filename);
346     return GNUNET_SYSERR;
347   }
348   /* Check hostkey file size, read entire thing into memory */
349   fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
350                               GNUNET_DISK_PERM_NONE);
351   if (NULL == fd)
352   {
353     LOG (GNUNET_ERROR_TYPE_ERROR,
354          "Could not open hostkeys file: %s\n", filename);
355     return GNUNET_SYSERR;
356   }
357   if (GNUNET_OK != 
358       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
359     fs = 0;
360   if (0 == fs)
361   {
362     GNUNET_DISK_file_close (fd);
363     return GNUNET_SYSERR;       /* File is empty */
364   }
365   if (0 != (fs % HOSTKEYFILESIZE))
366   {
367     GNUNET_DISK_file_close (fd);
368     LOG (GNUNET_ERROR_TYPE_ERROR,
369          "Incorrect hostkey file format: %s\n", filename);
370     return GNUNET_SYSERR;
371   }
372   GNUNET_break (NULL == system->hostkeys_data);
373   system->total_hostkeys = fs / HOSTKEYFILESIZE;
374   system->hostkeys_data = GNUNET_malloc_large (fs); /* free in hostkeys_unload */
375   GNUNET_assert (fs == GNUNET_DISK_file_read (fd, system->hostkeys_data, fs));
376   GNUNET_DISK_file_close (fd);
377   return GNUNET_OK;
378 }
379
380
381 /**
382  * Function to remove the loaded hostkeys
383  *
384  * @param system the testing system handle
385  */
386 void
387 GNUNET_TESTING_hostkeys_unload (struct GNUNET_TESTING_System *system)
388 {
389   GNUNET_break (NULL != system->hostkeys_data);
390   GNUNET_free_non_null (system->hostkeys_data);
391   system->hostkeys_data = NULL;
392   system->total_hostkeys = 0;
393 }
394
395
396 /**
397  * Testing includes a number of pre-created hostkeys for
398  * faster peer startup.  This function can be used to
399  * access the n-th key of those pre-created hostkeys; note
400  * that these keys are ONLY useful for testing and not
401  * secure as the private keys are part of the public 
402  * GNUnet source code.
403  *
404  * This is primarily a helper function used internally
405  * by 'GNUNET_TESTING_peer_configure'.
406  *
407  * @param system the testing system handle
408  * @param key_number desired pre-created hostkey to obtain
409  * @param id set to the peer's identity (hash of the public
410  *        key; if NULL, GNUNET_SYSERR is returned immediately
411  * @return GNUNET_SYSERR on error (not enough keys)
412  */
413 int
414 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
415                             uint32_t key_number,
416                             struct GNUNET_PeerIdentity *id)
417 {  
418   struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
419   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
420   
421   if ((NULL == id) || (NULL == system->hostkeys_data))
422     return GNUNET_SYSERR;
423   if (key_number >= system->total_hostkeys)
424   {
425     LOG (GNUNET_ERROR_TYPE_ERROR,
426          _("Key number %u does not exist\n"), key_number);
427     return GNUNET_SYSERR;
428   }   
429   private_key = GNUNET_CRYPTO_rsa_decode_key (system->hostkeys_data +
430                                               (key_number * HOSTKEYFILESIZE),
431                                               HOSTKEYFILESIZE);
432   if (NULL == private_key)
433   {
434     LOG (GNUNET_ERROR_TYPE_ERROR,
435          _("Error while decoding key %u\n"), key_number);
436     return GNUNET_SYSERR;
437   }
438   GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
439   GNUNET_CRYPTO_hash (&public_key,
440                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
441                       &(id->hashPubKey));
442   GNUNET_CRYPTO_rsa_key_free (private_key);
443   return GNUNET_OK;
444 }
445
446
447 /**
448  * Structure for holding data to build new configurations from a configuration
449  * template
450  */
451 struct UpdateContext
452 {
453   /**
454    * The system for which we are building configurations
455    */
456   struct GNUNET_TESTING_System *system;
457   
458   /**
459    * The configuration we are building
460    */
461   struct GNUNET_CONFIGURATION_Handle *cfg;
462
463   /**
464    * The customized service home path for this peer
465    */
466   char *service_home;
467
468   /**
469    * build status - to signal error while building a configuration
470    */
471   int status;
472 };
473
474
475 /**
476  * Function to iterate over options.  Copies
477  * the options to the target configuration,
478  * updating PORT values as needed.
479  *
480  * @param cls the UpdateContext
481  * @param section name of the section
482  * @param option name of the option
483  * @param value value of the option
484  */
485 static void
486 update_config (void *cls, const char *section, const char *option,
487                const char *value)
488 {
489   struct UpdateContext *uc = cls;
490   unsigned int ival;
491   char cval[12];
492   char uval[128];
493   char *single_variable;
494   char *per_host_variable;
495   unsigned long long num_per_host;
496   uint16_t new_port;
497
498   if (GNUNET_OK != uc->status)
499     return;
500   GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
501   GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
502   if ((0 == strcmp (option, "PORT")) && (1 == SSCANF (value, "%u", &ival)))
503   {
504     if ((ival != 0) &&
505         (GNUNET_YES !=
506          GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
507                                                single_variable)))
508     {
509       /* FIXME: What about UDP? */
510       new_port = GNUNET_TESTING_reserve_port (uc->system, GNUNET_YES);
511       if (0 == new_port)
512       {
513         uc->status = GNUNET_SYSERR;
514         return;
515       }
516       GNUNET_snprintf (cval, sizeof (cval), "%u", new_port);
517       value = cval;
518     }
519     else if ((ival != 0) &&
520              (GNUNET_YES ==
521               GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
522                                                     single_variable)) &&
523              GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
524                                                     per_host_variable,
525                                                     &num_per_host))
526     {
527       /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
528       /*                  ival + ctx->fdnum % num_per_host); */
529       /* value = cval; */
530       GNUNET_break (0);         /* FIXME */
531     }
532   }
533   if (0 == strcmp (option, "UNIXPATH"))
534   {
535     if (GNUNET_YES !=
536         GNUNET_CONFIGURATION_get_value_yesno (uc->cfg, "testing",
537                                               single_variable))
538     {
539       GNUNET_snprintf (uval, sizeof (uval), "%s\\%s.sock",
540                        uc->service_home, section);
541       value = uval;
542     }
543     else if ((GNUNET_YES ==
544               GNUNET_CONFIGURATION_get_value_number (uc->cfg, "testing",
545                                                      per_host_variable,
546                                                      &num_per_host)) &&
547              (num_per_host > 0))
548     {
549       GNUNET_break(0);          /* FIXME */
550     }
551   }
552   if ((0 == strcmp (option, "HOSTNAME")) && (NULL != uc->system->controller))
553   {
554     value = uc->system->controller;
555   }
556   GNUNET_free (single_variable);
557   GNUNET_free (per_host_variable);
558   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
559 }
560
561
562 /**
563  * Section iterator to set ACCEPT_FROM in all sections
564  *
565  * @param cls the UpdateContext
566  * @param section name of the section
567  */
568 static void
569 update_config_sections (void *cls,
570                         const char *section)
571 {
572   struct UpdateContext *uc = cls;
573   char *orig_allowed_hosts;
574   char *allowed_hosts;
575
576   if (GNUNET_OK != 
577       GNUNET_CONFIGURATION_get_value_string (uc->cfg, section, "ACCEPT_FROM",
578                                              &orig_allowed_hosts))
579   {
580     orig_allowed_hosts = "127.0.0.1;";
581   }
582   if (NULL == uc->system->controller)
583     allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
584   else
585     GNUNET_asprintf (&allowed_hosts, "%s %s;", orig_allowed_hosts,
586                      uc->system->controller);
587   GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, "ACCEPT_FROM",
588                                          allowed_hosts);
589   GNUNET_free (allowed_hosts);  
590 }
591
592
593 /**
594  * Create a new configuration using the given configuration
595  * as a template; ports and paths will be modified to select
596  * available ports on the local system.  If we run
597  * out of "*port" numbers, return SYSERR.
598  *
599  * This is primarily a helper function used internally
600  * by 'GNUNET_TESTING_peer_configure'.
601  *
602  * @param system system to use to coordinate resource usage
603  * @param cfg template configuration to update
604  * @return GNUNET_OK on success, GNUNET_SYSERR on error - the configuration will
605  *           be incomplete and should not be used there upon
606  */
607 int
608 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
609                                      struct GNUNET_CONFIGURATION_Handle *cfg)
610 {
611   struct UpdateContext uc;
612   
613   uc.system = system;
614   uc.cfg = cfg;
615   uc.status = GNUNET_OK;
616   GNUNET_asprintf (&uc.service_home, "%s\\%u", system->tmppath,
617                    system->path_counter++);
618   GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "SERVICEHOME",
619                                          uc.service_home);
620   /* make PORTs and UNIXPATHs unique */
621   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
622   /* allow connections to services from system controller host */
623   GNUNET_CONFIGURATION_iterate_sections (cfg, &update_config_sections, &uc);
624   /* enable loopback-based connections between peers */
625   GNUNET_CONFIGURATION_set_value_string (cfg, 
626                                          "nat",
627                                          "USE_LOCALADDR", "YES");
628   GNUNET_free (uc.service_home);
629   return uc.status;
630 }
631
632
633 /**
634  * Configure a GNUnet peer.  GNUnet must be installed on the local
635  * system and available in the PATH. 
636  *
637  * @param system system to use to coordinate resource usage
638  * @param cfg configuration to use; will be UPDATED (to reflect needed
639  *            changes in port numbers and paths)
640  * @param key_number number of the hostkey to use for the peer
641  * @param id identifier for the daemon, will be set, can be NULL
642  * @param emsg set to error message (set to NULL on success), can be NULL
643  * @return handle to the peer, NULL on error
644  */
645 struct GNUNET_TESTING_Peer *
646 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
647                                struct GNUNET_CONFIGURATION_Handle *cfg,
648                                uint32_t key_number,
649                                struct GNUNET_PeerIdentity *id,
650                                char **emsg)
651 {
652   struct GNUNET_TESTING_Peer *peer;
653   struct GNUNET_DISK_FileHandle *fd;
654   char *service_home;  
655   char hostkey_filename[128];
656   char *config_filename;
657
658   if (GNUNET_OK != GNUNET_TESTING_configuration_create (system, cfg))
659   {
660     GNUNET_asprintf (emsg,
661                      _("Failed to create configuration for peer (not enough free ports?)\n"));
662     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
663                 "%s", *emsg);   
664     return NULL;
665   }
666   if (key_number >= system->total_hostkeys)
667   {
668     GNUNET_asprintf (emsg,
669                      _("You attempted to create a testbed with more than %u hosts.  Please precompute more hostkeys first.\n"),
670                      (unsigned int) system->total_hostkeys);    
671     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
672                 "%s", *emsg);
673     return NULL;
674   }
675   if ((NULL != id) &&
676       (GNUNET_SYSERR == GNUNET_TESTING_hostkey_get (system, key_number, id)))
677   {
678     GNUNET_asprintf (emsg,
679                      _("Failed to initialize hostkey for peer %u\n"),
680                      (unsigned int) key_number);
681     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
682                 "%s", *emsg);   
683     return NULL;
684   }
685   GNUNET_assert (GNUNET_OK == 
686                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
687                                                         "SERVICE_HOME",
688                                                         &service_home));
689   GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s\\.hostkey",
690                    service_home);
691   fd = GNUNET_DISK_file_open (hostkey_filename,
692                               GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE,
693                               GNUNET_DISK_PERM_USER_READ 
694                               | GNUNET_DISK_PERM_USER_WRITE);
695   if (NULL == fd)
696   {
697     GNUNET_break (0); 
698     return NULL;
699   }
700   if (HOSTKEYFILESIZE !=
701       GNUNET_DISK_file_write (fd, system->hostkeys_data 
702                               + (key_number * HOSTKEYFILESIZE),
703                               HOSTKEYFILESIZE))
704   {
705     GNUNET_asprintf (emsg,
706                      _("Failed to write hostkey file for peer %u: %s\n"),
707                      (unsigned int) key_number,
708                      STRERROR (errno));
709     GNUNET_DISK_file_close (fd);
710     return NULL;
711   }
712   GNUNET_DISK_file_close (fd);
713   GNUNET_asprintf (&config_filename, "%s\\config", service_home);
714   GNUNET_free (service_home);
715   if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, config_filename))
716   {
717     GNUNET_asprintf (emsg,
718                      _("Failed to write configuration file `%s' for peer %u: %s\n"),
719                      config_filename,
720                      (unsigned int) key_number,
721                      STRERROR (errno));
722     return NULL;
723   }
724   peer = GNUNET_malloc (sizeof (struct GNUNET_TESTING_Peer));
725   peer->cfgfile = config_filename; /* Free in peer_destroy */
726   peer->main_binary = GNUNET_strdup ("gnunet-service-arm");
727   return peer;
728 }
729
730
731 /**
732  * Start the peer. 
733  *
734  * @param peer peer to start
735  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
736  */
737 int
738 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
739 {
740   if (NULL != peer->main_process)
741   {
742     GNUNET_break (0);
743     return GNUNET_SYSERR;
744   }
745   GNUNET_assert (NULL != peer->cfgfile);
746   peer->main_process = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL,
747                                                 peer->main_binary, "-c",
748                                                 peer->cfgfile,
749                                                 NULL);
750   if (NULL == peer->main_process)
751   {
752     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
753                 _("Failed to start `%s': %s\n"),
754                 peer->main_binary,
755                 STRERROR (errno));
756     return GNUNET_SYSERR;
757   }
758   return GNUNET_OK;
759 }
760
761
762 /**
763  * Stop the peer. 
764  *
765  * @param peer peer to stop
766  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
767  */
768 int
769 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
770 {
771   if (NULL == peer->main_process)
772   {
773     GNUNET_break (0);
774     return GNUNET_SYSERR;
775   }
776   (void) GNUNET_OS_process_kill (peer->main_process, SIGTERM);
777   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (peer->main_process));
778   GNUNET_OS_process_destroy (peer->main_process);
779   peer->main_process = NULL;
780   return GNUNET_OK;
781 }
782
783
784 /**
785  * Destroy the peer.  Releases resources locked during peer configuration.
786  * If the peer is still running, it will be stopped AND a warning will be
787  * printed (users of the API should stop the peer explicitly first).
788  *
789  * @param peer peer to destroy
790  */
791 void
792 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
793 {
794   if (NULL != peer->main_process)
795   {
796     GNUNET_break (0);
797     GNUNET_TESTING_peer_stop (peer);
798   }
799   GNUNET_free (peer->cfgfile);
800   GNUNET_free (peer->main_binary);
801   GNUNET_free (peer);
802 }
803
804
805 /**
806  * Start a single peer and run a test using the testing library.
807  * Starts a peer using the given configuration and then invokes the
808  * given callback.  This function ALSO initializes the scheduler loop
809  * and should thus be called directly from "main".  The testcase
810  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
811  *
812  * @param tmppath path for storing temporary data for the test
813  * @param cfgfilename name of the configuration file to use;
814  *         use NULL to only run with defaults
815  * @param tm main function of the testcase
816  * @param tm_cls closure for 'tm'
817  * @return 0 on success, 1 on error
818  */
819 int
820 GNUNET_TESTING_peer_run (const char *tmppath,
821                          const char *cfgfilename,
822                          GNUNET_TESTING_TestMain tm,
823                          void *tm_cls)
824 {
825   return GNUNET_TESTING_service_run (tmppath, "arm",
826                                      cfgfilename, tm, tm_cls);
827 }
828
829
830 /**
831  * Structure for holding service data
832  */
833 struct ServiceContext
834 {
835   /**
836    * The service's main process
837    */
838   struct GNUNET_OS_Process *main_process;
839
840   /**
841    * Callback to signal service startup
842    */
843   GNUNET_TESTING_TestMain tm;
844
845   /**
846    * Closure for the above callback
847    */
848   void *tm_cls;
849 };
850
851
852 /**
853  * Scheduler callback to stop service upon call to GNUNET_SCHEDULER_shutdown
854  *
855  * @param cls the ServiceContext
856  * @param tc the TaskContext
857  */
858 static void
859 stop_service (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
860 {
861   GNUNET_break (0);
862 }
863
864
865 /**
866  * Scheduler callback to stop service upon call to GNUNET_SCHEDULER_shutdown
867  *
868  * @param cls the ServiceContext
869  * @param tc the TaskContext
870  */
871 static void
872 check_service_status (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
873 {
874   GNUNET_break (0);
875 }
876
877
878 static void
879 service_run_main (void *cls,
880                   const struct GNUNET_SCHEDULER_TaskContext *tc)
881 {
882   struct ServiceContext *sc = cls;
883
884   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
885                                 &stop_service, sc);
886   GNUNET_SCHEDULER_add_delayed (TIME_REL_SEC(3), &check_service_status, sc);
887
888   // sc->tm (tm_cls);
889 }
890
891
892 /**
893  * Start a single service (no ARM, except of course if the given
894  * service name is 'arm') and run a test using the testing library.
895  * Starts a service using the given configuration and then invokes the
896  * given callback.  This function ALSO initializes the scheduler loop
897  * and should thus be called directly from "main".  The testcase
898  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
899  *
900  * This function is useful if the testcase is for a single service
901  * and if that service doesn't itself depend on other services.
902  *
903  * @param tmppath path for storing temporary data for the test
904  * @param service_name name of the service to run
905  * @param cfgfilename name of the configuration file to use;
906  *         use NULL to only run with defaults
907  * @param tm main function of the testcase
908  * @param tm_cls closure for 'tm'
909  * @return 0 on success, 1 on error
910  */
911 int
912 GNUNET_TESTING_service_run (const char *tmppath,
913                             const char *service_name,
914                             const char *cfgfilename,
915                             GNUNET_TESTING_TestMain tm,
916                             void *tm_cls)
917 {
918   struct ServiceContext *sc;
919   char uval[128];
920
921   GNUNET_assert (NULL != service_name);
922   GNUNET_snprintf (uval, sizeof (uval), "gnunet-service-%s", service_name);
923   sc = GNUNET_malloc (sizeof (struct ServiceContext));
924
925   // FIXME: GNUNET_TESTING_peer_start...
926   if (NULL == cfgfilename)
927     sc->main_process = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, uval, NULL);
928   else
929     sc->main_process = GNUNET_OS_start_process (GNUNET_NO, NULL, NULL, uval,
930                                                 "-c", cfgfilename, NULL);
931   if (NULL == sc->main_process)
932   {
933     LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to start process %s\n", service_name);
934     GNUNET_free (sc);
935     return 1;
936   }
937   sc->tm = tm;
938   sc->tm_cls = tm_cls;
939   GNUNET_SCHEDULER_run (&service_run_main, sc);
940   // FIXME: GNUNET_TESTING_peer_stop...
941   GNUNET_free (sc);
942   return 0;
943 }
944
945
946 /* end of testing_new.c */