/**
* Message to signal that a add host succeeded
*/
-#define GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTSUCCESS 462
+#define GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM 462
/**
* Message to configure a service to be shared among peers
GNUNET_TESTBED_controller_stop (struct GNUNET_TESTBED_Controller *controller);
+/**
+ * Opaque handle for host registration
+ */
+struct GNUNET_TESTBED_HostRegistrationHandle;
+
+
+/**
+ * Callback which will be called to after a host registration succeeded or failed
+ *
+ * @param cls the closure
+ * @param emsg the error message; NULL if host registration is successful
+ */
+typedef void (* GNUNET_TESTBED_HostRegistrationCompletion) (void *cls,
+ const char *emsg);
+
+
+/**
+ * Register a host with the controller
+ *
+ * @param controller the controller handle
+ * @param host the host to register
+ * @param cc the completion callback to call to inform the status of
+ * registration. After calling this callback the registration handle
+ * will be invalid. Cannot be NULL
+ * @param cc_cls the closure for the cc
+ * @return handle to the host registration which can be used to cancel the
+ * registration; NULL if another registration handle is present and
+ * is not cancelled
+ */
+struct GNUNET_TESTBED_HostRegistrationHandle *
+GNUNET_TESTBED_register_host (struct GNUNET_TESTBED_Controller *controller,
+ struct GNUNET_TESTBED_Host *host,
+ GNUNET_TESTBED_HostRegistrationCompletion cc,
+ void *cc_cls);
+
+
+/**
+ * Cancel the pending registration. Note that the registration message will
+ * already be queued to be sent to the service, cancellation has only the
+ * effect that the registration completion callback for the registration is
+ * never called and from our perspective the host is not registered until the
+ * completion callback is called.
+ *
+ * @param handle the registration handle to cancel
+ */
+void
+GNUNET_TESTBED_cancel_registration (struct GNUNET_TESTBED_HostRegistrationHandle
+ *handle);
+
+
/**
* Create a link from a 'master' controller to a slave controller.
* Whenever the master controller is asked to start a peer at the
}
-/**
- *
- */
-
-
/**
* Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages
*
host = GNUNET_TESTBED_host_create_with_id (host_id, hostname, username,
ntohs (msg->ssh_port));
GNUNET_SERVER_receive_done (client, GNUNET_OK);
- if (GNUNET_OK == host_list_add (host))
- return;
- /* We are unable to add a host */
- emsg = "A host exists with given host-id";
- GNUNET_TESTBED_host_destroy (host);
- reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage)
- + strlen (emsg) + 1;
- reply = GNUNET_malloc (reply_size);
- reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTSUCCESS);
+ reply_size = sizeof (struct GNUNET_TESTBED_HostConfirmedMessage);
+ if (GNUNET_OK != host_list_add (host))
+ {
+ /* We are unable to add a host */
+ emsg = "A host exists with given host-id";
+ LOG_DEBUG ("%s: %u", emsg, host_id);
+ GNUNET_TESTBED_host_destroy (host);
+ reply_size += strlen (emsg) + 1;
+ reply = GNUNET_malloc (reply_size);
+ memcpy (&reply[1], emsg, strlen (emsg) + 1);
+ }
+ else
+ reply = GNUNET_malloc (reply_size);
+ reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM);
reply->header.size = htons (reply_size);
- reply->host_id = htonl (host_id);
- memcpy (&reply[1], emsg, strlen (emsg) + 1);
+ reply->host_id = htonl (host_id);
queue_message (client, (struct GNUNET_MessageHeader *) reply);
}
+/**
+ * Iterator over hash map entries.
+ *
+ * @param cls closure
+ * @param key current key code
+ * @param value value in the hash map
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+int ss_exists_iterator (void *cls,
+ const struct GNUNET_HashCode * key,
+ void *value)
+{
+ struct SharedService *queried_ss = cls;
+ struct SharedService *ss = value;
+
+ if (0 == strcmp (ss->name, queried_ss->name))
+ return GNUNET_NO;
+ else
+ return GNUNET_YES;
+}
+
/**
* Message handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST messages
*
GNUNET_SERVER_receive_done (client, GNUNET_OK);
return;
}
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
ss = GNUNET_malloc (sizeof (struct SharedService));
ss->name = strdup (service_name);
ss->num_shared = ntohl (msg->num_peers);
GNUNET_CRYPTO_hash (ss->name, service_name_size, &hash);
+ if (GNUNET_SYSERR ==
+ GNUNET_CONTAINER_multihashmap_get_multiple (ss_map, &hash,
+ &ss_exists_iterator, ss))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "Service %s already configured as a shared service. "
+ "Ignoring service sharing request \n", ss->name);
+ GNUNET_free (ss->name);
+ GNUNET_free (ss);
+ return;
+ }
GNUNET_CONTAINER_multihashmap_put (ss_map, &hash, ss,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
- GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
}
(void) GNUNET_CONTAINER_multihashmap_iterate (ss_map, &ss_map_free_iterator,
NULL);
GNUNET_CONTAINER_multihashmap_destroy (ss_map);
+ /* Clear host array */
if (NULL != fh)
{
GNUNET_DISK_file_close (fh);
for (host_id = 0; host_id < host_list_size; host_id++)
if (NULL != host_list[host_id])
GNUNET_TESTBED_host_destroy (host_list[host_id]);
+ GNUNET_free (host_list);
GNUNET_free_non_null (master_context);
master_context = NULL;
}
* is interested in. In NBO.
*/
uint64_t event_mask GNUNET_PACKED;
-
};
#include "testbed.h"
#include "testbed_api_hosts.h"
-
+/**
+ * Generic logging shorthand
+ */
#define LOG(kind, ...) \
GNUNET_log_from (kind, "testbed-api", __VA_ARGS__);
+/**
+ * Debug logging
+ */
+#define LOG_DEBUG(...) \
+ LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__);
+
/**
* The message queue for sending messages to the controller service
};
+/**
+ * Structure for a controller link
+ */
+struct ControllerLink
+{
+ /**
+ * The next ptr for DLL
+ */
+ struct ControllerLink *next;
+
+ /**
+ * The prev ptr for DLL
+ */
+ struct ControllerLink *prev;
+
+ /**
+ * The host which will be referred in the peer start request. This is the
+ * host where the peer should be started
+ */
+ struct GNUNET_TESTBED_Host *delegated_host;
+
+ /**
+ * The host which will contacted to delegate the peer start request
+ */
+ struct GNUNET_TESTBED_Host *slave_host;
+
+ /**
+ * The configuration to be used to connect to slave host
+ */
+ const struct GNUNET_CONFIGURATION_Handle *slave_cfg;
+
+ /**
+ * GNUNET_YES if the slave should be started (and stopped) by us; GNUNET_NO
+ * if we are just allowed to use the slave via TCP/IP
+ */
+ int is_subordinate;
+};
+
+
/**
* Handle to interact with a GNUnet testbed controller. Each
* controller has at least one master handle which is created when the
*/
struct MessageQueue *mq_tail;
+ /**
+ * The head of the ControllerLink list
+ */
+ struct ControllerLink *cl_head;
+
+ /**
+ * The tail of the ControllerLink list
+ */
+ struct ControllerLink *cl_tail;
+
/**
* The client transmit handle
*/
struct GNUNET_CLIENT_TransmitHandle *th;
+ /**
+ * The host registration handle; NULL if no current registration requests are
+ * present
+ */
+ struct GNUNET_TESTBED_HostRegistrationHandle *rh;
+
/**
* The controller event mask
*/
};
+/**
+ * handle for host registration
+ */
+struct GNUNET_TESTBED_HostRegistrationHandle
+{
+ /**
+ * The host being registered
+ */
+ struct GNUNET_TESTBED_Host *host;
+
+ /**
+ * The Registartion completion callback
+ */
+ GNUNET_TESTBED_HostRegistrationCompletion cc;
+
+ /**
+ * The closure for above callback
+ */
+ void *cc_cls;
+};
+
+
+/**
+ * Handler for GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM message from
+ * controller (testbed service)
+ *
+ * @param c the controller handler
+ * @param msg message received
+ * @return GNUNET_YES if we can continue receiving from service; GNUNET_NO if
+ * not
+ */
+static int
+handle_addhostconfirm (struct GNUNET_TESTBED_Controller *c,
+ const struct GNUNET_TESTBED_HostConfirmedMessage *msg)
+{
+ struct GNUNET_TESTBED_HostRegistrationHandle *rh;
+ char *emsg;
+ uint16_t msg_size;
+
+ rh = c->rh;
+ if (NULL == rh)
+ {
+ return GNUNET_OK;
+ }
+ if (GNUNET_TESTBED_host_get_id_ (rh->host) != ntohl (msg->host_id))
+ {
+ LOG_DEBUG ("Mismatch in host id's %u, %u of host confirm msg\n",
+ GNUNET_TESTBED_host_get_id_ (rh->host), ntohl (msg->host_id));
+ return GNUNET_OK;
+ }
+ c->rh = NULL;
+ msg_size = ntohs (msg->header.size);
+ if (sizeof (struct GNUNET_TESTBED_HostConfirmedMessage) == msg_size)
+ {
+ LOG_DEBUG ("Host %u successfully registered\n", ntohl (msg->host_id));
+ GNUNET_TESTBED_mark_host_as_registered_ (rh->host);
+ rh->cc (rh->cc_cls, NULL);
+ GNUNET_free (rh);
+ return GNUNET_OK;
+ }
+ /* We have an error message */
+ emsg = (char *) &msg[1];
+ if ('\0' != emsg[msg_size -
+ sizeof (struct GNUNET_TESTBED_HostConfirmedMessage)])
+ {
+ GNUNET_break (0);
+ GNUNET_free (rh);
+ return GNUNET_NO;
+ }
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("Adding host %u failed with error: %s\n"),
+ ntohl (msg->host_id), emsg);
+ rh->cc (rh->cc_cls, emsg);
+ GNUNET_free (rh);
+ return GNUNET_OK;
+}
+
/**
* Handler for messages from controller (testbed service)
static void
message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
{
- struct GNUNET_TESTBED_Controller *c = cls;
+ struct GNUNET_TESTBED_Controller *c = cls;
+ int status;
/* FIXME: Add checks for message integrity */
+ if (NULL == msg)
+ {
+ LOG_DEBUG ("Receive timed out or connection to service dropped\n");
+ return;
+ }
+ status = GNUNET_OK;
switch (ntohs (msg->type))
{
+ case GNUNET_MESSAGE_TYPE_TESTBED_ADDHOSTCONFIRM:
+ status =
+ handle_addhostconfirm (c, (const struct
+ GNUNET_TESTBED_HostConfirmedMessage *) msg);
+ break;
default:
GNUNET_break (0);
}
- GNUNET_CLIENT_receive (c->client, &message_handler, c,
- GNUNET_TIME_UNIT_FOREVER_REL);
+ if (GNUNET_OK == status)
+ GNUNET_CLIENT_receive (c->client, &message_handler, c,
+ GNUNET_TIME_UNIT_FOREVER_REL);
}
}
+/**
+ * Register a host with the controller
+ *
+ * @param controller the controller handle
+ * @param host the host to register
+ * @param cc the completion callback to call to inform the status of
+ * registration. After calling this callback the registration handle
+ * will be invalid. Cannot be NULL.
+ * @param cc_cls the closure for the cc
+ * @return handle to the host registration which can be used to cancel the
+ * registration
+ */
+struct GNUNET_TESTBED_HostRegistrationHandle *
+GNUNET_TESTBED_register_host (struct GNUNET_TESTBED_Controller *controller,
+ struct GNUNET_TESTBED_Host *host,
+ GNUNET_TESTBED_HostRegistrationCompletion cc,
+ void *cc_cls)
+{
+ struct GNUNET_TESTBED_HostRegistrationHandle *rh;
+ struct GNUNET_TESTBED_AddHostMessage *msg;
+ const char *username;
+ const char *hostname;
+ uint16_t msg_size;
+ uint16_t user_name_length;
+
+ if (NULL != controller->rh)
+ return NULL;
+ hostname = GNUNET_TESTBED_host_get_hostname_ (host);
+ if (GNUNET_YES == GNUNET_TESTBED_is_host_registered_ (host))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "Host hostname: %s already registered\n",
+ (NULL == hostname) ? "localhost" : hostname);
+ return NULL;
+ }
+ rh = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_HostRegistrationHandle));
+ rh->host = host;
+ GNUNET_assert (NULL != cc);
+ rh->cc = cc;
+ rh->cc_cls = cc_cls;
+ controller->rh = rh;
+ username = GNUNET_TESTBED_host_get_username_ (host);
+ msg_size = (sizeof (struct GNUNET_TESTBED_AddHostMessage));
+ user_name_length = 0;
+ if (NULL != username)
+ {
+ user_name_length = strlen (username) + 1;
+ msg_size += user_name_length;
+ }
+ /* FIXME: what happens when hostname is NULL? localhost */
+ GNUNET_assert (NULL != hostname);
+ msg_size += strlen (hostname) + 1;
+ msg = GNUNET_malloc (msg_size);
+ msg->header.size = htons (msg_size);
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_ADDHOST);
+ msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (host));
+ msg->ssh_port = htons (GNUNET_TESTBED_host_get_ssh_port_ (host));
+ msg->user_name_length = htons (user_name_length);
+ if (NULL != username)
+ memcpy (&msg[1], username, user_name_length);
+ strcpy (((void *) msg) + user_name_length, hostname);
+ queue_message (controller, (struct GNUNET_MessageHeader *) msg);
+ return rh;
+}
+
+
+/**
+ * Cancel the pending registration. Note that if the registration message is
+ * already sent to the service the cancellation has only the effect that the
+ * registration completion callback for the registration is never called.
+ *
+ * @param handle the registration handle to cancel
+ */
+void
+GNUNET_TESTBED_cancel_registration (struct GNUNET_TESTBED_HostRegistrationHandle
+ *handle)
+{
+ GNUNET_break (0);
+}
+
+
/**
* Create a link from a 'master' controller to a slave controller.
* Whenever the master controller is asked to start a peer at the
* The port which is to be used for SSH
*/
uint16_t port;
+
+ /**
+ * Set this flag to 1 if host is registered with a controller; 0 if not
+ */
+ uint8_t is_registered;
};
/**
- * Obtain a host's unique global ID.
+ * Obtain the host's unique global ID.
*
* @param host handle to the host, NULL means 'localhost'
* @return id global host ID assigned to the host (0 is
}
+/**
+ * Obtain the host's hostname.
+ *
+ * @param host handle to the host, NULL means 'localhost'
+ * @return hostname of the host
+ */
+const char *
+GNUNET_TESTBED_host_get_hostname_ (const struct GNUNET_TESTBED_Host *host)
+{
+ return host->hostname;
+}
+
+
+/**
+ * Obtain the host's username
+ *
+ * @param host handle to the host, NULL means 'localhost'
+ * @return username to login to the host
+ */
+const char *
+GNUNET_TESTBED_host_get_username_ (const struct GNUNET_TESTBED_Host *host)
+{
+ return host->username;
+}
+
+
+/**
+ * Obtain the host's ssh port
+ *
+ * @param host handle to the host, NULL means 'localhost'
+ * @return username to login to the host
+ */
+uint16_t
+GNUNET_TESTBED_host_get_ssh_port_ (const struct GNUNET_TESTBED_Host *host)
+{
+ return host->port;
+}
+
+
/**
* Create a host to run peers and controllers on.
*
GNUNET_free (handle);
}
+
+/**
+ * Marks a host as registered with a controller
+ *
+ * @param host the host to mark
+ */
+void
+GNUNET_TESTBED_mark_host_as_registered_ (struct GNUNET_TESTBED_Host *host)
+{
+ host->is_registered = 1;
+}
+
+
+/**
+ * Checks whether a host has been registered
+ *
+ * @param host the host to check
+ * @return GNUNET_YES if registered; GNUNET_NO if not
+ */
+int
+GNUNET_TESTBED_is_host_registered_ (const struct GNUNET_TESTBED_Host *host)
+{
+ return (1 == host->is_registered) ? GNUNET_YES : GNUNET_NO;
+}
+
+
/* end of testbed_api_hosts.c */
GNUNET_TESTBED_host_get_id_ (const struct GNUNET_TESTBED_Host *host);
+/**
+ * Obtain the host's hostname.
+ *
+ * @param host handle to the host, NULL means 'localhost'
+ * @return hostname of the host
+ */
+const char *
+GNUNET_TESTBED_host_get_hostname_ (const struct GNUNET_TESTBED_Host *host);
+
+
+/**
+ * Obtain the host's username
+ *
+ * @param host handle to the host, NULL means 'localhost'
+ * @return username to login to the host
+ */
+const char *
+GNUNET_TESTBED_host_get_username_ (const struct GNUNET_TESTBED_Host *host);
+
+
+/**
+ * Obtain the host's ssh port
+ *
+ * @param host handle to the host, NULL means 'localhost'
+ * @return username to login to the host
+ */
+uint16_t
+GNUNET_TESTBED_host_get_ssh_port_ (const struct GNUNET_TESTBED_Host *host);
+
+
/**
* Opaque wrapper around GNUNET_HELPER_Handle
*/
void
GNUNET_TESTBED_host_stop_ (struct GNUNET_TESTBED_HelperHandle *handle);
+
+/**
+ * Marks a host as registered with a controller
+ *
+ * @param host the host to mark
+ */
+void
+GNUNET_TESTBED_mark_host_as_registered_ (struct GNUNET_TESTBED_Host *host);
+
+
+/**
+ * Checks whether a host has been registered
+ *
+ * @param host the host to check
+ * @return GNUNET_YES if registered; GNUNET_NO if not
+ */
+int
+GNUNET_TESTBED_is_host_registered_ (const struct GNUNET_TESTBED_Host *host);
+
#endif
/* end of testbed_api_hosts.h */