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