Converted to new guac_client_plugin API
This commit is contained in:
parent
5428aef912
commit
ae33a4ca48
@ -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.
|
||||
|
@ -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);
|
||||
client_plugin_handle = dlopen(protocol_lib, RTLD_LAZY);
|
||||
if (!client_plugin_handle) {
|
||||
guac_error = GUAC_STATUS_BAD_ARGUMENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dlerror(); /* Clear errors */
|
||||
|
||||
/* 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) {
|
||||
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);
|
||||
/* 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->__client_plugin_handle, "GUAC_CLIENT_ARGS");
|
||||
client_args = (const char**) dlsym(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);
|
||||
/* Fail if cannot find GUAC_CLIENT_ARGS */
|
||||
if (dlerror() != NULL) {
|
||||
guac_error = GUAC_STATUS_BAD_ARGUMENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( /* Send args */
|
||||
guac_protocol_send_args(io, client_args)
|
||||
|| guac_socket_flush(io)
|
||||
) {
|
||||
guac_socket_close(io);
|
||||
guac_instruction_free(instruction);
|
||||
/* Allocate plugin */
|
||||
plugin = malloc(sizeof(guac_client_plugin));
|
||||
if (plugin == NULL) {
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
guac_instruction_free(instruction);
|
||||
break;
|
||||
|
||||
} /* end if select */
|
||||
|
||||
guac_instruction_free(instruction);
|
||||
}
|
||||
/* Init and return plugin */
|
||||
plugin->__client_plugin_handle = client_plugin_handle;
|
||||
plugin->init_handler = alias.client_init;
|
||||
plugin->args = client_args;
|
||||
return plugin;
|
||||
|
||||
}
|
||||
|
||||
/* Wait for connect instruction */
|
||||
for (;;) {
|
||||
int guac_client_plugin_close(guac_client_plugin* plugin) {
|
||||
|
||||
int result;
|
||||
/* Unload client plugin */
|
||||
if (dlclose(plugin->__client_plugin_handle)) {
|
||||
guac_error = GUAC_STATUS_BAD_STATE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 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 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;
|
||||
}
|
||||
|
||||
/* If error occurs while waiting, exit with failure */
|
||||
if (result < 0) {
|
||||
guac_socket_close(io);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user