GUACAMOLE-559: Merge fix race condition in receipt of clipboard data by terminal.

This commit is contained in:
Nick Couchman 2018-07-31 08:10:47 -04:00
commit 427c4c8b44
14 changed files with 87 additions and 38 deletions

View File

@ -24,6 +24,7 @@
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
#include <guacamole/stream.h> #include <guacamole/stream.h>
#include <guacamole/user.h> #include <guacamole/user.h>
#include <pthread.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -37,6 +38,8 @@ guac_common_clipboard* guac_common_clipboard_alloc(int size) {
clipboard->length = 0; clipboard->length = 0;
clipboard->available = size; clipboard->available = size;
pthread_mutex_init(&(clipboard->lock), NULL);
return clipboard; return clipboard;
} }
@ -108,18 +111,37 @@ static void* __send_user_clipboard(guac_user* user, void* data) {
} }
void guac_common_clipboard_send(guac_common_clipboard* clipboard, guac_client* client) { void guac_common_clipboard_send(guac_common_clipboard* clipboard, guac_client* client) {
pthread_mutex_lock(&(clipboard->lock));
guac_client_log(client, GUAC_LOG_DEBUG, "Broadcasting clipboard to all connected users."); guac_client_log(client, GUAC_LOG_DEBUG, "Broadcasting clipboard to all connected users.");
guac_client_foreach_user(client, __send_user_clipboard, clipboard); guac_client_foreach_user(client, __send_user_clipboard, clipboard);
guac_client_log(client, GUAC_LOG_DEBUG, "Broadcast of clipboard complete."); guac_client_log(client, GUAC_LOG_DEBUG, "Broadcast of clipboard complete.");
pthread_mutex_unlock(&(clipboard->lock));
} }
void guac_common_clipboard_reset(guac_common_clipboard* clipboard, const char* mimetype) { void guac_common_clipboard_reset(guac_common_clipboard* clipboard,
const char* mimetype) {
pthread_mutex_lock(&(clipboard->lock));
/* Clear clipboard contents */
clipboard->length = 0; clipboard->length = 0;
/* Assign given mimetype */
strncpy(clipboard->mimetype, mimetype, sizeof(clipboard->mimetype) - 1); strncpy(clipboard->mimetype, mimetype, sizeof(clipboard->mimetype) - 1);
clipboard->mimetype[sizeof(clipboard->mimetype) - 1] = '\0';
pthread_mutex_unlock(&(clipboard->lock));
} }
void guac_common_clipboard_append(guac_common_clipboard* clipboard, const char* data, int length) { void guac_common_clipboard_append(guac_common_clipboard* clipboard, const char* data, int length) {
pthread_mutex_lock(&(clipboard->lock));
/* Truncate data to available length */ /* Truncate data to available length */
int remaining = clipboard->available - clipboard->length; int remaining = clipboard->available - clipboard->length;
if (remaining < length) if (remaining < length)
@ -131,5 +153,7 @@ void guac_common_clipboard_append(guac_common_clipboard* clipboard, const char*
/* Update length */ /* Update length */
clipboard->length += length; clipboard->length += length;
pthread_mutex_unlock(&(clipboard->lock));
} }

View File

@ -23,6 +23,7 @@
#include "config.h" #include "config.h"
#include <guacamole/client.h> #include <guacamole/client.h>
#include <pthread.h>
/** /**
* The maximum number of bytes to send in an individual blob when * The maximum number of bytes to send in an individual blob when
@ -35,6 +36,13 @@
*/ */
typedef struct guac_common_clipboard { typedef struct guac_common_clipboard {
/**
* Lock which restricts simultaneous access to the clipboard, guaranteeing
* ordered modifications to the clipboard and that changes to the clipboard
* are not allowed while the clipboard is being broadcast to all users.
*/
pthread_mutex_t lock;
/** /**
* The mimetype of the contained clipboard data. * The mimetype of the contained clipboard data.
*/ */

View File

@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include "client.h" #include "client.h"
#include "common/clipboard.h"
#include "common/recording.h" #include "common/recording.h"
#include "common-ssh/sftp.h" #include "common-ssh/sftp.h"
#include "ssh.h" #include "ssh.h"
@ -42,6 +43,9 @@ int guac_client_init(guac_client* client) {
guac_ssh_client* ssh_client = calloc(1, sizeof(guac_ssh_client)); guac_ssh_client* ssh_client = calloc(1, sizeof(guac_ssh_client));
client->data = ssh_client; client->data = ssh_client;
/* Init clipboard */
ssh_client->clipboard = guac_common_clipboard_alloc(GUAC_SSH_CLIPBOARD_MAX_LENGTH);
/* Set handlers */ /* Set handlers */
client->join_handler = guac_ssh_user_join_handler; client->join_handler = guac_ssh_user_join_handler;
client->free_handler = guac_ssh_client_free_handler; client->free_handler = guac_ssh_client_free_handler;
@ -106,6 +110,7 @@ int guac_ssh_client_free_handler(guac_client* client) {
guac_ssh_settings_free(ssh_client->settings); guac_ssh_settings_free(ssh_client->settings);
/* Free client structure */ /* Free client structure */
guac_common_clipboard_free(ssh_client->clipboard);
free(ssh_client); free(ssh_client);
guac_common_ssh_uninit(); guac_common_ssh_uninit();

View File

@ -22,6 +22,11 @@
#include <guacamole/client.h> #include <guacamole/client.h>
/**
* The maximum number of bytes to allow within the clipboard.
*/
#define GUAC_SSH_CLIPBOARD_MAX_LENGTH 262144
/** /**
* Handler which is invoked when the SSH client needs to be disconnected (if * Handler which is invoked when the SSH client needs to be disconnected (if
* connected) and freed. This can happen if initialization fails, or all users * connected) and freed. This can happen if initialization fails, or all users

View File

@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "clipboard.h" #include "clipboard.h"
#include "common/clipboard.h"
#include "ssh.h" #include "ssh.h"
#include "terminal/terminal.h" #include "terminal/terminal.h"
@ -32,7 +33,7 @@ int guac_ssh_clipboard_handler(guac_user* user, guac_stream* stream,
/* Clear clipboard and prepare for new data */ /* Clear clipboard and prepare for new data */
guac_client* client = user->client; guac_client* client = user->client;
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
guac_terminal_clipboard_reset(ssh_client->term, mimetype); guac_common_clipboard_reset(ssh_client->clipboard, mimetype);
/* Set handlers for clipboard stream */ /* Set handlers for clipboard stream */
stream->blob_handler = guac_ssh_clipboard_blob_handler; stream->blob_handler = guac_ssh_clipboard_blob_handler;
@ -47,7 +48,7 @@ int guac_ssh_clipboard_blob_handler(guac_user* user, guac_stream* stream,
/* Append new data */ /* Append new data */
guac_client* client = user->client; guac_client* client = user->client;
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
guac_terminal_clipboard_append(ssh_client->term, data, length); guac_common_clipboard_append(ssh_client->clipboard, data, length);
return 0; return 0;
} }

View File

@ -206,7 +206,7 @@ void* ssh_client_thread(void* data) {
} }
/* Create terminal */ /* Create terminal */
ssh_client->term = guac_terminal_create(client, ssh_client->term = guac_terminal_create(client, ssh_client->clipboard,
settings->font_name, settings->font_size, settings->font_name, settings->font_size,
settings->resolution, settings->width, settings->height, settings->resolution, settings->width, settings->height,
settings->color_scheme, settings->backspace); settings->color_scheme, settings->backspace);

View File

@ -22,6 +22,7 @@
#include "config.h" #include "config.h"
#include "common/clipboard.h"
#include "common/recording.h" #include "common/recording.h"
#include "common-ssh/sftp.h" #include "common-ssh/sftp.h"
#include "common-ssh/ssh.h" #include "common-ssh/ssh.h"
@ -89,6 +90,11 @@ typedef struct guac_ssh_client {
*/ */
pthread_mutex_t term_channel_lock; pthread_mutex_t term_channel_lock;
/**
* The current clipboard contents.
*/
guac_common_clipboard* clipboard;
/** /**
* The terminal which will render all output from the SSH client. * The terminal which will render all output from the SSH client.
*/ */

View File

@ -42,6 +42,9 @@ int guac_client_init(guac_client* client) {
guac_telnet_client* telnet_client = calloc(1, sizeof(guac_telnet_client)); guac_telnet_client* telnet_client = calloc(1, sizeof(guac_telnet_client));
client->data = telnet_client; client->data = telnet_client;
/* Init clipboard */
telnet_client->clipboard = guac_common_clipboard_alloc(GUAC_TELNET_CLIPBOARD_MAX_LENGTH);
/* Init telnet client */ /* Init telnet client */
telnet_client->socket_fd = -1; telnet_client->socket_fd = -1;
telnet_client->naws_enabled = 0; telnet_client->naws_enabled = 0;
@ -89,6 +92,7 @@ int guac_telnet_client_free_handler(guac_client* client) {
if (telnet_client->settings != NULL) if (telnet_client->settings != NULL)
guac_telnet_settings_free(telnet_client->settings); guac_telnet_settings_free(telnet_client->settings);
guac_common_clipboard_free(telnet_client->clipboard);
free(telnet_client); free(telnet_client);
return 0; return 0;

View File

@ -29,6 +29,11 @@
#include <libtelnet.h> #include <libtelnet.h>
/**
* The maximum number of bytes to allow within the clipboard.
*/
#define GUAC_TELNET_CLIPBOARD_MAX_LENGTH 262144
/** /**
* Free handler. Required by libguac and called when the guac_client is * Free handler. Required by libguac and called when the guac_client is
* disconnected and must be cleaned up. * disconnected and must be cleaned up.

View File

@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "clipboard.h" #include "clipboard.h"
#include "common/clipboard.h"
#include "telnet.h" #include "telnet.h"
#include "terminal/terminal.h" #include "terminal/terminal.h"
@ -32,7 +33,7 @@ int guac_telnet_clipboard_handler(guac_user* user, guac_stream* stream,
/* Clear clipboard and prepare for new data */ /* Clear clipboard and prepare for new data */
guac_client* client = user->client; guac_client* client = user->client;
guac_telnet_client* telnet_client = (guac_telnet_client*) client->data; guac_telnet_client* telnet_client = (guac_telnet_client*) client->data;
guac_terminal_clipboard_reset(telnet_client->term, mimetype); guac_common_clipboard_reset(telnet_client->clipboard, mimetype);
/* Set handlers for clipboard stream */ /* Set handlers for clipboard stream */
stream->blob_handler = guac_telnet_clipboard_blob_handler; stream->blob_handler = guac_telnet_clipboard_blob_handler;
@ -47,7 +48,7 @@ int guac_telnet_clipboard_blob_handler(guac_user* user, guac_stream* stream,
/* Append new data */ /* Append new data */
guac_client* client = user->client; guac_client* client = user->client;
guac_telnet_client* telnet_client = (guac_telnet_client*) client->data; guac_telnet_client* telnet_client = (guac_telnet_client*) client->data;
guac_terminal_clipboard_append(telnet_client->term, data, length); guac_common_clipboard_append(telnet_client->clipboard, data, length);
return 0; return 0;
} }

View File

@ -478,6 +478,7 @@ void* guac_telnet_client_thread(void* data) {
/* Create terminal */ /* Create terminal */
telnet_client->term = guac_terminal_create(client, telnet_client->term = guac_terminal_create(client,
telnet_client->clipboard,
settings->font_name, settings->font_size, settings->font_name, settings->font_size,
settings->resolution, settings->width, settings->height, settings->resolution, settings->width, settings->height,
settings->color_scheme, settings->backspace); settings->color_scheme, settings->backspace);

View File

@ -21,6 +21,7 @@
#define GUAC_TELNET_H #define GUAC_TELNET_H
#include "config.h" #include "config.h"
#include "common/clipboard.h"
#include "common/recording.h" #include "common/recording.h"
#include "settings.h" #include "settings.h"
#include "terminal/terminal.h" #include "terminal/terminal.h"
@ -66,6 +67,11 @@ typedef struct guac_telnet_client {
*/ */
int echo_enabled; int echo_enabled;
/**
* The current clipboard contents.
*/
guac_common_clipboard* clipboard;
/** /**
* The terminal which will render all output from the telnet client. * The terminal which will render all output from the telnet client.
*/ */

View File

@ -502,6 +502,7 @@ static void guac_terminal_parse_color_scheme(guac_client* client,
} }
guac_terminal* guac_terminal_create(guac_client* client, guac_terminal* guac_terminal_create(guac_client* client,
guac_common_clipboard* clipboard,
const char* font_name, int font_size, int dpi, const char* font_name, int font_size, int dpi,
int width, int height, const char* color_scheme, int width, int height, const char* color_scheme,
const int backspace) { const int backspace) {
@ -582,6 +583,7 @@ guac_terminal* guac_terminal_create(guac_client* client,
/* Init terminal state */ /* Init terminal state */
term->current_attributes = default_char.attributes; term->current_attributes = default_char.attributes;
term->default_char = default_char; term->default_char = default_char;
term->clipboard = clipboard;
/* Set pixel size */ /* Set pixel size */
term->width = width; term->width = width;
@ -632,9 +634,6 @@ guac_terminal* guac_terminal_create(guac_client* client,
term->current_cursor = GUAC_TERMINAL_CURSOR_BLANK; term->current_cursor = GUAC_TERMINAL_CURSOR_BLANK;
guac_common_cursor_set_blank(term->cursor); guac_common_cursor_set_blank(term->cursor);
/* Allocate clipboard */
term->clipboard = guac_common_clipboard_alloc(GUAC_TERMINAL_CLIPBOARD_MAX_LENGTH);
/* Start terminal thread */ /* Start terminal thread */
if (pthread_create(&(term->thread), NULL, if (pthread_create(&(term->thread), NULL,
guac_terminal_thread, (void*) term)) { guac_terminal_thread, (void*) term)) {
@ -682,9 +681,6 @@ void guac_terminal_free(guac_terminal* term) {
/* Free buffer */ /* Free buffer */
guac_terminal_buffer_free(term->buffer); guac_terminal_buffer_free(term->buffer);
/* Free clipboard */
guac_common_clipboard_free(term->clipboard);
/* Free scrollbar */ /* Free scrollbar */
guac_terminal_scrollbar_free(term->scrollbar); guac_terminal_scrollbar_free(term->scrollbar);
@ -2028,14 +2024,6 @@ void guac_terminal_scroll_handler(guac_terminal_scrollbar* scrollbar, int value)
} }
void guac_terminal_clipboard_reset(guac_terminal* term, const char* mimetype) {
guac_common_clipboard_reset(term->clipboard, mimetype);
}
void guac_terminal_clipboard_append(guac_terminal* term, const void* data, int length) {
guac_common_clipboard_append(term->clipboard, data, length);
}
int guac_terminal_sendf(guac_terminal* term, const char* format, ...) { int guac_terminal_sendf(guac_terminal* term, const char* format, ...) {
int written; int written;

View File

@ -58,11 +58,6 @@
*/ */
#define GUAC_TERMINAL_WHEEL_SCROLL_AMOUNT 3 #define GUAC_TERMINAL_WHEEL_SCROLL_AMOUNT 3
/**
* The maximum number of bytes to allow within the clipboard.
*/
#define GUAC_TERMINAL_CLIPBOARD_MAX_LENGTH 262144
/** /**
* The name of the color scheme having black foreground and white background. * The name of the color scheme having black foreground and white background.
*/ */
@ -443,7 +438,10 @@ struct guac_terminal {
guac_terminal_cursor_type current_cursor; guac_terminal_cursor_type current_cursor;
/** /**
* The current contents of the clipboard. * The current contents of the clipboard. This clipboard instance is
* maintained externally (will not be freed when this terminal is freed)
* and will be updated both internally by the terminal and externally
* through received clipboard instructions.
*/ */
guac_common_clipboard* clipboard; guac_common_clipboard* clipboard;
@ -461,6 +459,13 @@ struct guac_terminal {
* @param client * @param client
* The client to which the terminal will be rendered. * The client to which the terminal will be rendered.
* *
* @param clipboard
* The guac_common_clipboard which will contain the current clipboard
* state. It is expected that this clipboard instance will be updated
* both internally by the terminal and externally through received
* clipboard instructions. This clipboard will not be automatically
* freed when this terminal is freed.
*
* @param font_name * @param font_name
* The name of the font to use when rendering glyphs. * The name of the font to use when rendering glyphs.
* *
@ -493,6 +498,7 @@ struct guac_terminal {
* which renders all text to the given client. * which renders all text to the given client.
*/ */
guac_terminal* guac_terminal_create(guac_client* client, guac_terminal* guac_terminal_create(guac_client* client,
guac_common_clipboard* clipboard,
const char* font_name, int font_size, int dpi, const char* font_name, int font_size, int dpi,
int width, int height, const char* color_scheme, int width, int height, const char* color_scheme,
const int backspace); const int backspace);
@ -592,17 +598,6 @@ int guac_terminal_send_mouse(guac_terminal* term, guac_user* user,
*/ */
void guac_terminal_scroll_handler(guac_terminal_scrollbar* scrollbar, int value); void guac_terminal_scroll_handler(guac_terminal_scrollbar* scrollbar, int value);
/**
* Clears the current clipboard contents and sets the mimetype for future
* contents.
*/
void guac_terminal_clipboard_reset(guac_terminal* term, const char* mimetype);
/**
* Appends the given data to the current clipboard.
*/
void guac_terminal_clipboard_append(guac_terminal* term, const void* data, int length);
/** /**
* Replicates the current display state to a user that has just joined the * Replicates the current display state to a user that has just joined the
* connection. All instructions necessary to replicate state are sent over the * connection. All instructions necessary to replicate state are sent over the