-fixing testing testcase build issues
[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_disk_lib.h"
34 #include "gnunet_network_lib.h"
35 #include "gnunet_testing_lib-new.h"
36
37 #define LOG(kind,...)                                           \
38   GNUNET_log_from (kind, "gnunettestingnew", __VA_ARGS__)
39
40 /**
41  * AI_NUMERICSERV not defined in windows. A hack to keep on going.
42  */
43 #if !defined (AI_NUMERICSERV)
44 #define AI_NUMERICSERV 0
45 #endif
46
47 /**
48  * Size of a hostkey when written to a file
49  */
50 #ifndef HOSTKEYFILESIZE
51 #define HOSTKEYFILESIZE 914
52 #endif
53
54 /**
55  * Handle for a system on which GNUnet peers are executed;
56  * a system is used for reserving unique paths and ports.
57  */
58 struct GNUNET_TESTING_System
59 {
60   /**
61    * Prefix (i.e. "/tmp/gnunet-testing/") we prepend to each
62    * SERVICEHOME. 
63    */
64   char *tmppath;
65   /**
66    * The hostname of the controller
67    */
68   char *controller;
69
70   /**
71    * Bitmap where each TCP port that has already been reserved for
72    * some GNUnet peer is recorded.  Note that we additionally need to
73    * test if a port is already in use by non-GNUnet components before
74    * assigning it to a peer/service.  If we detect that a port is
75    * already in use, we also mark it in this bitmap.  So all the bits
76    * that are zero merely indicate ports that MIGHT be available for
77    * peers.
78    */
79   uint32_t reserved_tcp_ports[65536 / 32];
80
81   /**
82    * Bitmap where each UDP port that has already been reserved for
83    * some GNUnet peer is recorded.  Note that we additionally need to
84    * test if a port is already in use by non-GNUnet components before
85    * assigning it to a peer/service.  If we detect that a port is
86    * already in use, we also mark it in this bitmap.  So all the bits
87    * that are zero merely indicate ports that MIGHT be available for
88    * peers.
89    */
90   uint32_t reserved_udp_ports[65536 / 32];
91
92   /**
93    * Counter we use to make service home paths unique on this system;
94    * the full path consists of the tmppath and this number.  Each
95    * UNIXPATH for a peer is also modified to include the respective
96    * path counter to ensure uniqueness.  This field is incremented
97    * by one for each configured peer.  Even if peers are destroyed,
98    * we never re-use path counters.
99    */
100   uint32_t path_counter;
101
102   /**
103    * Last used port number
104    */
105   
106 };
107
108
109 /**
110  * Handle for a GNUnet peer controlled by testing.
111  */
112 struct GNUNET_TESTING_Peer
113 {
114
115   /**
116    * Path to the configuration file for this peer.
117    */
118   char *cfgfile;
119
120   /**
121    * Binary to be executed during 'GNUNET_TESTING_peer_start'.
122    * Typically 'gnunet-service-arm' (but can be set to a 
123    * specific service by 'GNUNET_TESTING_service_run' if
124    * necessary).
125    */ 
126   char *main_binary;
127   
128   /**
129    * Handle to the running binary of the service, NULL if the
130    * peer/service is currently not running.
131    */
132   struct GNUNET_OS_Process *main_process;
133
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   GNUNET_assert (NULL != system);
192   if (GNUNET_YES == remove_paths)
193     GNUNET_DISK_directory_remove (system->tmppath);
194   GNUNET_free (system->tmppath);
195   GNUNET_free_non_null (system->controller);
196   GNUNET_free (system);
197 }
198
199
200 /**
201  * Reserve a TCP or UDP port for a peer.
202  *
203  * @param system system to use for reservation tracking
204  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
205  * @return 0 if no free port was available
206  */
207 uint16_t 
208 GNUNET_TESTING_reserve_port (struct GNUNET_TESTING_System *system,
209                              int is_tcp)
210 {
211   struct GNUNET_NETWORK_Handle *socket;
212   struct addrinfo hint;
213   struct addrinfo *ret;
214   uint32_t *port_buckets;
215   char *open_port_str;
216   int bind_status;
217   uint32_t xor_image;
218   uint16_t index;
219   uint16_t open_port;
220   uint16_t pos;
221
222   hint.ai_family = AF_UNSPEC;   /* IPv4 and IPv6 */
223   hint.ai_socktype = (GNUNET_YES == is_tcp)? SOCK_STREAM : SOCK_DGRAM;
224   hint.ai_protocol = 0;
225   hint.ai_addrlen = 0;
226   hint.ai_addr = NULL;
227   hint.ai_canonname = NULL;
228   hint.ai_next = NULL;
229   hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV;  /* Wild card address */
230   port_buckets = (GNUNET_YES == is_tcp) ?
231     system->reserved_tcp_ports : system->reserved_udp_ports;
232   for (index = (LOW_PORT / 32) + 1; index < (HIGH_PORT / 32); index++)
233   {
234     xor_image = (UINT32_MAX ^ port_buckets[index]);
235     if (0 == xor_image)        /* Ports in the bucket are full */
236       continue;
237     pos = 0;
238     while (pos < 32)
239     {
240       if (0 == ((xor_image >> pos) & 1U))
241       {
242         pos++;
243         continue;
244       }
245       open_port = (index * 32) + pos;
246       GNUNET_asprintf (&open_port_str, "%u", open_port);
247       ret = NULL;
248       GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
249       GNUNET_free (open_port_str);  
250       socket = GNUNET_NETWORK_socket_create (ret->ai_family,
251                                              (GNUNET_YES == is_tcp) ?
252                                              SOCK_STREAM : SOCK_DGRAM,
253                                              0);
254       GNUNET_assert (NULL != socket);
255       bind_status = GNUNET_NETWORK_socket_bind (socket,
256                                                 ret->ai_addr,
257                                                 ret->ai_addrlen);
258       freeaddrinfo (ret);
259       GNUNET_NETWORK_socket_close (socket);
260       socket = NULL;
261       port_buckets[index] |= (1U << pos); /* Set the port bit */
262       if (GNUNET_OK == bind_status)
263         return open_port;
264       pos++;
265     }
266   }
267   return 0;
268 }
269
270
271 /**
272  * Release reservation of a TCP or UDP port for a peer
273  * (used during GNUNET_TESTING_peer_destroy).
274  *
275  * @param system system to use for reservation tracking
276  * @param is_tcp GNUNET_YES for TCP ports, GNUNET_NO for UDP
277  * @param port reserved port to release
278  */
279 void
280 GNUNET_TESTING_release_port (struct GNUNET_TESTING_System *system,
281                              int is_tcp,
282                              uint16_t port)
283 {
284   uint32_t *port_buckets;
285   uint16_t bucket;
286   uint16_t pos;
287
288   GNUNET_assert (NULL != system);
289   port_buckets = (GNUNET_YES == is_tcp) ?
290     system->reserved_tcp_ports : system->reserved_udp_ports;
291   bucket = port / 32;
292   pos = port % 32;
293   LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
294   if (0 == (port_buckets[bucket] & (1U << pos)))
295   {
296     GNUNET_break(0); /* Port was not reserved by us using reserve_port() */
297     return;
298   }
299   port_buckets[bucket] &= ~(1U << pos);
300 }
301
302
303 /**
304  * Reserve a SERVICEHOME path for a peer.
305  *
306  * @param system system to use for reservation tracking
307  * @return NULL on error, otherwise fresh unique path to use
308  *         as the servicehome for the peer; must be freed by the caller
309  */
310 // static 
311 char *
312 reserve_path (struct GNUNET_TESTING_System *system)
313 {
314   char *reserved_path;
315
316   GNUNET_asprintf (&reserved_path,
317                    "%s/%u", system->tmppath, system->path_counter++);
318   return reserved_path;
319 }             
320
321
322 /**
323  * Testing includes a number of pre-created hostkeys for
324  * faster peer startup.  This function can be used to
325  * access the n-th key of those pre-created hostkeys; note
326  * that these keys are ONLY useful for testing and not
327  * secure as the private keys are part of the public 
328  * GNUnet source code.
329  *
330  * This is primarily a helper function used internally
331  * by 'GNUNET_TESTING_peer_configure'.
332  *
333  * @param key_number desired pre-created hostkey to obtain
334  * @param filename where to store the hostkey (file will
335  *        be created, or overwritten if it already exists)
336  * @param id set to the peer's identity (hash of the public
337  *        key; if NULL, GNUNET_SYSERR is returned immediately
338  * @return GNUNET_SYSERR on error (not enough keys)
339  */
340 int
341 GNUNET_TESTING_hostkey_get (uint32_t key_number,
342                             const char *filename,
343                             struct GNUNET_PeerIdentity *id)
344 {
345   struct GNUNET_DISK_FileHandle *fd;
346   struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
347   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
348   char *file_data;
349   uint64_t fs;
350   uint32_t total_hostkeys;
351
352   if (NULL == id)
353     return GNUNET_SYSERR;
354   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
355   {
356     LOG (GNUNET_ERROR_TYPE_ERROR,
357          "Hostkeys file not found: %s\n", filename);
358     return GNUNET_SYSERR;
359   }
360   /* Check hostkey file size, read entire thing into memory */
361   fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
362                               GNUNET_DISK_PERM_NONE);
363   if (NULL == fd)
364   {
365     LOG (GNUNET_ERROR_TYPE_ERROR,
366          "Could not open hostkeys file: %s\n", filename);
367     return GNUNET_SYSERR;
368   }
369   if (GNUNET_OK != 
370       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
371     fs = 0;
372   if (0 == fs)
373   {
374     GNUNET_DISK_file_close (fd);
375     return GNUNET_SYSERR;       /* File is empty */
376   }
377   if (0 != (fs % HOSTKEYFILESIZE))
378   {
379     GNUNET_DISK_file_close (fd);
380     LOG (GNUNET_ERROR_TYPE_ERROR,
381          "Incorrect hostkey file format: %s\n", filename);
382     return GNUNET_SYSERR;
383   }
384   total_hostkeys = fs / HOSTKEYFILESIZE;
385   if (key_number >= total_hostkeys)
386   {
387     GNUNET_DISK_file_close (fd);
388     LOG (GNUNET_ERROR_TYPE_ERROR,
389          "Key number %u doesn't exist\n", key_number);
390     return GNUNET_SYSERR;
391   }
392   file_data = GNUNET_malloc_large (fs);
393   GNUNET_assert (fs == GNUNET_DISK_file_read (fd, file_data, fs));
394   GNUNET_DISK_file_close (fd);
395   private_key = GNUNET_CRYPTO_rsa_decode_key (file_data +
396                                               (key_number * HOSTKEYFILESIZE),
397                                               HOSTKEYFILESIZE);
398   if (NULL == private_key)
399   {
400     LOG (GNUNET_ERROR_TYPE_ERROR,
401          "Error while decoding key %u from %s\n", key_number, filename);
402     GNUNET_free (file_data);
403     return GNUNET_SYSERR;
404   }
405   GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
406   GNUNET_CRYPTO_hash (&public_key,
407                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
408                       &(id->hashPubKey));
409   GNUNET_free (file_data);
410   return GNUNET_OK;
411 }
412
413
414 /**
415  * Create a new configuration using the given configuration
416  * as a template; ports and paths will be modified to select
417  * available ports on the local system.  If we run
418  * out of "*port" numbers, return SYSERR.
419  *
420  * This is primarily a helper function used internally
421  * by 'GNUNET_TESTING_peer_configure'.
422  *
423  * @param system system to use to coordinate resource usage
424  * @param cfg template configuration to update
425  * @return GNUNET_OK on success, GNUNET_SYSERR on error
426  */
427 int
428 GNUNET_TESTING_configuration_create (struct GNUNET_TESTING_System *system,
429                                      struct GNUNET_CONFIGURATION_Handle *cfg)
430 {
431   GNUNET_break (0);
432   return GNUNET_SYSERR;
433 }
434
435
436 /**
437  * Configure a GNUnet peer.  GNUnet must be installed on the local
438  * system and available in the PATH. 
439  *
440  * @param system system to use to coordinate resource usage
441  * @param cfg configuration to use; will be UPDATED (to reflect needed
442  *            changes in port numbers and paths)
443  * @param key_number number of the hostkey to use for the peer
444  * @param id identifier for the daemon, will be set, can be NULL
445  * @param emsg set to error message (set to NULL on success), can be NULL
446  * @return handle to the peer, NULL on error
447  */
448 struct GNUNET_TESTING_Peer *
449 GNUNET_TESTING_peer_configure (struct GNUNET_TESTING_System *system,
450                                struct GNUNET_CONFIGURATION_Handle *cfg,
451                                uint32_t key_number,
452                                struct GNUNET_PeerIdentity *id,
453                                char **emsg)
454 {
455   GNUNET_break (0);
456   return NULL;
457 }
458
459
460 /**
461  * Start the peer. 
462  *
463  * @param peer peer to start
464  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer already running)
465  */
466 int
467 GNUNET_TESTING_peer_start (struct GNUNET_TESTING_Peer *peer)
468 {
469   GNUNET_break (0);
470   return GNUNET_SYSERR;
471 }
472
473
474 /**
475  * Stop the peer. 
476  *
477  * @param peer peer to stop
478  * @return GNUNET_OK on success, GNUNET_SYSERR on error (i.e. peer not running)
479  */
480 int
481 GNUNET_TESTING_peer_stop (struct GNUNET_TESTING_Peer *peer)
482 {
483   GNUNET_break (0);
484   return GNUNET_SYSERR;
485 }
486
487
488 /**
489  * Destroy the peer.  Releases resources locked during peer configuration.
490  * If the peer is still running, it will be stopped AND a warning will be
491  * printed (users of the API should stop the peer explicitly first).
492  *
493  * @param peer peer to destroy
494  */
495 void
496 GNUNET_TESTING_peer_destroy (struct GNUNET_TESTING_Peer *peer)
497 {
498   GNUNET_break (0);
499 }
500
501
502
503 /**
504  * Start a single peer and run a test using the testing library.
505  * Starts a peer using the given configuration and then invokes the
506  * given callback.  This function ALSO initializes the scheduler loop
507  * and should thus be called directly from "main".  The testcase
508  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
509  *
510  * @param tmppath path for storing temporary data for the test
511  * @param cfgfilename name of the configuration file to use;
512  *         use NULL to only run with defaults
513  * @param tm main function of the testcase
514  * @param tm_cls closure for 'tm'
515  * @return 0 on success, 1 on error
516  */
517 int
518 GNUNET_TESTING_peer_run (const char *tmppath,
519                          const char *cfgfilename,
520                          GNUNET_TESTING_TestMain tm,
521                          void *tm_cls)
522 {
523   return GNUNET_TESTING_service_run (tmppath, "arm",
524                                      cfgfilename, tm, tm_cls);
525 }
526
527
528
529 /**
530  * Start a single service (no ARM, except of course if the given
531  * service name is 'arm') and run a test using the testing library.
532  * Starts a service using the given configuration and then invokes the
533  * given callback.  This function ALSO initializes the scheduler loop
534  * and should thus be called directly from "main".  The testcase
535  * should self-terminate by invoking 'GNUNET_SCHEDULER_shutdown'.
536  *
537  * This function is useful if the testcase is for a single service
538  * and if that service doesn't itself depend on other services.
539  *
540  * @param tmppath path for storing temporary data for the test
541  * @param service_name name of the service to run
542  * @param cfgfilename name of the configuration file to use;
543  *         use NULL to only run with defaults
544  * @param tm main function of the testcase
545  * @param tm_cls closure for 'tm'
546  * @return 0 on success, 1 on error
547  */
548 int
549 GNUNET_TESTING_service_run (const char *tmppath,
550                             const char *service_name,
551                             const char *cfgfilename,
552                             GNUNET_TESTING_TestMain tm,
553                             void *tm_cls)
554 {
555   GNUNET_break (0);
556   return 1;
557 }
558
559
560
561 /* end of testing_new.c */