0774c023e33eac823be1a6c192f1681c4e184f74
[oweals/gnunet.git] / src / testbed / testbed_api_hosts.c
1 /*
2       This file is part of GNUnet
3       (C) 2008--2013 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_helper.h"
37 #include "testbed_api_operations.h"
38 #include "testbed_api_sd.h"
39
40 #include <zlib.h>
41
42 /**
43  * Generic logging shorthand
44  */
45 #define LOG(kind, ...)                          \
46   GNUNET_log_from (kind, "testbed-api-hosts", __VA_ARGS__);
47
48 /**
49  * Debug logging shorthand
50  */
51 #define LOG_DEBUG(...)                          \
52   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__);
53
54 /**
55  * Prints API violation message
56  */
57 #define API_VIOLATION(cond,errstr)              \
58   do {                                          \
59     if (cond)                                   \
60       break;                                    \
61     LOG (GNUNET_ERROR_TYPE_ERROR, "API violation detected: %s\n", errstr); \
62     GNUNET_assert (0);                                                  \
63   } while (0)
64
65 /**
66  * Log an error message at log-level 'level' that indicates a failure of the
67  * command 'cmd' with the message given by gai_strerror(rc).
68  */
69 #define LOG_GAI(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gai_strerror(rc)); } while(0)
70
71 /**
72  * Number of extra elements we create space for when we grow host list
73  */
74 #define HOST_LIST_GROW_STEP 10
75
76
77 /**
78  * A list entry for registered controllers list
79  */
80 struct RegisteredController
81 {
82   /**
83    * The controller at which this host is registered
84    */
85   const struct GNUNET_TESTBED_Controller *controller;
86
87   /**
88    * The next ptr for DLL
89    */
90   struct RegisteredController *next;
91
92   /**
93    * The prev ptr for DLL
94    */
95   struct RegisteredController *prev;
96 };
97
98
99 /**
100  * A slot to record time taken by an overlay connect operation
101  */
102 struct TimeSlot
103 {
104   /**
105    * A key to identify this timeslot
106    */
107   void *key;
108
109   /**
110    * Time
111    */
112   struct GNUNET_TIME_Relative time;
113
114   /**
115    * Number of timing values accumulated
116    */
117   unsigned int nvals;
118 };
119
120
121 /**
122  * Opaque handle to a host running experiments managed by the testing framework.
123  * The master process must be able to SSH to this host without password (via
124  * ssh-agent).
125  */
126 struct GNUNET_TESTBED_Host
127 {
128
129   /**
130    * The next pointer for DLL
131    */
132   struct GNUNET_TESTBED_Host *next;
133
134   /**
135    * The prev pointer for DLL
136    */
137   struct GNUNET_TESTBED_Host *prev;
138
139   /**
140    * The hostname of the host; NULL for localhost
141    */
142   const char *hostname;
143
144   /**
145    * The username to be used for SSH login
146    */
147   const char *username;
148
149   /**
150    * the configuration to use as a template while starting a controller on this
151    * host.  Operation queue size specific to a host are also read from this
152    * configuration handle.  After starting the controller, it points to the actual
153    * configuration with which the controller is running
154    */
155   struct GNUNET_CONFIGURATION_Handle *cfg;
156
157   /**
158    * The head for the list of controllers where this host is registered
159    */
160   struct RegisteredController *rc_head;
161
162   /**
163    * The tail for the list of controllers where this host is registered
164    */
165   struct RegisteredController *rc_tail;
166
167   /**
168    * Operation queue for simultaneous overlay connect operations target at this
169    * host
170    */
171   struct OperationQueue *opq_parallel_overlay_connect_operations;
172
173   /**
174    * An array of timing slots; size should be equal to the current number of parallel
175    * overlay connects
176    */
177   struct TimeSlot *tslots;
178
179   /**
180    * Handle for SD calculations amount parallel overlay connect operation finish
181    * times
182    */
183   struct SDHandle *poc_sd;  
184
185   /**
186    * The number of parallel overlay connects we do currently
187    */
188   unsigned int num_parallel_connects;
189
190   /**
191    * Counter to indicate when all the available time slots are filled
192    */
193   unsigned int tslots_filled;
194
195   /**
196    * Is a controller started on this host? FIXME: Is this needed?
197    */
198   int controller_started;
199
200   /**
201    * Is this host locked by GNUNET_TESTBED_controller_start()?
202    */
203   int locked;
204
205   /**
206    * Global ID we use to refer to a host on the network
207    */
208   uint32_t id;
209
210   /**
211    * The port which is to be used for SSH
212    */
213   uint16_t port;
214
215 };
216
217
218 /**
219  * Array of available hosts
220  */
221 static struct GNUNET_TESTBED_Host **host_list;
222
223 /**
224  * The size of the available hosts list
225  */
226 static unsigned int host_list_size;
227
228
229 /**
230  * Lookup a host by ID.
231  *
232  * @param id global host ID assigned to the host; 0 is
233  *        reserved to always mean 'localhost'
234  * @return handle to the host, NULL if host not found
235  */
236 struct GNUNET_TESTBED_Host *
237 GNUNET_TESTBED_host_lookup_by_id_ (uint32_t id)
238 {
239   if (host_list_size <= id)
240     return NULL;
241   return host_list[id];
242 }
243
244
245 /**
246  * Create a host by ID; given this host handle, we could not
247  * run peers at the host, but we can talk about the host
248  * internally.
249  *
250  * @param id global host ID assigned to the host; 0 is
251  *        reserved to always mean 'localhost'
252  * @param cfg the configuration to use as a template while starting a controller
253  *          on this host.  Operation queue sizes specific to a host are also
254  *          read from this configuration handle
255  * @return handle to the host, NULL on error
256  */
257 struct GNUNET_TESTBED_Host *
258 GNUNET_TESTBED_host_create_by_id_ (uint32_t id,
259                                    const struct GNUNET_CONFIGURATION_Handle
260                                    *cfg)
261 {
262   return GNUNET_TESTBED_host_create_with_id (id, NULL, NULL, cfg, 0);
263 }
264
265
266 /**
267  * Obtain the host's unique global ID.
268  *
269  * @param host handle to the host, NULL means 'localhost'
270  * @return id global host ID assigned to the host (0 is
271  *         'localhost', but then obviously not globally unique)
272  */
273 uint32_t
274 GNUNET_TESTBED_host_get_id_ (const struct GNUNET_TESTBED_Host * host)
275 {
276   return host->id;
277 }
278
279
280 /**
281  * Obtain the host's hostname.
282  *
283  * @param host handle to the host, NULL means 'localhost'
284  * @return hostname of the host
285  */
286 const char *
287 GNUNET_TESTBED_host_get_hostname (const struct GNUNET_TESTBED_Host *host)
288 {
289   return host->hostname;
290 }
291
292
293 /**
294  * Obtain the host's username
295  *
296  * @param host handle to the host, NULL means 'localhost'
297  * @return username to login to the host
298  */
299 const char *
300 GNUNET_TESTBED_host_get_username_ (const struct GNUNET_TESTBED_Host *host)
301 {
302   return host->username;
303 }
304
305
306 /**
307  * Obtain the host's ssh port
308  *
309  * @param host handle to the host, NULL means 'localhost'
310  * @return username to login to the host
311  */
312 uint16_t
313 GNUNET_TESTBED_host_get_ssh_port_ (const struct GNUNET_TESTBED_Host * host)
314 {
315   return host->port;
316 }
317
318
319 /**
320  * Check whether a controller is already started on the given host
321  *
322  * @param host the handle to the host
323  * @return GNUNET_YES if the controller is already started; GNUNET_NO if not
324  */
325 int
326 GNUNET_TESTBED_host_controller_started (const struct GNUNET_TESTBED_Host *host)
327 {
328   return host->controller_started;
329 }
330
331
332 /**
333  * Obtain the host's configuration template
334  *
335  * @param host handle to the host
336  * @return the host's configuration template
337  */
338 const struct GNUNET_CONFIGURATION_Handle *
339 GNUNET_TESTBED_host_get_cfg_ (const struct GNUNET_TESTBED_Host *host)
340 {
341   return host->cfg;
342 }
343
344
345 /**
346  * Function to replace host's configuration
347  *
348  * @param host the host handle
349  * @param new_cfg the new configuration to replace the old one
350  */
351 void
352 GNUNET_TESTBED_host_replace_cfg_ (struct GNUNET_TESTBED_Host *host,
353                                   const struct GNUNET_CONFIGURATION_Handle *new_cfg)
354 {
355   GNUNET_CONFIGURATION_destroy (host->cfg);
356   host->cfg = GNUNET_CONFIGURATION_dup (new_cfg);
357 }
358
359
360 /**
361  * Create a host to run peers and controllers on.
362  *
363  * @param id global host ID assigned to the host; 0 is
364  *        reserved to always mean 'localhost'
365  * @param hostname name of the host, use "NULL" for localhost
366  * @param username username to use for the login; may be NULL
367  * @param cfg the configuration to use as a template while starting a controller
368  *          on this host.  Operation queue sizes specific to a host are also
369  *          read from this configuration handle
370  * @param port port number to use for ssh; use 0 to let ssh decide
371  * @return handle to the host, NULL on error
372  */
373 struct GNUNET_TESTBED_Host *
374 GNUNET_TESTBED_host_create_with_id (uint32_t id, const char *hostname,
375                                     const char *username, 
376                                     const struct GNUNET_CONFIGURATION_Handle
377                                     *cfg,
378                                     uint16_t port)
379 {
380   struct GNUNET_TESTBED_Host *host;
381   unsigned int new_size;
382
383   if ((id < host_list_size) && (NULL != host_list[id]))
384   {
385     LOG (GNUNET_ERROR_TYPE_WARNING, "Host with id: %u already created\n", id);
386     return NULL;
387   }
388   host = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Host));
389   host->hostname = (NULL != hostname) ? GNUNET_strdup (hostname) : NULL;
390   host->username = (NULL != username) ? GNUNET_strdup (username) : NULL;
391   host->id = id;
392   host->port = (0 == port) ? 22 : port;
393   host->cfg = GNUNET_CONFIGURATION_dup (cfg);
394   host->opq_parallel_overlay_connect_operations =
395       GNUNET_TESTBED_operation_queue_create_ (0);
396   GNUNET_TESTBED_set_num_parallel_overlay_connects_ (host, 1);
397   host->poc_sd = GNUNET_TESTBED_SD_init_ (10);
398   new_size = host_list_size;
399   while (id >= new_size)
400     new_size += HOST_LIST_GROW_STEP;
401   if (new_size != host_list_size)
402     GNUNET_array_grow (host_list, host_list_size, new_size);
403   GNUNET_assert (id < host_list_size);
404   LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding host with id: %u\n", host->id);
405   host_list[id] = host;
406   return host;
407 }
408
409
410 /**
411  * Create a host to run peers and controllers on.
412  *
413  * @param hostname name of the host, use "NULL" for localhost
414  * @param username username to use for the login; may be NULL
415  * @param cfg the configuration to use as a template while starting a controller
416  *          on this host.  Operation queue sizes specific to a host are also
417  *          read from this configuration handle
418  * @param port port number to use for ssh; use 0 to let ssh decide
419  * @return handle to the host, NULL on error
420  */
421 struct GNUNET_TESTBED_Host *
422 GNUNET_TESTBED_host_create (const char *hostname, const char *username,
423                             const struct GNUNET_CONFIGURATION_Handle *cfg,
424                             uint16_t port)
425 {
426   static uint32_t uid_generator;
427
428   if (NULL == hostname)
429     return GNUNET_TESTBED_host_create_with_id (0, hostname, username, 
430                                                cfg, port);
431   return GNUNET_TESTBED_host_create_with_id (++uid_generator, hostname,
432                                              username, cfg, port);
433 }
434
435
436 /**
437  * Load a set of hosts from a configuration file.
438  *
439  * @param filename file with the host specification
440  * @param cfg the configuration to use as a template while starting a controller
441  *          on any of the loaded hosts.  Operation queue sizes specific to a host
442  *          are also read from this configuration handle
443  * @param hosts set to the hosts found in the file; caller must free this if
444  *          number of hosts returned is greater than 0
445  * @return number of hosts returned in 'hosts', 0 on error
446  */
447 unsigned int
448 GNUNET_TESTBED_hosts_load_from_file (const char *filename,
449                                      const struct GNUNET_CONFIGURATION_Handle
450                                      *cfg,
451                                      struct GNUNET_TESTBED_Host ***hosts)
452 {
453   //struct GNUNET_TESTBED_Host **host_array;
454   struct GNUNET_TESTBED_Host *starting_host;
455   char *data;
456   char *buf;
457   char username[256];
458   char hostname[256];
459   uint64_t fs;
460   short int port;
461   int ret;
462   unsigned int offset;
463   unsigned int count;
464
465
466   GNUNET_assert (NULL != filename);
467   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
468   {
469     LOG (GNUNET_ERROR_TYPE_WARNING, _("Hosts file %s not found\n"), filename);
470     return 0;
471   }
472   if (GNUNET_OK !=
473       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
474     fs = 0;
475   if (0 == fs)
476   {
477     LOG (GNUNET_ERROR_TYPE_WARNING, _("Hosts file %s has no data\n"), filename);
478     return 0;
479   }
480   data = GNUNET_malloc (fs);
481   if (fs != GNUNET_DISK_fn_read (filename, data, fs))
482   {
483     GNUNET_free (data);
484     LOG (GNUNET_ERROR_TYPE_WARNING, _("Hosts file %s cannot be read\n"),
485          filename);
486     return 0;
487   }
488   buf = data;
489   offset = 0;
490   starting_host = NULL;
491   count = 0;
492   while (offset < (fs - 1))
493   {
494     offset++;
495     if (((data[offset] == '\n')) && (buf != &data[offset]))
496     {
497       data[offset] = '\0';
498       ret =
499           SSCANF (buf, "%255[a-zA-Z0-9_]@%255[a-zA-Z0-9.]:%5hd", username,
500                   hostname, &port);
501       if (3 == ret)
502       {
503         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
504                     "Successfully read host %s, port %d and user %s from file\n",
505                     hostname, port, username);
506         /* We store hosts in a static list; hence we only require the starting
507          * host pointer in that list to access the newly created list of hosts */
508         if (NULL == starting_host)
509           starting_host = GNUNET_TESTBED_host_create (hostname, username, cfg,
510                                                       port);
511         else
512           (void) GNUNET_TESTBED_host_create (hostname, username, cfg, port);
513         count++;
514       }
515       else
516         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
517                     "Error reading line `%s' in hostfile\n", buf);
518       buf = &data[offset + 1];
519     }
520     else if ((data[offset] == '\n') || (data[offset] == '\0'))
521       buf = &data[offset + 1];
522   }
523   GNUNET_free (data);
524   if (NULL == starting_host)
525     return 0;
526   *hosts = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Host *) * count);
527   memcpy (*hosts, &host_list[GNUNET_TESTBED_host_get_id_ (starting_host)],
528           sizeof (struct GNUNET_TESTBED_Host *) * count);
529   return count;
530 }
531
532
533 /**
534  * Resolves a hostname using getaddrinfo
535  *
536  * @param host the hostname
537  * @return the string representing the IPv4 address of the given host; NULL upon error
538  */
539 const char *
540 simple_resolve (const char *host)
541 {
542   struct addrinfo *res;
543   const struct sockaddr_in *in_addr; 
544   char *hostip;
545   struct addrinfo hint;
546   unsigned int rc;
547
548   hint.ai_family = AF_INET;     /* IPv4 */
549   hint.ai_socktype = 0;
550   hint.ai_protocol = 0;
551   hint.ai_addrlen = 0;
552   hint.ai_addr = NULL;
553   hint.ai_canonname = NULL;
554   hint.ai_next = NULL;
555   hint.ai_flags = AI_NUMERICSERV;
556   res = NULL;
557   LOG_DEBUG ("Resolving [%s]\n", host);
558   if (0 != (rc = getaddrinfo (host, "22", &hint, &res)))
559   {
560     LOG_GAI (GNUNET_ERROR_TYPE_ERROR, "getaddrinfo", rc);
561     return NULL;
562   }
563   GNUNET_assert (NULL != res);
564   GNUNET_assert (NULL != res->ai_addr);
565   GNUNET_assert (sizeof (struct sockaddr_in) == res->ai_addrlen);
566   in_addr = (const struct sockaddr_in *) res->ai_addr;
567   hostip = inet_ntoa (in_addr->sin_addr);
568   GNUNET_assert (NULL != hostip);
569   LOG_DEBUG ("Resolved [%s] to [%s]\n", host, hostip);
570   return hostip;
571 }
572
573
574 /**
575  * Loads the set of host allocated by the LoadLeveler Job Scheduler.  This
576  * function is only available when compiled with support for LoadLeveler and is
577  * used for running on the SuperMUC
578  *
579  * @param cfg the configuration to use as a template while starting a controller
580  *          on any of the loaded hosts.  Operation queue sizes specific to a host
581  *          are also read from this configuration handle
582  * @param hosts set to the hosts found in the file; caller must free this if
583  *          number of hosts returned is greater than 0
584  * @return number of hosts returned in 'hosts', 0 on error
585  */
586 unsigned int
587 GNUNET_TESTBED_hosts_load_from_loadleveler (const struct
588                                             GNUNET_CONFIGURATION_Handle *cfg,
589                                             struct GNUNET_TESTBED_Host ***hosts)
590 {
591 #if !ENABLE_LL
592   LOG (GNUNET_ERROR_TYPE_ERROR, 
593        _("The function %s is only available when compiled with (--with-ll)\n"),
594        __func__);
595   GNUNET_assert (0);
596 #else
597   const char *hostfile;
598   char *buf;
599   char *hostname;
600   char **hostnames;
601   char **hostaddrs;
602   const char *hostip;
603   struct GNUNET_TESTBED_Host **host_list;
604   ssize_t rsize;
605   uint64_t size;
606   uint64_t offset;
607   enum {
608     SCAN,
609     SKIP,
610     TRIM,
611     READHOST
612   } pstep;
613   unsigned int host;
614   unsigned int nhosts;
615   unsigned int nhostaddrs;
616   
617   if (NULL == (hostfile = getenv ("MP_SAVEHOSTFILE")))
618   {
619     GNUNET_break (0);
620     return 0;
621   }
622   if (GNUNET_SYSERR == GNUNET_DISK_file_size (hostfile, &size, GNUNET_YES,
623                                               GNUNET_YES))
624   {
625     GNUNET_break (0);
626     return 0;
627   }
628   if (0 == size)
629   {
630     GNUNET_break (0);
631     return 0;
632   }
633   buf = GNUNET_malloc (size + 1);
634   rsize = GNUNET_DISK_fn_read (hostfile, buf, (size_t) size);
635   if ( (GNUNET_SYSERR == rsize) || ((ssize_t) size != rsize) )
636   {
637     GNUNET_free (buf);
638     GNUNET_break (0);
639     return 0;
640   }
641   size++;
642   offset = 0;
643   pstep = SCAN;
644   hostname = NULL;
645   hostnames = NULL;
646   hostaddrs = NULL;
647   nhosts = 0;
648   nhostaddrs = 0;
649   while (offset < size)
650   {
651     switch (pstep)
652     {
653     case SCAN:
654       if ('!' == buf[offset])
655         pstep = SKIP;
656       else 
657         pstep = TRIM;
658       break;
659     case SKIP:
660       if ('\n' == buf[offset])
661         pstep = SCAN;
662       break;
663     case TRIM:
664       if ('!' == buf[offset])
665       {
666         pstep = SKIP;
667         break;
668       }
669       if ( (' ' == buf[offset]) 
670            || ('\t' == buf[offset])
671            || ('\r' == buf[offset]) )
672         pstep = TRIM;
673       else
674       {
675         pstep = READHOST;
676         hostname = &buf[offset];        
677       }
678       break;
679     case READHOST:
680       if (isspace (buf[offset]))
681       {
682         buf[offset] = '\0';
683         for (host = 0; host < nhosts; host++)
684           if (0 == strcmp (hostnames[host], hostname))
685             break;
686         if (host == nhosts)
687         {
688           LOG_DEBUG ("Adding host [%s]\n", hostname);
689           hostname = GNUNET_strdup (hostname);
690           GNUNET_array_append (hostnames, nhosts, hostname);
691         }
692         else
693           LOG_DEBUG ("Not adding host [%s] as it is already included\n", hostname);
694         hostname = NULL;
695         pstep = SCAN;
696       }
697       break;
698     }
699     offset++;
700   }
701   GNUNET_free_non_null (buf);
702   if (NULL == hostnames)
703     return 0;
704   for (host = 0; host < nhosts; host++)
705   {
706     hostip = simple_resolve (hostnames[host]);
707     if (NULL == hostip)
708     {
709       nhosts = 0;
710       goto cleanup;
711     }
712     GNUNET_array_append (hostaddrs, nhostaddrs, GNUNET_strdup (hostip));
713   }
714   GNUNET_assert (nhostaddrs == nhosts);
715   if (NULL == hosts)
716     goto cleanup;
717   host_list = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Host *) * nhostaddrs);
718   for (host = 0; host < nhosts; host++)
719     host_list[host] = GNUNET_TESTBED_host_create (hostaddrs[host], NULL, cfg, 0);
720   *hosts = host_list;
721
722  cleanup:
723   for (host = 0; host < nhosts; host++)
724     GNUNET_free (hostnames[host]);
725   GNUNET_free(hostnames);
726   for (host = 0; (NULL != hostaddrs) && (host < nhostaddrs); host++)
727     GNUNET_free (hostaddrs[host]);
728   GNUNET_free (hostaddrs);
729   return nhosts;
730 #endif
731 }
732
733
734 /**
735  * Destroy a host handle.  Must only be called once everything
736  * running on that host has been stopped.
737  *
738  * @param host handle to destroy
739  */
740 void
741 GNUNET_TESTBED_host_destroy (struct GNUNET_TESTBED_Host *host)
742 {
743   struct RegisteredController *rc;
744   uint32_t id;
745
746   GNUNET_assert (host->id < host_list_size);
747   GNUNET_assert (host_list[host->id] == host);
748   host_list[host->id] = NULL;
749   /* clear registered controllers list */
750   for (rc = host->rc_head; NULL != rc; rc = host->rc_head)
751   {
752     GNUNET_CONTAINER_DLL_remove (host->rc_head, host->rc_tail, rc);
753     GNUNET_free (rc);
754   }
755   GNUNET_free_non_null ((char *) host->username);
756   GNUNET_free_non_null ((char *) host->hostname);
757   GNUNET_TESTBED_operation_queue_destroy_
758       (host->opq_parallel_overlay_connect_operations);
759   GNUNET_TESTBED_SD_destroy_ (host->poc_sd);
760   GNUNET_free_non_null (host->tslots);
761   GNUNET_CONFIGURATION_destroy (host->cfg);
762   GNUNET_free (host);
763   while (host_list_size >= HOST_LIST_GROW_STEP)
764   {
765     for (id = host_list_size - 1; id > host_list_size - HOST_LIST_GROW_STEP;
766          id--)
767       if (NULL != host_list[id])
768         break;
769     if (id != host_list_size - HOST_LIST_GROW_STEP)
770       break;
771     if (NULL != host_list[id])
772       break;
773     host_list_size -= HOST_LIST_GROW_STEP;
774   }
775   host_list =
776       GNUNET_realloc (host_list,
777                       sizeof (struct GNUNET_TESTBED_Host *) * host_list_size);
778 }
779
780
781 /**
782  * Marks a host as registered with a controller
783  *
784  * @param host the host to mark
785  * @param controller the controller at which this host is registered
786  */
787 void
788 GNUNET_TESTBED_mark_host_registered_at_ (struct GNUNET_TESTBED_Host *host,
789                                          const struct GNUNET_TESTBED_Controller
790                                          *const controller)
791 {
792   struct RegisteredController *rc;
793
794   for (rc = host->rc_head; NULL != rc; rc = rc->next)
795   {
796     if (controller == rc->controller)   /* already registered at controller */
797     {
798       GNUNET_break (0);
799       return;
800     }
801   }
802   rc = GNUNET_malloc (sizeof (struct RegisteredController));
803   rc->controller = controller;
804   GNUNET_CONTAINER_DLL_insert_tail (host->rc_head, host->rc_tail, rc);
805 }
806
807
808 /**
809  * Unmarks a host registered at a controller
810  *
811  * @param host the host to unmark
812  * @param controller the controller at which this host has to be unmarked
813  */
814 void
815 GNUNET_TESTBED_deregister_host_at_ (struct GNUNET_TESTBED_Host *host,
816                                     const struct GNUNET_TESTBED_Controller
817                                     *const controller)
818 {
819   struct RegisteredController *rc;
820
821   for (rc = host->rc_head; NULL != rc; rc=rc->next)
822     if (controller == rc->controller)
823       break;
824   if (NULL == rc)
825   {
826     GNUNET_break (0);
827     return;
828   }
829   GNUNET_CONTAINER_DLL_remove (host->rc_head, host->rc_tail, rc);
830   GNUNET_free (rc);
831 }
832
833
834 /**
835  * Checks whether a host has been registered
836  *
837  * @param host the host to check
838  * @param controller the controller at which host's registration is checked
839  * @return GNUNET_YES if registered; GNUNET_NO if not
840  */
841 int
842 GNUNET_TESTBED_is_host_registered_ (const struct GNUNET_TESTBED_Host *host,
843                                     const struct GNUNET_TESTBED_Controller
844                                     *const controller)
845 {
846   struct RegisteredController *rc;
847
848   for (rc = host->rc_head; NULL != rc; rc = rc->next)
849   {
850     if (controller == rc->controller)   /* already registered at controller */
851     {
852       return GNUNET_YES;
853     }
854   }
855   return GNUNET_NO;
856 }
857
858
859 /**
860  * Handle for controller process
861  */
862 struct GNUNET_TESTBED_ControllerProc
863 {
864   /**
865    * The process handle
866    */
867   struct GNUNET_HELPER_Handle *helper;
868
869   /**
870    * The arguments used to start the helper
871    */
872   char **helper_argv;
873
874   /**
875    * The host where the helper is run
876    */
877   struct GNUNET_TESTBED_Host *host;
878
879   /**
880    * The controller error callback
881    */
882   GNUNET_TESTBED_ControllerStatusCallback cb;
883
884   /**
885    * The closure for the above callback
886    */
887   void *cls;
888
889   /**
890    * The send handle for the helper
891    */
892   struct GNUNET_HELPER_SendHandle *shandle;
893
894   /**
895    * The message corresponding to send handle
896    */
897   struct GNUNET_MessageHeader *msg;
898
899 };
900
901
902 /**
903  * Function to copy NULL terminated list of arguments
904  *
905  * @param argv the NULL terminated list of arguments. Cannot be NULL.
906  * @return the copied NULL terminated arguments
907  */
908 static char **
909 copy_argv (const char *const *argv)
910 {
911   char **argv_dup;
912   unsigned int argp;
913
914   GNUNET_assert (NULL != argv);
915   for (argp = 0; NULL != argv[argp]; argp++) ;
916   argv_dup = GNUNET_malloc (sizeof (char *) * (argp + 1));
917   for (argp = 0; NULL != argv[argp]; argp++)
918     argv_dup[argp] = strdup (argv[argp]);
919   return argv_dup;
920 }
921
922
923 /**
924  * Function to join NULL terminated list of arguments
925  *
926  * @param argv1 the NULL terminated list of arguments. Cannot be NULL.
927  * @param argv2 the NULL terminated list of arguments. Cannot be NULL.
928  * @return the joined NULL terminated arguments
929  */
930 static char **
931 join_argv (const char *const *argv1, const char *const *argv2)
932 {
933   char **argvj;
934   char *argv;
935   unsigned int carg;
936   unsigned int cnt;
937
938   carg = 0;
939   argvj = NULL;
940   for (cnt = 0; NULL != argv1[cnt]; cnt++)
941   {
942     argv = GNUNET_strdup (argv1[cnt]);
943     GNUNET_array_append (argvj, carg, argv);
944   }
945   for (cnt = 0; NULL != argv2[cnt]; cnt++)
946   {
947     argv = GNUNET_strdup (argv2[cnt]);
948     GNUNET_array_append (argvj, carg, argv);
949   }
950   GNUNET_array_append (argvj, carg, NULL);
951   return argvj;
952 }
953
954
955 /**
956  * Frees the given NULL terminated arguments
957  *
958  * @param argv the NULL terminated list of arguments
959  */
960 static void
961 free_argv (char **argv)
962 {
963   unsigned int argp;
964
965   for (argp = 0; NULL != argv[argp]; argp++)
966     GNUNET_free (argv[argp]);
967   GNUNET_free (argv);
968 }
969
970
971 /**
972  * Generates arguments for opening a remote shell. Builds up the arguments
973  * from the environment variable GNUNET_TESTBED_RSH_CMD. The variable
974  * should not mention `-p' (port) option and destination address as these will
975  * be set locally in the function from its parameteres. If the environmental
976  * variable is not found then it defaults to `ssh -o BatchMode=yes -o
977  * NoHostAuthenticationForLocalhost=yes'
978  *
979  * @param port the destination port number
980  * @param dst the destination address
981  * @return NULL terminated list of arguments
982  */
983 static char **
984 gen_rsh_args (const char *port, const char *dst)
985 {
986   static const char *default_ssh_args[] = {
987     "ssh",
988     "-o",
989     "BatchMode=yes",
990     "-o",
991     "NoHostAuthenticationForLocalhost=yes",
992     NULL
993   };
994   char **ssh_args;
995   char *ssh_cmd;
996   char *ssh_cmd_cp;
997   char *arg;
998   unsigned int cnt;
999
1000   ssh_args = NULL;
1001   if (NULL != (ssh_cmd = getenv ("GNUNET_TESTBED_RSH_CMD")))
1002   {
1003     ssh_cmd = GNUNET_strdup (ssh_cmd);
1004     ssh_cmd_cp = ssh_cmd;
1005     for (cnt = 0; NULL != (arg = strtok (ssh_cmd, " ")); ssh_cmd = NULL)
1006       GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (arg));
1007     GNUNET_free (ssh_cmd_cp);
1008   }
1009   else
1010   {
1011     ssh_args = copy_argv (default_ssh_args);
1012     cnt = (sizeof (default_ssh_args)) / (sizeof (const char *));
1013     GNUNET_array_grow (ssh_args, cnt, cnt - 1);
1014   }
1015   GNUNET_array_append (ssh_args, cnt, GNUNET_strdup ("-p"));
1016   GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (port));
1017   GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (dst));
1018   GNUNET_array_append (ssh_args, cnt, NULL);
1019   return ssh_args;
1020 }
1021
1022
1023 /**
1024  * Generates the arguments needed for executing the given binary in a remote
1025  * shell. Builds the arguments from the environmental variable
1026  * GNUNET_TETSBED_RSH_CMD_SUFFIX. If the environmental variable is not found,
1027  * only the given binary name will be present in the returned arguments
1028  *
1029  * @param append_args the arguments to append after generating the suffix
1030  *          arguments. Can be NULL; if not must be NULL terminated 'char *' array
1031  * @return NULL-terminated args
1032  */
1033 static char **
1034 gen_rsh_suffix_args (const char * const *append_args)
1035 {
1036   char **rshell_args;
1037   char *rshell_cmd;
1038   char *rshell_cmd_cp;
1039   char *arg;
1040   unsigned int cnt;
1041   unsigned int append_cnt;
1042
1043   rshell_args = NULL;
1044   cnt = 0;
1045   if (NULL != (rshell_cmd = getenv ("GNUNET_TESTBED_RSH_CMD_SUFFIX")))
1046   {
1047     rshell_cmd = GNUNET_strdup (rshell_cmd);
1048     rshell_cmd_cp = rshell_cmd;
1049     for (; NULL != (arg = strtok (rshell_cmd, " ")); rshell_cmd = NULL)
1050       GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (arg));
1051     GNUNET_free (rshell_cmd_cp);
1052   }
1053   if (NULL != append_args)
1054   {
1055     for (append_cnt = 0; NULL != append_args[append_cnt]; append_cnt++)      
1056       GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (append_args[append_cnt]));
1057   }
1058   GNUNET_array_append (rshell_args, cnt, NULL);
1059   return rshell_args;
1060 }
1061
1062
1063 /**
1064  * Functions with this signature are called whenever a
1065  * complete message is received by the tokenizer.
1066  *
1067  * Do not call GNUNET_SERVER_mst_destroy in callback
1068  *
1069  * @param cls closure
1070  * @param client identification of the client
1071  * @param message the actual message
1072  *
1073  * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing
1074  */
1075 static int
1076 helper_mst (void *cls, void *client, const struct GNUNET_MessageHeader *message)
1077 {
1078   struct GNUNET_TESTBED_ControllerProc *cp = cls;
1079   const struct GNUNET_TESTBED_HelperReply *msg;
1080   const char *hostname;
1081   char *config;
1082   uLongf config_size;
1083   uLongf xconfig_size;
1084
1085   msg = (const struct GNUNET_TESTBED_HelperReply *) message;
1086   GNUNET_assert (sizeof (struct GNUNET_TESTBED_HelperReply) <
1087                  ntohs (msg->header.size));
1088   GNUNET_assert (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY ==
1089                  ntohs (msg->header.type));
1090   config_size = (uLongf) ntohs (msg->config_size);
1091   xconfig_size =
1092       (uLongf) (ntohs (msg->header.size) -
1093                 sizeof (struct GNUNET_TESTBED_HelperReply));
1094   config = GNUNET_malloc (config_size);
1095   GNUNET_assert (Z_OK ==
1096                  uncompress ((Bytef *) config, &config_size,
1097                              (const Bytef *) &msg[1], xconfig_size));
1098   /* Replace the configuration template present in the host with the
1099      controller's running configuration */
1100   GNUNET_CONFIGURATION_destroy (cp->host->cfg);
1101   cp->host->cfg = GNUNET_CONFIGURATION_create ();
1102   GNUNET_assert (GNUNET_CONFIGURATION_deserialize
1103                  (cp->host->cfg, config, config_size, GNUNET_NO));
1104   GNUNET_free (config);
1105   if ((NULL == cp->host) ||
1106       (NULL == (hostname = GNUNET_TESTBED_host_get_hostname (cp->host))))
1107     hostname = "localhost";
1108   /* Change the hostname so that we can connect to it */
1109   GNUNET_CONFIGURATION_set_value_string (cp->host->cfg, "testbed", "hostname",
1110                                          hostname);
1111   cp->host->locked = GNUNET_NO;
1112   cp->host->controller_started = GNUNET_YES;
1113   cp->cb (cp->cls, cp->host->cfg, GNUNET_OK);
1114   return GNUNET_OK;
1115 }
1116
1117
1118 /**
1119  * Continuation function from GNUNET_HELPER_send()
1120  *
1121  * @param cls closure
1122  * @param result GNUNET_OK on success,
1123  *               GNUNET_NO if helper process died
1124  *               GNUNET_SYSERR during GNUNET_HELPER_stop
1125  */
1126 static void
1127 clear_msg (void *cls, int result)
1128 {
1129   struct GNUNET_TESTBED_ControllerProc *cp = cls;
1130
1131   GNUNET_assert (NULL != cp->shandle);
1132   cp->shandle = NULL;
1133   GNUNET_free (cp->msg);
1134 }
1135
1136
1137 /**
1138  * Callback that will be called when the helper process dies. This is not called
1139  * when the helper process is stoped using GNUNET_HELPER_stop()
1140  *
1141  * @param cls the closure from GNUNET_HELPER_start()
1142  */
1143 static void
1144 helper_exp_cb (void *cls)
1145 {
1146   struct GNUNET_TESTBED_ControllerProc *cp = cls;
1147   GNUNET_TESTBED_ControllerStatusCallback cb;
1148   void *cb_cls;
1149
1150   cb = cp->cb;
1151   cb_cls = cp->cls;
1152   cp->helper = NULL;
1153   GNUNET_TESTBED_controller_stop (cp);
1154   if (NULL != cb)
1155     cb (cb_cls, NULL, GNUNET_SYSERR);
1156 }
1157
1158
1159 /**
1160  * Starts a controller process at the given host.  The given host's configration
1161  * is used as a Template configuration to use for the remote controller; the
1162  * remote controller will be started with a slightly modified configuration
1163  * (port numbers, unix domain sockets and service home values are changed as per
1164  * TESTING library on the remote host).  The modified configuration replaces the
1165  * host's existing configuration before signalling success through the
1166  * GNUNET_TESTBED_ControllerStatusCallback()
1167  *
1168  * @param trusted_ip the ip address of the controller which will be set as TRUSTED
1169  *          HOST(all connections form this ip are permitted by the testbed) when
1170  *          starting testbed controller at host. This can either be a single ip
1171  *          address or a network address in CIDR notation.
1172  * @param host the host where the controller has to be started.  CANNOT be NULL.
1173  * @param cb function called when the controller is successfully started or
1174  *          dies unexpectedly; GNUNET_TESTBED_controller_stop shouldn't be
1175  *          called if cb is called with GNUNET_SYSERR as status. Will never be
1176  *          called in the same task as 'GNUNET_TESTBED_controller_start'
1177  *          (synchronous errors will be signalled by returning NULL). This
1178  *          parameter cannot be NULL.
1179  * @param cls closure for above callbacks
1180  * @return the controller process handle, NULL on errors
1181  */
1182 struct GNUNET_TESTBED_ControllerProc *
1183 GNUNET_TESTBED_controller_start (const char *trusted_ip,
1184                                  struct GNUNET_TESTBED_Host *host,
1185                                  GNUNET_TESTBED_ControllerStatusCallback cb,
1186                                  void *cls)
1187 {
1188   struct GNUNET_TESTBED_ControllerProc *cp;
1189   struct GNUNET_TESTBED_HelperInit *msg;
1190   const struct GNUNET_CONFIGURATION_Handle *cfg;
1191   const char *hostname;
1192   static char *const binary_argv[] = {
1193     HELPER_TESTBED_BINARY, NULL
1194   };
1195   
1196   GNUNET_assert (NULL != host);
1197   GNUNET_assert (NULL != (cfg = GNUNET_TESTBED_host_get_cfg_ (host)));
1198   hostname = NULL;
1199   API_VIOLATION (GNUNET_NO == host->locked,
1200                  "Host is already locked by a previous call to GNUNET_TESTBED_controller_start()");
1201   host->locked = GNUNET_YES;
1202   API_VIOLATION (GNUNET_NO == host->controller_started,
1203                  "Attempting to start a controller on a host which is already started a controller");
1204   cp = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_ControllerProc));
1205   if (0 == GNUNET_TESTBED_host_get_id_ (host))
1206   {
1207     cp->helper =
1208         GNUNET_HELPER_start (GNUNET_YES, HELPER_TESTBED_BINARY, binary_argv,
1209                              &helper_mst, &helper_exp_cb, cp);
1210   }
1211   else
1212   {
1213     char *helper_binary_path_args[2];
1214     char **rsh_args;
1215     char **rsh_suffix_args;
1216     const char *username;
1217     char *port;
1218     char *dst;
1219
1220     username = GNUNET_TESTBED_host_get_username_ (host);
1221     hostname = GNUNET_TESTBED_host_get_hostname (host);
1222     GNUNET_asprintf (&port, "%u", GNUNET_TESTBED_host_get_ssh_port_ (host));
1223     if (NULL == username)
1224       GNUNET_asprintf (&dst, "%s", hostname);
1225     else
1226       GNUNET_asprintf (&dst, "%s@%s", username, hostname);
1227     LOG_DEBUG ("Starting SSH to destination %s\n", dst);
1228
1229     if (GNUNET_OK !=
1230         GNUNET_CONFIGURATION_get_value_string (cfg, "testbed",
1231                                                "HELPER_BINARY_PATH",
1232                                                &helper_binary_path_args[0]))
1233       helper_binary_path_args[0] =
1234           GNUNET_OS_get_libexec_binary_path (HELPER_TESTBED_BINARY);
1235     helper_binary_path_args[1] = NULL;
1236     rsh_args = gen_rsh_args (port, dst);
1237     rsh_suffix_args = gen_rsh_suffix_args ((const char **) helper_binary_path_args);
1238     cp->helper_argv =
1239         join_argv ((const char **) rsh_args, (const char **) rsh_suffix_args);
1240     free_argv (rsh_args);
1241     free_argv (rsh_suffix_args);
1242     GNUNET_free (port);
1243     GNUNET_free (dst);
1244     cp->helper =
1245         GNUNET_HELPER_start (GNUNET_NO, cp->helper_argv[0], cp->helper_argv, &helper_mst,
1246                              &helper_exp_cb, cp);
1247     GNUNET_free (helper_binary_path_args[0]);
1248   }
1249   if (NULL == cp->helper)
1250   {
1251     if (NULL != cp->helper_argv)
1252       free_argv (cp->helper_argv);
1253     GNUNET_free (cp);
1254     return NULL;
1255   }
1256   cp->host = host;
1257   cp->cb = cb;
1258   cp->cls = cls;
1259   msg = GNUNET_TESTBED_create_helper_init_msg_ (trusted_ip, hostname, cfg);
1260   cp->msg = &msg->header;
1261   cp->shandle =
1262       GNUNET_HELPER_send (cp->helper, &msg->header, GNUNET_NO, &clear_msg, cp);
1263   if (NULL == cp->shandle)
1264   {
1265     GNUNET_free (msg);
1266     GNUNET_TESTBED_controller_stop (cp);
1267     return NULL;
1268   }
1269   return cp;
1270 }
1271
1272
1273 /**
1274  * Stop the controller process (also will terminate all peers and controllers
1275  * dependent on this controller).  This function blocks until the testbed has
1276  * been fully terminated (!). The controller status cb from
1277  * GNUNET_TESTBED_controller_start() will not be called.
1278  *
1279  * @param cproc the controller process handle
1280  */
1281 void
1282 GNUNET_TESTBED_controller_stop (struct GNUNET_TESTBED_ControllerProc *cproc)
1283 {
1284   if (NULL != cproc->shandle)
1285     GNUNET_HELPER_send_cancel (cproc->shandle);
1286   if (NULL != cproc->helper)
1287     GNUNET_HELPER_soft_stop (cproc->helper);
1288   if (NULL != cproc->helper_argv)
1289     free_argv (cproc->helper_argv);
1290   cproc->host->controller_started = GNUNET_NO;
1291   cproc->host->locked = GNUNET_NO;
1292   GNUNET_free (cproc);
1293 }
1294
1295
1296 /**
1297  * The handle for whether a host is habitable or not
1298  */
1299 struct GNUNET_TESTBED_HostHabitableCheckHandle
1300 {
1301   /**
1302    * The host to check
1303    */
1304   const struct GNUNET_TESTBED_Host *host;
1305
1306   /**
1307    * The callback to call once we have the status
1308    */
1309   GNUNET_TESTBED_HostHabitableCallback cb;
1310
1311   /**
1312    * The callback closure
1313    */
1314   void *cb_cls;
1315
1316   /**
1317    * The process handle for the SSH process
1318    */
1319   struct GNUNET_OS_Process *auxp;
1320
1321   /**
1322    * The arguments used to start the helper
1323    */
1324   char **helper_argv;
1325
1326   /**
1327    * Task id for the habitability check task
1328    */
1329   GNUNET_SCHEDULER_TaskIdentifier habitability_check_task;
1330
1331   /**
1332    * How long we wait before checking the process status. Should grow
1333    * exponentially
1334    */
1335   struct GNUNET_TIME_Relative wait_time;
1336
1337 };
1338
1339
1340 /**
1341  * Task for checking whether a host is habitable or not
1342  *
1343  * @param cls GNUNET_TESTBED_HostHabitableCheckHandle
1344  * @param tc the scheduler task context
1345  */
1346 static void
1347 habitability_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1348 {
1349   struct GNUNET_TESTBED_HostHabitableCheckHandle *h = cls;
1350   void *cb_cls;
1351   GNUNET_TESTBED_HostHabitableCallback cb;
1352   const struct GNUNET_TESTBED_Host *host;
1353   unsigned long code;
1354   enum GNUNET_OS_ProcessStatusType type;
1355   int ret;
1356
1357   h->habitability_check_task = GNUNET_SCHEDULER_NO_TASK;
1358   ret = GNUNET_OS_process_status (h->auxp, &type, &code);
1359   if (GNUNET_SYSERR == ret)
1360   {
1361     GNUNET_break (0);
1362     ret = GNUNET_NO;
1363     goto call_cb;
1364   }
1365   if (GNUNET_NO == ret)
1366   {
1367     h->wait_time = GNUNET_TIME_STD_BACKOFF (h->wait_time);
1368     h->habitability_check_task =
1369         GNUNET_SCHEDULER_add_delayed (h->wait_time, &habitability_check, h);
1370     return;
1371   }
1372   GNUNET_OS_process_destroy (h->auxp);
1373   h->auxp = NULL;
1374   ret = (0 != code) ? GNUNET_NO : GNUNET_YES;
1375
1376 call_cb:
1377   if (NULL != h->auxp)
1378     GNUNET_OS_process_destroy (h->auxp);
1379   cb = h->cb;
1380   cb_cls = h->cb_cls;
1381   host = h->host;
1382   free_argv (h->helper_argv);
1383   GNUNET_free (h);
1384   if (NULL != cb)
1385     cb (cb_cls, host, ret);
1386 }
1387
1388
1389 /**
1390  * Checks whether a host can be used to start testbed service
1391  *
1392  * @param host the host to check
1393  * @param config the configuration handle to lookup the path of the testbed
1394  *          helper
1395  * @param cb the callback to call to inform about habitability of the given host
1396  * @param cb_cls the closure for the callback
1397  * @return NULL upon any error or a handle which can be passed to
1398  *           GNUNET_TESTBED_is_host_habitable_cancel()
1399  */
1400 struct GNUNET_TESTBED_HostHabitableCheckHandle *
1401 GNUNET_TESTBED_is_host_habitable (const struct GNUNET_TESTBED_Host *host,
1402                                   const struct GNUNET_CONFIGURATION_Handle
1403                                   *config,
1404                                   GNUNET_TESTBED_HostHabitableCallback cb,
1405                                   void *cb_cls)
1406 {
1407   struct GNUNET_TESTBED_HostHabitableCheckHandle *h;
1408   char **rsh_args;
1409   char **rsh_suffix_args;
1410   char *stat_args[3];
1411   const char *hostname;
1412   char *port;
1413   char *dst;
1414
1415   h = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostHabitableCheckHandle));
1416   h->cb = cb;
1417   h->cb_cls = cb_cls;
1418   h->host = host;
1419   hostname = (NULL == host->hostname) ? "127.0.0.1" : host->hostname;
1420   if (NULL == host->username)
1421     dst = GNUNET_strdup (hostname);
1422   else
1423     GNUNET_asprintf (&dst, "%s@%s", host->username, hostname);
1424   if (GNUNET_OK !=
1425       GNUNET_CONFIGURATION_get_value_string (config, "testbed",
1426                                              "HELPER_BINARY_PATH",
1427                                              &stat_args[1]))
1428     stat_args[1] =
1429         GNUNET_OS_get_libexec_binary_path (HELPER_TESTBED_BINARY);  
1430   GNUNET_asprintf (&port, "%u", host->port);
1431   rsh_args = gen_rsh_args (port, dst);
1432   GNUNET_free (port);
1433   GNUNET_free (dst);
1434   port = NULL;
1435   dst = NULL;
1436   stat_args[0] = "stat";
1437   stat_args[2] = NULL;
1438   rsh_suffix_args = gen_rsh_suffix_args ((const char **) stat_args);
1439   GNUNET_free (stat_args[1]);
1440   h->helper_argv = join_argv ((const char **) rsh_args,
1441                               (const char **) rsh_suffix_args);
1442   free_argv (rsh_suffix_args);
1443   free_argv (rsh_args);
1444   h->auxp =
1445       GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ERR, NULL,
1446                                    NULL, h->helper_argv[0], h->helper_argv);
1447   if (NULL == h->auxp)
1448   {
1449     GNUNET_break (0);           /* Cannot exec SSH? */
1450     GNUNET_free (h);
1451     return NULL;
1452   }
1453   h->wait_time = GNUNET_TIME_STD_BACKOFF (h->wait_time);
1454   h->habitability_check_task =
1455       GNUNET_SCHEDULER_add_delayed (h->wait_time, &habitability_check, h);
1456   return h;
1457 }
1458
1459
1460 /**
1461  * Function to cancel a request started using GNUNET_TESTBED_is_host_habitable()
1462  *
1463  * @param handle the habitability check handle
1464  */
1465 void
1466 GNUNET_TESTBED_is_host_habitable_cancel (struct
1467                                          GNUNET_TESTBED_HostHabitableCheckHandle
1468                                          *handle)
1469 {
1470   GNUNET_SCHEDULER_cancel (handle->habitability_check_task);
1471   (void) GNUNET_OS_process_kill (handle->auxp, SIGTERM);
1472   (void) GNUNET_OS_process_wait (handle->auxp);
1473   GNUNET_OS_process_destroy (handle->auxp);
1474   free_argv (handle->helper_argv);
1475   GNUNET_free (handle);
1476 }
1477
1478
1479 /**
1480  * handle for host registration
1481  */
1482 struct GNUNET_TESTBED_HostRegistrationHandle
1483 {
1484   /**
1485    * The host being registered
1486    */
1487   struct GNUNET_TESTBED_Host *host;
1488
1489   /**
1490    * The controller at which this host is being registered
1491    */
1492   struct GNUNET_TESTBED_Controller *c;
1493
1494   /**
1495    * The Registartion completion callback
1496    */
1497   GNUNET_TESTBED_HostRegistrationCompletion cc;
1498
1499   /**
1500    * The closure for above callback
1501    */
1502   void *cc_cls;
1503 };
1504
1505
1506 /**
1507  * Register a host with the controller
1508  *
1509  * @param controller the controller handle
1510  * @param host the host to register
1511  * @param cc the completion callback to call to inform the status of
1512  *          registration. After calling this callback the registration handle
1513  *          will be invalid. Cannot be NULL.
1514  * @param cc_cls the closure for the cc
1515  * @return handle to the host registration which can be used to cancel the
1516  *           registration
1517  */
1518 struct GNUNET_TESTBED_HostRegistrationHandle *
1519 GNUNET_TESTBED_register_host (struct GNUNET_TESTBED_Controller *controller,
1520                               struct GNUNET_TESTBED_Host *host,
1521                               GNUNET_TESTBED_HostRegistrationCompletion cc,
1522                               void *cc_cls)
1523 {
1524   struct GNUNET_TESTBED_HostRegistrationHandle *rh;
1525   struct GNUNET_TESTBED_AddHostMessage *msg;
1526   const char *username;
1527   const char *hostname;
1528   char *config;
1529   char *cconfig;
1530   void *ptr;
1531   size_t cc_size;
1532   size_t config_size;
1533   uint16_t msg_size;
1534   uint16_t username_length;
1535   uint16_t hostname_length;
1536
1537   if (NULL != controller->rh)
1538     return NULL;
1539   hostname = GNUNET_TESTBED_host_get_hostname (host);
1540   if (GNUNET_YES == GNUNET_TESTBED_is_host_registered_ (host, controller))
1541   {
1542     LOG (GNUNET_ERROR_TYPE_WARNING, "Host hostname: %s already registered\n",
1543          (NULL == hostname) ? "localhost" : hostname);
1544     return NULL;
1545   }
1546   rh = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostRegistrationHandle));
1547   rh->host = host;
1548   rh->c = controller;
1549   GNUNET_assert (NULL != cc);
1550   rh->cc = cc;
1551   rh->cc_cls = cc_cls;
1552   controller->rh = rh;
1553   username = GNUNET_TESTBED_host_get_username_ (host);
1554   username_length = 0;
1555   if (NULL != username)
1556     username_length = strlen (username);
1557   GNUNET_assert (NULL != hostname); /* Hostname must be present */
1558   hostname_length = strlen (hostname);
1559   GNUNET_assert (NULL != host->cfg);
1560   config = GNUNET_CONFIGURATION_serialize (host->cfg, &config_size);
1561   cc_size = GNUNET_TESTBED_compress_config_ (config, config_size, &cconfig);
1562   GNUNET_free (config);
1563   msg_size = (sizeof (struct GNUNET_TESTBED_AddHostMessage));
1564   msg_size += username_length;
1565   msg_size += hostname_length;
1566   msg_size += cc_size;
1567   msg = GNUNET_malloc (msg_size);
1568   msg->header.size = htons (msg_size);
1569   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST);
1570   msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (host));
1571   msg->ssh_port = htons (GNUNET_TESTBED_host_get_ssh_port_ (host));
1572   ptr = &msg[1];
1573   if (NULL != username)
1574   {
1575     msg->username_length = htons (username_length);
1576     ptr = memcpy (ptr, username, username_length);
1577     ptr += username_length;
1578   }
1579   msg->hostname_length = htons (hostname_length);
1580   ptr = memcpy (ptr, hostname, hostname_length);
1581   ptr += hostname_length;
1582   msg->config_size = htons (config_size);
1583   ptr = memcpy (ptr, cconfig, cc_size);
1584   ptr += cc_size;
1585   GNUNET_assert ((ptr - (void *) msg) == msg_size);
1586   GNUNET_free (cconfig);
1587   GNUNET_TESTBED_queue_message_ (controller,
1588                                  (struct GNUNET_MessageHeader *) msg);
1589   return rh;
1590 }
1591
1592
1593 /**
1594  * Cancel the pending registration. Note that if the registration message is
1595  * already sent to the service the cancellation has only the effect that the
1596  * registration completion callback for the registration is never called.
1597  *
1598  * @param handle the registration handle to cancel
1599  */
1600 void
1601 GNUNET_TESTBED_cancel_registration (struct GNUNET_TESTBED_HostRegistrationHandle
1602                                     *handle)
1603 {
1604   if (handle != handle->c->rh)
1605   {
1606     GNUNET_break (0);
1607     return;
1608   }
1609   handle->c->rh = NULL;
1610   GNUNET_free (handle);
1611 }
1612
1613
1614 /**
1615  * Initializes the operation queue for parallel overlay connects
1616  *
1617  * @param h the host handle
1618  * @param npoc the number of parallel overlay connects - the queue size
1619  */
1620 void
1621 GNUNET_TESTBED_set_num_parallel_overlay_connects_ (struct
1622                                                    GNUNET_TESTBED_Host *h,
1623                                                    unsigned int npoc)
1624 {
1625   //fprintf (stderr, "%d", npoc);
1626   GNUNET_free_non_null (h->tslots);
1627   h->tslots_filled = 0;
1628   h->num_parallel_connects = npoc;
1629   h->tslots = GNUNET_malloc (npoc * sizeof (struct TimeSlot));
1630   GNUNET_TESTBED_operation_queue_reset_max_active_
1631       (h->opq_parallel_overlay_connect_operations, npoc);
1632 }
1633
1634
1635 /**
1636  * Returns a timing slot which will be exclusively locked
1637  *
1638  * @param h the host handle
1639  * @param key a pointer which is associated to the returned slot; should not be
1640  *          NULL. It serves as a key to determine the correct owner of the slot
1641  * @return the time slot index in the array of time slots in the controller
1642  *           handle
1643  */
1644 unsigned int
1645 GNUNET_TESTBED_get_tslot_ (struct GNUNET_TESTBED_Host *h, void *key)
1646 {
1647   unsigned int slot;
1648
1649   GNUNET_assert (NULL != h->tslots);
1650   GNUNET_assert (NULL != key);
1651   for (slot = 0; slot < h->num_parallel_connects; slot++)
1652     if (NULL == h->tslots[slot].key)
1653     {
1654       h->tslots[slot].key = key;
1655       return slot;
1656     }
1657   GNUNET_assert (0);            /* We should always find a free tslot */
1658 }
1659
1660
1661 /**
1662  * Decides whether any change in the number of parallel overlay connects is
1663  * necessary to adapt to the load on the system
1664  *
1665  * @param h the host handle
1666  */
1667 static void
1668 decide_npoc (struct GNUNET_TESTBED_Host *h)
1669 {
1670   struct GNUNET_TIME_Relative avg;
1671   int sd;
1672   unsigned int slot;
1673   unsigned int nvals;
1674
1675   if (h->tslots_filled != h->num_parallel_connects)
1676     return;
1677   avg = GNUNET_TIME_UNIT_ZERO;
1678   nvals = 0;
1679   for (slot = 0; slot < h->num_parallel_connects; slot++)
1680   {
1681     avg = GNUNET_TIME_relative_add (avg, h->tslots[slot].time);
1682     nvals += h->tslots[slot].nvals;
1683   }
1684   GNUNET_assert (nvals >= h->num_parallel_connects);
1685   avg = GNUNET_TIME_relative_divide (avg, nvals);
1686   GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value != avg.rel_value);
1687   sd = GNUNET_TESTBED_SD_deviation_factor_ (h->poc_sd, (unsigned int) avg.rel_value);
1688   if ( (sd <= 5) ||
1689        (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1690                                        h->num_parallel_connects)) )
1691     GNUNET_TESTBED_SD_add_data_ (h->poc_sd, (unsigned int) avg.rel_value);
1692   if (GNUNET_SYSERR == sd)
1693   {
1694     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1695                                                        h->num_parallel_connects);
1696     return;
1697   }
1698   GNUNET_assert (0 <= sd);
1699   if (0 == sd)
1700   {
1701     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1702                                                        h->num_parallel_connects
1703                                                        * 2);
1704     return;
1705   }
1706   if (1 == sd)
1707   {
1708     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1709                                                        h->num_parallel_connects
1710                                                        + 1);
1711     return;
1712   }
1713   if (1 == h->num_parallel_connects)
1714   {
1715     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h, 1);
1716     return;
1717   }
1718   if (2 == sd)
1719   {
1720     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1721                                                        h->num_parallel_connects
1722                                                        - 1);
1723     return;
1724   }
1725   GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1726                                                      h->num_parallel_connects /
1727                                                      2);
1728 }
1729
1730
1731 /**
1732  * Releases a time slot thus making it available for be used again
1733  *
1734  * @param h the host handle
1735  * @param index the index of the the time slot
1736  * @param key the key to prove ownership of the timeslot
1737  * @return GNUNET_YES if the time slot is successfully removed; GNUNET_NO if the
1738  *           time slot cannot be removed - this could be because of the index
1739  *           greater than existing number of time slots or `key' being different
1740  */
1741 int
1742 GNUNET_TESTBED_release_time_slot_ (struct GNUNET_TESTBED_Host *h,
1743                                    unsigned int index, void *key)
1744 {
1745   struct TimeSlot *slot;
1746
1747   GNUNET_assert (NULL != key);
1748   if (index >= h->num_parallel_connects)
1749     return GNUNET_NO;
1750   slot = &h->tslots[index];
1751   if (key != slot->key)
1752     return GNUNET_NO;
1753   slot->key = NULL;
1754   return GNUNET_YES;
1755 }
1756
1757
1758 /**
1759  * Function to update a time slot
1760  *
1761  * @param h the host handle
1762  * @param index the index of the time slot to update
1763  * @param key the key to identify ownership of the slot
1764  * @param time the new time
1765  * @param failed should this reading be treated as coming from a fail event
1766  */
1767 void
1768 GNUNET_TESTBED_update_time_slot_ (struct GNUNET_TESTBED_Host *h,
1769                                   unsigned int index, void *key,
1770                                   struct GNUNET_TIME_Relative time, int failed)
1771 {
1772   struct TimeSlot *slot;
1773
1774   if (GNUNET_YES == failed)
1775   {
1776     if (1 == h->num_parallel_connects)
1777     {
1778       GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h, 1);
1779       return;
1780     }
1781     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1782                                                        h->num_parallel_connects
1783                                                        - 1);
1784   }
1785   if (GNUNET_NO == GNUNET_TESTBED_release_time_slot_ (h, index, key))
1786     return;
1787   slot = &h->tslots[index];
1788   slot->nvals++;
1789   if (GNUNET_TIME_UNIT_ZERO.rel_value == slot->time.rel_value)
1790   {
1791     slot->time = time;
1792     h->tslots_filled++;
1793     decide_npoc (h);
1794     return;
1795   }
1796   slot->time = GNUNET_TIME_relative_add (slot->time, time);
1797 }
1798
1799
1800 /**
1801  * Queues the given operation in the queue for parallel overlay connects of the
1802  * given host
1803  *
1804  * @param h the host handle
1805  * @param op the operation to queue in the given host's parally overlay connect
1806  *          queue 
1807  */
1808 void
1809 GNUNET_TESTBED_host_queue_oc_ (struct GNUNET_TESTBED_Host *h, 
1810                                struct GNUNET_TESTBED_Operation *op)
1811 {  
1812   GNUNET_TESTBED_operation_queue_insert_
1813       (h->opq_parallel_overlay_connect_operations, op);
1814 }
1815
1816
1817 /**
1818  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM message from
1819  * controller (testbed service)
1820  *
1821  * @param c the controller handler
1822  * @param msg message received
1823  * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if
1824  *           not
1825  */
1826 int
1827 GNUNET_TESTBED_host_handle_addhostconfirm_ (struct GNUNET_TESTBED_Controller *c,
1828                                             const struct
1829                                             GNUNET_TESTBED_HostConfirmedMessage
1830                                             *msg)
1831 {
1832   struct GNUNET_TESTBED_HostRegistrationHandle *rh;
1833   char *emsg;
1834   uint16_t msg_size;
1835
1836   rh = c->rh;
1837   if (NULL == rh)
1838   {
1839     return GNUNET_OK;
1840   }
1841   if (GNUNET_TESTBED_host_get_id_ (rh->host) != ntohl (msg->host_id))
1842   {
1843     LOG_DEBUG ("Mismatch in host id's %u, %u of host confirm msg\n",
1844                GNUNET_TESTBED_host_get_id_ (rh->host), ntohl (msg->host_id));
1845     return GNUNET_OK;
1846   }
1847   c->rh = NULL;
1848   msg_size = ntohs (msg->header.size);
1849   if (sizeof (struct GNUNET_TESTBED_HostConfirmedMessage) == msg_size)
1850   {
1851     LOG_DEBUG ("Host %u successfully registered\n", ntohl (msg->host_id));
1852     GNUNET_TESTBED_mark_host_registered_at_ (rh->host, c);
1853     rh->cc (rh->cc_cls, NULL);
1854     GNUNET_free (rh);
1855     return GNUNET_OK;
1856   }
1857   /* We have an error message */
1858   emsg = (char *) &msg[1];
1859   if ('\0' !=
1860       emsg[msg_size - sizeof (struct GNUNET_TESTBED_HostConfirmedMessage)])
1861   {
1862     GNUNET_break (0);
1863     GNUNET_free (rh);
1864     return GNUNET_NO;
1865   }
1866   LOG (GNUNET_ERROR_TYPE_ERROR, _("Adding host %u failed with error: %s\n"),
1867        ntohl (msg->host_id), emsg);
1868   rh->cc (rh->cc_cls, emsg);
1869   GNUNET_free (rh);
1870   return GNUNET_OK;
1871 }
1872
1873 /* end of testbed_api_hosts.c */