+ char *config;
+ uLongf config_size;
+ uLongf xconfig_size;
+
+ msg = (const struct GNUNET_TESTBED_HelperReply *) message;
+ GNUNET_assert (sizeof (struct GNUNET_TESTBED_HelperReply) <
+ ntohs (msg->header.size));
+ GNUNET_assert (GNUNET_MESSAGE_TYPE_TESTBED_HELPER_REPLY ==
+ ntohs (msg->header.type));
+ config_size = (uLongf) ntohs (msg->config_size);
+ xconfig_size =
+ (uLongf) (ntohs (msg->header.size) -
+ sizeof (struct GNUNET_TESTBED_HelperReply));
+ config = GNUNET_malloc (config_size);
+ GNUNET_assert (Z_OK ==
+ uncompress ((Bytef *) config, &config_size,
+ (const Bytef *) &msg[1], xconfig_size));
+ /* Replace the configuration template present in the host with the
+ controller's running configuration */
+ GNUNET_CONFIGURATION_destroy (cp->host->cfg);
+ cp->host->cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_assert (GNUNET_CONFIGURATION_deserialize
+ (cp->host->cfg, config, config_size, GNUNET_NO));
+ GNUNET_free (config);
+ if (NULL == (hostname = GNUNET_TESTBED_host_get_hostname (cp->host)))
+ hostname = "localhost";
+ /* Change the hostname so that we can connect to it */
+ GNUNET_CONFIGURATION_set_value_string (cp->host->cfg, "testbed", "hostname",
+ hostname);
+ cp->host->locked = GNUNET_NO;
+ cp->host->controller_started = GNUNET_YES;
+ cp->cb (cp->cls, cp->host->cfg, GNUNET_OK);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Continuation function from GNUNET_HELPER_send()
+ *
+ * @param cls closure
+ * @param result GNUNET_OK on success,
+ * GNUNET_NO if helper process died
+ * GNUNET_SYSERR during GNUNET_HELPER_stop
+ */
+static void
+clear_msg (void *cls, int result)
+{
+ struct GNUNET_TESTBED_ControllerProc *cp = cls;
+
+ GNUNET_assert (NULL != cp->shandle);
+ cp->shandle = NULL;
+ GNUNET_free (cp->msg);
+ cp->msg = NULL;
+}
+
+
+/**
+ * Callback that will be called when the helper process dies. This is not called
+ * when the helper process is stoped using GNUNET_HELPER_stop()
+ *
+ * @param cls the closure from GNUNET_HELPER_start()
+ */
+static void
+helper_exp_cb (void *cls)
+{
+ struct GNUNET_TESTBED_ControllerProc *cp = cls;
+ GNUNET_TESTBED_ControllerStatusCallback cb;
+ void *cb_cls;
+
+ cb = cp->cb;
+ cb_cls = cp->cls;
+ cp->helper = NULL;
+ GNUNET_TESTBED_controller_stop (cp);
+ if (NULL != cb)
+ cb (cb_cls, NULL, GNUNET_SYSERR);
+}
+
+
+/**
+ * Starts a controller process at the given host. The given host's configration
+ * is used as a Template configuration to use for the remote controller; the
+ * remote controller will be started with a slightly modified configuration
+ * (port numbers, unix domain sockets and service home values are changed as per
+ * TESTING library on the remote host). The modified configuration replaces the
+ * host's existing configuration before signalling success through the
+ * GNUNET_TESTBED_ControllerStatusCallback()
+ *
+ * @param trusted_ip the ip address of the controller which will be set as TRUSTED
+ * HOST(all connections form this ip are permitted by the testbed) when
+ * starting testbed controller at host. This can either be a single ip
+ * address or a network address in CIDR notation.
+ * @param host the host where the controller has to be started. CANNOT be NULL.
+ * @param cb function called when the controller is successfully started or
+ * dies unexpectedly; GNUNET_TESTBED_controller_stop shouldn't be
+ * called if cb is called with GNUNET_SYSERR as status. Will never be
+ * called in the same task as 'GNUNET_TESTBED_controller_start'
+ * (synchronous errors will be signalled by returning NULL). This
+ * parameter cannot be NULL.
+ * @param cls closure for above callbacks
+ * @return the controller process handle, NULL on errors
+ */
+struct GNUNET_TESTBED_ControllerProc *
+GNUNET_TESTBED_controller_start (const char *trusted_ip,
+ struct GNUNET_TESTBED_Host *host,
+ GNUNET_TESTBED_ControllerStatusCallback cb,
+ void *cls)
+{
+ struct GNUNET_TESTBED_ControllerProc *cp;
+ struct GNUNET_TESTBED_HelperInit *msg;
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+ const char *hostname;
+ static char *const binary_argv[] = {
+ HELPER_TESTBED_BINARY, NULL
+ };
+
+ GNUNET_assert (NULL != host);
+ GNUNET_assert (NULL != (cfg = GNUNET_TESTBED_host_get_cfg_ (host)));
+ hostname = NULL;
+ API_VIOLATION (GNUNET_NO == host->locked,
+ "Host is already locked by a previous call to GNUNET_TESTBED_controller_start()");
+ host->locked = GNUNET_YES;
+ API_VIOLATION (GNUNET_NO == host->controller_started,
+ "Attempting to start a controller on a host which is already started a controller");
+ cp = GNUNET_new (struct GNUNET_TESTBED_ControllerProc);
+ if (0 == GNUNET_TESTBED_host_get_id_ (host))
+ {
+ cp->helper =
+ GNUNET_HELPER_start (GNUNET_YES, HELPER_TESTBED_BINARY, binary_argv,
+ &helper_mst, &helper_exp_cb, cp);
+ }
+ else
+ {
+ char *helper_binary_path_args[2];
+ char **rsh_args;
+ char **rsh_suffix_args;
+ const char *username;
+ char *port;
+ char *argstr;
+ char *aux;
+ unsigned int cnt;
+
+ username = host->username;
+ hostname = host->hostname;
+ GNUNET_asprintf (&port, "%u", host->port);
+ LOG_DEBUG ("Starting remote connection to destination %s\n", hostname);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (cfg, "testbed",
+ "HELPER_BINARY_PATH",
+ &helper_binary_path_args[0]))
+ helper_binary_path_args[0] =
+ GNUNET_OS_get_libexec_binary_path (HELPER_TESTBED_BINARY);
+ helper_binary_path_args[1] = NULL;
+ rsh_args = gen_rsh_args (port, hostname, username);
+ rsh_suffix_args = gen_rsh_suffix_args ((const char **) helper_binary_path_args);
+ cp->helper_argv =
+ join_argv ((const char **) rsh_args, (const char **) rsh_suffix_args);
+ free_argv (rsh_args);
+ free_argv (rsh_suffix_args);
+ GNUNET_free (port);
+ argstr = GNUNET_strdup ("");
+ for (cnt = 0; NULL != cp->helper_argv[cnt]; cnt++)
+ {
+ aux = argstr;
+ GNUNET_assert (0 < GNUNET_asprintf (&argstr, "%s %s", aux, cp->helper_argv[cnt]));
+ GNUNET_free (aux);
+ }
+ LOG_DEBUG ("Helper cmd str: %s\n", argstr);
+ GNUNET_free (argstr);
+ cp->helper =
+ GNUNET_HELPER_start (GNUNET_NO, cp->helper_argv[0], cp->helper_argv, &helper_mst,
+ &helper_exp_cb, cp);
+ GNUNET_free (helper_binary_path_args[0]);
+ }
+ if (NULL == cp->helper)
+ {
+ if (NULL != cp->helper_argv)
+ free_argv (cp->helper_argv);
+ GNUNET_free (cp);
+ return NULL;
+ }
+ cp->host = host;
+ cp->cb = cb;
+ cp->cls = cls;
+ msg = GNUNET_TESTBED_create_helper_init_msg_ (trusted_ip, hostname, cfg);
+ cp->msg = &msg->header;
+ cp->shandle =
+ GNUNET_HELPER_send (cp->helper, &msg->header, GNUNET_NO, &clear_msg, cp);
+ if (NULL == cp->shandle)
+ {
+ GNUNET_free (msg);
+ GNUNET_TESTBED_controller_stop (cp);
+ return NULL;
+ }
+ return cp;
+}
+
+
+/**
+ * Sends termination signal to the controller's helper process
+ *
+ * @param cproc the handle to the controller's helper process
+ */
+void
+GNUNET_TESTBED_controller_kill_ (struct GNUNET_TESTBED_ControllerProc *cproc)
+{
+ if (NULL != cproc->shandle)
+ GNUNET_HELPER_send_cancel (cproc->shandle);
+ if (NULL != cproc->helper)
+ GNUNET_HELPER_kill (cproc->helper, GNUNET_YES);
+}
+
+
+/**
+ * Cleans-up the controller's helper process handle
+ *
+ * @param cproc the handle to the controller's helper process
+ */
+void
+GNUNET_TESTBED_controller_destroy_ (struct GNUNET_TESTBED_ControllerProc *cproc)
+{
+ if (NULL != cproc->helper)
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_HELPER_wait (cproc->helper));
+ GNUNET_HELPER_destroy (cproc->helper);
+ }
+ if (NULL != cproc->helper_argv)
+ free_argv (cproc->helper_argv);
+ cproc->host->controller_started = GNUNET_NO;
+ cproc->host->locked = GNUNET_NO;
+ GNUNET_free_non_null (cproc->msg);
+ GNUNET_free (cproc);
+}
+
+
+/**
+ * Stop the controller process (also will terminate all peers and controllers
+ * dependent on this controller). This function blocks until the testbed has
+ * been fully terminated (!). The controller status cb from
+ * GNUNET_TESTBED_controller_start() will not be called.
+ *
+ * @param cproc the controller process handle
+ */
+void
+GNUNET_TESTBED_controller_stop (struct GNUNET_TESTBED_ControllerProc *cproc)
+{
+ GNUNET_TESTBED_controller_kill_ (cproc);
+ GNUNET_TESTBED_controller_destroy_ (cproc);
+}
+
+
+/**
+ * The handle for whether a host is habitable or not
+ */
+struct GNUNET_TESTBED_HostHabitableCheckHandle
+{
+ /**
+ * The host to check
+ */
+ const struct GNUNET_TESTBED_Host *host;
+
+ /**
+ * The callback to call once we have the status
+ */
+ GNUNET_TESTBED_HostHabitableCallback cb;
+
+ /**
+ * The callback closure
+ */
+ void *cb_cls;
+
+ /**
+ * The process handle for the SSH process
+ */