Converted to new guac_client_plugin API

This commit is contained in:
Michael Jumper 2011-11-25 13:04:59 -08:00
parent 5428aef912
commit ae33a4ca48
2 changed files with 137 additions and 205 deletions

View File

@ -51,6 +51,7 @@
*/ */
typedef struct guac_client guac_client; 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 * 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); 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 * Possible current states of the Guacamole client. Currently, the only
* two states are RUNNING and STOPPING. * two states are RUNNING and STOPPING.
@ -104,6 +110,33 @@ typedef enum guac_client_state {
} 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. * Guacamole proxy client.
* *
@ -113,10 +146,11 @@ typedef enum guac_client_state {
struct guac_client { struct guac_client {
/** /**
* The guac_socket structure to be used to communicate with the web-client. It is * The guac_socket structure to be used to communicate with the web-client.
* expected that the implementor of any Guacamole proxy client will provide * It is expected that the implementor of any Guacamole proxy client will
* their own mechanism of I/O for their protocol. The guac_socket structure is * provide their own mechanism of I/O for their protocol. The guac_socket
* used only to communicate conveniently with the Guacamole web-client. * structure is used only to communicate conveniently with the Guacamole
* web-client.
*/ */
guac_socket* io; guac_socket* io;
@ -157,11 +191,6 @@ struct guac_client {
*/ */
guac_timestamp last_sent_timestamp; 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 * Arbitrary reference to proxy client-specific data. Implementors of a
* Guacamole proxy client can store any data they want here, which can then * Guacamole proxy client can store any data they want here, which can then
@ -288,7 +317,7 @@ struct guac_client {
* *
* void function_of_daemon() { * 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; * client->log_info_handler = log_handler;
* *
@ -315,7 +344,7 @@ struct guac_client {
* *
* void function_of_daemon() { * 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; * 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 * Initialize and return a new guac_client. The pluggable client will be
* chosen based on the first connect message received on the given file * initialized using the arguments provided.
* descriptor.
* *
* @param client_fd The file descriptor associated with the socket associated * @param plugin The client plugin to use to create the new client.
* with the connection to the web-client tunnel. * @param io The guac_socket the client should use for communication.
* @param usec_timeout The maximum number of microseconds to wait for each * @param argc The number of arguments being passed to the client.
* instruction during the initial client handshake. * @param argv All arguments to be passed to the client.
* @return A pointer to the newly initialized 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. * Free all resources associated with the given client.

View File

@ -44,6 +44,7 @@
#include "protocol.h" #include "protocol.h"
#include "client.h" #include "client.h"
#include "client-handlers.h" #include "client-handlers.h"
#include "error.h"
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(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; 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* guac_client_alloc_layer(guac_client* client, int index) {
guac_layer* allocd_layer; 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_client_plugin* plugin;
guac_socket* io = guac_socket_open(client_fd);
/* Reference to dlopen()'d plugin */
void* client_plugin_handle;
/* Client args description */
const char** client_args;
/* Pluggable client */ /* Pluggable client */
char protocol_lib[256] = "libguac-client-"; char protocol_lib[256] = "libguac-client-";
@ -140,179 +127,100 @@ guac_client* guac_get_client(int client_fd, int usec_timeout) {
void* obj; void* obj;
} alias; } 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 */ /* Load client plugin */
client->__client_plugin_handle = dlopen(protocol_lib, RTLD_LAZY); client_plugin_handle = dlopen(protocol_lib, RTLD_LAZY);
if (!(client->__client_plugin_handle)) { if (!client_plugin_handle) {
guac_client_log_error(client, "Could not open client plugin for protocol \"%s\": %s\n", protocol, dlerror()); guac_error = GUAC_STATUS_BAD_ARGUMENT;
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; return NULL;
} }
dlerror(); /* Clear errors */ dlerror(); /* Clear errors */
/* Get init function */ /* Get init function */
alias.obj = dlsym(client->__client_plugin_handle, "guac_client_init"); alias.obj = dlsym(client_plugin_handle, "guac_client_init");
if ((error = dlerror()) != NULL) { /* Fail if cannot find guac_client_init */
guac_client_log_error(client, "Could not get guac_client_init in plugin: %s\n", error); if (dlerror() != NULL) {
guac_protocol_send_error(io, "Invalid server-side client plugin."); guac_error = GUAC_STATUS_BAD_ARGUMENT;
guac_socket_flush(io);
guac_socket_close(io);
guac_instruction_free(instruction);
return NULL; return NULL;
} }
/* Get usage strig */ /* Get usage strig */
client_args = (const char**) dlsym(client->__client_plugin_handle, "GUAC_CLIENT_ARGS"); client_args = (const char**) dlsym(client_plugin_handle, "GUAC_CLIENT_ARGS");
if ((error = dlerror()) != NULL) { /* Fail if cannot find GUAC_CLIENT_ARGS */
guac_client_log_error(client, "Could not get GUAC_CLIENT_ARGS in plugin: %s\n", error); if (dlerror() != NULL) {
guac_protocol_send_error(io, "Invalid server-side client plugin."); guac_error = GUAC_STATUS_BAD_ARGUMENT;
guac_socket_flush(io);
guac_socket_close(io);
guac_instruction_free(instruction);
return NULL; return NULL;
} }
if ( /* Send args */ /* Allocate plugin */
guac_protocol_send_args(io, client_args) plugin = malloc(sizeof(guac_client_plugin));
|| guac_socket_flush(io) if (plugin == NULL) {
) { guac_error = GUAC_STATUS_NO_MEMORY;
guac_socket_close(io);
guac_instruction_free(instruction);
return NULL; return NULL;
} }
guac_instruction_free(instruction); /* Init and return plugin */
break; plugin->__client_plugin_handle = client_plugin_handle;
plugin->init_handler = alias.client_init;
} /* end if select */ plugin->args = client_args;
return plugin;
guac_instruction_free(instruction);
}
} }
/* Wait for connect instruction */ int guac_client_plugin_close(guac_client_plugin* plugin) {
for (;;) {
int result; /* Unload client plugin */
if (dlclose(plugin->__client_plugin_handle)) {
guac_error = GUAC_STATUS_BAD_STATE;
return -1;
}
/* Wait for data until timeout */ return 0;
result = guac_protocol_instructions_waiting(io, usec_timeout);
if (result == 0) { }
guac_protocol_send_error(io, "Connect timeout.");
guac_socket_close(io); 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; return NULL;
} }
/* If error occurs while waiting, exit with failure */ /* Init new client */
if (result < 0) { memset(client, 0, sizeof(guac_client));
guac_socket_close(io);
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 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; return client;
} /* end if connect */
guac_instruction_free(instruction);
}
}
} }
void guac_client_free(guac_client* client) { void guac_client_free(guac_client* client) {
if (client->free_handler) { 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 */ /* Free all layers */