d979d575ba91fdfbd3ac5b76614bd4a6efb74790
[oweals/gnunet.git] / src / testbed / testbed_api_hosts.c
1 /*
2       This file is part of GNUnet
3       (C) 2008--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 testbed/testbed_api_hosts.c
23  * @brief API for manipulating 'hosts' controlled by the GNUnet testing service;
24  *        allows parsing hosts files, starting, stopping and communicating (via
25  *        SSH/stdin/stdout) with the remote (or local) processes
26  * @author Christian Grothoff
27  */
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_testbed_service.h"
31 #include "gnunet_core_service.h"
32 #include "gnunet_transport_service.h"
33
34 #include "testbed_api.h"
35 #include "testbed_api_hosts.h"
36 #include "testbed_api_operations.h"
37 #include "testbed_api_sd.h"
38
39 #include <zlib.h>
40
41 /**
42  * Generic logging shorthand
43  */
44 #define LOG(kind, ...)                          \
45   GNUNET_log_from (kind, "testbed-api-hosts", __VA_ARGS__);
46
47 /**
48  * Debug logging shorthand
49  */
50 #define LOG_DEBUG(...)                          \
51   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__);
52
53 /**
54  * Number of extra elements we create space for when we grow host list
55  */
56 #define HOST_LIST_GROW_STEP 10
57
58
59 /**
60  * A list entry for registered controllers list
61  */
62 struct RegisteredController
63 {
64   /**
65    * The controller at which this host is registered
66    */
67   const struct GNUNET_TESTBED_Controller *controller;
68
69   /**
70    * The next ptr for DLL
71    */
72   struct RegisteredController *next;
73
74   /**
75    * The prev ptr for DLL
76    */
77   struct RegisteredController *prev;
78 };
79
80
81 /**
82  * A slot to record time taken by an overlay connect operation
83  */
84 struct TimeSlot
85 {
86   /**
87    * A key to identify this timeslot
88    */
89   void *key;
90
91   /**
92    * Time
93    */
94   struct GNUNET_TIME_Relative time;
95
96   /**
97    * Number of timing values accumulated
98    */
99   unsigned int nvals;
100 };
101
102
103 /**
104  * Opaque handle to a host running experiments managed by the testing framework.
105  * The master process must be able to SSH to this host without password (via
106  * ssh-agent).
107  */
108 struct GNUNET_TESTBED_Host
109 {
110
111   /**
112    * The next pointer for DLL
113    */
114   struct GNUNET_TESTBED_Host *next;
115
116   /**
117    * The prev pointer for DLL
118    */
119   struct GNUNET_TESTBED_Host *prev;
120
121   /**
122    * The hostname of the host; NULL for localhost
123    */
124   const char *hostname;
125
126   /**
127    * The username to be used for SSH login
128    */
129   const char *username;
130
131   /**
132    * the configuration to use as a template while starting a controller on this
133    * host.  Operation queue size specific to a host are also read from this
134    * configuration handle
135    */
136   struct GNUNET_CONFIGURATION_Handle *cfg;
137
138   /**
139    * The head for the list of controllers where this host is registered
140    */
141   struct RegisteredController *rc_head;
142
143   /**
144    * The tail for the list of controllers where this host is registered
145    */
146   struct RegisteredController *rc_tail;
147
148   /**
149    * Operation queue for simultaneous overlay connect operations target at this
150    * host
151    */
152   struct OperationQueue *opq_parallel_overlay_connect_operations;
153
154   /**
155    * An array of timing slots; size should be equal to the current number of parallel
156    * overlay connects
157    */
158   struct TimeSlot *tslots;
159
160   /**
161    * Handle for SD calculations amount parallel overlay connect operation finish
162    * times
163    */
164   struct SDHandle *poc_sd;  
165
166   /**
167    * The number of parallel overlay connects we do currently
168    */
169   unsigned int num_parallel_connects;
170
171   /**
172    * Counter to indicate when all the available time slots are filled
173    */
174   unsigned int tslots_filled;
175
176   /**
177    * Is a controller started on this host?
178    */
179   int controller_started;
180
181   /**
182    * Global ID we use to refer to a host on the network
183    */
184   uint32_t id;
185
186   /**
187    * The port which is to be used for SSH
188    */
189   uint16_t port;
190
191 };
192
193
194 /**
195  * Array of available hosts
196  */
197 static struct GNUNET_TESTBED_Host **host_list;
198
199 /**
200  * The size of the available hosts list
201  */
202 static unsigned int host_list_size;
203
204
205 /**
206  * Lookup a host by ID.
207  *
208  * @param id global host ID assigned to the host; 0 is
209  *        reserved to always mean 'localhost'
210  * @return handle to the host, NULL if host not found
211  */
212 struct GNUNET_TESTBED_Host *
213 GNUNET_TESTBED_host_lookup_by_id_ (uint32_t id)
214 {
215   if (host_list_size <= id)
216     return NULL;
217   return host_list[id];
218 }
219
220
221 /**
222  * Create a host by ID; given this host handle, we could not
223  * run peers at the host, but we can talk about the host
224  * internally.
225  *
226  * @param id global host ID assigned to the host; 0 is
227  *        reserved to always mean 'localhost'
228  * @param cfg the configuration to use as a template while starting a controller
229  *          on this host.  Operation queue sizes specific to a host are also
230  *          read from this configuration handle
231  * @return handle to the host, NULL on error
232  */
233 struct GNUNET_TESTBED_Host *
234 GNUNET_TESTBED_host_create_by_id_ (uint32_t id,
235                                    const struct GNUNET_CONFIGURATION_Handle
236                                    *cfg)
237 {
238   return GNUNET_TESTBED_host_create_with_id (id, NULL, NULL, cfg, 0);
239 }
240
241
242 /**
243  * Obtain the host's unique global ID.
244  *
245  * @param host handle to the host, NULL means 'localhost'
246  * @return id global host ID assigned to the host (0 is
247  *         'localhost', but then obviously not globally unique)
248  */
249 uint32_t
250 GNUNET_TESTBED_host_get_id_ (const struct GNUNET_TESTBED_Host * host)
251 {
252   return host->id;
253 }
254
255
256 /**
257  * Obtain the host's hostname.
258  *
259  * @param host handle to the host, NULL means 'localhost'
260  * @return hostname of the host
261  */
262 const char *
263 GNUNET_TESTBED_host_get_hostname (const struct GNUNET_TESTBED_Host *host)
264 {
265   return host->hostname;
266 }
267
268
269 /**
270  * Obtain the host's username
271  *
272  * @param host handle to the host, NULL means 'localhost'
273  * @return username to login to the host
274  */
275 const char *
276 GNUNET_TESTBED_host_get_username_ (const struct GNUNET_TESTBED_Host *host)
277 {
278   return host->username;
279 }
280
281
282 /**
283  * Obtain the host's ssh port
284  *
285  * @param host handle to the host, NULL means 'localhost'
286  * @return username to login to the host
287  */
288 uint16_t
289 GNUNET_TESTBED_host_get_ssh_port_ (const struct GNUNET_TESTBED_Host * host)
290 {
291   return host->port;
292 }
293
294
295 /**
296  * Check whether a controller is already started on the given host
297  *
298  * @param host the handle to the host
299  * @return GNUNET_YES if the controller is already started; GNUNET_NO if not
300  */
301 int
302 GNUNET_TESTBED_host_controller_started (const struct GNUNET_TESTBED_Host *host)
303 {
304   return host->controller_started;
305 }
306
307
308 /**
309  * Obtain the host's configuration template
310  *
311  * @param host handle to the host
312  * @return the host's configuration template
313  */
314 const struct GNUNET_CONFIGURATION_Handle *
315 GNUNET_TESTBED_host_get_cfg_ (const struct GNUNET_TESTBED_Host *host)
316 {
317   return host->cfg;
318 }
319
320
321 /**
322  * Create a host to run peers and controllers on.
323  *
324  * @param id global host ID assigned to the host; 0 is
325  *        reserved to always mean 'localhost'
326  * @param hostname name of the host, use "NULL" for localhost
327  * @param username username to use for the login; may be NULL
328  * @param cfg the configuration to use as a template while starting a controller
329  *          on this host.  Operation queue sizes specific to a host are also
330  *          read from this configuration handle
331  * @param port port number to use for ssh; use 0 to let ssh decide
332  * @return handle to the host, NULL on error
333  */
334 struct GNUNET_TESTBED_Host *
335 GNUNET_TESTBED_host_create_with_id (uint32_t id, const char *hostname,
336                                     const char *username, 
337                                     const struct GNUNET_CONFIGURATION_Handle
338                                     *cfg,
339                                     uint16_t port)
340 {
341   struct GNUNET_TESTBED_Host *host;
342   unsigned int new_size;
343
344   if ((id < host_list_size) && (NULL != host_list[id]))
345   {
346     LOG (GNUNET_ERROR_TYPE_WARNING, "Host with id: %u already created\n", id);
347     return NULL;
348   }
349   host = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Host));
350   host->hostname = (NULL != hostname) ? GNUNET_strdup (hostname) : NULL;
351   host->username = (NULL != username) ? GNUNET_strdup (username) : NULL;
352   host->id = id;
353   host->port = (0 == port) ? 22 : port;
354   host->cfg = GNUNET_CONFIGURATION_dup (cfg);
355   host->opq_parallel_overlay_connect_operations =
356       GNUNET_TESTBED_operation_queue_create_ (0);
357   GNUNET_TESTBED_set_num_parallel_overlay_connects_ (host, 1);
358   host->poc_sd = GNUNET_TESTBED_SD_init_ (10);
359   new_size = host_list_size;
360   while (id >= new_size)
361     new_size += HOST_LIST_GROW_STEP;
362   if (new_size != host_list_size)
363     GNUNET_array_grow (host_list, host_list_size, new_size);
364   GNUNET_assert (id < host_list_size);
365   LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding host with id: %u\n", host->id);
366   host_list[id] = host;
367   return host;
368 }
369
370
371 /**
372  * Create a host to run peers and controllers on.
373  *
374  * @param hostname name of the host, use "NULL" for localhost
375  * @param username username to use for the login; may be NULL
376  * @param cfg the configuration to use as a template while starting a controller
377  *          on this host.  Operation queue sizes specific to a host are also
378  *          read from this configuration handle
379  * @param port port number to use for ssh; use 0 to let ssh decide
380  * @return handle to the host, NULL on error
381  */
382 struct GNUNET_TESTBED_Host *
383 GNUNET_TESTBED_host_create (const char *hostname, const char *username,
384                             const struct GNUNET_CONFIGURATION_Handle *cfg,
385                             uint16_t port)
386 {
387   static uint32_t uid_generator;
388
389   if (NULL == hostname)
390     return GNUNET_TESTBED_host_create_with_id (0, hostname, username, 
391                                                cfg, port);
392   return GNUNET_TESTBED_host_create_with_id (++uid_generator, hostname,
393                                              username, cfg, port);
394 }
395
396
397 /**
398  * Load a set of hosts from a configuration file.
399  *
400  * @param filename file with the host specification
401  * @param cfg the configuration to use as a template while starting a controller
402  *          on any of the loaded hosts.  Operation queue sizes specific to a host
403  *          are also read from this configuration handle
404  * @param hosts set to the hosts found in the file; caller must free this if
405  *          number of hosts returned is greater than 0
406  * @return number of hosts returned in 'hosts', 0 on error
407  */
408 unsigned int
409 GNUNET_TESTBED_hosts_load_from_file (const char *filename,
410                                      const struct GNUNET_CONFIGURATION_Handle
411                                      *cfg,
412                                      struct GNUNET_TESTBED_Host ***hosts)
413 {
414   //struct GNUNET_TESTBED_Host **host_array;
415   struct GNUNET_TESTBED_Host *starting_host;
416   char *data;
417   char *buf;
418   char username[256];
419   char hostname[256];
420   uint64_t fs;
421   short int port;
422   int ret;
423   unsigned int offset;
424   unsigned int count;
425
426
427   GNUNET_assert (NULL != filename);
428   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
429   {
430     LOG (GNUNET_ERROR_TYPE_WARNING, _("Hosts file %s not found\n"), filename);
431     return 0;
432   }
433   if (GNUNET_OK !=
434       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
435     fs = 0;
436   if (0 == fs)
437   {
438     LOG (GNUNET_ERROR_TYPE_WARNING, _("Hosts file %s has no data\n"), filename);
439     return 0;
440   }
441   data = GNUNET_malloc (fs);
442   if (fs != GNUNET_DISK_fn_read (filename, data, fs))
443   {
444     GNUNET_free (data);
445     LOG (GNUNET_ERROR_TYPE_WARNING, _("Hosts file %s cannot be read\n"),
446          filename);
447     return 0;
448   }
449   buf = data;
450   offset = 0;
451   starting_host = NULL;
452   count = 0;
453   while (offset < (fs - 1))
454   {
455     offset++;
456     if (((data[offset] == '\n')) && (buf != &data[offset]))
457     {
458       data[offset] = '\0';
459       ret =
460           SSCANF (buf, "%255[a-zA-Z0-9_]@%255[a-zA-Z0-9.]:%5hd", username,
461                   hostname, &port);
462       if (3 == ret)
463       {
464         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
465                     "Successfully read host %s, port %d and user %s from file\n",
466                     hostname, port, username);
467         /* We store hosts in a static list; hence we only require the starting
468          * host pointer in that list to access the newly created list of hosts */
469         if (NULL == starting_host)
470           starting_host = GNUNET_TESTBED_host_create (hostname, username, cfg,
471                                                       port);
472         else
473           (void) GNUNET_TESTBED_host_create (hostname, username, cfg, port);
474         count++;
475       }
476       else
477         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
478                     "Error reading line `%s' in hostfile\n", buf);
479       buf = &data[offset + 1];
480     }
481     else if ((data[offset] == '\n') || (data[offset] == '\0'))
482       buf = &data[offset + 1];
483   }
484   GNUNET_free (data);
485   if (NULL == starting_host)
486     return 0;
487   *hosts = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Host *) * count);
488   memcpy (*hosts, &host_list[GNUNET_TESTBED_host_get_id_ (starting_host)],
489           sizeof (struct GNUNET_TESTBED_Host *) * count);
490   return count;
491 }
492
493
494 /**
495  * Destroy a host handle.  Must only be called once everything
496  * running on that host has been stopped.
497  *
498  * @param host handle to destroy
499  */
500 void
501 GNUNET_TESTBED_host_destroy (struct GNUNET_TESTBED_Host *host)
502 {
503   struct RegisteredController *rc;
504   uint32_t id;
505
506   GNUNET_assert (host->id < host_list_size);
507   GNUNET_assert (host_list[host->id] == host);
508   host_list[host->id] = NULL;
509   /* clear registered controllers list */
510   for (rc = host->rc_head; NULL != rc; rc = host->rc_head)
511   {
512     GNUNET_CONTAINER_DLL_remove (host->rc_head, host->rc_tail, rc);
513     GNUNET_free (rc);
514   }
515   GNUNET_free_non_null ((char *) host->username);
516   GNUNET_free_non_null ((char *) host->hostname);
517   GNUNET_TESTBED_operation_queue_destroy_
518       (host->opq_parallel_overlay_connect_operations);
519   GNUNET_TESTBED_SD_destroy_ (host->poc_sd);
520   GNUNET_free_non_null (host->tslots);
521   GNUNET_CONFIGURATION_destroy (host->cfg);
522   GNUNET_free (host);
523   while (host_list_size >= HOST_LIST_GROW_STEP)
524   {
525     for (id = host_list_size - 1; id > host_list_size - HOST_LIST_GROW_STEP;
526          id--)
527       if (NULL != host_list[id])
528         break;
529     if (id != host_list_size - HOST_LIST_GROW_STEP)
530       break;
531     if (NULL != host_list[id])
532       break;
533     host_list_size -= HOST_LIST_GROW_STEP;
534   }
535   host_list =
536       GNUNET_realloc (host_list,
537                       sizeof (struct GNUNET_TESTBED_Host *) * host_list_size);
538 }
539
540
541 /**
542  * Marks a host as registered with a controller
543  *
544  * @param host the host to mark
545  * @param controller the controller at which this host is registered
546  */
547 void
548 GNUNET_TESTBED_mark_host_registered_at_ (struct GNUNET_TESTBED_Host *host,
549                                          const struct GNUNET_TESTBED_Controller
550                                          *const controller)
551 {
552   struct RegisteredController *rc;
553
554   for (rc = host->rc_head; NULL != rc; rc = rc->next)
555   {
556     if (controller == rc->controller)   /* already registered at controller */
557     {
558       GNUNET_break (0);
559       return;
560     }
561   }
562   rc = GNUNET_malloc (sizeof (struct RegisteredController));
563   rc->controller = controller;
564   GNUNET_CONTAINER_DLL_insert_tail (host->rc_head, host->rc_tail, rc);
565 }
566
567
568 /**
569  * Checks whether a host has been registered
570  *
571  * @param host the host to check
572  * @param controller the controller at which host's registration is checked
573  * @return GNUNET_YES if registered; GNUNET_NO if not
574  */
575 int
576 GNUNET_TESTBED_is_host_registered_ (const struct GNUNET_TESTBED_Host *host,
577                                     const struct GNUNET_TESTBED_Controller
578                                     *const controller)
579 {
580   struct RegisteredController *rc;
581
582   for (rc = host->rc_head; NULL != rc; rc = rc->next)
583   {
584     if (controller == rc->controller)   /* already registered at controller */
585     {
586       return GNUNET_YES;
587     }
588   }
589   return GNUNET_NO;
590 }
591
592
593 /**
594  * Handle for controller process
595  */
596 struct GNUNET_TESTBED_ControllerProc
597 {
598   /**
599    * The process handle
600    */
601   struct GNUNET_HELPER_Handle *helper;
602
603   /**
604    * The arguments used to start the helper
605    */
606   char **helper_argv;
607
608   /**
609    * The host where the helper is run
610    */
611   struct GNUNET_TESTBED_Host *host;
612
613   /**
614    * The controller error callback
615    */
616   GNUNET_TESTBED_ControllerStatusCallback cb;
617
618   /**
619    * The closure for the above callback
620    */
621   void *cls;
622
623   /**
624    * The send handle for the helper
625    */
626   struct GNUNET_HELPER_SendHandle *shandle;
627
628   /**
629    * The message corresponding to send handle
630    */
631   struct GNUNET_MessageHeader *msg;
632
633   /**
634    * The configuration of the running testbed service
635    */
636   struct GNUNET_CONFIGURATION_Handle *cfg;
637
638 };
639
640
641 /**
642  * Function to copy NULL terminated list of arguments
643  *
644  * @param argv the NULL terminated list of arguments. Cannot be NULL.
645  * @return the copied NULL terminated arguments
646  */
647 static char **
648 copy_argv (const char *const *argv)
649 {
650   char **argv_dup;
651   unsigned int argp;
652
653   GNUNET_assert (NULL != argv);
654   for (argp = 0; NULL != argv[argp]; argp++) ;
655   argv_dup = GNUNET_malloc (sizeof (char *) * (argp + 1));
656   for (argp = 0; NULL != argv[argp]; argp++)
657     argv_dup[argp] = strdup (argv[argp]);
658   return argv_dup;
659 }
660
661
662 /**
663  * Function to join NULL terminated list of arguments
664  *
665  * @param argv1 the NULL terminated list of arguments. Cannot be NULL.
666  * @param argv2 the NULL terminated list of arguments. Cannot be NULL.
667  * @return the joined NULL terminated arguments
668  */
669 static char **
670 join_argv (const char *const *argv1, const char *const *argv2)
671 {
672   char **argvj;
673   char *argv;
674   unsigned int carg;
675   unsigned int cnt;
676
677   carg = 0;
678   argvj = NULL;
679   for (cnt = 0; NULL != argv1[cnt]; cnt++)
680   {
681     argv = GNUNET_strdup (argv1[cnt]);
682     GNUNET_array_append (argvj, carg, argv);
683   }
684   for (cnt = 0; NULL != argv2[cnt]; cnt++)
685   {
686     argv = GNUNET_strdup (argv2[cnt]);
687     GNUNET_array_append (argvj, carg, argv);
688   }
689   GNUNET_array_append (argvj, carg, NULL);
690   return argvj;
691 }
692
693
694 /**
695  * Frees the given NULL terminated arguments
696  *
697  * @param argv the NULL terminated list of arguments
698  */
699 static void
700 free_argv (char **argv)
701 {
702   unsigned int argp;
703
704   for (argp = 0; NULL != argv[argp]; argp++)
705     GNUNET_free (argv[argp]);
706   GNUNET_free (argv);
707 }
708
709
710 /**
711  * Generates arguments for opening a remote shell. Builds up the arguments
712  * from the environment variable GNUNET_TESTBED_RSH_CMD. The variable
713  * should not mention `-p' (port) option and destination address as these will
714  * be set locally in the function from its parameteres. If the environmental
715  * variable is not found then it defaults to `ssh -o BatchMode=yes -o
716  * NoHostAuthenticationForLocalhost=yes'
717  *
718  * @param port the destination port number
719  * @param dst the destination address
720  * @return NULL terminated list of arguments
721  */
722 static char **
723 gen_rsh_args (const char *port, const char *dst)
724 {
725   static const char *default_ssh_args[] = {
726     "ssh",
727     "-o",
728     "BatchMode=yes",
729     "-o",
730     "NoHostAuthenticationForLocalhost=yes",
731     NULL
732   };
733   char **ssh_args;
734   char *ssh_cmd;
735   char *ssh_cmd_cp;
736   char *arg;
737   unsigned int cnt;
738
739   ssh_args = NULL;
740   if (NULL != (ssh_cmd = getenv ("GNUNET_TESTBED_RSH_CMD")))
741   {
742     ssh_cmd = GNUNET_strdup (ssh_cmd);
743     ssh_cmd_cp = ssh_cmd;
744     for (cnt = 0; NULL != (arg = strtok (ssh_cmd, " ")); ssh_cmd = NULL)
745       GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (arg));
746     GNUNET_free (ssh_cmd_cp);
747   }
748   else
749   {
750     ssh_args = copy_argv (default_ssh_args);
751     cnt = (sizeof (default_ssh_args)) / (sizeof (const char *));
752     GNUNET_array_grow (ssh_args, cnt, cnt - 1);
753   }
754   GNUNET_array_append (ssh_args, cnt, GNUNET_strdup ("-p"));
755   GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (port));
756   GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (dst));
757   GNUNET_array_append (ssh_args, cnt, NULL);
758   return ssh_args;
759 }
760
761
762 /**
763  * Generates the arguments needed for executing the given binary in a remote
764  * shell. Builds the arguments from the environmental variable
765  * GNUNET_TETSBED_RSH_CMD_SUFFIX. If the environmental variable is not found,
766  * only the given binary name will be present in the returned arguments
767  *
768  * @param append_args the arguments to append after generating the suffix
769  *          arguments. Can be NULL; if not must be NULL terminated 'char *' array
770  * @return NULL-terminated args
771  */
772 static char **
773 gen_rsh_suffix_args (const char * const *append_args)
774 {
775   char **rshell_args;
776   char *rshell_cmd;
777   char *rshell_cmd_cp;
778   char *arg;
779   unsigned int cnt;
780   unsigned int append_cnt;
781
782   rshell_args = NULL;
783   cnt = 0;
784   if (NULL != (rshell_cmd = getenv ("GNUNET_TESTBED_RSH_CMD_SUFFIX")))
785   {
786     rshell_cmd = GNUNET_strdup (rshell_cmd);
787     rshell_cmd_cp = rshell_cmd;
788     for (; NULL != (arg = strtok (rshell_cmd, " ")); rshell_cmd = NULL)
789       GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (arg));
790     GNUNET_free (rshell_cmd_cp);
791   }
792   if (NULL != append_args)
793   {
794     for (append_cnt = 0; NULL != append_args[append_cnt]; append_cnt++)      
795       GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (append_args[append_cnt]));
796   }
797   GNUNET_array_append (rshell_args, cnt, NULL);
798   return rshell_args;
799 }
800
801
802 /**
803  * Functions with this signature are called whenever a
804  * complete message is received by the tokenizer.
805  *
806  * Do not call GNUNET_SERVER_mst_destroy in callback
807  *
808  * @param cls closure
809  * @param client identification of the client
810  * @param message the actual message
811  *
812  * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing
813  */
814 static int
815 helper_mst (void *cls, void *client, const struct GNUNET_MessageHeader *message)
816 {
817   struct GNUNET_TESTBED_ControllerProc *cp = cls;
818   const struct GNUNET_TESTBED_HelperReply *msg;
819   const char *hostname;
820   char *config;
821   uLongf config_size;
822   uLongf xconfig_size;
823
824   msg = (const struct GNUNET_TESTBED_HelperReply *) message;
825   GNUNET_assert (sizeof (struct GNUNET_TESTBED_HelperReply) <
826                  ntohs (msg->header.size));
827   GNUNET_assert (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY ==
828                  ntohs (msg->header.type));
829   config_size = (uLongf) ntohs (msg->config_size);
830   xconfig_size =
831       (uLongf) (ntohs (msg->header.size) -
832                 sizeof (struct GNUNET_TESTBED_HelperReply));
833   config = GNUNET_malloc (config_size);
834   GNUNET_assert (Z_OK ==
835                  uncompress ((Bytef *) config, &config_size,
836                              (const Bytef *) &msg[1], xconfig_size));
837   GNUNET_assert (NULL == cp->cfg);
838   cp->cfg = GNUNET_CONFIGURATION_create ();
839   GNUNET_assert (GNUNET_CONFIGURATION_deserialize
840                  (cp->cfg, config, config_size, GNUNET_NO));
841   GNUNET_free (config);
842   if ((NULL == cp->host) ||
843       (NULL == (hostname = GNUNET_TESTBED_host_get_hostname (cp->host))))
844     hostname = "localhost";
845   /* Change the hostname so that we can connect to it */
846   GNUNET_CONFIGURATION_set_value_string (cp->cfg, "testbed", "hostname",
847                                          hostname);
848   cp->cb (cp->cls, cp->cfg, GNUNET_OK);
849   return GNUNET_OK;
850 }
851
852
853 /**
854  * Continuation function from GNUNET_HELPER_send()
855  *
856  * @param cls closure
857  * @param result GNUNET_OK on success,
858  *               GNUNET_NO if helper process died
859  *               GNUNET_SYSERR during GNUNET_HELPER_stop
860  */
861 static void
862 clear_msg (void *cls, int result)
863 {
864   struct GNUNET_TESTBED_ControllerProc *cp = cls;
865
866   GNUNET_assert (NULL != cp->shandle);
867   cp->shandle = NULL;
868   GNUNET_free (cp->msg);
869 }
870
871
872 /**
873  * Callback that will be called when the helper process dies. This is not called
874  * when the helper process is stoped using GNUNET_HELPER_stop()
875  *
876  * @param cls the closure from GNUNET_HELPER_start()
877  */
878 static void
879 helper_exp_cb (void *cls)
880 {
881   struct GNUNET_TESTBED_ControllerProc *cp = cls;
882   GNUNET_TESTBED_ControllerStatusCallback cb;
883   void *cb_cls;
884
885   cb = cp->cb;
886   cb_cls = cp->cls;
887   cp->helper = NULL;
888   GNUNET_TESTBED_controller_stop (cp);
889   if (NULL != cb)
890     cb (cb_cls, NULL, GNUNET_SYSERR);
891 }
892
893
894 /**
895  * Starts a controller process at the given host
896  *
897  * @param trusted_ip the ip address of the controller which will be set as TRUSTED
898  *          HOST(all connections form this ip are permitted by the testbed) when
899  *          starting testbed controller at host. This can either be a single ip
900  *          address or a network address in CIDR notation.
901  * @param host the host where the controller has to be started; NULL for
902  *          localhost
903  * @param cfg template configuration to use for the remote controller; the
904  *          remote controller will be started with a slightly modified
905  *          configuration (port numbers, unix domain sockets and service home
906  *          values are changed as per TESTING library on the remote host)
907  * @param cb function called when the controller is successfully started or
908  *          dies unexpectedly; GNUNET_TESTBED_controller_stop shouldn't be
909  *          called if cb is called with GNUNET_SYSERR as status. Will never be
910  *          called in the same task as 'GNUNET_TESTBED_controller_start'
911  *          (synchronous errors will be signalled by returning NULL). This
912  *          parameter cannot be NULL.
913  * @param cls closure for above callbacks
914  * @return the controller process handle, NULL on errors
915  */
916 struct GNUNET_TESTBED_ControllerProc *
917 GNUNET_TESTBED_controller_start (const char *trusted_ip,
918                                  struct GNUNET_TESTBED_Host *host,
919                                  const struct GNUNET_CONFIGURATION_Handle *cfg,
920                                  GNUNET_TESTBED_ControllerStatusCallback cb,
921                                  void *cls)
922 {
923   struct GNUNET_TESTBED_ControllerProc *cp;
924   struct GNUNET_TESTBED_HelperInit *msg;
925   const char *hostname;
926
927   static char *const binary_argv[] = {
928     HELPER_TESTBED_BINARY, NULL
929   };
930
931   hostname = NULL;  
932   cp = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_ControllerProc));
933   if ((NULL == host) || (0 == GNUNET_TESTBED_host_get_id_ (host)))
934   {
935     cp->helper =
936         GNUNET_HELPER_start (GNUNET_YES, HELPER_TESTBED_BINARY, binary_argv,
937                              &helper_mst, &helper_exp_cb, cp);
938   }
939   else
940   {
941     char *helper_binary_path_args[2];
942     char **rsh_args;
943     char **rsh_suffix_args;
944     const char *username;
945     char *port;
946     char *dst;
947
948     username = GNUNET_TESTBED_host_get_username_ (host);
949     hostname = GNUNET_TESTBED_host_get_hostname (host);
950     GNUNET_asprintf (&port, "%u", GNUNET_TESTBED_host_get_ssh_port_ (host));
951     if (NULL == username)
952       GNUNET_asprintf (&dst, "%s", hostname);
953     else
954       GNUNET_asprintf (&dst, "%s@%s", username, hostname);
955     LOG_DEBUG ("Starting SSH to destination %s\n", dst);
956
957     if (GNUNET_OK !=
958         GNUNET_CONFIGURATION_get_value_string (cfg, "testbed",
959                                                "HELPER_BINARY_PATH",
960                                                &helper_binary_path_args[0]))
961       helper_binary_path_args[0] =
962           GNUNET_OS_get_libexec_binary_path (HELPER_TESTBED_BINARY);
963     helper_binary_path_args[1] = NULL;
964     rsh_args = gen_rsh_args (port, dst);
965     rsh_suffix_args = gen_rsh_suffix_args ((const char **) helper_binary_path_args);
966     cp->helper_argv =
967         join_argv ((const char **) rsh_args, (const char **) rsh_suffix_args);
968     free_argv (rsh_args);
969     free_argv (rsh_suffix_args);
970     GNUNET_free (port);
971     GNUNET_free (dst);
972     cp->helper =
973         GNUNET_HELPER_start (GNUNET_NO, cp->helper_argv[0], cp->helper_argv, &helper_mst,
974                              &helper_exp_cb, cp);
975     GNUNET_free (helper_binary_path_args[0]);
976   }
977   if (NULL == cp->helper)
978   {
979     if (NULL != cp->helper_argv)
980       free_argv (cp->helper_argv);
981     GNUNET_free (cp);
982     return NULL;
983   }
984   cp->host = host;
985   cp->cb = cb;
986   cp->cls = cls;
987   msg = GNUNET_TESTBED_create_helper_init_msg_ (trusted_ip, hostname, cfg);
988   cp->msg = &msg->header;
989   cp->shandle =
990       GNUNET_HELPER_send (cp->helper, &msg->header, GNUNET_NO, &clear_msg, cp);
991   if (NULL == cp->shandle)
992   {
993     GNUNET_free (msg);
994     GNUNET_TESTBED_controller_stop (cp);
995     return NULL;
996   }
997   return cp;
998 }
999
1000
1001 /**
1002  * Stop the controller process (also will terminate all peers and controllers
1003  * dependent on this controller).  This function blocks until the testbed has
1004  * been fully terminated (!). The controller status cb from
1005  * GNUNET_TESTBED_controller_start() will not be called.
1006  *
1007  * @param cproc the controller process handle
1008  */
1009 void
1010 GNUNET_TESTBED_controller_stop (struct GNUNET_TESTBED_ControllerProc *cproc)
1011 {
1012   if (NULL != cproc->shandle)
1013     GNUNET_HELPER_send_cancel (cproc->shandle);
1014   if (NULL != cproc->helper)
1015     GNUNET_HELPER_soft_stop (cproc->helper);
1016   if (NULL != cproc->cfg)
1017     GNUNET_CONFIGURATION_destroy (cproc->cfg);
1018   if (NULL != cproc->helper_argv)
1019     free_argv (cproc->helper_argv);
1020   GNUNET_free (cproc);
1021 }
1022
1023
1024 /**
1025  * The handle for whether a host is habitable or not
1026  */
1027 struct GNUNET_TESTBED_HostHabitableCheckHandle
1028 {
1029   /**
1030    * The host to check
1031    */
1032   const struct GNUNET_TESTBED_Host *host;
1033
1034   /* /\** */
1035   /*  * the configuration handle to lookup the path of the testbed helper */
1036   /*  *\/ */
1037   /* const struct GNUNET_CONFIGURATION_Handle *cfg; */
1038
1039   /**
1040    * The callback to call once we have the status
1041    */
1042   GNUNET_TESTBED_HostHabitableCallback cb;
1043
1044   /**
1045    * The callback closure
1046    */
1047   void *cb_cls;
1048
1049   /**
1050    * The process handle for the SSH process
1051    */
1052   struct GNUNET_OS_Process *auxp;
1053
1054   /**
1055    * The arguments used to start the helper
1056    */
1057   char **helper_argv;
1058
1059   /**
1060    * Task id for the habitability check task
1061    */
1062   GNUNET_SCHEDULER_TaskIdentifier habitability_check_task;
1063
1064   /**
1065    * How long we wait before checking the process status. Should grow
1066    * exponentially
1067    */
1068   struct GNUNET_TIME_Relative wait_time;
1069
1070 };
1071
1072
1073 /**
1074  * Task for checking whether a host is habitable or not
1075  *
1076  * @param cls GNUNET_TESTBED_HostHabitableCheckHandle
1077  * @param tc the scheduler task context
1078  */
1079 static void
1080 habitability_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1081 {
1082   struct GNUNET_TESTBED_HostHabitableCheckHandle *h = cls;
1083   void *cb_cls;
1084   GNUNET_TESTBED_HostHabitableCallback cb;
1085   const struct GNUNET_TESTBED_Host *host;
1086   unsigned long code;
1087   enum GNUNET_OS_ProcessStatusType type;
1088   int ret;
1089
1090   h->habitability_check_task = GNUNET_SCHEDULER_NO_TASK;
1091   ret = GNUNET_OS_process_status (h->auxp, &type, &code);
1092   if (GNUNET_SYSERR == ret)
1093   {
1094     GNUNET_break (0);
1095     ret = GNUNET_NO;
1096     goto call_cb;
1097   }
1098   if (GNUNET_NO == ret)
1099   {
1100     h->wait_time = GNUNET_TIME_STD_BACKOFF (h->wait_time);
1101     h->habitability_check_task =
1102         GNUNET_SCHEDULER_add_delayed (h->wait_time, &habitability_check, h);
1103     return;
1104   }
1105   GNUNET_OS_process_destroy (h->auxp);
1106   h->auxp = NULL;
1107   ret = (0 != code) ? GNUNET_NO : GNUNET_YES;
1108
1109 call_cb:
1110   if (NULL != h->auxp)
1111     GNUNET_OS_process_destroy (h->auxp);
1112   cb = h->cb;
1113   cb_cls = h->cb_cls;
1114   host = h->host;
1115   GNUNET_free (h);
1116   if (NULL != cb)
1117     cb (cb_cls, host, ret);
1118 }
1119
1120
1121 /**
1122  * Checks whether a host can be used to start testbed service
1123  *
1124  * @param host the host to check
1125  * @param config the configuration handle to lookup the path of the testbed
1126  *          helper
1127  * @param cb the callback to call to inform about habitability of the given host
1128  * @param cb_cls the closure for the callback
1129  * @return NULL upon any error or a handle which can be passed to
1130  *           GNUNET_TESTBED_is_host_habitable_cancel()
1131  */
1132 struct GNUNET_TESTBED_HostHabitableCheckHandle *
1133 GNUNET_TESTBED_is_host_habitable (const struct GNUNET_TESTBED_Host *host,
1134                                   const struct GNUNET_CONFIGURATION_Handle
1135                                   *config,
1136                                   GNUNET_TESTBED_HostHabitableCallback cb,
1137                                   void *cb_cls)
1138 {
1139   struct GNUNET_TESTBED_HostHabitableCheckHandle *h;
1140   char **rsh_args;
1141   char **rsh_suffix_args;
1142   char *stat_args[3];
1143   const char *hostname;
1144   char *port;
1145   char *dst;
1146
1147   h = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostHabitableCheckHandle));
1148   h->cb = cb;
1149   h->cb_cls = cb_cls;
1150   h->host = host;
1151   hostname = (NULL == host->hostname) ? "127.0.0.1" : host->hostname;
1152   if (NULL == host->username)
1153     dst = GNUNET_strdup (hostname);
1154   else
1155     GNUNET_asprintf (&dst, "%s@%s", host->username, hostname);
1156   if (GNUNET_OK !=
1157       GNUNET_CONFIGURATION_get_value_string (config, "testbed",
1158                                              "HELPER_BINARY_PATH",
1159                                              &stat_args[1]))
1160     stat_args[1] =
1161         GNUNET_OS_get_libexec_binary_path (HELPER_TESTBED_BINARY);  
1162   GNUNET_asprintf (&port, "%u", host->port);
1163   rsh_args = gen_rsh_args (port, dst);
1164   GNUNET_free (port);
1165   GNUNET_free (dst);
1166   port = NULL;
1167   dst = NULL;
1168   stat_args[0] = "stat";
1169   stat_args[2] = NULL;
1170   rsh_suffix_args = gen_rsh_suffix_args ((const char **) stat_args);
1171   h->helper_argv = join_argv ((const char **) rsh_args,
1172                               (const char **) rsh_suffix_args);
1173   GNUNET_free (rsh_suffix_args);
1174   GNUNET_free (rsh_args);
1175   h->auxp =
1176       GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ERR, NULL,
1177                                    NULL, h->helper_argv[0], h->helper_argv);
1178   if (NULL == h->auxp)
1179   {
1180     GNUNET_break (0);           /* Cannot exec SSH? */
1181     GNUNET_free (h);
1182     return NULL;
1183   }
1184   h->wait_time = GNUNET_TIME_STD_BACKOFF (h->wait_time);
1185   h->habitability_check_task =
1186       GNUNET_SCHEDULER_add_delayed (h->wait_time, &habitability_check, h);
1187   return h;
1188 }
1189
1190
1191 /**
1192  * Function to cancel a request started using GNUNET_TESTBED_is_host_habitable()
1193  *
1194  * @param handle the habitability check handle
1195  */
1196 void
1197 GNUNET_TESTBED_is_host_habitable_cancel (struct
1198                                          GNUNET_TESTBED_HostHabitableCheckHandle
1199                                          *handle)
1200 {
1201   GNUNET_SCHEDULER_cancel (handle->habitability_check_task);
1202   (void) GNUNET_OS_process_kill (handle->auxp, SIGTERM);
1203   (void) GNUNET_OS_process_wait (handle->auxp);
1204   GNUNET_OS_process_destroy (handle->auxp);
1205   free_argv (handle->helper_argv);
1206   GNUNET_free (handle);
1207 }
1208
1209
1210 /**
1211  * Initializes the operation queue for parallel overlay connects
1212  *
1213  * @param h the host handle
1214  * @param npoc the number of parallel overlay connects - the queue size
1215  */
1216 void
1217 GNUNET_TESTBED_set_num_parallel_overlay_connects_ (struct
1218                                                    GNUNET_TESTBED_Host *h,
1219                                                    unsigned int npoc)
1220 {
1221   //fprintf (stderr, "%d", npoc);
1222   GNUNET_free_non_null (h->tslots);
1223   h->tslots_filled = 0;
1224   h->num_parallel_connects = npoc;
1225   h->tslots = GNUNET_malloc (npoc * sizeof (struct TimeSlot));
1226   GNUNET_TESTBED_operation_queue_reset_max_active_
1227       (h->opq_parallel_overlay_connect_operations, npoc);
1228 }
1229
1230
1231 /**
1232  * Returns a timing slot which will be exclusively locked
1233  *
1234  * @param h the host handle
1235  * @param key a pointer which is associated to the returned slot; should not be
1236  *          NULL. It serves as a key to determine the correct owner of the slot
1237  * @return the time slot index in the array of time slots in the controller
1238  *           handle
1239  */
1240 unsigned int
1241 GNUNET_TESTBED_get_tslot_ (struct GNUNET_TESTBED_Host *h, void *key)
1242 {
1243   unsigned int slot;
1244
1245   GNUNET_assert (NULL != h->tslots);
1246   GNUNET_assert (NULL != key);
1247   for (slot = 0; slot < h->num_parallel_connects; slot++)
1248     if (NULL == h->tslots[slot].key)
1249     {
1250       h->tslots[slot].key = key;
1251       return slot;
1252     }
1253   GNUNET_assert (0);            /* We should always find a free tslot */
1254 }
1255
1256
1257 /**
1258  * Decides whether any change in the number of parallel overlay connects is
1259  * necessary to adapt to the load on the system
1260  *
1261  * @param h the host handle
1262  */
1263 static void
1264 decide_npoc (struct GNUNET_TESTBED_Host *h)
1265 {
1266   struct GNUNET_TIME_Relative avg;
1267   int sd;
1268   unsigned int slot;
1269   unsigned int nvals;
1270
1271   if (h->tslots_filled != h->num_parallel_connects)
1272     return;
1273   avg = GNUNET_TIME_UNIT_ZERO;
1274   nvals = 0;
1275   for (slot = 0; slot < h->num_parallel_connects; slot++)
1276   {
1277     avg = GNUNET_TIME_relative_add (avg, h->tslots[slot].time);
1278     nvals += h->tslots[slot].nvals;
1279   }
1280   GNUNET_assert (nvals >= h->num_parallel_connects);
1281   avg = GNUNET_TIME_relative_divide (avg, nvals);
1282   GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value != avg.rel_value);
1283   sd = GNUNET_TESTBED_SD_deviation_factor_ (h->poc_sd, (unsigned int) avg.rel_value);
1284   if ( (sd <= 5) ||
1285        (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1286                                        h->num_parallel_connects)) )
1287     GNUNET_TESTBED_SD_add_data_ (h->poc_sd, (unsigned int) avg.rel_value);
1288   if (GNUNET_SYSERR == sd)
1289   {
1290     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1291                                                        h->num_parallel_connects);
1292     return;
1293   }
1294   GNUNET_assert (0 <= sd);
1295   if (0 == sd)
1296   {
1297     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1298                                                        h->num_parallel_connects
1299                                                        * 2);
1300     return;
1301   }
1302   if (1 == sd)
1303   {
1304     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1305                                                        h->num_parallel_connects
1306                                                        + 1);
1307     return;
1308   }
1309   if (1 == h->num_parallel_connects)
1310   {
1311     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h, 1);
1312     return;
1313   }
1314   if (2 == sd)
1315   {
1316     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1317                                                        h->num_parallel_connects
1318                                                        - 1);
1319     return;
1320   }
1321   GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1322                                                      h->num_parallel_connects /
1323                                                      2);
1324 }
1325
1326
1327 /**
1328  * Releases a time slot thus making it available for be used again
1329  *
1330  * @param h the host handle
1331  * @param index the index of the the time slot
1332  * @param key the key to prove ownership of the timeslot
1333  * @return GNUNET_YES if the time slot is successfully removed; GNUNET_NO if the
1334  *           time slot cannot be removed - this could be because of the index
1335  *           greater than existing number of time slots or `key' being different
1336  */
1337 int
1338 GNUNET_TESTBED_release_time_slot_ (struct GNUNET_TESTBED_Host *h,
1339                                    unsigned int index, void *key)
1340 {
1341   struct TimeSlot *slot;
1342
1343   GNUNET_assert (NULL != key);
1344   if (index >= h->num_parallel_connects)
1345     return GNUNET_NO;
1346   slot = &h->tslots[index];
1347   if (key != slot->key)
1348     return GNUNET_NO;
1349   slot->key = NULL;
1350   return GNUNET_YES;
1351 }
1352
1353
1354 /**
1355  * Function to update a time slot
1356  *
1357  * @param h the host handle
1358  * @param index the index of the time slot to update
1359  * @param key the key to identify ownership of the slot
1360  * @param time the new time
1361  * @param failed should this reading be treated as coming from a fail event
1362  */
1363 void
1364 GNUNET_TESTBED_update_time_slot_ (struct GNUNET_TESTBED_Host *h,
1365                                   unsigned int index, void *key,
1366                                   struct GNUNET_TIME_Relative time, int failed)
1367 {
1368   struct TimeSlot *slot;
1369
1370   if (GNUNET_YES == failed)
1371   {
1372     if (1 == h->num_parallel_connects)
1373     {
1374       GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h, 1);
1375       return;
1376     }
1377     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1378                                                        h->num_parallel_connects
1379                                                        - 1);
1380   }
1381   if (GNUNET_NO == GNUNET_TESTBED_release_time_slot_ (h, index, key))
1382     return;
1383   slot = &h->tslots[index];
1384   slot->nvals++;
1385   if (GNUNET_TIME_UNIT_ZERO.rel_value == slot->time.rel_value)
1386   {
1387     slot->time = time;
1388     h->tslots_filled++;
1389     decide_npoc (h);
1390     return;
1391   }
1392   slot->time = GNUNET_TIME_relative_add (slot->time, time);
1393 }
1394
1395
1396 /**
1397  * Queues the given operation in the queue for parallel overlay connects of the
1398  * given host
1399  *
1400  * @param h the host handle
1401  * @param op the operation to queue in the given host's parally overlay connect
1402  *          queue 
1403  */
1404 void
1405 GNUNET_TESTBED_host_queue_oc_ (struct GNUNET_TESTBED_Host *h, 
1406                                struct GNUNET_TESTBED_Operation *op)
1407 {  
1408   GNUNET_TESTBED_operation_queue_insert_
1409       (h->opq_parallel_overlay_connect_operations, op);
1410 }
1411
1412 /* end of testbed_api_hosts.c */