+ struct RegisteredController *rc;
+
+ for (rc = host->rc_head; NULL != rc; rc = rc->next)
+ {
+ if (controller == rc->controller) /* already registered at controller */
+ {
+ GNUNET_break (0);
+ return;
+ }
+ }
+ rc = GNUNET_new (struct RegisteredController);
+ rc->controller = controller;
+ GNUNET_CONTAINER_DLL_insert_tail (host->rc_head, host->rc_tail, rc);
+}
+
+
+/**
+ * Unmarks a host registered at a controller
+ *
+ * @param host the host to unmark
+ * @param controller the controller at which this host has to be unmarked
+ */
+void
+GNUNET_TESTBED_deregister_host_at_ (struct GNUNET_TESTBED_Host *host,
+ const struct GNUNET_TESTBED_Controller
+ *const controller)
+{
+ struct RegisteredController *rc;
+
+ for (rc = host->rc_head; NULL != rc; rc=rc->next)
+ if (controller == rc->controller)
+ break;
+ if (NULL == rc)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ GNUNET_CONTAINER_DLL_remove (host->rc_head, host->rc_tail, rc);
+ GNUNET_free (rc);
+}
+
+
+/**
+ * Checks whether a host has been registered
+ *
+ * @param host the host to check
+ * @param controller the controller at which host's registration is checked
+ * @return GNUNET_YES if registered; GNUNET_NO if not
+ */
+int
+GNUNET_TESTBED_is_host_registered_ (const struct GNUNET_TESTBED_Host *host,
+ const struct GNUNET_TESTBED_Controller
+ *const controller)
+{
+ struct RegisteredController *rc;
+
+ for (rc = host->rc_head; NULL != rc; rc = rc->next)
+ {
+ if (controller == rc->controller) /* already registered at controller */
+ {
+ return GNUNET_YES;
+ }
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Handle for controller process
+ */
+struct GNUNET_TESTBED_ControllerProc
+{
+ /**
+ * The process handle
+ */
+ struct GNUNET_HELPER_Handle *helper;
+
+ /**
+ * The arguments used to start the helper
+ */
+ char **helper_argv;
+
+ /**
+ * The host where the helper is run
+ */
+ struct GNUNET_TESTBED_Host *host;
+
+ /**
+ * The controller error callback
+ */
+ GNUNET_TESTBED_ControllerStatusCallback cb;
+
+ /**
+ * The closure for the above callback
+ */
+ void *cls;
+
+ /**
+ * The send handle for the helper
+ */
+ struct GNUNET_HELPER_SendHandle *shandle;
+
+ /**
+ * The message corresponding to send handle
+ */
+ struct GNUNET_MessageHeader *msg;
+
+};
+
+
+/**
+ * Function to copy NULL terminated list of arguments
+ *
+ * @param argv the NULL terminated list of arguments. Cannot be NULL.
+ * @return the copied NULL terminated arguments
+ */
+static char **
+copy_argv (const char *const *argv)
+{
+ char **argv_dup;
+ unsigned int argp;
+
+ GNUNET_assert (NULL != argv);
+ for (argp = 0; NULL != argv[argp]; argp++) ;
+ argv_dup = GNUNET_malloc (sizeof (char *) * (argp + 1));
+ for (argp = 0; NULL != argv[argp]; argp++)
+ argv_dup[argp] = strdup (argv[argp]);
+ return argv_dup;
+}
+
+
+/**
+ * Function to join NULL terminated list of arguments
+ *
+ * @param argv1 the NULL terminated list of arguments. Cannot be NULL.
+ * @param argv2 the NULL terminated list of arguments. Cannot be NULL.
+ * @return the joined NULL terminated arguments
+ */
+static char **
+join_argv (const char *const *argv1, const char *const *argv2)
+{
+ char **argvj;
+ char *argv;
+ unsigned int carg;
+ unsigned int cnt;
+
+ carg = 0;
+ argvj = NULL;
+ for (cnt = 0; NULL != argv1[cnt]; cnt++)
+ {
+ argv = GNUNET_strdup (argv1[cnt]);
+ GNUNET_array_append (argvj, carg, argv);
+ }
+ for (cnt = 0; NULL != argv2[cnt]; cnt++)
+ {
+ argv = GNUNET_strdup (argv2[cnt]);
+ GNUNET_array_append (argvj, carg, argv);
+ }
+ GNUNET_array_append (argvj, carg, NULL);
+ return argvj;
+}
+
+
+/**
+ * Frees the given NULL terminated arguments
+ *
+ * @param argv the NULL terminated list of arguments
+ */
+static void
+free_argv (char **argv)
+{
+ unsigned int argp;
+
+ for (argp = 0; NULL != argv[argp]; argp++)
+ GNUNET_free (argv[argp]);
+ GNUNET_free (argv);
+}
+
+
+/**
+ * Generates arguments for opening a remote shell. Builds up the arguments
+ * from the environment variable GNUNET_TESTBED_RSH_CMD. The variable
+ * should not mention `-p' (port) option and destination address as these will
+ * be set locally in the function from its parameteres. If the environmental
+ * variable is not found then it defaults to `ssh -o BatchMode=yes -o
+ * NoHostAuthenticationForLocalhost=yes'
+ *
+ * @param port the destination port number
+ * @param hostname the hostname of the target host
+ * @param username the username to use while connecting to target host
+ * @return NULL terminated list of arguments
+ */
+static char **
+gen_rsh_args (const char *port, const char *hostname, const char *username)
+{
+ static const char *default_ssh_args[] = {
+ "ssh",
+ "-o",
+ "BatchMode=yes",
+ "-o",
+ "NoHostAuthenticationForLocalhost=yes",
+ "%h",
+ NULL
+ };
+ char **ssh_args;
+ char *ssh_cmd;
+ char *ssh_cmd_cp;
+ char *arg;
+ const char *new_arg;
+ unsigned int size;
+ unsigned int cnt;
+
+ ssh_args = NULL;
+ if (NULL != (ssh_cmd = getenv ("GNUNET_TESTBED_RSH_CMD")))
+ {
+ ssh_cmd = GNUNET_strdup (ssh_cmd);
+ ssh_cmd_cp = ssh_cmd;
+ for (size = 0; NULL != (arg = strtok (ssh_cmd, " ")); ssh_cmd = NULL)
+ GNUNET_array_append (ssh_args, size, GNUNET_strdup (arg));
+ GNUNET_free (ssh_cmd_cp);
+ }
+ else
+ {
+ ssh_args = copy_argv (default_ssh_args);
+ size = (sizeof (default_ssh_args)) / (sizeof (const char *));
+ GNUNET_array_grow (ssh_args, size, size - 1);
+ }
+ for (cnt = 0; cnt < size; cnt++)
+ {
+ arg = ssh_args[cnt];
+ if ('%' != arg[0])
+ continue;
+ switch (arg[1])
+ {
+ case 'p':
+ new_arg = port;
+ break;
+
+ case 'u':
+ new_arg = username;
+ break;
+
+ case 'h':
+ new_arg = hostname;
+ break;
+
+ default:
+ continue;
+ }
+ if (NULL == new_arg)
+ continue;
+ GNUNET_free (arg);
+ ssh_args[cnt] = GNUNET_strdup (new_arg);
+ }
+ GNUNET_array_append (ssh_args, size, NULL);
+ return ssh_args;
+}
+
+
+/**
+ * Generates the arguments needed for executing the given binary in a remote
+ * shell. Builds the arguments from the environmental variable
+ * GNUNET_TETSBED_RSH_CMD_SUFFIX. If the environmental variable is not found,
+ * only the given binary name will be present in the returned arguments
+ *
+ * @param append_args the arguments to append after generating the suffix
+ * arguments. Can be NULL; if not must be NULL terminated 'char *' array
+ * @return NULL-terminated args
+ */
+static char **
+gen_rsh_suffix_args (const char * const *append_args)
+{
+ char **rshell_args;
+ char *rshell_cmd;
+ char *rshell_cmd_cp;
+ char *arg;
+ unsigned int cnt;
+ unsigned int append_cnt;
+
+ rshell_args = NULL;
+ cnt = 0;
+ if (NULL != (rshell_cmd = getenv ("GNUNET_TESTBED_RSH_CMD_SUFFIX")))
+ {
+ rshell_cmd = GNUNET_strdup (rshell_cmd);
+ rshell_cmd_cp = rshell_cmd;
+ for (; NULL != (arg = strtok (rshell_cmd, " ")); rshell_cmd = NULL)
+ GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (arg));
+ GNUNET_free (rshell_cmd_cp);
+ }
+ if (NULL != append_args)
+ {
+ for (append_cnt = 0; NULL != append_args[append_cnt]; append_cnt++)
+ GNUNET_array_append (rshell_args, cnt, GNUNET_strdup (append_args[append_cnt]));
+ }
+ GNUNET_array_append (rshell_args, cnt, NULL);
+ return rshell_args;
+}
+
+
+/**
+ * Functions with this signature are called whenever a
+ * complete message is received by the tokenizer.
+ *
+ * Do not call GNUNET_SERVER_mst_destroy in callback
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param message the actual message
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing
+ */
+static int
+helper_mst (void *cls, void *client, const struct GNUNET_MessageHeader *message)
+{
+ struct GNUNET_TESTBED_ControllerProc *cp = cls;
+ const struct GNUNET_TESTBED_HelperReply *msg;
+ const char *hostname;
+ 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
+ };