Commit 4370fe9f for guacamole.apache.org
commit 4370fe9fd29cd5492068e34087d53f5764d23163
Author: Bradley Bennett <bbennett@keepersecurity.com>
Date: Fri Mar 6 15:58:47 2026 -0500
GUACAMOLE-2238: Multiple guacamole-server code paths do not correctly retry on EINTR.
diff --git a/src/common/io.c b/src/common/io.c
index 2e2a5ad7..a81a131c 100644
--- a/src/common/io.c
+++ b/src/common/io.c
@@ -20,6 +20,9 @@
#include "config.h"
#include "common/io.h"
+#include <guacamole/error.h>
+
+#include <errno.h>
#include <unistd.h>
int guac_common_write(int fd, void* buffer, int length) {
@@ -29,7 +32,8 @@ int guac_common_write(int fd, void* buffer, int length) {
while (length > 0) {
/* Attempt write */
- int bytes_written = write(fd, bytes, length);
+ int bytes_written;
+ GUAC_RETRY_EINTR(bytes_written, write(fd, bytes, length));
if (bytes_written < 0)
return bytes_written;
@@ -51,7 +55,8 @@ int guac_common_read(int fd, void* buffer, int length) {
while (length > 0) {
/* Attempt read */
- int bytes_read = read(fd, bytes, length);
+ int bytes_read;
+ GUAC_RETRY_EINTR(bytes_read, read(fd, bytes, length));
if (bytes_read < 0)
return bytes_read;
diff --git a/src/guacd/conf-file.c b/src/guacd/conf-file.c
index ed74dcbc..874a39ba 100644
--- a/src/guacd/conf-file.c
+++ b/src/guacd/conf-file.c
@@ -24,6 +24,7 @@
#include "conf-parse.h"
#include <guacamole/client.h>
+#include <guacamole/error.h>
#include <guacamole/mem.h>
#include <guacamole/string.h>
@@ -134,7 +135,11 @@ int guacd_conf_parse_file(guacd_config* conf, int fd) {
int parsed = 0;
/* Attempt to fill remaining space in buffer */
- while ((chars_read = read(fd, buffer + length, sizeof(buffer) - length)) > 0) {
+ while (1) {
+ GUAC_RETRY_EINTR(chars_read, read(fd, buffer + length, sizeof(buffer) - length));
+
+ if (chars_read <= 0)
+ break;
length += chars_read;
diff --git a/src/guacd/connection.c b/src/guacd/connection.c
index 2c8c00d6..c07d6643 100644
--- a/src/guacd/connection.c
+++ b/src/guacd/connection.c
@@ -71,7 +71,8 @@ static int __write_all(int fd, char* buffer, int length) {
int remaining_length = length;
while (remaining_length > 0) {
- int written = write(fd, buffer, remaining_length);
+ int written;
+ GUAC_RETRY_EINTR(written, write(fd, buffer, remaining_length));
if (written < 0)
return -1;
@@ -140,7 +141,12 @@ void* guacd_connection_io_thread(void* data) {
pthread_create(&write_thread, NULL, guacd_connection_write_thread, params);
/* Transfer data from file descriptor to socket */
- while ((length = read(params->fd, buffer, sizeof(buffer))) > 0) {
+ while (1) {
+ GUAC_RETRY_EINTR(length, read(params->fd, buffer, sizeof(buffer)));
+
+ if (length <= 0)
+ break;
+
if (guac_socket_write(params->socket, buffer, length))
break;
guac_socket_flush(params->socket);
@@ -329,7 +335,8 @@ static int guacd_route_connection(guacd_proc_map* map, guac_socket* socket) {
guacd_proc_map_add(map, proc);
/* Wait for child to finish */
- waitpid(proc->pid, NULL, 0);
+ pid_t wait_result;
+ GUAC_RETRY_EINTR(wait_result, waitpid(proc->pid, NULL, 0));
/* Remove client */
if (guacd_proc_map_remove(map, proc->client->connection_id) == NULL)
diff --git a/src/guacd/daemon.c b/src/guacd/daemon.c
index 251d348f..b374eb9b 100644
--- a/src/guacd/daemon.c
+++ b/src/guacd/daemon.c
@@ -506,7 +506,8 @@ int main(int argc, char* argv[]) {
"Child processes may pile up in the process table.");
}
- /* Clean up and exit if SIGINT or SIGTERM signals are caught */
+ /* Clean up and exit if SIGINT or SIGTERM signals are caught; don't set
+ SA_RESTART as we rely on accept() to return EINTR.*/
struct sigaction signal_stop_action = { .sa_handler = signal_stop_handler };
sigaction(SIGINT, &signal_stop_action, NULL);
sigaction(SIGTERM, &signal_stop_action, NULL);
diff --git a/src/guacd/move-fd.c b/src/guacd/move-fd.c
index 74c98e5f..3d155797 100644
--- a/src/guacd/move-fd.c
+++ b/src/guacd/move-fd.c
@@ -31,6 +31,8 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <guacamole/error.h>
+
int guacd_send_fd(int sock, int fd) {
struct msghdr message = {0};
@@ -58,7 +60,10 @@ int guacd_send_fd(int sock, int fd) {
memcpy(CMSG_DATA(control), &fd, sizeof(fd));
/* Send file descriptor */
- return (sendmsg(sock, &message, 0) == sizeof(message_data));
+ ssize_t result;
+ GUAC_RETRY_EINTR(result, sendmsg(sock, &message, 0));
+
+ return (result == sizeof(message_data));
}
@@ -82,7 +87,10 @@ int guacd_recv_fd(int sock) {
message.msg_controllen = sizeof(buffer);
/* Receive file descriptor */
- if (recvmsg(sock, &message, 0) == sizeof(message_data)) {
+ ssize_t result;
+ GUAC_RETRY_EINTR(result, recvmsg(sock, &message, 0));
+
+ if (result == sizeof(message_data)) {
/* Validate payload */
if (message_data[0] != 'G') {
diff --git a/src/guacd/proc.c b/src/guacd/proc.c
index 2c387128..4040a3be 100644
--- a/src/guacd/proc.c
+++ b/src/guacd/proc.c
@@ -357,7 +357,11 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) {
guacd_proc_self = proc;
/* Clean up and exit if SIGINT or SIGTERM signals are caught */
- struct sigaction signal_stop_action = { .sa_handler = signal_stop_handler };
+ struct sigaction signal_stop_action = {
+ .sa_handler = signal_stop_handler,
+ /* Restart system calls interrupted by signal delivery */
+ .sa_flags = SA_RESTART
+ };
sigaction(SIGINT, &signal_stop_action, NULL);
sigaction(SIGTERM, &signal_stop_action, NULL);
@@ -393,7 +397,12 @@ cleanup_client:
/* Verify whether children were all properly reaped */
pid_t child_pid;
- while ((child_pid = waitpid(0, NULL, WNOHANG)) > 0) {
+ while (1) {
+ GUAC_RETRY_EINTR(child_pid, waitpid(0, NULL, WNOHANG));
+
+ if (child_pid <= 0)
+ break;
+
guacd_log(GUAC_LOG_DEBUG, "Automatically reaped unreaped "
"(zombie) child process with PID %i.", child_pid);
}
@@ -503,7 +512,12 @@ static void guacd_proc_kill(guacd_proc* proc) {
/* Wait for all processes within process group to terminate */
pid_t child_pid;
- while ((child_pid = waitpid(-proc->pid, NULL, 0)) > 0 || errno == EINTR) {
+ while (1) {
+ GUAC_RETRY_EINTR(child_pid, waitpid(-proc->pid, NULL, 0));
+
+ if (child_pid <= 0)
+ break;
+
guacd_log(GUAC_LOG_DEBUG, "Child process %i of connection \"%s\" has terminated",
child_pid, proc->client->connection_id);
}
diff --git a/src/libguac/guacamole/error.h b/src/libguac/guacamole/error.h
index 5fdf558a..0384569f 100644
--- a/src/libguac/guacamole/error.h
+++ b/src/libguac/guacamole/error.h
@@ -21,43 +21,61 @@
#define _GUAC_ERROR_H
/**
- * Provides functions and structures required for handling return values and
- * errors.
+ * Provides functions, structures, and macros required for handling return
+ * values and errors.
*
* @file error.h
*/
#include "error-types.h"
-/**
- * Returns a human-readable explanation of the status code given.
- */
-const char* guac_status_string(guac_status status);
+#include <errno.h>
/**
- * Returns the status code associated with the error which occurred during the
- * last function call. This value will only be set by functions documented to
- * use it (most libguac functions), and is undefined if no error occurred.
+ * Executes the given expression and retries while the given retry condition
+ * evaluates to non-zero.
*
- * The storage of this value is thread-local. Assignment of a status code to
- * guac_error in one thread will not affect its value in another thread.
+ * This can be used to retry operations that need custom retry logic.
+ *
+ * @param retval
+ * The variable that should receive the result of each evaluation of the
+ * expression.
+ *
+ * @param expression
+ * The expression to execute.
+ *
+ * @param retry_condition
+ * The condition that determines whether the expression should be retried.
*/
-#define guac_error (*__guac_error())
-
-guac_status* __guac_error();
+#define GUAC_RETRY_UNTIL(retval, expression, retry_condition) \
+ do { \
+ do { \
+ (retval) = (expression); \
+ } while (retry_condition); \
+ } while (0)
/**
- * Returns a message describing the error which occurred during the last
- * function call. If an error occurred, but no message is associated with it,
- * NULL is returned. This value is undefined if no error occurred.
+ * Executes the given expression and retries if it returns a negative value
+ * and errno is set to EINTR.
*
- * The storage of this value is thread-local. Assignment of a message to
- * guac_error_message in one thread will not affect its value in another
- * thread.
+ * This is necessary for system calls and similar functions that may be
+ * interrupted by signal delivery before completion. In such cases, the call
+ * can fail with EINTR even though no real error has occurred and the
+ * operation should simply be retried.
+ *
+ * This can be used for blocking system calls and similar operations that
+ * should continue waiting or processing after signal delivery.
+ *
+ * @param retval
+ * The variable that should receive the result of each evaluation of the
+ * expression.
+ *
+ * @param expression
+ * The expression to execute.
*/
-#define guac_error_message (*__guac_error_message())
-
-const char** __guac_error_message();
+#define GUAC_RETRY_EINTR(retval, expression) \
+ GUAC_RETRY_UNTIL((retval), (expression), \
+ (retval) < 0 && errno == EINTR)
/**
* Returns a human-readable explanation of the status code given.
diff --git a/src/libguac/socket-fd.c b/src/libguac/socket-fd.c
index 5c1b463f..b8bb04ef 100644
--- a/src/libguac/socket-fd.c
+++ b/src/libguac/socket-fd.c
@@ -18,12 +18,13 @@
*/
#include "config.h"
+#include "wait-fd.h"
#include "guacamole/mem.h"
#include "guacamole/error.h"
#include "guacamole/socket.h"
-#include "wait-fd.h"
+#include <errno.h>
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
@@ -106,7 +107,7 @@ ssize_t guac_socket_fd_write(guac_socket* socket,
retval = send(data->fd, buffer, count, 0);
#else
/* Use write() for all other platforms */
- retval = write(data->fd, buffer, count);
+ GUAC_RETRY_EINTR(retval, write(data->fd, buffer, count));
#endif
/* Record errors in guac_error */
@@ -154,7 +155,7 @@ static ssize_t guac_socket_fd_read_handler(guac_socket* socket,
retval = recv(data->fd, buf, count, 0);
#else
/* Use read() for all other platforms */
- retval = read(data->fd, buf, count);
+ GUAC_RETRY_EINTR(retval, read(data->fd, buf, count));
#endif
/* Record errors in guac_error */
diff --git a/src/libguac/socket-wsa.c b/src/libguac/socket-wsa.c
index 18e79475..ec17d827 100644
--- a/src/libguac/socket-wsa.c
+++ b/src/libguac/socket-wsa.c
@@ -93,7 +93,9 @@ ssize_t guac_socket_wsa_write(guac_socket* socket,
/* Write until completely written */
while (count > 0) {
- int retval = send(data->sock, buffer, count, 0);
+ int retval;
+ GUAC_RETRY_UNTIL(retval, send(data->sock, buffer, count, 0),
+ retval < 0 && WSAGetLastError() == WSAEINTR);
/* Record errors in guac_error */
if (retval < 0) {
@@ -134,7 +136,9 @@ static ssize_t guac_socket_wsa_read_handler(guac_socket* socket,
guac_socket_wsa_data* data = (guac_socket_wsa_data*) socket->data;
/* Read from socket */
- int retval = recv(data->sock, buf, count, 0);
+ int retval;
+ GUAC_RETRY_UNTIL(retval, recv(data->sock, buf, count, 0),
+ retval < 0 && WSAGetLastError() == WSAEINTR);
/* Record errors in guac_error */
if (retval < 0) {
@@ -327,19 +331,26 @@ static int guac_socket_wsa_select_handler(guac_socket* socket,
struct timeval timeout;
int retval;
- /* Initialize fd_set with single underlying socket handle */
- FD_ZERO(&sockets);
- FD_SET(data->sock, &sockets);
-
/* No timeout if usec_timeout is negative */
- if (usec_timeout < 0)
- retval = select(0, &sockets, NULL, NULL, NULL);
+ if (usec_timeout < 0) {
+ do {
+ /* On retry, fd_set contents are undefined. */
+ FD_ZERO(&sockets);
+ FD_SET(data->sock, &sockets);
+ retval = select(0, &sockets, NULL, NULL, NULL);
+ } while (retval < 0 && WSAGetLastError() == WSAEINTR);
+ }
/* Handle timeout if specified */
else {
- timeout.tv_sec = usec_timeout / 1000000;
- timeout.tv_usec = usec_timeout % 1000000;
- retval = select(0, &sockets, NULL, NULL, &timeout);
+ do {
+ timeout.tv_sec = usec_timeout / 1000000;
+ timeout.tv_usec = usec_timeout % 1000000;
+ /* On retry, fd_set contents are undefined. */
+ FD_ZERO(&sockets);
+ FD_SET(data->sock, &sockets);
+ retval = select(0, &sockets, NULL, NULL, &timeout);
+ } while (retval < 0 && WSAGetLastError() == WSAEINTR);
}
/* Properly set guac_error */
diff --git a/src/libguac/socket.c b/src/libguac/socket.c
index b7a4b77e..2c2cd874 100644
--- a/src/libguac/socket.c
+++ b/src/libguac/socket.c
@@ -18,13 +18,13 @@
*/
#include "config.h"
-
#include "guacamole/mem.h"
#include "guacamole/error.h"
#include "guacamole/protocol.h"
#include "guacamole/socket.h"
#include "guacamole/timestamp.h"
+#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <stddef.h>
@@ -47,11 +47,6 @@ static void* __guac_socket_keep_alive_thread(void* data) {
int old_cancelstate;
- /* Calculate sleep interval */
- struct timespec interval;
- interval.tv_sec = GUAC_SOCKET_KEEP_ALIVE_INTERVAL / 1000;
- interval.tv_nsec = (GUAC_SOCKET_KEEP_ALIVE_INTERVAL % 1000) * 1000000L;
-
/* Socket keep-alive loop */
guac_socket* socket = (guac_socket*) data;
while (socket->state == GUAC_SOCKET_OPEN) {
@@ -68,10 +63,17 @@ static void* __guac_socket_keep_alive_thread(void* data) {
}
+ /* Calculate sleep interval every loop as nanosleep updates it
+ with the remaining time interval */
+ struct timespec interval;
+ interval.tv_sec = GUAC_SOCKET_KEEP_ALIVE_INTERVAL / 1000;
+ interval.tv_nsec = (GUAC_SOCKET_KEEP_ALIVE_INTERVAL % 1000) * 1000000L;
+
/* Sleep until next keep-alive check, but allow thread cancellation
* during that sleep */
+ int sleep_result;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
- nanosleep(&interval, NULL);
+ GUAC_RETRY_EINTR(sleep_result, nanosleep(&interval, &interval));
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
}
diff --git a/src/libguac/tcp.c b/src/libguac/tcp.c
index 79122658..ba797658 100644
--- a/src/libguac/tcp.c
+++ b/src/libguac/tcp.c
@@ -97,20 +97,33 @@ int guac_tcp_connect(const char* hostname, const char* port, const int timeout)
continue;
}
- /* Structure that stores our timeout setting. */
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
/* Connect and wait for timeout */
if ((retval = connect(fd, current_address->ai_addr, current_address->ai_addrlen)) < 0) {
- if (errno == EINPROGRESS) {
- /* Set up timeout. */
+ /* If connect() is in progress (EINPROGRESS) or interrupted by a signal
+ * (EINTR), wait for the socket to become writable and check SO_ERROR. */
+ if (errno == EINPROGRESS || errno == EINTR) {
+
+ /* Prevent overflowing fd_set. */
+ if (fd >= FD_SETSIZE) {
+ guac_error = GUAC_STATUS_INVALID_ARGUMENT;
+ guac_error_message = "File descriptor exceeds FD_SETSIZE.";
+ close(fd);
+ fd = -1;
+ continue;
+ }
+
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
-
- retval = select(fd + 1, NULL, &fdset, NULL, &tv);
+
+ /* Linux (kernel/glibc verified): select() does not modify
+ fd_set on -1, and updates struct timeval to reflect elapsed
+ time on EINTR, so neither needs reinitialization on retry. */
+ GUAC_RETRY_EINTR(retval, select(fd + 1, NULL, &fdset, NULL, &tv));
if (retval > 0) {
int so_error = 0;
@@ -126,14 +139,14 @@ int guac_tcp_connect(const char* hostname, const char* port, const int timeout)
fd = -1;
continue;
}
-
+
/* This indicates that the connection is successful, so we
break so that the socket can be immediately returned. */
if (so_error == 0) {
break;
}
- /* There's a socket error, so we retrieve it and move to
+ /* There's a socket error, so we retrieve it and move to
the next address. */
else {
guac_error = GUAC_STATUS_REFUSED;
@@ -145,7 +158,7 @@ int guac_tcp_connect(const char* hostname, const char* port, const int timeout)
}
}
-
+
else {
guac_error = GUAC_STATUS_REFUSED;
guac_error_message = "Unable to connect via socket.";
diff --git a/src/libguac/timestamp.c b/src/libguac/timestamp.c
index 9020a6e1..ee207f28 100644
--- a/src/libguac/timestamp.c
+++ b/src/libguac/timestamp.c
@@ -19,8 +19,10 @@
#include "config.h"
+#include "guacamole/error.h"
#include "guacamole/timestamp.h"
+#include <errno.h>
#include <sys/time.h>
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_NANOSLEEP)
@@ -66,7 +68,8 @@ void guac_timestamp_msleep(int duration) {
};
/* Sleep for specified interval */
- nanosleep(&sleep_period, NULL);
+ int sleep_result;
+ GUAC_RETRY_EINTR(sleep_result, nanosleep(&sleep_period, &sleep_period));
}
diff --git a/src/libguac/wait-fd.c b/src/libguac/wait-fd.c
index d6079be3..05359951 100644
--- a/src/libguac/wait-fd.c
+++ b/src/libguac/wait-fd.c
@@ -19,6 +19,9 @@
#include "config.h"
+#include "guacamole/error.h"
+#include <errno.h>
+
#ifdef ENABLE_WINSOCK
# include <winsock2.h>
#else
@@ -39,17 +42,29 @@ int guac_wait_for_fd(int fd, int usec_timeout) {
.revents = 0
}};
+ int retval;
+
/* No timeout if usec_timeout is negative */
- if (usec_timeout < 0)
- return poll(fds, 1, -1);
+ if (usec_timeout < 0) {
+ GUAC_RETRY_EINTR(retval, poll(fds, 1, -1));
+ return retval;
+ }
/* Handle timeout if specified, rounding up to poll()'s granularity */
- return poll(fds, 1, (usec_timeout + 999) / 1000);
+ GUAC_RETRY_EINTR(retval, poll(fds, 1, (usec_timeout + 999) / 1000));
+ return retval;
}
#else
int guac_wait_for_fd(int fd, int usec_timeout) {
+ /* Prevent overflowing fd_set. */
+ if (fd >= FD_SETSIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int retval;
fd_set fds;
/* Initialize fd_set with single underlying file descriptor */
@@ -57,8 +72,10 @@ int guac_wait_for_fd(int fd, int usec_timeout) {
FD_SET(fd, &fds);
/* No timeout if usec_timeout is negative */
- if (usec_timeout < 0)
- return select(fd + 1, &fds, NULL, NULL, NULL);
+ if (usec_timeout < 0) {
+ GUAC_RETRY_EINTR(retval, select(fd + 1, &fds, NULL, NULL, NULL));
+ return retval;
+ }
/* Handle timeout if specified */
struct timeval timeout = {
@@ -66,7 +83,11 @@ int guac_wait_for_fd(int fd, int usec_timeout) {
.tv_usec = usec_timeout % 1000000
};
- return select(fd + 1, &fds, NULL, NULL, &timeout);
+ /* Linux (kernel/glibc verified): select() does not modify
+ fd_set on -1, and updates struct timeval to reflect elapsed
+ time on EINTR, so neither needs reinitialization on retry. */
+ GUAC_RETRY_EINTR(retval, select(fd + 1, &fds, NULL, NULL, &timeout));
+ return retval;
}
#endif
diff --git a/src/libguac/wol.c b/src/libguac/wol.c
index 32ddedfd..0b301a3c 100644
--- a/src/libguac/wol.c
+++ b/src/libguac/wol.c
@@ -18,7 +18,6 @@
*/
#include "config.h"
-
#include "guacamole/error.h"
#include "guacamole/tcp.h"
#include "guacamole/timestamp.h"
@@ -300,8 +299,9 @@ static ssize_t __guac_wol_send_packet(const char* broadcast_addr,
}
/* Send the packet and return number of bytes sent. */
- int bytes = sendto(wol_socket, packet, GUAC_WOL_PACKET_SIZE, 0,
- (struct sockaddr*) &wol_dest, wol_dest_size);
+ ssize_t bytes;
+ GUAC_RETRY_EINTR(bytes, sendto(wol_socket, packet, GUAC_WOL_PACKET_SIZE, 0,
+ (struct sockaddr*) &wol_dest, wol_dest_size));
close(wol_socket);
return bytes;
diff --git a/src/protocols/rdp/fs.c b/src/protocols/rdp/fs.c
index 2cafd28a..8e9125e4 100644
--- a/src/protocols/rdp/fs.c
+++ b/src/protocols/rdp/fs.c
@@ -22,6 +22,7 @@
#include "upload.h"
#include <guacamole/client.h>
+#include <guacamole/error.h>
#include <guacamole/mem.h>
#include <guacamole/object.h>
#include <guacamole/pool.h>
@@ -424,7 +425,7 @@ int guac_rdp_fs_read(guac_rdp_fs* fs, int file_id, uint64_t offset,
/* Attempt read */
lseek(file->fd, offset, SEEK_SET);
- bytes_read = read(file->fd, buffer, length);
+ GUAC_RETRY_EINTR(bytes_read, read(file->fd, buffer, length));
/* Translate errno on error */
if (bytes_read < 0)
@@ -448,7 +449,7 @@ int guac_rdp_fs_write(guac_rdp_fs* fs, int file_id, uint64_t offset,
/* Attempt write */
lseek(file->fd, offset, SEEK_SET);
- bytes_written = write(file->fd, buffer, length);
+ GUAC_RETRY_EINTR(bytes_written, write(file->fd, buffer, length));
/* Translate errno on error */
if (bytes_written < 0)
diff --git a/src/protocols/rdp/print-job.c b/src/protocols/rdp/print-job.c
index 2b6ca1aa..eda0275a 100644
--- a/src/protocols/rdp/print-job.c
+++ b/src/protocols/rdp/print-job.c
@@ -21,6 +21,7 @@
#include "rdp.h"
#include <guacamole/client.h>
+#include <guacamole/error.h>
#include <guacamole/mem.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
@@ -399,7 +400,11 @@ static void* guac_rdp_print_job_output_thread(void* data) {
"process...");
/* Read continuously while data remains */
- while ((length = read(job->output_fd, buffer, sizeof(buffer))) > 0) {
+ while (1) {
+ GUAC_RETRY_EINTR(length, read(job->output_fd, buffer, sizeof(buffer)));
+
+ if (length <= 0)
+ break;
/* Wait for client to be ready for blob */
if (guac_rdp_print_job_wait_for_ack(job)) {
@@ -627,7 +632,8 @@ int guac_rdp_print_job_write(guac_rdp_print_job* job,
* 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);
+ int write_status;
+ GUAC_RETRY_EINTR(write_status, write(job->input_fd, buffer, length));
/* Restore RDP message lock state */
if (!unlock_status)
diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c
index 4f99fb97..c988568b 100644
--- a/src/protocols/ssh/ssh.c
+++ b/src/protocols/ssh/ssh.c
@@ -35,6 +35,7 @@
#include <libssh2.h>
#include <libssh2_sftp.h>
#include <guacamole/client.h>
+#include <guacamole/error.h>
#include <guacamole/mem.h>
#include <guacamole/recording.h>
#include <guacamole/socket.h>
@@ -553,8 +554,11 @@ void* ssh_client_thread(void* data) {
.revents = 0,
}};
+ int wait_result;
/* Wait up to computed timeout */
- if (poll(fds, 1, timeout) < 0)
+ GUAC_RETRY_EINTR(wait_result, poll(fds, 1, timeout));
+
+ if (wait_result < 0)
break;
}
diff --git a/src/protocols/telnet/telnet.c b/src/protocols/telnet/telnet.c
index e14c2314..f89a0a34 100644
--- a/src/protocols/telnet/telnet.c
+++ b/src/protocols/telnet/telnet.c
@@ -24,6 +24,7 @@
#include "terminal/terminal.h"
#include <guacamole/client.h>
+#include <guacamole/error.h>
#include <guacamole/mem.h>
#include <guacamole/protocol.h>
#include <guacamole/recording.h>
@@ -75,7 +76,8 @@ static int __guac_telnet_write_all(int fd, const char* buffer, int size) {
while (remaining > 0) {
/* Attempt to write data */
- int ret_val = write(fd, buffer, remaining);
+ int ret_val;
+ GUAC_RETRY_EINTR(ret_val, write(fd, buffer, remaining));
if (ret_val <= 0)
return -1;
@@ -478,8 +480,12 @@ static int __guac_telnet_wait(int socket_fd) {
.revents = 0,
}};
+ int wait_result;
+
/* Wait for one second */
- return poll(fds, 1, 1000);
+ GUAC_RETRY_EINTR(wait_result, poll(fds, 1, 1000));
+
+ return wait_result;
}
@@ -607,7 +613,8 @@ void* guac_telnet_client_thread(void* data) {
if (wait_result == 0)
continue;
- int bytes_read = read(telnet_client->socket_fd, buffer, sizeof(buffer));
+ int bytes_read;
+ GUAC_RETRY_EINTR(bytes_read, read(telnet_client->socket_fd, buffer, sizeof(buffer)));
if (bytes_read <= 0)
break;
diff --git a/src/terminal/common.c b/src/terminal/common.c
index 3b83d4c8..863dda7e 100644
--- a/src/terminal/common.c
+++ b/src/terminal/common.c
@@ -21,7 +21,9 @@
#include "terminal/types.h"
#include <guacamole/assert.h>
+#include <guacamole/error.h>
+#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
@@ -97,7 +99,8 @@ int guac_terminal_write_all(int fd, const char* buffer, int size) {
while (remaining > 0) {
/* Attempt to write data */
- int ret_val = write(fd, buffer, remaining);
+ int ret_val;
+ GUAC_RETRY_EINTR(ret_val, write(fd, buffer, remaining));
if (ret_val <= 0)
return -1;
diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c
index 307e854c..4b2a18df 100644
--- a/src/terminal/terminal.c
+++ b/src/terminal/terminal.c
@@ -641,7 +641,10 @@ int guac_terminal_render_frame(guac_terminal* terminal) {
int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size) {
int stdin_fd = terminal->stdin_pipe_fd[0];
- return read(stdin_fd, c, size);
+ int retval;
+
+ GUAC_RETRY_EINTR(retval, read(stdin_fd, c, size));
+ return retval;
}
void guac_terminal_notify(guac_terminal* terminal) {