- remove unused code
[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_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  * Checks whether a host has been registered
810  *
811  * @param host the host to check
812  * @param controller the controller at which host's registration is checked
813  * @return GNUNET_YES if registered; GNUNET_NO if not
814  */
815 int
816 GNUNET_TESTBED_is_host_registered_ (const struct GNUNET_TESTBED_Host *host,
817                                     const struct GNUNET_TESTBED_Controller
818                                     *const controller)
819 {
820   struct RegisteredController *rc;
821
822   for (rc = host->rc_head; NULL != rc; rc = rc->next)
823   {
824     if (controller == rc->controller)   /* already registered at controller */
825     {
826       return GNUNET_YES;
827     }
828   }
829   return GNUNET_NO;
830 }
831
832
833 /**
834  * Handle for controller process
835  */
836 struct GNUNET_TESTBED_ControllerProc
837 {
838   /**
839    * The process handle
840    */
841   struct GNUNET_HELPER_Handle *helper;
842
843   /**
844    * The arguments used to start the helper
845    */
846   char **helper_argv;
847
848   /**
849    * The host where the helper is run
850    */
851   struct GNUNET_TESTBED_Host *host;
852
853   /**
854    * The controller error callback
855    */
856   GNUNET_TESTBED_ControllerStatusCallback cb;
857
858   /**
859    * The closure for the above callback
860    */
861   void *cls;
862
863   /**
864    * The send handle for the helper
865    */
866   struct GNUNET_HELPER_SendHandle *shandle;
867
868   /**
869    * The message corresponding to send handle
870    */
871   struct GNUNET_MessageHeader *msg;
872
873 };
874
875
876 /**
877  * Function to copy NULL terminated list of arguments
878  *
879  * @param argv the NULL terminated list of arguments. Cannot be NULL.
880  * @return the copied NULL terminated arguments
881  */
882 static char **
883 copy_argv (const char *const *argv)
884 {
885   char **argv_dup;
886   unsigned int argp;
887
888   GNUNET_assert (NULL != argv);
889   for (argp = 0; NULL != argv[argp]; argp++) ;
890   argv_dup = GNUNET_malloc (sizeof (char *) * (argp + 1));
891   for (argp = 0; NULL != argv[argp]; argp++)
892     argv_dup[argp] = strdup (argv[argp]);
893   return argv_dup;
894 }
895
896
897 /**
898  * Function to join NULL terminated list of arguments
899  *
900  * @param argv1 the NULL terminated list of arguments. Cannot be NULL.
901  * @param argv2 the NULL terminated list of arguments. Cannot be NULL.
902  * @return the joined NULL terminated arguments
903  */
904 static char **
905 join_argv (const char *const *argv1, const char *const *argv2)
906 {
907   char **argvj;
908   char *argv;
909   unsigned int carg;
910   unsigned int cnt;
911
912   carg = 0;
913   argvj = NULL;
914   for (cnt = 0; NULL != argv1[cnt]; cnt++)
915   {
916     argv = GNUNET_strdup (argv1[cnt]);
917     GNUNET_array_append (argvj, carg, argv);
918   }
919   for (cnt = 0; NULL != argv2[cnt]; cnt++)
920   {
921     argv = GNUNET_strdup (argv2[cnt]);
922     GNUNET_array_append (argvj, carg, argv);
923   }
924   GNUNET_array_append (argvj, carg, NULL);
925   return argvj;
926 }
927
928
929 /**
930  * Frees the given NULL terminated arguments
931  *
932  * @param argv the NULL terminated list of arguments
933  */
934 static void
935 free_argv (char **argv)
936 {
937   unsigned int argp;
938
939   for (argp = 0; NULL != argv[argp]; argp++)
940     GNUNET_free (argv[argp]);
941   GNUNET_free (argv);
942 }
943
944
945 /**
946  * Generates arguments for opening a remote shell. Builds up the arguments
947  * from the environment variable GNUNET_TESTBED_RSH_CMD. The variable
948  * should not mention `-p' (port) option and destination address as these will
949  * be set locally in the function from its parameteres. If the environmental
950  * variable is not found then it defaults to `ssh -o BatchMode=yes -o
951  * NoHostAuthenticationForLocalhost=yes'
952  *
953  * @param port the destination port number
954  * @param dst the destination address
955  * @return NULL terminated list of arguments
956  */
957 static char **
958 gen_rsh_args (const char *port, const char *dst)
959 {
960   static const char *default_ssh_args[] = {
961     "ssh",
962     "-o",
963     "BatchMode=yes",
964     "-o",
965     "NoHostAuthenticationForLocalhost=yes",
966     NULL
967   };
968   char **ssh_args;
969   char *ssh_cmd;
970   char *ssh_cmd_cp;
971   char *arg;
972   unsigned int cnt;
973
974   ssh_args = NULL;
975   if (NULL != (ssh_cmd = getenv ("GNUNET_TESTBED_RSH_CMD")))
976   {
977     ssh_cmd = GNUNET_strdup (ssh_cmd);
978     ssh_cmd_cp = ssh_cmd;
979     for (cnt = 0; NULL != (arg = strtok (ssh_cmd, " ")); ssh_cmd = NULL)
980       GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (arg));
981     GNUNET_free (ssh_cmd_cp);
982   }
983   else
984   {
985     ssh_args = copy_argv (default_ssh_args);
986     cnt = (sizeof (default_ssh_args)) / (sizeof (const char *));
987     GNUNET_array_grow (ssh_args, cnt, cnt - 1);
988   }
989   GNUNET_array_append (ssh_args, cnt, GNUNET_strdup ("-p"));
990   GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (port));
991   GNUNET_array_append (ssh_args, cnt, GNUNET_strdup (dst));
992   GNUNET_array_append (ssh_args, cnt, NULL);
993   return ssh_args;
994 }
995
996
997 /**
998  * Generates the arguments needed for executing the given binary in a remote
999  * shell. Builds the arguments from the environmental variable
1000  * GNUNET_TETSBED_RSH_CMD_SUFFIX. If the environmental variable is not found,
1001  * only the given binary name will be present in the returned arguments
1002  *
1003  * @param append_args the arguments to append after generating the suffix
1004  *          arguments. Can be NULL; if not must be NULL terminated 'char *' array
1005  * @return NULL-terminated args
1006  */
1007 static char **
1008 gen_rsh_suffix_args (const char * const *append_args)
1009 {
1010   char **rshell_args;
1011   char *rshell_cmd;
1012   char *rshell_cmd_cp;
1013   char *arg;
1014   unsigned int cnt;
1015   unsigned int append_cnt;
1016
1017   rshell_args = NULL;
1018   cnt = 0;
1019   if (NULL != (rshell_cmd = getenv ("GNUNET_TESTBED_RSH_CMD_SUFFIX")))
1020   {
1021     rshell_cmd = GNUNET_strdup (rshell_cmd);
1022     rshell_cmd_cp = rshell_cmd;
1023     for (; NULL != (arg = strtok (rshell_cmd, " ")); rshell_cmd = NULL)
1024       GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (arg));
1025     GNUNET_free (rshell_cmd_cp);
1026   }
1027   if (NULL != append_args)
1028   {
1029     for (append_cnt = 0; NULL != append_args[append_cnt]; append_cnt++)      
1030       GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (append_args[append_cnt]));
1031   }
1032   GNUNET_array_append (rshell_args, cnt, NULL);
1033   return rshell_args;
1034 }
1035
1036
1037 /**
1038  * Functions with this signature are called whenever a
1039  * complete message is received by the tokenizer.
1040  *
1041  * Do not call GNUNET_SERVER_mst_destroy in callback
1042  *
1043  * @param cls closure
1044  * @param client identification of the client
1045  * @param message the actual message
1046  *
1047  * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing
1048  */
1049 static int
1050 helper_mst (void *cls, void *client, const struct GNUNET_MessageHeader *message)
1051 {
1052   struct GNUNET_TESTBED_ControllerProc *cp = cls;
1053   const struct GNUNET_TESTBED_HelperReply *msg;
1054   const char *hostname;
1055   char *config;
1056   uLongf config_size;
1057   uLongf xconfig_size;
1058
1059   msg = (const struct GNUNET_TESTBED_HelperReply *) message;
1060   GNUNET_assert (sizeof (struct GNUNET_TESTBED_HelperReply) <
1061                  ntohs (msg->header.size));
1062   GNUNET_assert (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY ==
1063                  ntohs (msg->header.type));
1064   config_size = (uLongf) ntohs (msg->config_size);
1065   xconfig_size =
1066       (uLongf) (ntohs (msg->header.size) -
1067                 sizeof (struct GNUNET_TESTBED_HelperReply));
1068   config = GNUNET_malloc (config_size);
1069   GNUNET_assert (Z_OK ==
1070                  uncompress ((Bytef *) config, &config_size,
1071                              (const Bytef *) &msg[1], xconfig_size));
1072   /* Replace the configuration template present in the host with the
1073      controller's running configuration */
1074   GNUNET_CONFIGURATION_destroy (cp->host->cfg);
1075   cp->host->cfg = GNUNET_CONFIGURATION_create ();
1076   GNUNET_assert (GNUNET_CONFIGURATION_deserialize
1077                  (cp->host->cfg, config, config_size, GNUNET_NO));
1078   GNUNET_free (config);
1079   if ((NULL == cp->host) ||
1080       (NULL == (hostname = GNUNET_TESTBED_host_get_hostname (cp->host))))
1081     hostname = "localhost";
1082   /* Change the hostname so that we can connect to it */
1083   GNUNET_CONFIGURATION_set_value_string (cp->host->cfg, "testbed", "hostname",
1084                                          hostname);
1085   cp->host->locked = GNUNET_NO;
1086   cp->host->controller_started = GNUNET_YES;
1087   cp->cb (cp->cls, cp->host->cfg, GNUNET_OK);
1088   return GNUNET_OK;
1089 }
1090
1091
1092 /**
1093  * Continuation function from GNUNET_HELPER_send()
1094  *
1095  * @param cls closure
1096  * @param result GNUNET_OK on success,
1097  *               GNUNET_NO if helper process died
1098  *               GNUNET_SYSERR during GNUNET_HELPER_stop
1099  */
1100 static void
1101 clear_msg (void *cls, int result)
1102 {
1103   struct GNUNET_TESTBED_ControllerProc *cp = cls;
1104
1105   GNUNET_assert (NULL != cp->shandle);
1106   cp->shandle = NULL;
1107   GNUNET_free (cp->msg);
1108 }
1109
1110
1111 /**
1112  * Callback that will be called when the helper process dies. This is not called
1113  * when the helper process is stoped using GNUNET_HELPER_stop()
1114  *
1115  * @param cls the closure from GNUNET_HELPER_start()
1116  */
1117 static void
1118 helper_exp_cb (void *cls)
1119 {
1120   struct GNUNET_TESTBED_ControllerProc *cp = cls;
1121   GNUNET_TESTBED_ControllerStatusCallback cb;
1122   void *cb_cls;
1123
1124   cb = cp->cb;
1125   cb_cls = cp->cls;
1126   cp->helper = NULL;
1127   GNUNET_TESTBED_controller_stop (cp);
1128   if (NULL != cb)
1129     cb (cb_cls, NULL, GNUNET_SYSERR);
1130 }
1131
1132
1133 /**
1134  * Starts a controller process at the given host
1135  *
1136  * @param trusted_ip the ip address of the controller which will be set as TRUSTED
1137  *          HOST(all connections form this ip are permitted by the testbed) when
1138  *          starting testbed controller at host. This can either be a single ip
1139  *          address or a network address in CIDR notation.
1140  * @param host the host where the controller has to be started; NULL for
1141  *          localhost
1142  * @param cfg template configuration to use for the remote controller; the
1143  *          remote controller will be started with a slightly modified
1144  *          configuration (port numbers, unix domain sockets and service home
1145  *          values are changed as per TESTING library on the remote host)
1146  * @param cb function called when the controller is successfully started or
1147  *          dies unexpectedly; GNUNET_TESTBED_controller_stop shouldn't be
1148  *          called if cb is called with GNUNET_SYSERR as status. Will never be
1149  *          called in the same task as 'GNUNET_TESTBED_controller_start'
1150  *          (synchronous errors will be signalled by returning NULL). This
1151  *          parameter cannot be NULL.
1152  * @param cls closure for above callbacks
1153  * @return the controller process handle, NULL on errors
1154  */
1155 struct GNUNET_TESTBED_ControllerProc *
1156 GNUNET_TESTBED_controller_start (const char *trusted_ip,
1157                                  struct GNUNET_TESTBED_Host *host,
1158                                  const struct GNUNET_CONFIGURATION_Handle *cfg,
1159                                  GNUNET_TESTBED_ControllerStatusCallback cb,
1160                                  void *cls)
1161 {
1162   struct GNUNET_TESTBED_ControllerProc *cp;
1163   struct GNUNET_TESTBED_HelperInit *msg;
1164   const char *hostname;
1165
1166   static char *const binary_argv[] = {
1167     HELPER_TESTBED_BINARY, NULL
1168   };
1169
1170   hostname = NULL;
1171   API_VIOLATION (GNUNET_NO == host->locked,
1172                  "Host is already locked by a previous call to GNUNET_TESTBED_controller_start()");
1173   host->locked = GNUNET_YES;
1174   API_VIOLATION (GNUNET_NO == host->controller_started,
1175                  "Attempting to start a controller on a host which is already started a controller");
1176   cp = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_ControllerProc));
1177   if ((NULL == host) || (0 == GNUNET_TESTBED_host_get_id_ (host)))
1178   {
1179     cp->helper =
1180         GNUNET_HELPER_start (GNUNET_YES, HELPER_TESTBED_BINARY, binary_argv,
1181                              &helper_mst, &helper_exp_cb, cp);
1182   }
1183   else
1184   {
1185     char *helper_binary_path_args[2];
1186     char **rsh_args;
1187     char **rsh_suffix_args;
1188     const char *username;
1189     char *port;
1190     char *dst;
1191
1192     username = GNUNET_TESTBED_host_get_username_ (host);
1193     hostname = GNUNET_TESTBED_host_get_hostname (host);
1194     GNUNET_asprintf (&port, "%u", GNUNET_TESTBED_host_get_ssh_port_ (host));
1195     if (NULL == username)
1196       GNUNET_asprintf (&dst, "%s", hostname);
1197     else
1198       GNUNET_asprintf (&dst, "%s@%s", username, hostname);
1199     LOG_DEBUG ("Starting SSH to destination %s\n", dst);
1200
1201     if (GNUNET_OK !=
1202         GNUNET_CONFIGURATION_get_value_string (cfg, "testbed",
1203                                                "HELPER_BINARY_PATH",
1204                                                &helper_binary_path_args[0]))
1205       helper_binary_path_args[0] =
1206           GNUNET_OS_get_libexec_binary_path (HELPER_TESTBED_BINARY);
1207     helper_binary_path_args[1] = NULL;
1208     rsh_args = gen_rsh_args (port, dst);
1209     rsh_suffix_args = gen_rsh_suffix_args ((const char **) helper_binary_path_args);
1210     cp->helper_argv =
1211         join_argv ((const char **) rsh_args, (const char **) rsh_suffix_args);
1212     free_argv (rsh_args);
1213     free_argv (rsh_suffix_args);
1214     GNUNET_free (port);
1215     GNUNET_free (dst);
1216     cp->helper =
1217         GNUNET_HELPER_start (GNUNET_NO, cp->helper_argv[0], cp->helper_argv, &helper_mst,
1218                              &helper_exp_cb, cp);
1219     GNUNET_free (helper_binary_path_args[0]);
1220   }
1221   if (NULL == cp->helper)
1222   {
1223     if (NULL != cp->helper_argv)
1224       free_argv (cp->helper_argv);
1225     GNUNET_free (cp);
1226     return NULL;
1227   }
1228   cp->host = host;
1229   cp->cb = cb;
1230   cp->cls = cls;
1231   msg = GNUNET_TESTBED_create_helper_init_msg_ (trusted_ip, hostname, cfg);
1232   cp->msg = &msg->header;
1233   cp->shandle =
1234       GNUNET_HELPER_send (cp->helper, &msg->header, GNUNET_NO, &clear_msg, cp);
1235   if (NULL == cp->shandle)
1236   {
1237     GNUNET_free (msg);
1238     GNUNET_TESTBED_controller_stop (cp);
1239     return NULL;
1240   }
1241   return cp;
1242 }
1243
1244
1245 /**
1246  * Stop the controller process (also will terminate all peers and controllers
1247  * dependent on this controller).  This function blocks until the testbed has
1248  * been fully terminated (!). The controller status cb from
1249  * GNUNET_TESTBED_controller_start() will not be called.
1250  *
1251  * @param cproc the controller process handle
1252  */
1253 void
1254 GNUNET_TESTBED_controller_stop (struct GNUNET_TESTBED_ControllerProc *cproc)
1255 {
1256   if (NULL != cproc->shandle)
1257     GNUNET_HELPER_send_cancel (cproc->shandle);
1258   if (NULL != cproc->helper)
1259     GNUNET_HELPER_soft_stop (cproc->helper);
1260   if (NULL != cproc->helper_argv)
1261     free_argv (cproc->helper_argv);
1262   cproc->host->controller_started = GNUNET_NO;
1263   cproc->host->locked = GNUNET_NO;
1264   GNUNET_free (cproc);
1265 }
1266
1267
1268 /**
1269  * The handle for whether a host is habitable or not
1270  */
1271 struct GNUNET_TESTBED_HostHabitableCheckHandle
1272 {
1273   /**
1274    * The host to check
1275    */
1276   const struct GNUNET_TESTBED_Host *host;
1277
1278   /**
1279    * The callback to call once we have the status
1280    */
1281   GNUNET_TESTBED_HostHabitableCallback cb;
1282
1283   /**
1284    * The callback closure
1285    */
1286   void *cb_cls;
1287
1288   /**
1289    * The process handle for the SSH process
1290    */
1291   struct GNUNET_OS_Process *auxp;
1292
1293   /**
1294    * The arguments used to start the helper
1295    */
1296   char **helper_argv;
1297
1298   /**
1299    * Task id for the habitability check task
1300    */
1301   GNUNET_SCHEDULER_TaskIdentifier habitability_check_task;
1302
1303   /**
1304    * How long we wait before checking the process status. Should grow
1305    * exponentially
1306    */
1307   struct GNUNET_TIME_Relative wait_time;
1308
1309 };
1310
1311
1312 /**
1313  * Task for checking whether a host is habitable or not
1314  *
1315  * @param cls GNUNET_TESTBED_HostHabitableCheckHandle
1316  * @param tc the scheduler task context
1317  */
1318 static void
1319 habitability_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1320 {
1321   struct GNUNET_TESTBED_HostHabitableCheckHandle *h = cls;
1322   void *cb_cls;
1323   GNUNET_TESTBED_HostHabitableCallback cb;
1324   const struct GNUNET_TESTBED_Host *host;
1325   unsigned long code;
1326   enum GNUNET_OS_ProcessStatusType type;
1327   int ret;
1328
1329   h->habitability_check_task = GNUNET_SCHEDULER_NO_TASK;
1330   ret = GNUNET_OS_process_status (h->auxp, &type, &code);
1331   if (GNUNET_SYSERR == ret)
1332   {
1333     GNUNET_break (0);
1334     ret = GNUNET_NO;
1335     goto call_cb;
1336   }
1337   if (GNUNET_NO == ret)
1338   {
1339     h->wait_time = GNUNET_TIME_STD_BACKOFF (h->wait_time);
1340     h->habitability_check_task =
1341         GNUNET_SCHEDULER_add_delayed (h->wait_time, &habitability_check, h);
1342     return;
1343   }
1344   GNUNET_OS_process_destroy (h->auxp);
1345   h->auxp = NULL;
1346   ret = (0 != code) ? GNUNET_NO : GNUNET_YES;
1347
1348 call_cb:
1349   if (NULL != h->auxp)
1350     GNUNET_OS_process_destroy (h->auxp);
1351   cb = h->cb;
1352   cb_cls = h->cb_cls;
1353   host = h->host;
1354   GNUNET_free (h);
1355   if (NULL != cb)
1356     cb (cb_cls, host, ret);
1357 }
1358
1359
1360 /**
1361  * Checks whether a host can be used to start testbed service
1362  *
1363  * @param host the host to check
1364  * @param config the configuration handle to lookup the path of the testbed
1365  *          helper
1366  * @param cb the callback to call to inform about habitability of the given host
1367  * @param cb_cls the closure for the callback
1368  * @return NULL upon any error or a handle which can be passed to
1369  *           GNUNET_TESTBED_is_host_habitable_cancel()
1370  */
1371 struct GNUNET_TESTBED_HostHabitableCheckHandle *
1372 GNUNET_TESTBED_is_host_habitable (const struct GNUNET_TESTBED_Host *host,
1373                                   const struct GNUNET_CONFIGURATION_Handle
1374                                   *config,
1375                                   GNUNET_TESTBED_HostHabitableCallback cb,
1376                                   void *cb_cls)
1377 {
1378   struct GNUNET_TESTBED_HostHabitableCheckHandle *h;
1379   char **rsh_args;
1380   char **rsh_suffix_args;
1381   char *stat_args[3];
1382   const char *hostname;
1383   char *port;
1384   char *dst;
1385
1386   h = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostHabitableCheckHandle));
1387   h->cb = cb;
1388   h->cb_cls = cb_cls;
1389   h->host = host;
1390   hostname = (NULL == host->hostname) ? "127.0.0.1" : host->hostname;
1391   if (NULL == host->username)
1392     dst = GNUNET_strdup (hostname);
1393   else
1394     GNUNET_asprintf (&dst, "%s@%s", host->username, hostname);
1395   if (GNUNET_OK !=
1396       GNUNET_CONFIGURATION_get_value_string (config, "testbed",
1397                                              "HELPER_BINARY_PATH",
1398                                              &stat_args[1]))
1399     stat_args[1] =
1400         GNUNET_OS_get_libexec_binary_path (HELPER_TESTBED_BINARY);  
1401   GNUNET_asprintf (&port, "%u", host->port);
1402   rsh_args = gen_rsh_args (port, dst);
1403   GNUNET_free (port);
1404   GNUNET_free (dst);
1405   port = NULL;
1406   dst = NULL;
1407   stat_args[0] = "stat";
1408   stat_args[2] = NULL;
1409   rsh_suffix_args = gen_rsh_suffix_args ((const char **) stat_args);
1410   h->helper_argv = join_argv ((const char **) rsh_args,
1411                               (const char **) rsh_suffix_args);
1412   GNUNET_free (rsh_suffix_args);
1413   GNUNET_free (rsh_args);
1414   h->auxp =
1415       GNUNET_OS_start_process_vap (GNUNET_NO, GNUNET_OS_INHERIT_STD_ERR, NULL,
1416                                    NULL, h->helper_argv[0], h->helper_argv);
1417   if (NULL == h->auxp)
1418   {
1419     GNUNET_break (0);           /* Cannot exec SSH? */
1420     GNUNET_free (h);
1421     return NULL;
1422   }
1423   h->wait_time = GNUNET_TIME_STD_BACKOFF (h->wait_time);
1424   h->habitability_check_task =
1425       GNUNET_SCHEDULER_add_delayed (h->wait_time, &habitability_check, h);
1426   return h;
1427 }
1428
1429
1430 /**
1431  * Function to cancel a request started using GNUNET_TESTBED_is_host_habitable()
1432  *
1433  * @param handle the habitability check handle
1434  */
1435 void
1436 GNUNET_TESTBED_is_host_habitable_cancel (struct
1437                                          GNUNET_TESTBED_HostHabitableCheckHandle
1438                                          *handle)
1439 {
1440   GNUNET_SCHEDULER_cancel (handle->habitability_check_task);
1441   (void) GNUNET_OS_process_kill (handle->auxp, SIGTERM);
1442   (void) GNUNET_OS_process_wait (handle->auxp);
1443   GNUNET_OS_process_destroy (handle->auxp);
1444   free_argv (handle->helper_argv);
1445   GNUNET_free (handle);
1446 }
1447
1448
1449 /**
1450  * handle for host registration
1451  */
1452 struct GNUNET_TESTBED_HostRegistrationHandle
1453 {
1454   /**
1455    * The host being registered
1456    */
1457   struct GNUNET_TESTBED_Host *host;
1458
1459   /**
1460    * The controller at which this host is being registered
1461    */
1462   struct GNUNET_TESTBED_Controller *c;
1463
1464   /**
1465    * The Registartion completion callback
1466    */
1467   GNUNET_TESTBED_HostRegistrationCompletion cc;
1468
1469   /**
1470    * The closure for above callback
1471    */
1472   void *cc_cls;
1473 };
1474
1475
1476 /**
1477  * Register a host with the controller
1478  *
1479  * @param controller the controller handle
1480  * @param host the host to register
1481  * @param cc the completion callback to call to inform the status of
1482  *          registration. After calling this callback the registration handle
1483  *          will be invalid. Cannot be NULL.
1484  * @param cc_cls the closure for the cc
1485  * @return handle to the host registration which can be used to cancel the
1486  *           registration
1487  */
1488 struct GNUNET_TESTBED_HostRegistrationHandle *
1489 GNUNET_TESTBED_register_host (struct GNUNET_TESTBED_Controller *controller,
1490                               struct GNUNET_TESTBED_Host *host,
1491                               GNUNET_TESTBED_HostRegistrationCompletion cc,
1492                               void *cc_cls)
1493 {
1494   struct GNUNET_TESTBED_HostRegistrationHandle *rh;
1495   struct GNUNET_TESTBED_AddHostMessage *msg;
1496   const char *username;
1497   const char *hostname;
1498   char *config;
1499   char *cconfig;
1500   void *ptr;
1501   size_t cc_size;
1502   size_t config_size;
1503   uint16_t msg_size;
1504   uint16_t username_length;
1505   uint16_t hostname_length;
1506
1507   if (NULL != controller->rh)
1508     return NULL;
1509   hostname = GNUNET_TESTBED_host_get_hostname (host);
1510   if (GNUNET_YES == GNUNET_TESTBED_is_host_registered_ (host, controller))
1511   {
1512     LOG (GNUNET_ERROR_TYPE_WARNING, "Host hostname: %s already registered\n",
1513          (NULL == hostname) ? "localhost" : hostname);
1514     return NULL;
1515   }
1516   rh = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostRegistrationHandle));
1517   rh->host = host;
1518   rh->c = controller;
1519   GNUNET_assert (NULL != cc);
1520   rh->cc = cc;
1521   rh->cc_cls = cc_cls;
1522   controller->rh = rh;
1523   username = GNUNET_TESTBED_host_get_username_ (host);
1524   username_length = 0;
1525   if (NULL != username)
1526     username_length = strlen (username);
1527   GNUNET_assert (NULL != hostname); /* Hostname must be present */
1528   hostname_length = strlen (hostname);
1529   GNUNET_assert (NULL != host->cfg);
1530   config = GNUNET_CONFIGURATION_serialize (host->cfg, &config_size);
1531   cc_size = GNUNET_TESTBED_compress_config_ (config, config_size, &cconfig);
1532   GNUNET_free (config);
1533   msg_size = (sizeof (struct GNUNET_TESTBED_AddHostMessage));
1534   msg_size += username_length;
1535   msg_size += hostname_length;
1536   msg_size += cc_size;
1537   msg = GNUNET_malloc (msg_size);
1538   msg->header.size = htons (msg_size);
1539   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADD_HOST);
1540   msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (host));
1541   msg->ssh_port = htons (GNUNET_TESTBED_host_get_ssh_port_ (host));
1542   ptr = &msg[1];
1543   if (NULL != username)
1544   {
1545     msg->username_length = htons (username_length);
1546     ptr = memcpy (ptr, username, username_length);
1547     ptr += username_length;
1548   }
1549   msg->hostname_length = htons (hostname_length);
1550   ptr = memcpy (ptr, hostname, hostname_length);
1551   ptr += hostname_length;
1552   msg->config_size = htons (config_size);
1553   ptr = memcpy (ptr, cconfig, cc_size);
1554   ptr += cc_size;
1555   GNUNET_assert ((ptr - (void *) msg) == msg_size);
1556   GNUNET_free (cconfig);
1557   GNUNET_TESTBED_queue_message_ (controller,
1558                                  (struct GNUNET_MessageHeader *) msg);
1559   return rh;
1560 }
1561
1562
1563 /**
1564  * Cancel the pending registration. Note that if the registration message is
1565  * already sent to the service the cancellation has only the effect that the
1566  * registration completion callback for the registration is never called.
1567  *
1568  * @param handle the registration handle to cancel
1569  */
1570 void
1571 GNUNET_TESTBED_cancel_registration (struct GNUNET_TESTBED_HostRegistrationHandle
1572                                     *handle)
1573 {
1574   if (handle != handle->c->rh)
1575   {
1576     GNUNET_break (0);
1577     return;
1578   }
1579   handle->c->rh = NULL;
1580   GNUNET_free (handle);
1581 }
1582
1583
1584 /**
1585  * Initializes the operation queue for parallel overlay connects
1586  *
1587  * @param h the host handle
1588  * @param npoc the number of parallel overlay connects - the queue size
1589  */
1590 void
1591 GNUNET_TESTBED_set_num_parallel_overlay_connects_ (struct
1592                                                    GNUNET_TESTBED_Host *h,
1593                                                    unsigned int npoc)
1594 {
1595   //fprintf (stderr, "%d", npoc);
1596   GNUNET_free_non_null (h->tslots);
1597   h->tslots_filled = 0;
1598   h->num_parallel_connects = npoc;
1599   h->tslots = GNUNET_malloc (npoc * sizeof (struct TimeSlot));
1600   GNUNET_TESTBED_operation_queue_reset_max_active_
1601       (h->opq_parallel_overlay_connect_operations, npoc);
1602 }
1603
1604
1605 /**
1606  * Returns a timing slot which will be exclusively locked
1607  *
1608  * @param h the host handle
1609  * @param key a pointer which is associated to the returned slot; should not be
1610  *          NULL. It serves as a key to determine the correct owner of the slot
1611  * @return the time slot index in the array of time slots in the controller
1612  *           handle
1613  */
1614 unsigned int
1615 GNUNET_TESTBED_get_tslot_ (struct GNUNET_TESTBED_Host *h, void *key)
1616 {
1617   unsigned int slot;
1618
1619   GNUNET_assert (NULL != h->tslots);
1620   GNUNET_assert (NULL != key);
1621   for (slot = 0; slot < h->num_parallel_connects; slot++)
1622     if (NULL == h->tslots[slot].key)
1623     {
1624       h->tslots[slot].key = key;
1625       return slot;
1626     }
1627   GNUNET_assert (0);            /* We should always find a free tslot */
1628 }
1629
1630
1631 /**
1632  * Decides whether any change in the number of parallel overlay connects is
1633  * necessary to adapt to the load on the system
1634  *
1635  * @param h the host handle
1636  */
1637 static void
1638 decide_npoc (struct GNUNET_TESTBED_Host *h)
1639 {
1640   struct GNUNET_TIME_Relative avg;
1641   int sd;
1642   unsigned int slot;
1643   unsigned int nvals;
1644
1645   if (h->tslots_filled != h->num_parallel_connects)
1646     return;
1647   avg = GNUNET_TIME_UNIT_ZERO;
1648   nvals = 0;
1649   for (slot = 0; slot < h->num_parallel_connects; slot++)
1650   {
1651     avg = GNUNET_TIME_relative_add (avg, h->tslots[slot].time);
1652     nvals += h->tslots[slot].nvals;
1653   }
1654   GNUNET_assert (nvals >= h->num_parallel_connects);
1655   avg = GNUNET_TIME_relative_divide (avg, nvals);
1656   GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value != avg.rel_value);
1657   sd = GNUNET_TESTBED_SD_deviation_factor_ (h->poc_sd, (unsigned int) avg.rel_value);
1658   if ( (sd <= 5) ||
1659        (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1660                                        h->num_parallel_connects)) )
1661     GNUNET_TESTBED_SD_add_data_ (h->poc_sd, (unsigned int) avg.rel_value);
1662   if (GNUNET_SYSERR == sd)
1663   {
1664     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1665                                                        h->num_parallel_connects);
1666     return;
1667   }
1668   GNUNET_assert (0 <= sd);
1669   if (0 == sd)
1670   {
1671     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1672                                                        h->num_parallel_connects
1673                                                        * 2);
1674     return;
1675   }
1676   if (1 == sd)
1677   {
1678     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1679                                                        h->num_parallel_connects
1680                                                        + 1);
1681     return;
1682   }
1683   if (1 == h->num_parallel_connects)
1684   {
1685     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h, 1);
1686     return;
1687   }
1688   if (2 == sd)
1689   {
1690     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1691                                                        h->num_parallel_connects
1692                                                        - 1);
1693     return;
1694   }
1695   GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1696                                                      h->num_parallel_connects /
1697                                                      2);
1698 }
1699
1700
1701 /**
1702  * Releases a time slot thus making it available for be used again
1703  *
1704  * @param h the host handle
1705  * @param index the index of the the time slot
1706  * @param key the key to prove ownership of the timeslot
1707  * @return GNUNET_YES if the time slot is successfully removed; GNUNET_NO if the
1708  *           time slot cannot be removed - this could be because of the index
1709  *           greater than existing number of time slots or `key' being different
1710  */
1711 int
1712 GNUNET_TESTBED_release_time_slot_ (struct GNUNET_TESTBED_Host *h,
1713                                    unsigned int index, void *key)
1714 {
1715   struct TimeSlot *slot;
1716
1717   GNUNET_assert (NULL != key);
1718   if (index >= h->num_parallel_connects)
1719     return GNUNET_NO;
1720   slot = &h->tslots[index];
1721   if (key != slot->key)
1722     return GNUNET_NO;
1723   slot->key = NULL;
1724   return GNUNET_YES;
1725 }
1726
1727
1728 /**
1729  * Function to update a time slot
1730  *
1731  * @param h the host handle
1732  * @param index the index of the time slot to update
1733  * @param key the key to identify ownership of the slot
1734  * @param time the new time
1735  * @param failed should this reading be treated as coming from a fail event
1736  */
1737 void
1738 GNUNET_TESTBED_update_time_slot_ (struct GNUNET_TESTBED_Host *h,
1739                                   unsigned int index, void *key,
1740                                   struct GNUNET_TIME_Relative time, int failed)
1741 {
1742   struct TimeSlot *slot;
1743
1744   if (GNUNET_YES == failed)
1745   {
1746     if (1 == h->num_parallel_connects)
1747     {
1748       GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h, 1);
1749       return;
1750     }
1751     GNUNET_TESTBED_set_num_parallel_overlay_connects_ (h,
1752                                                        h->num_parallel_connects
1753                                                        - 1);
1754   }
1755   if (GNUNET_NO == GNUNET_TESTBED_release_time_slot_ (h, index, key))
1756     return;
1757   slot = &h->tslots[index];
1758   slot->nvals++;
1759   if (GNUNET_TIME_UNIT_ZERO.rel_value == slot->time.rel_value)
1760   {
1761     slot->time = time;
1762     h->tslots_filled++;
1763     decide_npoc (h);
1764     return;
1765   }
1766   slot->time = GNUNET_TIME_relative_add (slot->time, time);
1767 }
1768
1769
1770 /**
1771  * Queues the given operation in the queue for parallel overlay connects of the
1772  * given host
1773  *
1774  * @param h the host handle
1775  * @param op the operation to queue in the given host's parally overlay connect
1776  *          queue 
1777  */
1778 void
1779 GNUNET_TESTBED_host_queue_oc_ (struct GNUNET_TESTBED_Host *h, 
1780                                struct GNUNET_TESTBED_Operation *op)
1781 {  
1782   GNUNET_TESTBED_operation_queue_insert_
1783       (h->opq_parallel_overlay_connect_operations, op);
1784 }
1785
1786
1787 /**
1788  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM message from
1789  * controller (testbed service)
1790  *
1791  * @param c the controller handler
1792  * @param msg message received
1793  * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if
1794  *           not
1795  */
1796 int
1797 GNUNET_TESTBED_host_handle_addhostconfirm_ (struct GNUNET_TESTBED_Controller *c,
1798                                             const struct
1799                                             GNUNET_TESTBED_HostConfirmedMessage
1800                                             *msg)
1801 {
1802   struct GNUNET_TESTBED_HostRegistrationHandle *rh;
1803   char *emsg;
1804   uint16_t msg_size;
1805
1806   rh = c->rh;
1807   if (NULL == rh)
1808   {
1809     return GNUNET_OK;
1810   }
1811   if (GNUNET_TESTBED_host_get_id_ (rh->host) != ntohl (msg->host_id))
1812   {
1813     LOG_DEBUG ("Mismatch in host id's %u, %u of host confirm msg\n",
1814                GNUNET_TESTBED_host_get_id_ (rh->host), ntohl (msg->host_id));
1815     return GNUNET_OK;
1816   }
1817   c->rh = NULL;
1818   msg_size = ntohs (msg->header.size);
1819   if (sizeof (struct GNUNET_TESTBED_HostConfirmedMessage) == msg_size)
1820   {
1821     LOG_DEBUG ("Host %u successfully registered\n", ntohl (msg->host_id));
1822     GNUNET_TESTBED_mark_host_registered_at_ (rh->host, c);
1823     rh->cc (rh->cc_cls, NULL);
1824     GNUNET_free (rh);
1825     return GNUNET_OK;
1826   }
1827   /* We have an error message */
1828   emsg = (char *) &msg[1];
1829   if ('\0' !=
1830       emsg[msg_size - sizeof (struct GNUNET_TESTBED_HostConfirmedMessage)])
1831   {
1832     GNUNET_break (0);
1833     GNUNET_free (rh);
1834     return GNUNET_NO;
1835   }
1836   LOG (GNUNET_ERROR_TYPE_ERROR, _("Adding host %u failed with error: %s\n"),
1837        ntohl (msg->host_id), emsg);
1838   rh->cc (rh->cc_cls, emsg);
1839   GNUNET_free (rh);
1840   return GNUNET_OK;
1841 }
1842
1843 /* end of testbed_api_hosts.c */