GUACAMOLE-1115: Do not hold general RDP message lock while waiting for print operations.

Holding the message lock will block handling of things like mouse and
keyboard events, as the message lock must be acquired before sending the
corresponding messages to the RDP server. If mouse and keyboard events
are blocked, then handling of further Guacamole instructions like "ack"
is also blocked. If the print job is blocked until an "ack" is received,
this results in deadlock.
This commit is contained in:
Michael Jumper 2022-03-17 17:32:37 +00:00
parent 75a11b05b2
commit d734bac590

View File

@ -18,6 +18,7 @@
*/ */
#include "print-job.h" #include "print-job.h"
#include "rdp.h"
#include <guacamole/client.h> #include <guacamole/client.h>
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
@ -603,6 +604,9 @@ static void guac_rdp_print_job_read_filename(guac_rdp_print_job* job,
int guac_rdp_print_job_write(guac_rdp_print_job* job, int guac_rdp_print_job_write(guac_rdp_print_job* job,
void* buffer, int length) { void* buffer, int length) {
guac_client* client = job->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* Create print job, if not yet created */ /* Create print job, if not yet created */
if (job->bytes_received == 0) { if (job->bytes_received == 0) {
@ -618,19 +622,40 @@ int guac_rdp_print_job_write(guac_rdp_print_job* job,
/* Update counter of bytes received */ /* Update counter of bytes received */
job->bytes_received += length; job->bytes_received += length;
/* Write data to filter process */ /* Write data to filter process, unblocking any threads waiting on the
return write(job->input_fd, buffer, length); * generic RDP message lock as this may be a lengthy operation that depends
* on other threads sending outstanding messages (resulting in deadlock if
* those messages are blocked) */
int unlock_status = pthread_mutex_unlock(&(rdp_client->message_lock));
int write_status = write(job->input_fd, buffer, length);
/* Restore RDP message lock state */
if (!unlock_status)
pthread_mutex_lock(&(rdp_client->message_lock));
return write_status;
} }
void guac_rdp_print_job_free(guac_rdp_print_job* job) { void guac_rdp_print_job_free(guac_rdp_print_job* job) {
guac_client* client = job->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
/* No more input will be provided */ /* No more input will be provided */
close(job->input_fd); close(job->input_fd);
/* Wait for job to terminate */ /* Wait for job to terminate, unblocking any threads waiting on the generic
* RDP message lock as this may be a lengthy operation that depends on
* other threads sending outstanding messages (resulting in deadlock if
* those messages are blocked) */
int unlock_status = pthread_mutex_unlock(&(rdp_client->message_lock));
pthread_join(job->output_thread, NULL); pthread_join(job->output_thread, NULL);
/* Restore RDP message lock state */
if (!unlock_status)
pthread_mutex_lock(&(rdp_client->message_lock));
/* Destroy lock */ /* Destroy lock */
pthread_mutex_destroy(&(job->state_lock)); pthread_mutex_destroy(&(job->state_lock));