From ae33a4ca485b5cb8f39e3909c2398e9000ab8746 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 25 Nov 2011 13:04:59 -0800 Subject: [PATCH] Converted to new guac_client_plugin API --- libguac/include/client.h | 70 ++++++---- libguac/src/client.c | 272 +++++++++++++-------------------------- 2 files changed, 137 insertions(+), 205 deletions(-) diff --git a/libguac/include/client.h b/libguac/include/client.h index d3aa7e0a..ce59e00d 100644 --- a/libguac/include/client.h +++ b/libguac/include/client.h @@ -51,6 +51,7 @@ */ typedef struct guac_client guac_client; +typedef struct guac_client_plugin guac_client_plugin; /** * Handler for server messages (where "server" refers to the server that @@ -84,6 +85,11 @@ typedef int guac_client_free_handler(guac_client* client); */ typedef void guac_client_log_handler(guac_client* client, const char* format, va_list args); +/** + * Handler which should initialize the given guac_client. + */ +typedef int guac_client_init_handler(guac_client* client, int argc, char** argv); + /** * Possible current states of the Guacamole client. Currently, the only * two states are RUNNING and STOPPING. @@ -104,6 +110,33 @@ typedef enum guac_client_state { } guac_client_state; +/** + * A handle to a client plugin, containing enough information about the + * plugin to complete the initial protocol handshake and instantiate a new + * client supporting the protocol provided by the client plugin. + */ +struct guac_client_plugin { + + /** + * Reference to dlopen'd client plugin. + */ + void* __client_plugin_handle; + + /** + * Reference to the init handler of this client plugin. This + * function will be called when the client plugin is started. + */ + guac_client_init_handler* init_handler; + + /** + * NULL-terminated array of all arguments accepted by this client + * plugin, in order. The values of these arguments will be passed + * to the init_handler if the client plugin is started. + */ + const char** args; + +}; + /** * Guacamole proxy client. * @@ -113,10 +146,11 @@ typedef enum guac_client_state { struct guac_client { /** - * The guac_socket structure to be used to communicate with the web-client. It is - * expected that the implementor of any Guacamole proxy client will provide - * their own mechanism of I/O for their protocol. The guac_socket structure is - * used only to communicate conveniently with the Guacamole web-client. + * The guac_socket structure to be used to communicate with the web-client. + * It is expected that the implementor of any Guacamole proxy client will + * provide their own mechanism of I/O for their protocol. The guac_socket + * structure is used only to communicate conveniently with the Guacamole + * web-client. */ guac_socket* io; @@ -157,11 +191,6 @@ struct guac_client { */ guac_timestamp last_sent_timestamp; - /** - * Reference to dlopen'd client plugin. - */ - void* __client_plugin_handle; - /** * Arbitrary reference to proxy client-specific data. Implementors of a * Guacamole proxy client can store any data they want here, which can then @@ -288,7 +317,7 @@ struct guac_client { * * void function_of_daemon() { * - * guac_client* client = [client from guac_get_client()]; + * guac_client* client = [client from guac_client_plugin_get_client()]; * * client->log_info_handler = log_handler; * @@ -315,7 +344,7 @@ struct guac_client { * * void function_of_daemon() { * - * guac_client* client = [client from guac_get_client()]; + * guac_client* client = [client from guac_client_plugin_get_client()]; * * client->log_error_handler = log_handler; * @@ -327,23 +356,18 @@ struct guac_client { }; -/** - * Handler which should initialize the given guac_client. - */ -typedef int guac_client_init_handler(guac_client* client, int argc, char** argv); - /** * Initialize and return a new guac_client. The pluggable client will be - * chosen based on the first connect message received on the given file - * descriptor. + * initialized using the arguments provided. * - * @param client_fd The file descriptor associated with the socket associated - * with the connection to the web-client tunnel. - * @param usec_timeout The maximum number of microseconds to wait for each - * instruction during the initial client handshake. + * @param plugin The client plugin to use to create the new client. + * @param io The guac_socket the client should use for communication. + * @param argc The number of arguments being passed to the client. + * @param argv All arguments to be passed to the client. * @return A pointer to the newly initialized client. */ -guac_client* guac_get_client(int client_fd, int usec_timeout); +guac_client* guac_client_plugin_get_client(guac_client_plugin* plugin, + guac_socket* io, int argc, char** argv); /** * Free all resources associated with the given client. diff --git a/libguac/src/client.c b/libguac/src/client.c index 7fc5c473..85f9c72c 100644 --- a/libguac/src/client.c +++ b/libguac/src/client.c @@ -44,6 +44,7 @@ #include "protocol.h" #include "client.h" #include "client-handlers.h" +#include "error.h" #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) @@ -56,25 +57,6 @@ guac_layer __GUAC_DEFAULT_LAYER = { const guac_layer* GUAC_DEFAULT_LAYER = &__GUAC_DEFAULT_LAYER; -guac_client* __guac_alloc_client(guac_socket* io) { - - /* Allocate new client (not handoff) */ - guac_client* client = malloc(sizeof(guac_client)); - memset(client, 0, sizeof(guac_client)); - - /* Init new client */ - client->io = io; - client->last_received_timestamp = client->last_sent_timestamp = guac_protocol_get_timestamp(); - client->state = RUNNING; - - client->__all_layers = NULL; - client->__available_buffers = NULL; - - client->__next_buffer_index = -1; - - return client; -} - guac_layer* guac_client_alloc_layer(guac_client* client, int index) { guac_layer* allocd_layer; @@ -127,10 +109,15 @@ void guac_client_free_buffer(guac_client* client, guac_layer* layer) { } -guac_client* guac_get_client(int client_fd, int usec_timeout) { +guac_client_plugin* guac_client_plugin_open(const char* protocol) { - guac_client* client; - guac_socket* io = guac_socket_open(client_fd); + guac_client_plugin* plugin; + + /* Reference to dlopen()'d plugin */ + void* client_plugin_handle; + + /* Client args description */ + const char** client_args; /* Pluggable client */ char protocol_lib[256] = "libguac-client-"; @@ -140,179 +127,100 @@ guac_client* guac_get_client(int client_fd, int usec_timeout) { void* obj; } alias; - char* error; - - /* Client args description */ - const char** client_args; - - /* Client arguments */ - int argc; - char** argv; - - /* Instruction */ - guac_instruction* instruction; - - /* Wait for select instruction */ - for (;;) { - - int result; - - /* Wait for data until timeout */ - result = guac_protocol_instructions_waiting(io, usec_timeout); - if (result == 0) { - guac_protocol_send_error(io, "Select timeout."); - guac_socket_close(io); - return NULL; - } - - /* If error occurs while waiting, exit with failure */ - if (result < 0) { - guac_socket_close(io); - return NULL; - } - - instruction = guac_protocol_read_instruction(io, usec_timeout); - if (instruction == NULL) { - guac_socket_close(io); - return NULL; - } - - /* Select instruction read */ - else { - - if (strcmp(instruction->opcode, "select") == 0) { - - /* Get protocol from message */ - char* protocol = instruction->argv[0]; - - strcat(protocol_lib, protocol); - strcat(protocol_lib, ".so"); - - /* Create new client */ - client = __guac_alloc_client(io); - - /* Load client plugin */ - client->__client_plugin_handle = dlopen(protocol_lib, RTLD_LAZY); - if (!(client->__client_plugin_handle)) { - guac_client_log_error(client, "Could not open client plugin for protocol \"%s\": %s\n", protocol, dlerror()); - guac_protocol_send_error(io, "Could not load server-side client plugin."); - guac_socket_flush(io); - guac_socket_close(io); - guac_instruction_free(instruction); - return NULL; - } - - dlerror(); /* Clear errors */ - - /* Get init function */ - alias.obj = dlsym(client->__client_plugin_handle, "guac_client_init"); - - if ((error = dlerror()) != NULL) { - guac_client_log_error(client, "Could not get guac_client_init in plugin: %s\n", error); - guac_protocol_send_error(io, "Invalid server-side client plugin."); - guac_socket_flush(io); - guac_socket_close(io); - guac_instruction_free(instruction); - return NULL; - } - - /* Get usage strig */ - client_args = (const char**) dlsym(client->__client_plugin_handle, "GUAC_CLIENT_ARGS"); - - if ((error = dlerror()) != NULL) { - guac_client_log_error(client, "Could not get GUAC_CLIENT_ARGS in plugin: %s\n", error); - guac_protocol_send_error(io, "Invalid server-side client plugin."); - guac_socket_flush(io); - guac_socket_close(io); - guac_instruction_free(instruction); - return NULL; - } - - if ( /* Send args */ - guac_protocol_send_args(io, client_args) - || guac_socket_flush(io) - ) { - guac_socket_close(io); - guac_instruction_free(instruction); - return NULL; - } - - guac_instruction_free(instruction); - break; - - } /* end if select */ - - guac_instruction_free(instruction); - } - + /* Load client plugin */ + client_plugin_handle = dlopen(protocol_lib, RTLD_LAZY); + if (!client_plugin_handle) { + guac_error = GUAC_STATUS_BAD_ARGUMENT; + return NULL; } - /* Wait for connect instruction */ - for (;;) { + dlerror(); /* Clear errors */ - int result; - - /* Wait for data until timeout */ - result = guac_protocol_instructions_waiting(io, usec_timeout); - if (result == 0) { - guac_protocol_send_error(io, "Connect timeout."); - guac_socket_close(io); - return NULL; - } - - /* If error occurs while waiting, exit with failure */ - if (result < 0) { - guac_socket_close(io); - return NULL; - } - - instruction = guac_protocol_read_instruction(io, usec_timeout); - if (instruction == NULL) { - guac_client_log_error(client, "Error reading instruction while waiting for connect"); - guac_socket_close(io); - return NULL; - } - - /* Connect instruction read */ - else { - - if (strcmp(instruction->opcode, "connect") == 0) { - - /* Initialize client arguments */ - argc = instruction->argc; - argv = instruction->argv; - - if (alias.client_init(client, argc, argv) != 0) { - /* NOTE: On error, proxy client will send appropriate error message */ - guac_instruction_free(instruction); - guac_socket_close(io); - return NULL; - } - - guac_instruction_free(instruction); - return client; - - } /* end if connect */ - - guac_instruction_free(instruction); - } + /* Get init function */ + alias.obj = dlsym(client_plugin_handle, "guac_client_init"); + /* Fail if cannot find guac_client_init */ + if (dlerror() != NULL) { + guac_error = GUAC_STATUS_BAD_ARGUMENT; + return NULL; } + /* Get usage strig */ + client_args = (const char**) dlsym(client_plugin_handle, "GUAC_CLIENT_ARGS"); + + /* Fail if cannot find GUAC_CLIENT_ARGS */ + if (dlerror() != NULL) { + guac_error = GUAC_STATUS_BAD_ARGUMENT; + return NULL; + } + + /* Allocate plugin */ + plugin = malloc(sizeof(guac_client_plugin)); + if (plugin == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + return NULL; + } + + /* Init and return plugin */ + plugin->__client_plugin_handle = client_plugin_handle; + plugin->init_handler = alias.client_init; + plugin->args = client_args; + return plugin; + +} + +int guac_client_plugin_close(guac_client_plugin* plugin) { + + /* Unload client plugin */ + if (dlclose(plugin->__client_plugin_handle)) { + guac_error = GUAC_STATUS_BAD_STATE; + return -1; + } + + return 0; + +} + +guac_client* guac_client_plugin_get_client(guac_client_plugin* plugin, + guac_socket* io, int argc, char** argv) { + + /* Allocate new client */ + guac_client* client = malloc(sizeof(guac_client)); + if (client == NULL) { + guac_error = GUAC_STATUS_NO_MEMORY; + return NULL; + } + + /* Init new client */ + memset(client, 0, sizeof(guac_client)); + + client->io = io; + client->last_received_timestamp = + client->last_sent_timestamp = guac_protocol_get_timestamp(); + + client->state = RUNNING; + + client->__all_layers = NULL; + client->__available_buffers = NULL; + + client->__next_buffer_index = -1; + + if (plugin->init_handler(client, argc, argv) != 0) { + guac_client_free(client); + return NULL; + } + + return client; + } void guac_client_free(guac_client* client) { if (client->free_handler) { - if (client->free_handler(client)) - guac_client_log_error(client, "Error calling client free handler"); - } - guac_socket_close(client->io); + /* FIXME: Errors currently ignored... */ + client->free_handler(client); - /* Unload client plugin */ - if (dlclose(client->__client_plugin_handle)) { - guac_client_log_error(client, "Could not close client plugin while unloading client: %s", dlerror()); } /* Free all layers */