uint16_t port);
+
+/**
+ * Create a host to run peers and controllers on. This function is used
+ * if a peer learns about a host via IPC between controllers (and thus
+ * some higher-level controller has already determined the unique IDs).
+ *
+ * @param id global host ID assigned to the host; 0 is
+ * reserved to always mean 'localhost'
+ * @param hostname name of the host, use "NULL" for localhost
+ * @param username username to use for the login; may be NULL
+ * @param port port number to use for ssh; use 0 to let ssh decide
+ * @return handle to the host, NULL on error
+ */
+struct GNUNET_TESTBED_Host *
+GNUNET_TESTBED_host_create_with_id (uint32_t id,
+ const char *hostname,
+ const char *username,
+ uint16_t port);
+
+
/**
* Load a set of hosts from a configuration file.
*
...);
-
/**
* Ask the testbed controller to write the current overlay topology to
* a file. Naturally, the file will only contain a snapshot as the
};
+/**
+ * Wrapped stdin.
+ */
+static struct GNUNET_DISK_FileHandle *fh;
+
/**
* The master context; generated with the first INIT message
*/
host = GNUNET_TESTBED_host_create (hostname, username, ntohs
(msg->ssh_port));
/* Store host in a hashmap? But the host_id will be different */
+ /* hashmap? maybe array? 4-8 bytes/host and O(1) lookup vs.
+ > 80 bytes for hash map with slightly worse lookup; only
+ if we really get a tiny fraction of the hosts, the hash
+ map would result in any savings... (GNUNET_array_grow) */
}
+
/**
* Task to clean up and shutdown nicely
*
shutdown_task (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
+ shutdown_task_id = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_SCHEDULER_shutdown ();
LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down testbed service\n");
+ if (NULL != fh)
+ {
+ GNUNET_DISK_file_close (fh);
+ fh = NULL;
+ }
GNUNET_free_non_null (master_context);
+ master_context = NULL;
}
{
LOG (GNUNET_ERROR_TYPE_DEBUG, "Master client disconnected\n");
GNUNET_SERVER_client_drop (client);
- GNUNET_SCHEDULER_cancel (shutdown_task_id);
- shutdown_task_id =
- GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
+ /* should not be needed as we're terminated by failure to read
+ from stdin, but if stdin fails for some reason, this shouldn't
+ hurt for now --- might need to revise this later if we ever
+ decide that master connections might be temporarily down
+ for some reason */
+ GNUNET_SCHEDULER_shutdown ();
}
}
message_handlers);
GNUNET_SERVER_disconnect_notify (server,
&client_disconnect_cb,
- NULL);
- shutdown_task_id =
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
- &shutdown_task,
- NULL);
+ NULL);
+ fh = GNUNET_DISK_get_handle_from_native (stdin);
+ if (NULL == fh)
+ shutdown_task_id =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &shutdown_task,
+ NULL);
+ else
+ shutdown_task_id =
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ fh,
+ &shutdown_task,
+ NULL);
}
* The controller event mask
*/
uint64_t event_mask;
+
+ /**
+ * Did we start the receive loop yet?
+ */
+ int in_receive;
};
+
+/**
+ * Handler for messages from controller (testbed service)
+ *
+ * @param cls the controller handler
+ * @param msg message received, NULL on timeout or fatal error
+ */
+static void
+message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+ struct GNUNET_TESTBED_Controller *c = cls;
+
+ /* FIXME: Add checks for message integrity */
+ switch (ntohs (msg->type))
+ {
+ default:
+ GNUNET_break (0);
+ }
+ GNUNET_CLIENT_receive (c->client, &message_handler, c,
+ GNUNET_TIME_UNIT_FOREVER_REL);
+}
+
+
/**
* Function called to notify a client about the connection begin ready to queue
* more data. "buf" will be NULL and "size" zero if the connection was closed
GNUNET_TIME_UNIT_FOREVER_REL,
GNUNET_NO, &transmit_ready_notify,
c);
+ if ( (GNUNET_NO == c->in_receive) &&
+ (size > 0) )
+ {
+ c->in_receive = GNUNET_YES;
+ GNUNET_CLIENT_receive (c->client, &message_handler, c,
+ GNUNET_TIME_UNIT_FOREVER_REL);
+ }
return size;
}
}
-/**
- * Handler for messages from controller (testbed service)
- *
- * @param cls the controller handler
- * @param msg message received, NULL on timeout or fatal error
- */
-static void
-message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
-{
- struct GNUNET_TESTBED_Controller *c = cls;
-
- /* FIXME: Add checks for message integrity */
- switch (ntohs (msg->type))
- {
- default:
- GNUNET_break (0);
- }
- GNUNET_CLIENT_receive (c->client, &message_handler, c,
- GNUNET_TIME_UNIT_FOREVER_REL);
-}
-
-
-/**
- * ?Callback for messages recevied from server?
- *
- * 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
-server_mst_cb (void *cls, void *client,
- const struct GNUNET_MessageHeader *message)
-{
- struct GNUNET_TESTBED_Controller *c = cls;
- struct GNUNET_TESTBED_InitMessage *msg;
-
- c->client = GNUNET_CLIENT_connect ("testbed", c->cfg);
- if (NULL == c->client)
- return GNUNET_SYSERR; /* FIXME: Call controller startup_cb ? */
- GNUNET_CLIENT_receive (c->client, &message_handler, c,
- GNUNET_TIME_UNIT_FOREVER_REL);
- msg = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_InitMessage));
- msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_INIT);
- msg->header.size = htons (sizeof (struct GNUNET_TESTBED_InitMessage));
- msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (c->host));
- msg->event_mask = GNUNET_htonll (c->event_mask);
- queue_message (c, (struct GNUNET_MessageHeader *) msg);
- return GNUNET_OK;
-}
-
-
/**
* Start a controller process using the given configuration at the
* given host.
"gnunet-service-testbed",
NULL
};
+ struct GNUNET_TESTBED_InitMessage *msg;
+
controller = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Controller));
- controller->helper = GNUNET_TESTBED_host_run_ (host, binary_argv,
- &server_mst_cb, controller);
+ controller->helper = GNUNET_TESTBED_host_run_ (host, binary_argv);
if (NULL == controller->helper)
{
GNUNET_free (controller);
controller->cc_cls = cc_cls;
controller->event_mask = event_mask;
controller->cfg = GNUNET_CONFIGURATION_dup (cfg);
+ controller->client = GNUNET_CLIENT_connect ("testbed", controller->cfg);
+ if (NULL == controller->client)
+ {
+ GNUNET_TESTBED_controller_stop (controller);
+ return NULL;
+ }
+ msg = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_InitMessage));
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_INIT);
+ msg->header.size = htons (sizeof (struct GNUNET_TESTBED_InitMessage));
+ msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (controller->host));
+ msg->event_mask = GNUNET_htonll (controller->event_mask);
+ queue_message (controller, (struct GNUNET_MessageHeader *) msg);
return controller;
}
GNUNET_free (mq_entry->msg);
GNUNET_free (mq_entry);
}
- GNUNET_CLIENT_disconnect (controller->client);
+ if (NULL != controller->client)
+ GNUNET_CLIENT_disconnect (controller->client);
GNUNET_TESTBED_host_stop_ (controller->helper);
GNUNET_CONFIGURATION_destroy (controller->cfg);
GNUNET_free (controller);
GNUNET_TESTBED_overlay_write_topology_to_file (struct GNUNET_TESTBED_Controller *controller,
const char *filename)
{
+ GNUNET_break (0);
}
uint32_t
GNUNET_TESTBED_host_get_id_ (const struct GNUNET_TESTBED_Host *host)
{
- return host->unique_id;
+ return host->unique_id;
}
* @return handle to the host, NULL on error
*/
struct GNUNET_TESTBED_Host *
-GNUNET_TESTBED_host_create_with_id_ (uint32_t id,
- const char *hostname,
- const char *username,
- uint16_t port)
+GNUNET_TESTBED_host_create_with_id (uint32_t id,
+ const char *hostname,
+ const char *username,
+ uint16_t port)
{
struct GNUNET_TESTBED_Host *host;
static uint32_t uid_generator;
if (NULL == hostname)
- return GNUNET_TESTBED_host_create_with_id_ (0, hostname, username, port);
- return GNUNET_TESTBED_host_create_with_id_ (++uid_generator,
- hostname, username,
- port);
+ return GNUNET_TESTBED_host_create_with_id (0, hostname, username, port);
+ return GNUNET_TESTBED_host_create_with_id (++uid_generator,
+ hostname, username,
+ port);
}
GNUNET_TESTBED_hosts_load_from_file (const char *filename,
struct GNUNET_TESTBED_Host **hosts)
{
+ // see testing_group.c, GNUNET_TESTING_hosts_load
GNUNET_break (0);
return 0;
}
struct GNUNET_TESTBED_HelperHandle
{
/**
- * The helper handle
+ * The process handle
*/
- struct GNUNET_HELPER_Handle *handle;
+ struct GNUNET_OS_Process *process;
+
+ /**
+ * Pipe connecting to stdin of the process.
+ */
+ struct GNUNET_DISK_PipeHandle *cpipe;
/**
* The port number for ssh; used for helpers starting ssh
*
* @param host host to use, use "NULL" for localhost
* @param binary_argv binary name and command-line arguments to give to the binary
- * @param cb function to call for messages received from the binary
- * @param cb_cls closure for cb
* @return handle to terminate the command, NULL on error
*/
struct GNUNET_TESTBED_HelperHandle *
-GNUNET_TESTBED_host_run_ (struct GNUNET_TESTBED_Host *host,
- char *const binary_argv[],
- GNUNET_SERVER_MessageTokenizerCallback cb, void *cb_cls)
+GNUNET_TESTBED_host_run_ (const struct GNUNET_TESTBED_Host *host,
+ char *const binary_argv[])
{
- /* FIXME: decide on the SSH command line, prepend it and
- run GNUNET_HELPER_start with the modified binary_name and binary_argv! */
struct GNUNET_TESTBED_HelperHandle *h;
- char *const local_args[] = {NULL};
+ unsigned int argc;
+ argc = 0;
+ while (NULL != binary_argv[argc])
+ argc++;
h = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HelperHandle));
+ h->cpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_YES, GNUNET_NO);
if (0 == host->unique_id)
{
- h->handle = GNUNET_HELPER_start ("gnunet-service-testbed", local_args,
- cb, cb_cls);
+ h->process = GNUNET_OS_start_process_vap (GNUNET_YES,
+ h->cpipe, NULL,
+ "gnunet-service-testbed",
+ binary_argv);
}
else
{
+ char *remote_args[argc + 6 + 1];
+ unsigned int argp;
+
GNUNET_asprintf (&h->port, "%d", host->port);
GNUNET_asprintf (&h->dst, "%s@%s", host->hostname, host->username);
- char *remote_args[] = {"ssh", "-p", h->port, "-q", h->dst,
- "gnunet-service-testbed", NULL};
- h->handle = GNUNET_HELPER_start ("ssh", remote_args, cb, cb_cls);
+ argp = 0;
+ remote_args[argp++] = "ssh";
+ remote_args[argp++] = "-p";
+ remote_args[argp++] = h->port;
+ remote_args[argp++] = "-q";
+ remote_args[argp++] = h->dst;
+ remote_args[argp++] = "gnunet-service-testbed";
+ while (NULL != binary_argv[argp-6])
+ {
+ remote_args[argp] = binary_argv[argp - 6];
+ argp++;
+ }
+ remote_args[argp++] = NULL;
+ GNUNET_assert (argp == argc + 6 + 1);
+ h->process = GNUNET_OS_start_process_vap (GNUNET_YES,
+ h->cpipe, NULL,
+ "ssh",
+ remote_args);
}
- if (NULL == h->handle)
+ if (NULL == h->process)
{
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_pipe_close (h->cpipe));
+ GNUNET_free_non_null (h->port);
+ GNUNET_free_non_null (h->dst);
GNUNET_free (h);
return NULL;
- }
+ }
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_pipe_close_end (h->cpipe, GNUNET_DISK_PIPE_END_READ));
return h;
}
void
GNUNET_TESTBED_host_stop_ (struct GNUNET_TESTBED_HelperHandle *handle)
{
- GNUNET_HELPER_stop (handle->handle);
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_pipe_close (handle->cpipe));
+ GNUNET_break (0 == GNUNET_OS_process_kill (handle->process, SIGTERM));
+ GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (handle->process));
+ GNUNET_OS_process_destroy (handle->process);
GNUNET_free_non_null (handle->port);
GNUNET_free_non_null (handle->dst);
GNUNET_free (handle);
GNUNET_TESTBED_host_create_by_id_ (uint32_t id);
-/**
- * Create a host to run peers and controllers on. This function is used
- * if a peer learns about a host via IPC between controllers (and thus
- * some higher-level controller has already determined the unique IDs).
- *
- * @param id global host ID assigned to the host; 0 is
- * reserved to always mean 'localhost'
- * @param hostname name of the host, use "NULL" for localhost
- * @param username username to use for the login; may be NULL
- * @param port port number to use for ssh; use 0 to let ssh decide
- * @return handle to the host, NULL on error
- */
-struct GNUNET_TESTBED_Host *
-GNUNET_TESTBED_host_create_with_id_ (uint32_t id,
- const char *hostname,
- const char *username,
- uint16_t port);
-
-
/**
* Obtain a host's unique global ID.
*
*
* @param host host to use, use "NULL" for localhost
* @param binary_argv binary name and command-line arguments to give to the binary
- * @param cb function to call for messages received from the binary
- * @param cb_cls closure for cb
* @return handle to terminate the command, NULL on error
*/
struct GNUNET_TESTBED_HelperHandle *
-GNUNET_TESTBED_host_run_ (struct GNUNET_TESTBED_Host *host,
- char *const binary_argv[],
- GNUNET_SERVER_MessageTokenizerCallback cb, void *cb_cls);
+GNUNET_TESTBED_host_run_ (const struct GNUNET_TESTBED_Host *host,
+ char *const binary_argv[]);
/**