From 5c9fac9cafe7d0911b80cc1aeab52b2e0d0cff9b Mon Sep 17 00:00:00 2001 From: Sree Harsha Totakura Date: Tue, 26 Jun 2012 19:07:46 +0000 Subject: [PATCH] testbed host registration --- src/include/gnunet_protocols.h | 2 +- src/include/gnunet_testbed_service.h | 50 ++++++ src/testbed/gnunet-service-testbed.c | 71 ++++++-- src/testbed/testbed.h | 1 - src/testbed/testbed_api.c | 241 ++++++++++++++++++++++++++- src/testbed/testbed_api_hosts.c | 72 +++++++- src/testbed/testbed_api_hosts.h | 49 ++++++ 7 files changed, 461 insertions(+), 25 deletions(-) diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index 02f53c7da..47a2eb599 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -1346,7 +1346,7 @@ extern "C" /** * 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 diff --git a/src/include/gnunet_testbed_service.h b/src/include/gnunet_testbed_service.h index a3f092172..7dd799cf1 100644 --- a/src/include/gnunet_testbed_service.h +++ b/src/include/gnunet_testbed_service.h @@ -447,6 +447,56 @@ void 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 diff --git a/src/testbed/gnunet-service-testbed.c b/src/testbed/gnunet-service-testbed.c index 774482162..93a1cf815 100644 --- a/src/testbed/gnunet-service-testbed.c +++ b/src/testbed/gnunet-service-testbed.c @@ -270,11 +270,6 @@ route_message (uint32_t host_id, const struct GNUNET_MessageHeader *msg) } -/** - * - */ - - /** * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_INIT messages * @@ -363,22 +358,49 @@ handle_add_host (void *cls, 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 * @@ -423,13 +445,24 @@ handle_configure_shared_service (void *cls, 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); } @@ -474,6 +507,7 @@ shutdown_task (void *cls, (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); @@ -482,6 +516,7 @@ shutdown_task (void *cls, 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; } diff --git a/src/testbed/testbed.h b/src/testbed/testbed.h index 17da85952..f909295ff 100644 --- a/src/testbed/testbed.h +++ b/src/testbed/testbed.h @@ -54,7 +54,6 @@ struct GNUNET_TESTBED_InitMessage * is interested in. In NBO. */ uint64_t event_mask GNUNET_PACKED; - }; diff --git a/src/testbed/testbed_api.c b/src/testbed/testbed_api.c index 094334e58..4075e1b99 100644 --- a/src/testbed/testbed_api.c +++ b/src/testbed/testbed_api.c @@ -35,10 +35,18 @@ #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 @@ -62,6 +70,45 @@ struct MessageQueue }; +/** + * 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 @@ -114,11 +161,27 @@ struct GNUNET_TESTBED_Controller */ 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 */ @@ -131,6 +194,82 @@ struct GNUNET_TESTBED_Controller }; +/** + * 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) @@ -141,16 +280,29 @@ struct GNUNET_TESTBED_Controller 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); } @@ -355,6 +507,87 @@ GNUNET_TESTBED_controller_stop (struct GNUNET_TESTBED_Controller *controller) } +/** + * 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 diff --git a/src/testbed/testbed_api_hosts.c b/src/testbed/testbed_api_hosts.c index ca3afb29a..766a87ad2 100644 --- a/src/testbed/testbed_api_hosts.c +++ b/src/testbed/testbed_api_hosts.c @@ -72,6 +72,11 @@ struct GNUNET_TESTBED_Host * 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; }; @@ -126,7 +131,7 @@ GNUNET_TESTBED_host_create_by_id_ (uint32_t id) /** - * 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 @@ -139,6 +144,45 @@ 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) +{ + 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. * @@ -336,4 +380,30 @@ GNUNET_TESTBED_host_stop_ (struct GNUNET_TESTBED_HelperHandle *handle) 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 */ diff --git a/src/testbed/testbed_api_hosts.h b/src/testbed/testbed_api_hosts.h index eaf2a4d11..db7392b49 100644 --- a/src/testbed/testbed_api_hosts.h +++ b/src/testbed/testbed_api_hosts.h @@ -65,6 +65,36 @@ uint32_t 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 */ @@ -94,5 +124,24 @@ GNUNET_TESTBED_host_run_ (const struct GNUNET_TESTBED_Host *host, 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 */ -- 2.25.1